Skip to content

feat: enhance detailing functionality with breadcrumb support #981

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ require (
github.com/qor5/imaging v1.6.4
github.com/qor5/web v1.3.2
github.com/qor5/web/v3 v3.0.12-0.20250322025751-d36834ab80b4
github.com/qor5/x/v3 v3.0.14-0.20250523074001-acfb06785d4a
github.com/qor5/x/v3 v3.0.14-0.20250529021946-7cf4a1807865
github.com/samber/lo v1.47.0
github.com/shurcooL/sanitized_anchor_name v1.0.0
github.com/spf13/cast v1.6.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,8 @@ github.com/qor5/x/v3 v3.0.14-0.20250523072426-119af8528563 h1:mZl3SAXrKzpyNR0xsd
github.com/qor5/x/v3 v3.0.14-0.20250523072426-119af8528563/go.mod h1:SjJJvVZ/j2Xcceflqhzoz2Dy59UhTQ339/u/mgUGl5M=
github.com/qor5/x/v3 v3.0.14-0.20250523074001-acfb06785d4a h1:5uWdj5tI03z37LQaka3c3TTk95ShkavzLTjZsvvV/cE=
github.com/qor5/x/v3 v3.0.14-0.20250523074001-acfb06785d4a/go.mod h1:SjJJvVZ/j2Xcceflqhzoz2Dy59UhTQ339/u/mgUGl5M=
github.com/qor5/x/v3 v3.0.14-0.20250529021946-7cf4a1807865 h1:l8oRD2opecQBvzqTog/+Ce7VE8mjP8Vp5PO1N7lEqYg=
github.com/qor5/x/v3 v3.0.14-0.20250529021946-7cf4a1807865/go.mod h1:SjJJvVZ/j2Xcceflqhzoz2Dy59UhTQ339/u/mgUGl5M=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
Expand Down
26 changes: 13 additions & 13 deletions pagebuilder/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@
}

dp := pm.Detailing(detailList...)
dp.WrapPageFunc(func(in web.PageFunc) web.PageFunc {
return func(ctx *web.EventContext) (r web.PageResponse, err error) {
r, err = in(ctx)
if err != nil {
return
}

Check warning on line 64 in pagebuilder/page.go

View check run for this annotation

Codecov / codecov/patch

pagebuilder/page.go#L63-L64

Added lines #L63 - L64 were not covered by tests
r.Body = h.Div(r.Body).Class("px-6")
return
}
})

dp.Field("Title").ComponentFunc(func(obj interface{}, field *presets.FieldContext, ctx *web.EventContext) h.HTMLComponent {
msgr := i18n.MustGetModuleMessages(ctx.R, I18nPageBuilderKey, Messages_en_US).(*Messages)
var (
Expand All @@ -71,20 +82,9 @@
Color(ColorPrimary).Size(SizeSmall).Class("px-1 mx-1").Attr("style", "height:20px")
}

listingHref := pm.Info().ListingHref()
return h.Div(
VBtn("").Size(SizeXSmall).Icon("mdi-arrow-left").Tile(true).Variant(VariantOutlined).Attr("@click",
fmt.Sprintf(`
const last = vars.__history.last();
if (last && last.url && last.url.startsWith(%q)) {
$event.view.window.history.back();
return;
}
%s`, listingHref, web.GET().URL(listingHref).PushState(true).Go(),
),
),
h.H1("{{vars.pageTitle}}").Class("page-main-title ml-4"),
versionBadge.Class("mt-2 ml-2"),
h.H1("{{vars.pageTitle}}").Class("page-main-title"),
versionBadge.Class("ml-2"),
).Class("d-inline-flex align-center")
})
// register modelBuilder
Expand Down
81 changes: 74 additions & 7 deletions presets/detailing.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
)

type (
DetailingStyle string
DetailingLayout string
DetailingStyle string
DetailingLayout string
DetailingBreadcrumbFunc func(ctx *web.EventContext, obj any, id string) (BreadcrumbItemsFunc, error)
)

const (
Expand All @@ -42,6 +43,7 @@
layouts []DetailingLayout
idCurrentActiveProcessor IdCurrentActiveProcessor
FieldsBuilder
breadcrumbFunc DetailingBreadcrumbFunc
}

type pageTitle interface {
Expand Down Expand Up @@ -99,6 +101,11 @@
return b
}

func (b *DetailingBuilder) WrapPageFunc(w func(in web.PageFunc) web.PageFunc) (r *DetailingBuilder) {
b.pageFunc = w(b.pageFunc)
return b
}

func (b *DetailingBuilder) FetchFunc(v FetchFunc) (r *DetailingBuilder) {
b.fetcher = v
return b
Expand Down Expand Up @@ -133,10 +140,7 @@
}

func (b *DetailingBuilder) GetPageFunc() web.PageFunc {
if b.pageFunc != nil {
return b.pageFunc
}
return b.defaultPageFunc
return b.pageFunc
}

func (b *DetailingBuilder) AppendTabsPanelFunc(v TabComponentFunc) (r *DetailingBuilder) {
Expand Down Expand Up @@ -253,7 +257,13 @@
for i, layout := range b.layouts {
layoutClass[i] = string(layout)
}

if b.breadcrumbFunc != nil {
itemFunc, err := b.breadcrumbFunc(ctx, b.mb.Info(), ctx.Param(ParamID))
if err != nil {
return r, err
}

Check warning on line 264 in presets/detailing.go

View check run for this annotation

Codecov / codecov/patch

presets/detailing.go#L263-L264

Added lines #L263 - L264 were not covered by tests
ctx.WithContextValue(BreadcrumbItemsFuncKey{}, itemFunc)
}
r.Body = VContainer().Children(
notice,
h.Div().Class("d-flex flex-column", strings.Join(layoutClass, ", ")).Children(
Expand Down Expand Up @@ -454,3 +464,60 @@
}
return b
}

func (b *DetailingBuilder) defaultBreadcrumbFunc(ctx *web.EventContext, obj any, id string) (BreadcrumbItemsFunc, error) {
var (
msgr = b.mb.mustGetMessages(ctx.R)
titleComp h.HTMLComponent
title = msgr.DetailingObjectTitle(b.mb.Info().LabelName(ctx, true), getPageTitle(obj, id))
)
if b.titleFunc != nil {
style, ok := ctx.ContextValue(ctxKeyDetailingStyle{}).(DetailingStyle)
if !ok {
style = DetailingStylePage
}
xtitle, xtitleComp, err := b.titleFunc(ctx, obj, style, title)
if err != nil {
return nil, err
}

Check warning on line 482 in presets/detailing.go

View check run for this annotation

Codecov / codecov/patch

presets/detailing.go#L481-L482

Added lines #L481 - L482 were not covered by tests
if xtitleComp != nil {
titleComp = xtitleComp
}

Check warning on line 485 in presets/detailing.go

View check run for this annotation

Codecov / codecov/patch

presets/detailing.go#L484-L485

Added lines #L484 - L485 were not covered by tests
if xtitle != "" {
title = xtitle
}
}
if titleComp == nil {
titleComp = h.Text(title)
}
return func(ctx *web.EventContext, disableLast bool) (r []h.HTMLComponent) {
listingHref := b.mb.Info().ListingHref()
r = []h.HTMLComponent{
VBreadcrumbsItem(h.Text(b.mb.Info().LabelName(ctx, false))).
Href(listingHref).
Attr("@click.stop", web.Plaid().PushState(true).URL(listingHref).Go()),
}
if b.mb.hasDetailing && !b.drawer {
detailingHref := b.mb.Info().DetailingHref(ctx.Param(ParamID))
r = append(r, VBreadcrumbsItem(titleComp).
Href(detailingHref).
Attr("@click.stop", web.Plaid().PushState(true).URL(detailingHref).Go()).
Disabled(disableLast))
}
return r
}, nil
}

func (b *DetailingBuilder) Breadcrumb(f DetailingBreadcrumbFunc) *DetailingBuilder {
b.breadcrumbFunc = f
return b
}

func (b *DetailingBuilder) GetBreadcrumb() DetailingBreadcrumbFunc {
return b.breadcrumbFunc

Check warning on line 517 in presets/detailing.go

View check run for this annotation

Codecov / codecov/patch

presets/detailing.go#L516-L517

Added lines #L516 - L517 were not covered by tests
}

func (b *DetailingBuilder) WrapBreadcrumb(w func(DetailingBreadcrumbFunc) DetailingBreadcrumbFunc) *DetailingBuilder {
b.breadcrumbFunc = w(b.breadcrumbFunc)
return b

Check warning on line 522 in presets/detailing.go

View check run for this annotation

Codecov / codecov/patch

presets/detailing.go#L520-L522

Added lines #L520 - L522 were not covered by tests
}
2 changes: 2 additions & 0 deletions presets/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ func (mb *ModelBuilder) newDetailing() (r *DetailingBuilder) {
if mb.p.dataOperator != nil {
mb.detailing.FetchFunc(mb.p.dataOperator.Fetch)
}
mb.detailing.Breadcrumb(mb.detailing.defaultBreadcrumbFunc)
mb.detailing.PageFunc(mb.detailing.defaultPageFunc)
return
}

Expand Down
22 changes: 22 additions & 0 deletions presets/presets.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ type Builder struct {
}

type AssetFunc func(ctx *web.EventContext)
type (
BreadcrumbItemsFuncKey struct{}
BreadcrumbItemsFunc func(ctx *web.EventContext, disableLast bool) (r []h.HTMLComponent)
)

type extraAsset struct {
path string
Expand Down Expand Up @@ -947,6 +951,10 @@ func (b *Builder) defaultLayout(in web.PageFunc, cfg *LayoutConfig) (out web.Pag
innerPageTitleCompo, ok := ctx.ContextValue(CtxPageTitleComponent).(h.HTMLComponent)
if !ok {
innerPageTitleCompo = VToolbarTitle(innerPr.PageTitle) // Class("text-h6 font-weight-regular"),
breadcrumbFunc, ok := ctx.ContextValue(BreadcrumbItemsFuncKey{}).(BreadcrumbItemsFunc)
if ok {
innerPageTitleCompo = CreateVXBreadcrumbs(ctx, breadcrumbFunc)
}
} else {
ctx.WithContextValue(CtxPageTitleComponent, nil)
}
Expand Down Expand Up @@ -1377,3 +1385,17 @@ func redirectSlashes(next http.Handler) http.Handler {
next.ServeHTTP(w, r)
})
}

func CreateVXBreadcrumbs(ctx *web.EventContext, f BreadcrumbItemsFunc) h.HTMLComponent {
items := f(ctx, true)

joinedItems := make([]h.HTMLComponent, 0, len(items)*2-1)
for i, item := range items {
joinedItems = append(joinedItems, item)
if i < len(items)-1 {
joinedItems = append(joinedItems, VBreadcrumbsDivider(h.Text("»")))
}
}

return vuetifyx.VXBreadcrumbs(joinedItems...).Class("pa-0")
}
1 change: 1 addition & 0 deletions utils/testflow/gentool/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/qor5/web/v3 v3.0.12-0.20250225073451-8e876be98c21 h1:Ho7ZJ04vT96lqe6g7PJhc9eFEUsEgDGpHyPhqzwGF3E=
github.com/qor5/web/v3 v3.0.12-0.20250225073451-8e876be98c21/go.mod h1:32vdHHcZb2JimlcaclW9hLUyimdXjrllZDHTh3rl6d0=
github.com/qor5/web/v3 v3.0.12-0.20250322025751-d36834ab80b4 h1:gv/yjsDE+xa/nKb14a1mABOz2Hv66OHn8xtR3TQsvA8=
github.com/qor5/web/v3 v3.0.12-0.20250322025751-d36834ab80b4/go.mod h1:zU8n7tDAwYgq3HWMpe0dgmywBZaZx3ENBfwmjwEwYPo=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
Expand Down
Loading