123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476 |
- package presets
- import (
- "context"
- "errors"
- "fmt"
- "net/http"
- "net/url"
- "path"
- "strconv"
- "strings"
- "github.com/qor5/admin/presets/actions"
- . "github.com/qor5/ui/vuetify"
- vx "github.com/qor5/ui/vuetifyx"
- "github.com/qor5/web"
- "github.com/qor5/x/i18n"
- "github.com/qor5/x/perm"
- h "github.com/theplant/htmlgo"
- )
- type ListingBuilder struct {
- mb *ModelBuilder
- bulkActions []*BulkActionBuilder
- actions []*ActionBuilder
- actionsAsMenu bool
- rowMenu *RowMenuBuilder
- filterDataFunc FilterDataFunc
- filterTabsFunc FilterTabsFunc
- newBtnFunc ComponentFunc
- pageFunc web.PageFunc
- cellWrapperFunc vx.CellWrapperFunc
- Searcher SearchFunc
- searchColumns []string
- perPage int64
- totalVisible int64
- orderBy string
- orderableFields []*OrderableField
- selectableColumns bool
- conditions []*SQLCondition
- dialogWidth string
- dialogHeight string
- FieldsBuilder
- }
- func (mb *ModelBuilder) Listing(vs ...string) (r *ListingBuilder) {
- r = mb.listing
- if len(vs) == 0 {
- return
- }
- r.Only(vs...)
- return r
- }
- func (b *ListingBuilder) Only(vs ...string) (r *ListingBuilder) {
- r = b
- ivs := make([]interface{}, 0, len(vs))
- for _, v := range vs {
- ivs = append(ivs, v)
- }
- r.FieldsBuilder = *r.FieldsBuilder.Only(ivs...)
- return
- }
- func (b *ListingBuilder) PageFunc(pf web.PageFunc) (r *ListingBuilder) {
- b.pageFunc = pf
- return b
- }
- func (b *ListingBuilder) CellWrapperFunc(cwf vx.CellWrapperFunc) (r *ListingBuilder) {
- b.cellWrapperFunc = cwf
- return b
- }
- func (b *ListingBuilder) SearchFunc(v SearchFunc) (r *ListingBuilder) {
- b.Searcher = v
- return b
- }
- func (b *ListingBuilder) SearchColumns(vs ...string) (r *ListingBuilder) {
- b.searchColumns = vs
- return b
- }
- func (b *ListingBuilder) PerPage(v int64) (r *ListingBuilder) {
- b.perPage = v
- return b
- }
- func (b *ListingBuilder) TotalVisible(v int64) (r *ListingBuilder) {
- b.totalVisible = v
- return b
- }
- func (b *ListingBuilder) OrderBy(v string) (r *ListingBuilder) {
- b.orderBy = v
- return b
- }
- func (b *ListingBuilder) NewButtonFunc(v ComponentFunc) (r *ListingBuilder) {
- b.newBtnFunc = v
- return b
- }
- func (b *ListingBuilder) ActionsAsMenu(v bool) (r *ListingBuilder) {
- b.actionsAsMenu = v
- return b
- }
- type OrderableField struct {
- FieldName string
- DBColumn string
- }
- func (b *ListingBuilder) OrderableFields(v []*OrderableField) (r *ListingBuilder) {
- b.orderableFields = v
- return b
- }
- func (b *ListingBuilder) SelectableColumns(v bool) (r *ListingBuilder) {
- b.selectableColumns = v
- return b
- }
- func (b *ListingBuilder) Conditions(v []*SQLCondition) (r *ListingBuilder) {
- b.conditions = v
- return b
- }
- func (b *ListingBuilder) DialogWidth(v string) (r *ListingBuilder) {
- b.dialogWidth = v
- return b
- }
- func (b *ListingBuilder) DialogHeight(v string) (r *ListingBuilder) {
- b.dialogHeight = v
- return b
- }
- func (b *ListingBuilder) GetPageFunc() web.PageFunc {
- if b.pageFunc != nil {
- return b.pageFunc
- }
- return b.defaultPageFunc
- }
- const bulkPanelOpenParamName = "bulkOpen"
- const actionPanelOpenParamName = "actionOpen"
- const DeleteConfirmPortalName = "deleteConfirm"
- const dataTablePortalName = "dataTable"
- const dataTableAdditionsPortalName = "dataTableAdditions"
- const listingDialogContentPortalName = "listingDialogContentPortal"
- func (b *ListingBuilder) defaultPageFunc(ctx *web.EventContext) (r web.PageResponse, err error) {
- if b.mb.Info().Verifier().Do(PermList).WithReq(ctx.R).IsAllowed() != nil {
- err = perm.PermissionDenied
- return
- }
- msgr := MustGetMessages(ctx.R)
- title := msgr.ListingObjectTitle(i18n.T(ctx.R, ModelsI18nModuleKey, b.mb.label))
- r.PageTitle = title
- r.Body = b.listingComponent(ctx, false)
- return
- }
- func (b *ListingBuilder) listingComponent(
- ctx *web.EventContext,
- inDialog bool,
- ) h.HTMLComponent {
- ctx.R = ctx.R.WithContext(context.WithValue(ctx.R.Context(), ctxInDialog, inDialog))
- msgr := MustGetMessages(ctx.R)
- var tabsAndActionsBar h.HTMLComponent
- {
- filterTabs := b.filterTabs(ctx, inDialog)
- var actionsComponent h.HTMLComponents
- if v := b.actionsComponent(msgr, ctx, inDialog); v != nil {
- actionsComponent = append(actionsComponent, v)
- }
- if b.newBtnFunc != nil {
- if btn := b.newBtnFunc(ctx); btn != nil {
- actionsComponent = append(actionsComponent, b.newBtnFunc(ctx))
- }
- } else {
- disableNewBtn := b.mb.Info().Verifier().Do(PermCreate).WithReq(ctx.R).IsAllowed() != nil
- if !disableNewBtn {
- onclick := web.Plaid().EventFunc(actions.New)
- if inDialog {
- onclick.URL(ctx.R.RequestURI).
- Query(ParamOverlay, actions.Dialog).
- Query(ParamInDialog, true).
- Query(ParamListingQueries, ctx.Queries().Encode())
- }
- actionsComponent = append(actionsComponent, VBtn(msgr.New).
- Color("primary").
- Depressed(true).
- Dark(true).Class("ml-2").
- Disabled(disableNewBtn).
- Attr("@click", onclick.Go()))
- }
- }
- if filterTabs != nil || len(actionsComponent) > 0 {
- tabsAndActionsBar = VToolbar(
- filterTabs,
- VSpacer(),
- actionsComponent,
- ).Flat(true)
- }
- }
- var filterBar h.HTMLComponent
- if b.filterDataFunc != nil {
- fd := b.filterDataFunc(ctx)
- fd.SetByQueryString(ctx.R.URL.RawQuery)
- filterBar = b.filterBar(ctx, msgr, fd, inDialog)
- }
- dataTable, dataTableAdditions := b.getTableComponents(ctx, inDialog)
- var dialogHeaderBar h.HTMLComponent
- if inDialog {
- title := msgr.ListingObjectTitle(i18n.T(ctx.R, ModelsI18nModuleKey, b.mb.label))
- var searchBox h.HTMLComponent
- if b.mb.layoutConfig == nil || !b.mb.layoutConfig.SearchBoxInvisible {
- searchBox = VTextField().
- PrependInnerIcon("search").
- Placeholder(msgr.Search).
- HideDetails(true).
- Value(ctx.R.URL.Query().Get("keyword")).
- Attr("@keyup.enter", web.Plaid().
- URL(ctx.R.RequestURI).
- Query("keyword", web.Var("[$event.target.value]")).
- MergeQuery(true).
- EventFunc(actions.UpdateListingDialog).
- Go()).
- Attr("@click:clear", web.Plaid().
- URL(ctx.R.RequestURI).
- Query("keyword", "").
- MergeQuery(true).
- EventFunc(actions.UpdateListingDialog).
- Go()).
- Class("ma-0 pa-0 mr-6")
- }
- dialogHeaderBar = VAppBar(
- VToolbarTitle("").
- Children(h.Text(title)),
- VSpacer(),
- searchBox,
- VBtn("").Icon(true).
- Children(VIcon("close")).
- Large(true).
- Attr("@click.stop", CloseListingDialogVarScript),
- ).Color("white").Elevation(0).Dense(true)
- }
- return VContainer(
- dialogHeaderBar,
- tabsAndActionsBar,
- h.Div(
- VCard(
- filterBar,
- VDivider(),
- VCardText(
- web.Portal(dataTable).Name(dataTablePortalName),
- ).Class("pa-0"),
- ),
- web.Portal(dataTableAdditions).Name(dataTableAdditionsPortalName),
- ).Class("mt-2"),
- ).Fluid(true).
- Class("white").
- Attr(web.InitContextVars, `{currEditingListItemID: ''}`)
- }
- func (b *ListingBuilder) cellComponentFunc(f *FieldBuilder) vx.CellComponentFunc {
- return func(obj interface{}, fieldName string, ctx *web.EventContext) h.HTMLComponent {
- return f.compFunc(obj, b.mb.getComponentFuncField(f), ctx)
- }
- }
- func getSelectedIds(ctx *web.EventContext) (selected []string) {
- selectedValue := ctx.R.URL.Query().Get(ParamSelectedIds)
- if len(selectedValue) > 0 {
- selected = strings.Split(selectedValue, ",")
- }
- return selected
- }
- func (b *ListingBuilder) bulkPanel(
- bulk *BulkActionBuilder,
- selectedIds []string,
- processedSelectedIds []string,
- ctx *web.EventContext,
- ) (r h.HTMLComponent) {
- msgr := MustGetMessages(ctx.R)
- var errComp h.HTMLComponent
- if vErr, ok := ctx.Flash.(*web.ValidationErrors); ok {
- if gErr := vErr.GetGlobalError(); gErr != "" {
- errComp = VAlert(h.Text(gErr)).
- Border("left").
- Type("error").
- Elevation(2).
- ColoredBorder(true)
- }
- }
- var processSelectedIdsNotice h.HTMLComponent
- if len(processedSelectedIds) < len(selectedIds) {
- unactionables := make([]string, 0, len(selectedIds))
- {
- processedSelectedIdsM := make(map[string]struct{})
- for _, v := range processedSelectedIds {
- processedSelectedIdsM[v] = struct{}{}
- }
- for _, v := range selectedIds {
- if _, ok := processedSelectedIdsM[v]; !ok {
- unactionables = append(unactionables, v)
- }
- }
- }
- if len(unactionables) > 0 {
- var noticeText string
- if bulk.selectedIdsProcessorNoticeFunc != nil {
- noticeText = bulk.selectedIdsProcessorNoticeFunc(selectedIds, processedSelectedIds, unactionables)
- } else {
- var idsText string
- if len(unactionables) <= 10 {
- idsText = strings.Join(unactionables, ", ")
- } else {
- idsText = fmt.Sprintf("%s...(+%d)", strings.Join(unactionables[:10], ", "), len(unactionables)-10)
- }
- noticeText = msgr.BulkActionSelectedIdsProcessNotice(idsText)
- }
- processSelectedIdsNotice = VAlert(h.Text(noticeText)).
- Type("warning")
- }
- }
- onOK := web.Plaid().EventFunc(actions.DoBulkAction).
- Query(ParamBulkActionName, bulk.name).
- MergeQuery(true)
- if isInDialogFromQuery(ctx) {
- onOK.URL(ctx.R.RequestURI)
- }
- return VCard(
- VCardTitle(
- h.Text(bulk.NameLabel.label),
- ),
- VCardText(
- errComp,
- processSelectedIdsNotice,
- bulk.compFunc(selectedIds, ctx),
- ),
- VCardActions(
- VSpacer(),
- VBtn(msgr.Cancel).
- Depressed(true).
- Class("ml-2").
- Attr("@click", closeDialogVarScript),
- VBtn(msgr.OK).
- Color("primary").
- Depressed(true).
- Dark(true).
- Attr("@click", onOK.Go()),
- ),
- )
- }
- func (b *ListingBuilder) actionPanel(action *ActionBuilder, ctx *web.EventContext) (r h.HTMLComponent) {
- msgr := MustGetMessages(ctx.R)
- var errComp h.HTMLComponent
- if vErr, ok := ctx.Flash.(*web.ValidationErrors); ok {
- if gErr := vErr.GetGlobalError(); gErr != "" {
- errComp = VAlert(h.Text(gErr)).
- Border("left").
- Type("error").
- Elevation(2).
- ColoredBorder(true)
- }
- }
- onOK := web.Plaid().EventFunc(actions.DoListingAction).
- Query(ParamListingActionName, action.name).
- MergeQuery(true)
- if isInDialogFromQuery(ctx) {
- onOK.URL(ctx.R.RequestURI)
- }
- var comp h.HTMLComponent
- if action.compFunc != nil {
- comp = action.compFunc("", ctx)
- }
- return VCard(
- VCardTitle(
- h.Text(action.NameLabel.label),
- ),
- VCardText(
- errComp,
- comp,
- ),
- VCardActions(
- VSpacer(),
- VBtn(msgr.Cancel).
- Depressed(true).
- Class("ml-2").
- Attr("@click", closeDialogVarScript),
- VBtn(msgr.OK).
- Color("primary").
- Depressed(true).
- Dark(true).
- Attr("@click", onOK.Go()),
- ),
- )
- }
- func (b *ListingBuilder) deleteConfirmation(ctx *web.EventContext) (r web.EventResponse, err error) {
- msgr := MustGetMessages(ctx.R)
- id := ctx.R.FormValue(ParamID)
- promptID := id
- if v := ctx.R.FormValue("prompt_id"); v != "" {
- promptID = v
- }
- r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{
- Name: DeleteConfirmPortalName,
- Body: VDialog(
- VCard(
- VCardTitle(h.Text(msgr.DeleteConfirmationText(promptID))),
- VCardActions(
- VSpacer(),
- VBtn(msgr.Cancel).
- Depressed(true).
- Class("ml-2").
- On("click", "vars.deleteConfirmation = false"),
- VBtn(msgr.Delete).
- Color("primary").
- Depressed(true).
- Dark(true).
- Attr("@click", web.Plaid().
- EventFunc(actions.DoDelete).
- Queries(ctx.Queries()).
- URL(ctx.R.URL.Path).
- Go()),
- ),
- ),
- ).MaxWidth("600px").
- Attr("v-model", "vars.deleteConfirmation").
- Attr(web.InitContextVars, `{deleteConfirmation: false}`),
- })
- r.VarsScript = "setTimeout(function(){ vars.deleteConfirmation = true }, 100)"
- return
- }
- func (b *ListingBuilder) openActionDialog(ctx *web.EventContext) (r web.EventResponse, err error) {
- actionName := ctx.R.URL.Query().Get(actionPanelOpenParamName)
- action := getAction(b.actions, actionName)
- if action == nil {
- err = errors.New("cannot find requested action")
- return
- }
- b.mb.p.dialog(
- &r,
- b.actionPanel(action, ctx),
- action.dialogWidth,
- )
- return
- }
- func (b *ListingBuilder) openBulkActionDialog(ctx *web.EventContext) (r web.EventResponse, err error) {
- msgr := MustGetMessages(ctx.R)
- selected := getSelectedIds(ctx)
- bulkName := ctx.R.URL.Query().Get(bulkPanelOpenParamName)
- bulk := getBulkAction(b.bulkActions, bulkName)
- if bulk == nil {
- err = errors.New("cannot find requested action")
- return
- }
- if len(selected) == 0 {
- ShowMessage(&r, "Please select record", "warning")
- return
- }
- // If selectedIdsProcessorFunc is not nil, process the request in it and skip the confirmation dialog
- var processedSelectedIds []string
- if bulk.selectedIdsProcessorFunc != nil {
- processedSelectedIds, err = bulk.selectedIdsProcessorFunc(selected, ctx)
- if err != nil {
- return
- }
- if len(processedSelectedIds) == 0 {
- if bulk.selectedIdsProcessorNoticeFunc != nil {
- ShowMessage(&r, bulk.selectedIdsProcessorNoticeFunc(selected, processedSelectedIds, selected), "warning")
- } else {
- ShowMessage(&r, msgr.BulkActionNoAvailableRecords, "warning")
- }
- return
- }
- } else {
- processedSelectedIds = selected
- }
- b.mb.p.dialog(
- &r,
- b.bulkPanel(bulk, selected, processedSelectedIds, ctx),
- bulk.dialogWidth,
- )
- return
- }
- func (b *ListingBuilder) doBulkAction(ctx *web.EventContext) (r web.EventResponse, err error) {
- bulk := getBulkAction(b.bulkActions, ctx.R.FormValue(ParamBulkActionName))
- if bulk == nil {
- panic("bulk required")
- }
- if b.mb.Info().Verifier().SnakeDo(PermBulkActions, bulk.name).WithReq(ctx.R).IsAllowed() != nil {
- ShowMessage(&r, perm.PermissionDenied.Error(), "warning")
- return
- }
- selectedIds := getSelectedIds(ctx)
- var err1 error
- var processedSelectedIds []string
- if bulk.selectedIdsProcessorFunc != nil {
- processedSelectedIds, err1 = bulk.selectedIdsProcessorFunc(selectedIds, ctx)
- } else {
- processedSelectedIds = selectedIds
- }
- if err1 == nil {
- err1 = bulk.updateFunc(processedSelectedIds, ctx)
- }
- if err1 != nil {
- if _, ok := err1.(*web.ValidationErrors); !ok {
- vErr := &web.ValidationErrors{}
- vErr.GlobalError(err1.Error())
- ctx.Flash = vErr
- }
- }
- if ctx.Flash != nil {
- r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{
- Name: dialogContentPortalName,
- Body: b.bulkPanel(bulk, selectedIds, processedSelectedIds, ctx),
- })
- return
- }
- msgr := MustGetMessages(ctx.R)
- ShowMessage(&r, msgr.SuccessfullyUpdated, "")
- if isInDialogFromQuery(ctx) {
- qs := ctx.Queries()
- qs.Del(bulkPanelOpenParamName)
- qs.Del(ParamBulkActionName)
- web.AppendVarsScripts(&r,
- closeDialogVarScript,
- web.Plaid().
- URL(ctx.R.RequestURI).
- EventFunc(actions.UpdateListingDialog).
- Queries(qs).
- Go(),
- )
- } else {
- r.PushState = web.Location(url.Values{bulkPanelOpenParamName: []string{}}).MergeQuery(true)
- }
- return
- }
- func (b *ListingBuilder) doListingAction(ctx *web.EventContext) (r web.EventResponse, err error) {
- action := getAction(b.actions, ctx.R.FormValue(ParamListingActionName))
- if action == nil {
- panic("action required")
- }
- if b.mb.Info().Verifier().SnakeDo(PermDoListingAction, action.name).WithReq(ctx.R).IsAllowed() != nil {
- ShowMessage(&r, perm.PermissionDenied.Error(), "warning")
- return
- }
- if err := action.updateFunc("", ctx); err != nil {
- if _, ok := err.(*web.ValidationErrors); !ok {
- vErr := &web.ValidationErrors{}
- vErr.GlobalError(err.Error())
- ctx.Flash = vErr
- }
- }
- if ctx.Flash != nil {
- r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{
- Name: dialogContentPortalName,
- Body: b.actionPanel(action, ctx),
- })
- return
- }
- msgr := MustGetMessages(ctx.R)
- ShowMessage(&r, msgr.SuccessfullyUpdated, "")
- if isInDialogFromQuery(ctx) {
- qs := ctx.Queries()
- qs.Del(actionPanelOpenParamName)
- qs.Del(ParamListingActionName)
- web.AppendVarsScripts(&r,
- closeDialogVarScript,
- web.Plaid().
- URL(ctx.R.RequestURI).
- EventFunc(actions.UpdateListingDialog).
- Queries(qs).
- Go(),
- )
- } else {
- r.PushState = web.Location(url.Values{actionPanelOpenParamName: []string{}}).MergeQuery(true)
- }
- return
- }
- const ActiveFilterTabQueryKey = "active_filter_tab"
- func (b *ListingBuilder) filterTabs(
- ctx *web.EventContext,
- inDialog bool,
- ) (r h.HTMLComponent) {
- if b.filterTabsFunc == nil {
- return
- }
- qs := ctx.R.URL.Query()
- tabs := VTabs().ShowArrows(true)
- tabsData := b.filterTabsFunc(ctx)
- for i, tab := range tabsData {
- if tab.ID == "" {
- tab.ID = fmt.Sprintf("tab%d", i)
- }
- }
- value := -1
- activeTabValue := qs.Get(ActiveFilterTabQueryKey)
- for i, td := range tabsData {
- // Find selected tab by active_filter_tab=xx in the url query
- if activeTabValue == td.ID {
- value = i
- }
- tabContent := h.Text(td.Label)
- if td.AdvancedLabel != nil {
- tabContent = td.AdvancedLabel
- }
- totalQuery := url.Values{}
- totalQuery.Set(ActiveFilterTabQueryKey, td.ID)
- for k, v := range td.Query {
- totalQuery[k] = v
- }
- onclick := web.Plaid().Queries(totalQuery)
- if inDialog {
- onclick.URL(ctx.R.RequestURI).
- EventFunc(actions.UpdateListingDialog)
- } else {
- onclick.PushState(true)
- }
- tabs.AppendChildren(
- VTab(tabContent).
- Attr("@click", onclick.Go()),
- )
- }
- return tabs.Value(value)
- }
- type selectColumns struct {
- DisplayColumns []string `json:"displayColumns,omitempty"`
- SortedColumns []sortedColumn `json:"sortedColumns,omitempty"`
- }
- type sortedColumn struct {
- Name string `json:"name"`
- Label string `json:"label"`
- }
- func (b *ListingBuilder) selectColumnsBtn(
- pageURL *url.URL,
- ctx *web.EventContext,
- inDialog bool,
- ) (btn h.HTMLComponent, displaySortedFields []*FieldBuilder) {
- var (
- _, respath = path.Split(pageURL.Path)
- displayColumnsName = fmt.Sprintf("%s_display_columns", respath)
- sortedColumnsName = fmt.Sprintf("%s_sorted_columns", respath)
- originalColumns []string
- displayColumns []string
- sortedColumns []string
- )
- for _, f := range b.fields {
- if b.mb.Info().Verifier().Do(PermList).SnakeOn("f_"+f.name).WithReq(ctx.R).IsAllowed() != nil {
- continue
- }
- originalColumns = append(originalColumns, f.name)
- }
- // get the columns setting from url params or cookie data
- if urldata := pageURL.Query().Get(displayColumnsName); urldata != "" {
- if urlColumns := strings.Split(urldata, ","); len(urlColumns) > 0 {
- displayColumns = urlColumns
- }
- }
- if urldata := pageURL.Query().Get(sortedColumnsName); urldata != "" {
- if urlColumns := strings.Split(urldata, ","); len(urlColumns) > 0 {
- sortedColumns = urlColumns
- }
- }
- // get the columns setting from cookie data
- if len(displayColumns) == 0 {
- cookiedata, err := ctx.R.Cookie(displayColumnsName)
- if err == nil {
- if cookieColumns := strings.Split(cookiedata.Value, ","); len(cookieColumns) > 0 {
- displayColumns = cookieColumns
- }
- }
- }
- if len(sortedColumns) == 0 {
- cookiedata, err := ctx.R.Cookie(sortedColumnsName)
- if err == nil {
- if cookieColumns := strings.Split(cookiedata.Value, ","); len(cookieColumns) > 0 {
- sortedColumns = cookieColumns
- }
- }
- }
- // check if listing fileds is changed. if yes, use the original columns
- var originalFiledsChanged bool
- if len(sortedColumns) > 0 && len(originalColumns) != len(sortedColumns) {
- originalFiledsChanged = true
- }
- if len(sortedColumns) > 0 && !originalFiledsChanged {
- for _, sortedColumn := range sortedColumns {
- var find bool
- for _, originalColumn := range originalColumns {
- if sortedColumn == originalColumn {
- find = true
- break
- }
- }
- if !find {
- originalFiledsChanged = true
- break
- }
- }
- }
- if len(displayColumns) > 0 && !originalFiledsChanged {
- for _, displayColumn := range displayColumns {
- var find bool
- for _, originalColumn := range originalColumns {
- if displayColumn == originalColumn {
- find = true
- break
- }
- }
- if !find {
- originalFiledsChanged = true
- break
- }
- }
- }
- // save display columns setting on cookie
- if !originalFiledsChanged && len(displayColumns) > 0 {
- http.SetCookie(ctx.W, &http.Cookie{
- Name: displayColumnsName,
- Value: strings.Join(displayColumns, ","),
- })
- }
- // save sorted columns setting on cookie
- if !originalFiledsChanged && len(sortedColumns) > 0 {
- http.SetCookie(ctx.W, &http.Cookie{
- Name: sortedColumnsName,
- Value: strings.Join(sortedColumns, ","),
- })
- }
- // set the data for displaySortedFields on data table
- if originalFiledsChanged || (len(sortedColumns) == 0 && len(displayColumns) == 0) {
- displaySortedFields = b.fields
- }
- if originalFiledsChanged || len(displayColumns) == 0 {
- displayColumns = originalColumns
- }
- if originalFiledsChanged || len(sortedColumns) == 0 {
- sortedColumns = originalColumns
- }
- if len(displaySortedFields) == 0 {
- for _, sortedColumn := range sortedColumns {
- for _, displayColumn := range displayColumns {
- if sortedColumn == displayColumn {
- displaySortedFields = append(displaySortedFields, b.Field(sortedColumn))
- break
- }
- }
- }
- }
- // set the data for selected columns on toolbar
- selectColumns := selectColumns{
- DisplayColumns: displayColumns,
- }
- for _, sc := range sortedColumns {
- selectColumns.SortedColumns = append(selectColumns.SortedColumns, sortedColumn{
- Name: sc,
- Label: i18n.PT(ctx.R, ModelsI18nModuleKey, b.mb.label, b.mb.getLabel(b.Field(sc).NameLabel)),
- })
- }
- msgr := MustGetMessages(ctx.R)
- onOK := web.Plaid().
- Query(displayColumnsName, web.Var("locals.displayColumns")).
- Query(sortedColumnsName, web.Var("locals.sortedColumns.map(column => column.name )")).
- MergeQuery(true)
- if inDialog {
- onOK.URL(ctx.R.RequestURI).
- EventFunc(actions.UpdateListingDialog)
- }
- // add the HTML component of columns setting into toolbar
- btn = VMenu(
- web.Slot(
- VBtn("").Children(VIcon("settings")).Attr("v-on", "on").Text(true).Fab(true).Small(true),
- ).Name("activator").Scope("{ on }"),
- web.Scope(VList(
- h.Tag("vx-draggable").Attr("v-model", "locals.sortedColumns", "draggable", ".vx_column_item", "animation", "300").Children(
- h.Div(
- VListItem(
- VListItemContent(
- VListItemTitle(
- VSwitch().Dense(true).Attr("v-model", "locals.displayColumns", ":value", "column.name", ":label", "column.label", "@click", "event.preventDefault()"),
- ),
- ),
- VListItemIcon(
- VIcon("reorder"),
- ).Attr("style", "margin-top: 28px"),
- ),
- VDivider(),
- ).Attr("v-for", "(column, index) in locals.sortedColumns", ":key", "column.name", "class", "vx_column_item"),
- ),
- VListItem(
- VListItemAction(VBtn(msgr.Cancel).Elevation(0).Attr("@click", `vars.selectColumnsMenu = false`)),
- VListItemAction(VBtn(msgr.OK).Elevation(0).Color("primary").Attr("@click", `vars.selectColumnsMenu = false;`+onOK.Go()))),
- ).Dense(true)).
- Init(h.JSONString(selectColumns)).
- VSlot("{ locals }"),
- ).OffsetY(true).CloseOnClick(false).CloseOnContentClick(false).
- Attr(web.InitContextVars, `{selectColumnsMenu: false}`).
- Attr("v-model", "vars.selectColumnsMenu")
- return
- }
- func (b *ListingBuilder) filterBar(
- ctx *web.EventContext,
- msgr *Messages,
- fd vx.FilterData,
- inDialog bool,
- ) (filterBar h.HTMLComponent) {
- if fd == nil {
- return nil
- }
- noVisiableItem := true
- for _, d := range fd {
- if !d.Invisible {
- noVisiableItem = false
- break
- }
- }
- if noVisiableItem {
- return nil
- }
- ft := vx.FilterTranslations{}
- ft.Clear = msgr.FiltersClear
- ft.Add = msgr.FiltersAdd
- ft.Apply = msgr.FilterApply
- for _, d := range fd {
- d.Translations = vx.FilterIndependentTranslations{
- FilterBy: msgr.FilterBy(d.Label),
- }
- }
- ft.Date.To = msgr.FiltersDateTo
- ft.Number.And = msgr.FiltersNumberAnd
- ft.Number.Equals = msgr.FiltersNumberEquals
- ft.Number.Between = msgr.FiltersNumberBetween
- ft.Number.GreaterThan = msgr.FiltersNumberGreaterThan
- ft.Number.LessThan = msgr.FiltersNumberLessThan
- ft.String.Equals = msgr.FiltersStringEquals
- ft.String.Contains = msgr.FiltersStringContains
- ft.MultipleSelect.In = msgr.FiltersMultipleSelectIn
- ft.MultipleSelect.NotIn = msgr.FiltersMultipleSelectNotIn
- filter := vx.VXFilter(fd).Translations(ft)
- if inDialog {
- filter.OnChange(web.Plaid().
- URL(ctx.R.RequestURI).
- StringQuery(web.Var("$event.encodedFilterData")).
- ClearMergeQuery(web.Var("$event.filterKeys")).
- EventFunc(actions.UpdateListingDialog).
- Go())
- }
- return VToolbar(
- filter,
- ).Flat(true).AutoHeight(true).Class("py-2")
- }
- func getLocalPerPage(
- ctx *web.EventContext,
- mb *ModelBuilder,
- ) int64 {
- c, err := ctx.R.Cookie("_perPage")
- if err != nil {
- return 0
- }
- vals := strings.Split(c.Value, "$")
- for _, v := range vals {
- vvs := strings.Split(v, "#")
- if len(vvs) != 2 {
- continue
- }
- if vvs[0] == mb.uriName {
- r, _ := strconv.ParseInt(vvs[1], 10, 64)
- return r
- }
- }
- return 0
- }
- func setLocalPerPage(
- ctx *web.EventContext,
- mb *ModelBuilder,
- v int64,
- ) {
- var oldVals []string
- {
- c, err := ctx.R.Cookie("_perPage")
- if err == nil {
- oldVals = strings.Split(c.Value, "$")
- }
- }
- newVals := []string{fmt.Sprintf("%s#%d", mb.uriName, v)}
- for _, v := range oldVals {
- vvs := strings.Split(v, "#")
- if len(vvs) != 2 {
- continue
- }
- if vvs[0] == mb.uriName {
- continue
- }
- newVals = append(newVals, v)
- }
- http.SetCookie(ctx.W, &http.Cookie{
- Name: "_perPage",
- Value: strings.Join(newVals, "$"),
- })
- }
- type ColOrderBy struct {
- FieldName string
- // ASC, DESC
- OrderBy string
- }
- func GetOrderBysFromQuery(query url.Values) []*ColOrderBy {
- r := make([]*ColOrderBy, 0)
- qs := strings.Split(query.Get("order_by"), ",")
- for _, q := range qs {
- ss := strings.Split(q, "_")
- ssl := len(ss)
- if ssl == 1 {
- continue
- }
- if ss[ssl-1] != "ASC" && ss[ssl-1] != "DESC" {
- continue
- }
- r = append(r, &ColOrderBy{
- FieldName: strings.Join(ss[:ssl-1], "_"),
- OrderBy: ss[ssl-1],
- })
- }
- return r
- }
- func newQueryWithFieldToggleOrderBy(query url.Values, fieldName string) url.Values {
- oldOrderBys := GetOrderBysFromQuery(query)
- newOrderBysQueryValue := []string{}
- existed := false
- for _, oob := range oldOrderBys {
- if oob.FieldName == fieldName {
- existed = true
- if oob.OrderBy == "ASC" {
- newOrderBysQueryValue = append(newOrderBysQueryValue, oob.FieldName+"_DESC")
- }
- continue
- }
- newOrderBysQueryValue = append(newOrderBysQueryValue, oob.FieldName+"_"+oob.OrderBy)
- }
- if !existed {
- newOrderBysQueryValue = append(newOrderBysQueryValue, fieldName+"_ASC")
- }
- newQuery := make(url.Values)
- for k, v := range query {
- newQuery[k] = v
- }
- newQuery.Set("order_by", strings.Join(newOrderBysQueryValue, ","))
- return newQuery
- }
- func (b *ListingBuilder) getTableComponents(
- ctx *web.EventContext,
- inDialog bool,
- ) (
- dataTable h.HTMLComponent,
- // pagination, no-record message
- datatableAdditions h.HTMLComponent,
- ) {
- msgr := MustGetMessages(ctx.R)
- qs := ctx.R.URL.Query()
- var requestPerPage int64
- qPerPageStr := qs.Get("per_page")
- qPerPage, _ := strconv.ParseInt(qPerPageStr, 10, 64)
- if qPerPage != 0 {
- setLocalPerPage(ctx, b.mb, qPerPage)
- requestPerPage = qPerPage
- } else if cPerPage := getLocalPerPage(ctx, b.mb); cPerPage != 0 {
- requestPerPage = cPerPage
- }
- perPage := b.perPage
- if requestPerPage != 0 {
- perPage = requestPerPage
- }
- if perPage == 0 {
- perPage = 50
- }
- if perPage > 1000 {
- perPage = 1000
- }
- totalVisible := b.totalVisible
- if totalVisible == 0 {
- totalVisible = 10
- }
- var orderBySQL string
- orderBys := GetOrderBysFromQuery(qs)
- // map[FieldName]DBColumn
- orderableFieldMap := make(map[string]string)
- for _, v := range b.orderableFields {
- orderableFieldMap[v.FieldName] = v.DBColumn
- }
- for _, ob := range orderBys {
- dbCol, ok := orderableFieldMap[ob.FieldName]
- if !ok {
- continue
- }
- orderBySQL += fmt.Sprintf("%s %s,", dbCol, ob.OrderBy)
- }
- if orderBySQL != "" {
- orderBySQL = orderBySQL[:len(orderBySQL)-1]
- }
- if orderBySQL == "" {
- if b.orderBy != "" {
- orderBySQL = b.orderBy
- } else {
- orderBySQL = fmt.Sprintf("%s DESC", b.mb.primaryField)
- }
- }
- searchParams := &SearchParams{
- KeywordColumns: b.searchColumns,
- Keyword: qs.Get("keyword"),
- PerPage: perPage,
- OrderBy: orderBySQL,
- PageURL: ctx.R.URL,
- SQLConditions: b.conditions,
- }
- searchParams.Page, _ = strconv.ParseInt(qs.Get("page"), 10, 64)
- if searchParams.Page == 0 {
- searchParams.Page = 1
- }
- var fd vx.FilterData
- if b.filterDataFunc != nil {
- fd = b.filterDataFunc(ctx)
- cond, args := fd.SetByQueryString(ctx.R.URL.RawQuery)
- searchParams.SQLConditions = append(searchParams.SQLConditions, &SQLCondition{
- Query: cond,
- Args: args,
- })
- }
- if b.Searcher == nil || b.mb.p.dataOperator == nil {
- panic("presets.New().DataOperator(...) required")
- }
- var objs interface{}
- var totalCount int
- var err error
- objs, totalCount, err = b.Searcher(b.mb.NewModelSlice(), searchParams, ctx)
- if err != nil {
- panic(err)
- }
- haveCheckboxes := len(b.bulkActions) > 0
- pagesCount := int(int64(totalCount)/searchParams.PerPage + 1)
- if int64(totalCount)%searchParams.PerPage == 0 {
- pagesCount--
- }
- var cellWraperFunc = func(cell h.MutableAttrHTMLComponent, id string, obj interface{}, dataTableID string) h.HTMLComponent {
- tdbind := cell
- if b.mb.hasDetailing && !b.mb.detailing.drawer {
- tdbind.SetAttr("@click.self", web.Plaid().
- PushStateURL(
- b.mb.Info().
- DetailingHref(id)).
- Go())
- } else {
- event := actions.Edit
- if b.mb.hasDetailing {
- event = actions.DetailingDrawer
- }
- onclick := web.Plaid().
- EventFunc(event).
- Query(ParamID, id)
- if inDialog {
- onclick.URL(ctx.R.RequestURI).
- Query(ParamOverlay, actions.Dialog).
- Query(ParamInDialog, true).
- Query(ParamListingQueries, ctx.Queries().Encode())
- }
- tdbind.SetAttr("@click.self",
- onclick.Go()+fmt.Sprintf(`; vars.currEditingListItemID="%s-%s"`, dataTableID, id))
- }
- return tdbind
- }
- if b.cellWrapperFunc != nil {
- cellWraperFunc = b.cellWrapperFunc
- }
- var displayFields = b.fields
- var selectColumnsBtn h.HTMLComponent
- if b.selectableColumns {
- selectColumnsBtn, displayFields = b.selectColumnsBtn(ctx.R.URL, ctx, inDialog)
- }
- sDataTable := vx.DataTable(objs).
- CellWrapperFunc(cellWraperFunc).
- HeadCellWrapperFunc(func(cell h.MutableAttrHTMLComponent, field string, title string) h.HTMLComponent {
- if _, ok := orderableFieldMap[field]; ok {
- var orderBy string
- var orderByIdx int
- for i, ob := range orderBys {
- if ob.FieldName == field {
- orderBy = ob.OrderBy
- orderByIdx = i + 1
- break
- }
- }
- th := h.Th("").Style("cursor: pointer; white-space: nowrap;").
- Children(
- h.Span(title).
- Style("text-decoration: underline;"),
- h.If(orderBy == "ASC",
- VIcon("arrow_drop_up").Small(true),
- h.Span(fmt.Sprint(orderByIdx)),
- ).ElseIf(orderBy == "DESC",
- VIcon("arrow_drop_down").Small(true),
- h.Span(fmt.Sprint(orderByIdx)),
- ).Else(
- // take up place
- h.Span("").Style("visibility: hidden;").Children(
- VIcon("arrow_drop_down").Small(true),
- h.Span(fmt.Sprint(orderByIdx)),
- ),
- ),
- )
- qs.Del("__execute_event__")
- newQuery := newQueryWithFieldToggleOrderBy(qs, field)
- onclick := web.Plaid().
- Queries(newQuery)
- if inDialog {
- onclick.URL(ctx.R.RequestURI).
- EventFunc(actions.UpdateListingDialog)
- } else {
- onclick.PushState(true)
- }
- th.Attr("@click", onclick.Go())
- cell = th
- }
- return cell
- }).
- RowWrapperFunc(func(row h.MutableAttrHTMLComponent, id string, obj interface{}, dataTableID string) h.HTMLComponent {
- row.SetAttr(":class", fmt.Sprintf(`{"vx-list-item--active primary--text": vars.presetsRightDrawer && vars.currEditingListItemID==="%s-%s"}`, dataTableID, id))
- return row
- }).
- RowMenuItemFuncs(b.RowMenu().listingItemFuncs(ctx)...).
- Selectable(haveCheckboxes).
- SelectionParamName(ParamSelectedIds).
- SelectedCountLabel(msgr.ListingSelectedCountNotice).
- SelectableColumnsBtn(selectColumnsBtn).
- ClearSelectionLabel(msgr.ListingClearSelection)
- if inDialog {
- sDataTable.OnSelectAllFunc(func(idsOfPage []string, ctx *web.EventContext) string {
- return web.Plaid().
- URL(ctx.R.RequestURI).
- EventFunc(actions.UpdateListingDialog).
- Query(ParamSelectedIds,
- web.Var(fmt.Sprintf(`{value: %s, add: $event, remove: !$event}`, h.JSONString(idsOfPage))),
- ).
- MergeQuery(true).
- Go()
- })
- sDataTable.OnSelectFunc(func(id string, ctx *web.EventContext) string {
- return web.Plaid().
- URL(ctx.R.RequestURI).
- EventFunc(actions.UpdateListingDialog).
- Query(ParamSelectedIds,
- web.Var(fmt.Sprintf(`{value: %s, add: $event, remove: !$event}`, h.JSONString(id))),
- ).
- MergeQuery(true).
- Go()
- })
- sDataTable.OnClearSelectionFunc(func(ctx *web.EventContext) string {
- return web.Plaid().
- URL(ctx.R.RequestURI).
- EventFunc(actions.UpdateListingDialog).
- Query(ParamSelectedIds, "").
- MergeQuery(true).
- Go()
- })
- }
- dataTable = sDataTable
- for _, f := range displayFields {
- if b.mb.Info().Verifier().Do(PermList).SnakeOn("f_"+f.name).WithReq(ctx.R).IsAllowed() != nil {
- continue
- }
- f = b.getFieldOrDefault(f.name) // fill in empty compFunc and setter func with default
- dataTable.(*vx.DataTableBuilder).Column(f.name).
- Title(i18n.PT(ctx.R, ModelsI18nModuleKey, b.mb.label, b.mb.getLabel(f.NameLabel))).
- CellComponentFunc(b.cellComponentFunc(f))
- }
- if totalCount > 0 {
- tpb := vx.VXTablePagination().
- Total(int64(totalCount)).
- CurrPage(searchParams.Page).
- PerPage(searchParams.PerPage).
- CustomPerPages([]int64{b.perPage}).
- PerPageText(msgr.PaginationRowsPerPage)
- if inDialog {
- tpb.OnSelectPerPage(web.Plaid().
- URL(ctx.R.RequestURI).
- Query("per_page", web.Var("[$event]")).
- MergeQuery(true).
- EventFunc(actions.UpdateListingDialog).
- Go())
- tpb.OnPrevPage(web.Plaid().
- URL(ctx.R.RequestURI).
- Query("page", searchParams.Page-1).
- MergeQuery(true).
- EventFunc(actions.UpdateListingDialog).
- Go())
- tpb.OnNextPage(web.Plaid().
- URL(ctx.R.RequestURI).
- Query("page", searchParams.Page+1).
- MergeQuery(true).
- EventFunc(actions.UpdateListingDialog).
- Go())
- }
- datatableAdditions = tpb
- } else {
- datatableAdditions = h.Div(h.Text(msgr.ListingNoRecordToShow)).Class("mt-10 text-center grey--text text--darken-2")
- }
- return
- }
- func (b *ListingBuilder) reloadList(ctx *web.EventContext) (r web.EventResponse, err error) {
- dataTable, dataTableAdditions := b.getTableComponents(ctx, false)
- r.UpdatePortals = append(r.UpdatePortals,
- &web.PortalUpdate{
- Name: dataTablePortalName,
- Body: dataTable,
- },
- &web.PortalUpdate{
- Name: dataTableAdditionsPortalName,
- Body: dataTableAdditions,
- },
- )
- return
- }
- func (b *ListingBuilder) actionsComponent(
- msgr *Messages,
- ctx *web.EventContext,
- inDialog bool,
- ) h.HTMLComponent {
- var actionBtns []h.HTMLComponent
- // Render bulk actions
- for _, ba := range b.bulkActions {
- if b.mb.Info().Verifier().SnakeDo(PermBulkActions, ba.name).WithReq(ctx.R).IsAllowed() != nil {
- continue
- }
- var btn h.HTMLComponent
- if ba.buttonCompFunc != nil {
- btn = ba.buttonCompFunc(ctx)
- } else {
- buttonColor := ba.buttonColor
- if buttonColor == "" {
- buttonColor = ColorSecondary
- }
- onclick := web.Plaid().EventFunc(actions.OpenBulkActionDialog).
- Queries(url.Values{bulkPanelOpenParamName: []string{ba.name}}).
- MergeQuery(true)
- if inDialog {
- onclick.URL(ctx.R.RequestURI).
- Query(ParamInDialog, inDialog)
- }
- btn = VBtn(b.mb.getLabel(ba.NameLabel)).
- Color(buttonColor).
- Depressed(true).
- Dark(true).
- Class("ml-2").
- Attr("@click", onclick.Go())
- }
- actionBtns = append(actionBtns, btn)
- }
- // Render actions
- for _, ba := range b.actions {
- if b.mb.Info().Verifier().SnakeDo(PermActions, ba.name).WithReq(ctx.R).IsAllowed() != nil {
- continue
- }
- var btn h.HTMLComponent
- if ba.buttonCompFunc != nil {
- btn = ba.buttonCompFunc(ctx)
- } else {
- buttonColor := ba.buttonColor
- if buttonColor == "" {
- buttonColor = ColorPrimary
- }
- onclick := web.Plaid().EventFunc(actions.OpenActionDialog).
- Queries(url.Values{actionPanelOpenParamName: []string{ba.name}}).
- MergeQuery(true)
- if inDialog {
- onclick.URL(ctx.R.RequestURI).
- Query(ParamInDialog, inDialog)
- }
- btn = VBtn(b.mb.getLabel(ba.NameLabel)).
- Color(buttonColor).
- Depressed(true).
- Dark(true).
- Class("ml-2").
- Attr("@click", onclick.Go())
- }
- actionBtns = append(actionBtns, btn)
- }
- if len(actionBtns) == 0 {
- return nil
- }
- if b.actionsAsMenu {
- var listItems []h.HTMLComponent
- for _, btn := range actionBtns {
- listItems = append(listItems, VListItem(btn))
- }
- return h.Components(VMenu(
- web.Slot(
- VBtn("Actions").
- Attr("v-bind", "attrs").
- Attr("v-on", "on"),
- ).Name("activator").Scope("{ on, attrs }"),
- VList(listItems...),
- ).OpenOnHover(true).
- OffsetY(true).
- AllowOverflow(true))
- }
- return h.Components(actionBtns...)
- }
- func (b *ListingBuilder) openListingDialog(ctx *web.EventContext) (r web.EventResponse, err error) {
- content := VCard(
- web.Portal(b.listingComponent(ctx, true)).
- Name(listingDialogContentPortalName),
- ).Attr("id", "listingDialog")
- dialog := VDialog(content).
- Attr("v-model", "vars.presetsListingDialog")
- if b.dialogWidth != "" {
- dialog.Width(b.dialogWidth)
- }
- if b.dialogHeight != "" {
- content.Attr("height", b.dialogHeight)
- }
- r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{
- Name: ListingDialogPortalName,
- Body: web.Scope(dialog).VSlot("{ plaidForm }"),
- })
- r.VarsScript = "setTimeout(function(){ vars.presetsListingDialog = true }, 100)"
- return
- }
- func (b *ListingBuilder) updateListingDialog(ctx *web.EventContext) (r web.EventResponse, err error) {
- r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{
- Name: listingDialogContentPortalName,
- Body: b.listingComponent(ctx, true),
- })
- web.AppendVarsScripts(&r, `
- var listingDialogElem = document.getElementById('listingDialog');
- if (listingDialogElem.offsetHeight > parseInt(listingDialogElem.style.minHeight || '0', 10)) {
- listingDialogElem.style.minHeight = listingDialogElem.offsetHeight+'px';
- };`)
- return
- }
|