mirror of
https://github.com/acaloiaro/frm
synced 2026-07-21 18:29:12 +00:00
There is a bug in the logic builder that causes logic not to be saved, even when changes are made, until the checkbox "Show field" is clicked.
515 lines
22 KiB
Text
515 lines
22 KiB
Text
package builder
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/acaloiaro/frm"
|
|
"github.com/acaloiaro/frm/types"
|
|
"github.com/acaloiaro/frm/ui"
|
|
"github.com/acaloiaro/frm/ui/collector"
|
|
"github.com/acaloiaro/frm/ui/fields"
|
|
"github.com/acaloiaro/frm/ui/selector"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// Application DOM events
|
|
const (
|
|
// FieldsFormEvent is the event triggered when any of a form's fields have been updated in the UI. When triggered,
|
|
// the fields form pushes updates from the UI to the draft form on the backend.
|
|
FieldsFormUpdateEvent = "fields-form-updated"
|
|
// FormSettingsEvent is the event triggered when any of a form's settings have been updated in the UI. When triggered,
|
|
// the fields form pushes updates from the UI to the draft form on the backend.
|
|
FormSettingsUpdateEvent = "form-settings-updated"
|
|
// LogicConfiguratorTargetFieldSelected is the event triggered when a form field is selected as a the target for field logic
|
|
LogicConfiguratorTargetFieldSelected = "logic-configurator-target-field-selected"
|
|
)
|
|
|
|
// Logic field group form field names
|
|
const (
|
|
// FieldLogicChosenFieldID is the name of a HTML form field. The value of the form field with this name represents the field that was chosen as the target for field-specific logic.
|
|
//
|
|
// TODO: provide an example
|
|
FieldLogicTargetFieldID = "target_field_id"
|
|
// FieldLogicChosenField is the name of a HTML form field. The value of the form field represents the boolean comparator to be used to evaluate the chosen field against its field value.
|
|
//
|
|
// TODO: provide an example
|
|
FieldLogicComparator = "comparator"
|
|
// FieldLogicChosenFieldValue is the name of a HTML Form field. The value of the form field represents the value that the target field's value is compared against when deciding whether or not to display a field in the UI.
|
|
//
|
|
// TODO: provide an example
|
|
FieldLogicTargetFieldValue = "target_field_value"
|
|
)
|
|
|
|
// Field groups within the the form field configuration screen
|
|
const (
|
|
FieldGroupLogic = "logic"
|
|
)
|
|
|
|
// Builder is the primary form builder UI, surrounded by the app chrome
|
|
templ Builder(form frm.Form) {
|
|
@ui.App("Form builder") {
|
|
<section id="app-container h-2/3 max-h-1/3">
|
|
@FormBuilderNav(form)
|
|
<section id="builder-main" class="flex pt-20">
|
|
<!-- Left column -->
|
|
@builderColumnLeft(form)
|
|
<!-- Middle Column -->
|
|
@collector.FormPreview(collector.ViewerArgs{Preview: true, Form: form})
|
|
<!-- Right Column -->
|
|
@builderColumnRight(form)
|
|
</section>
|
|
</section>
|
|
}
|
|
}
|
|
|
|
// FormBuilderNavTitle renders the title of the form in the nav bar. This is a separate template so that the title can be updated when settings change, without updating, and losing the state of, the rest of the nav bar
|
|
templ FormBuilderNavTitle(form frm.Form) {
|
|
<div id="form-builder-nav-title" data-hx-swap-oob="true" class="flex-grow flex justify-center">
|
|
<h3 tabindex="0" class="rounded px-2 cursor-pointer relative font-medium py-1 text-2xl w-2/3 text-center text-gray-500 truncate form-editor-title" style="height: auto;" id="form-editor-title">
|
|
{ form.Name }
|
|
</h3>
|
|
</div>
|
|
}
|
|
|
|
// FormBuilderNav is the top-of-the-page navigation bar
|
|
templ FormBuilderNav(form frm.Form) {
|
|
<div id="form-builder-nav" data-hx-swap-oob="true" class="fixed w-full p-2 flex gap-x-2 items-center bg-white z-50">
|
|
<div id="form-editor-navbar-tabs">
|
|
<div role="tablist" aria-orientation="horizontal" class="tabs tabs-boxed bg-gray-50 dark:bg-gray-800 rounded-lg p-1 h-auto grid grid-cols-2 items-center gap-x-1.5 px-2">
|
|
@ui.MutedButton(ui.ButtonArgs{Label: "Build", Classes: []string{"cursor-pointer tab-active [--fallback-p:white] [--fallback-pc:black] place-self-center"}}, templ.Attributes{
|
|
"role": "tab",
|
|
"tabindex": 0,
|
|
"_": "on click take .tab-active from .tab-active then add .hidden to .active-section then take .active-section for #form-fields then remove .hidden from #form-fields",
|
|
})
|
|
@ui.MutedButton(ui.ButtonArgs{Label: "Settings", Classes: []string{"cursor-pointer [--fallback-p:white] [--fallback-pc:black] place-self-center"}}, templ.Attributes{
|
|
"role": "tab",
|
|
"tabindex": 2,
|
|
"_": "on click take .tab-active from .tab-active then add .hidden to .active-section take .active-section for #settings-main then remove .hidden from #settings-main",
|
|
})
|
|
</div>
|
|
</div>
|
|
@FormBuilderNavTitle(form)
|
|
<div class="flex items-stretch gap-x-2">
|
|
<div class="inline-flex items-center relative">
|
|
<a href="#" class="text-sm p-2 hover:bg-gray-100 cursor-pointer rounded-lg text-gray-500 hover:text-gray-800 cursor-pointer"><span class="iconify i-heroicons:question-mark-circle w-5 h-5" aria-hidden="true"></span></a><!---->
|
|
</div>
|
|
<div class="relative inline-flex">
|
|
<button
|
|
type="button"
|
|
class="focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:cursor-not-allowed aria-disabled:opacity-75 flex-shrink-0 font-medium rounded-md text-sm gap-x-1.5 shadow-sm text-white dark:text-gray-900 bg-primary-500 hover:bg-primary-600 disabled:bg-primary-500 aria-disabled:bg-primary-500 dark:bg-primary-400 dark:hover:bg-primary-500 dark:disabled:bg-primary-400 dark:aria-disabled:bg-primary-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 dark:focus-visible:outline-primary-400 inline-flex items-center px-8 md:px-4 py-2"
|
|
data-hx-put={ ui.FormUrl[string](ctx, form, "/publish") }
|
|
data-hx-trigger="click"
|
|
data-hx-swap="none"
|
|
>
|
|
<!---->
|
|
<svg class="w-4 h-4 text-white inline mr-1 -mt-1" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M17 21V13H7V21M7 3V8H15M19 21H5C4.46957 21 3.96086 20.7893 3.58579 20.4142C3.21071 20.0391 3 19.5304 3 19V5C3 4.46957 3.21071 3.96086 3.58579 3.58579C3.96086 3.21071 4.46957 3 5 3H16L21 8V19C21 19.5304 20.7893 20.0391 20.4142 20.4142C20.0391 20.7893 19.5304 21 19 21Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg> Save Form <!---->
|
|
</button><!---->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
// FormSettings is the UI for configuring form-level settings.
|
|
templ FormSettings(form frm.Form) {
|
|
<div id="settings-main" class="hidden">
|
|
<form
|
|
id="settings-form"
|
|
data-hx-put={ ui.FormUrl[string](ctx, form, "/settings") }
|
|
data-hx-trigger={ FormSettingsUpdateEvent }
|
|
data-hx-swap-oob="true"
|
|
>
|
|
<div class="pt-3">
|
|
<label for="form-name-field" class="pr-1">
|
|
Form name
|
|
</label>
|
|
@fields.RequiredFieldIndicator()
|
|
</div>
|
|
<input
|
|
id="form-name-field"
|
|
name="name"
|
|
type="text"
|
|
class="w-full rounded-md"
|
|
value={ form.Name }
|
|
placeholder="Enter a good name for your form"
|
|
autocomplete="off"
|
|
_={ fmt.Sprintf("on keyup debounced at 600ms trigger '%s'", FormSettingsUpdateEvent) }
|
|
/>
|
|
</form>
|
|
</div>
|
|
}
|
|
|
|
// builderColumnLeft is the left-hand panel of the form builder UI
|
|
templ builderColumnLeft(form frm.Form) {
|
|
<section id="builder-main-left-col" class="flex flex-col gap-3 w-1/4 min-w-max h-full p-4 text-gray-800 rounded-md">
|
|
@FormFields(form)
|
|
@FormSettings(form)
|
|
</section>
|
|
}
|
|
|
|
// FormFields lists a form's fields as a sortable list, that when re-sorted, updates the fields' sort order in the form
|
|
templ FormFields(form frm.Form) {
|
|
<div id="form-fields" class="active-section">
|
|
@FormFieldsForm(form)
|
|
</div>
|
|
}
|
|
|
|
// FormFieldsForm is the list of fields on the left-hand side of the screen that displays a sorted list of form fields
|
|
templ FormFieldsForm(form frm.Form) {
|
|
<div id="form-fields-form" data-hx-swap-oob="true" class="flex flex-col gap-3 w-full">
|
|
@ui.Button(ui.ButtonArgs{
|
|
Label: "Add field",
|
|
Classes: []string{"flex-grow", "justify-center", "uppercase"},
|
|
}, templ.Attributes{
|
|
"_": "on click toggle .hidden on .active-configurator then take .active-configurator from .active-configurator for #configure-add-field then remove .hidden from #configure-add-field",
|
|
}) {
|
|
@ui.HeroIcon("solid", "plus")
|
|
}
|
|
<div class="w-full border-b pb-2"></div>
|
|
<form
|
|
data-hx-put={ ui.FormUrl[string](ctx, form, "/fields/order") }
|
|
data-hx-trigger="end"
|
|
data-hx-swap="outerHTML"
|
|
data-hx-target="#form-fields-form"
|
|
data-hx-indidcator="#ind"
|
|
>
|
|
<div class="flex flex-col gap-1 sortable">
|
|
for _, field := range fields.SortFields(form.Fields) {
|
|
<div class="mx-auto w-full border-gray-300 transition-colors bg-gray-50 hover:bg-gray-100 rounded-lg sortme max-w-84 field-row">
|
|
<input name="order" type="hidden" value={ field.ID.String() }/>
|
|
<div class="group flex items-center gap-x-0.5 py-1.5 pr-1">
|
|
<!-- field item -->
|
|
<a
|
|
href="#"
|
|
class="w-full"
|
|
_={ fmt.Sprintf("on click add .hidden to .active-configurator then take .active-configurator from .active-configurator for #configure-%s then remove .hidden from #configure-%s", field.ID.String(), field.ID.String()) }
|
|
>
|
|
<div class="flex flex-col cursor-pointer">
|
|
<div tabindex="0" class="dark:hover:bg-gray-800 rounded px-2 relative text-gray-700 max-w-72 min-h-6" style="height: auto;">
|
|
<p class="w-full cursor-pointer truncate">{ field.Label }</p>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
if field.Required {
|
|
<div class="relative inline-flex">
|
|
<button class="hidden rounded p-0.5 transition-colors hover:bg-nt-blue-lighter items-center px-1 justify-center md:flex text-red-500">
|
|
<div class="h-6 text-center text-2xl font-bold text-inherit -mt-0.5">* </div>
|
|
</button>
|
|
</div>
|
|
}
|
|
<div class="cursor-move">
|
|
@ui.HeroIcon("solid", "bars-3")
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
}
|
|
|
|
// builerColumnRight is the right-hand panel of the form build UI, used for adding and configuring fields
|
|
templ builderColumnRight(form frm.Form) {
|
|
<section id="builder-main-right-col" class="w-1/4 h-full p-4 text-gray-800 rounded-md">
|
|
<div id="configure-add-field" class="hidden">
|
|
<div class="h-12 border-b text-lg text-center uppercase">Add field</div>
|
|
for i, fieldType := range types.FormFieldTypeValues() {
|
|
<div class="group flex items-center my-1.5 pr-1">
|
|
<a href="#" class="w-full">
|
|
<div class="flex flex-col">
|
|
<div
|
|
tabindex={ fmt.Sprint(i) }
|
|
class="hover:bg-gray-50 dark:hover:bg-gray-800 rounded cursor-pointer relative truncate text-gray-700 min-w-16 min-h-6"
|
|
style="height: auto;"
|
|
data-hx-post={ ui.FormUrl[string](ctx, form, "/fields") }
|
|
data-hx-trigger="click"
|
|
data-hx-vals={ fmt.Sprintf(`{"field_type": "%s"}`, fieldType) }
|
|
data-hx-swap="none"
|
|
>
|
|
@fields.FormFieldTypeLabel(types.FormFieldType(fieldType))
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
}
|
|
</div>
|
|
@FormFieldConfigurator(form)
|
|
</section>
|
|
}
|
|
|
|
// FormFieldConfigurator is the view on the right-hand side of the screen for configuring individual fields
|
|
templ FormFieldConfigurator(form frm.Form) {
|
|
<form
|
|
id="fields-form"
|
|
data-hx-put={ ui.FormUrl[string](ctx, form, "/fields") }
|
|
data-hx-trigger={ FieldsFormUpdateEvent }
|
|
data-hx-swap="none"
|
|
data-hx-swap-oob="true"
|
|
>
|
|
for _, field := range fields.SortFields(form.Fields) {
|
|
<div id={ fmt.Sprintf("configure-%s", field.ID.String()) } class="hidden">
|
|
<div class="mx-auto w-full border-gray-300 transition-colors pb-3">
|
|
@fields.FormFieldTypeLabel(types.FormFieldType(field.Type))
|
|
</div>
|
|
<div id={ fmt.Sprintf("configurator-tabs-%s", field.ID.String()) } class="border-b pb-4">
|
|
<div role="tablist" aria-orientation="horizontal" class="tabs tabs-boxed bg-gray-50 dark:bg-gray-800 rounded-lg h-auto flex gap-2">
|
|
@ui.MutedButton(ui.ButtonArgs{Label: "Settings", Classes: []string{"cursor-pointer tab-active [--fallback-p:white] [--fallback-pc:black] place-self-center w-1/2"}}, templ.Attributes{
|
|
"role": "tab",
|
|
"tabindex": 1,
|
|
"_": fmt.Sprintf("on click take .tab-active from <#configurator-tabs-%s .tab-active/> then add .hidden to #field-%s-logic then take .active-configurator-section from #field-%s-logic for #%s then remove .hidden from #%s", field.ID.String(), field.ID.String(), field.ID.String(), fmt.Sprintf("field-%s-settings", field.ID.String()), fmt.Sprintf("field-%s-settings", field.ID.String())),
|
|
})
|
|
@ui.MutedButton(ui.ButtonArgs{Label: "Logic", Classes: []string{"cursor-pointer [--fallback-p:white] [--fallback-pc:black] place-self-center w-1/2"}}, templ.Attributes{
|
|
"role": "tab",
|
|
"tabindex": 2,
|
|
"_": fmt.Sprintf("on click take .tab-active from <#configurator-tabs-%s .tab-active/> then add .hidden to #field-%s-settings then take .active-configurator-section from #field-%s-settings for #%s then remove .hidden from #%s", field.ID.String(), field.ID.String(), field.ID.String(), fmt.Sprintf("field-%s-logic", field.ID.String()), fmt.Sprintf("field-%s-logic", field.ID.String())),
|
|
})
|
|
</div>
|
|
</div>
|
|
<!-- Form fields settings configurations -->
|
|
@fieldSettingsConfiguration(form, field)
|
|
<!-- Form fields logic configurations -->
|
|
@fieldLogicConfiguration(form, field)
|
|
</div>
|
|
}
|
|
</form>
|
|
}
|
|
|
|
// fieldSettingsConfiguration configures the settings for a specific field
|
|
// FIELD_TYPES: field types may be added/modified/removed below
|
|
templ fieldSettingsConfiguration(form frm.Form, field types.FormField) {
|
|
<div id={ fmt.Sprintf("field-%s-settings", field.ID.String()) } class="active-configurator-section">
|
|
<input name={ fields.FieldName(field, "", "required") } type="hidden" value={ fmt.Sprint(field.Required) }/>
|
|
<input name={ fields.FieldName(field, "", "hidden") } type="hidden" value={ fmt.Sprint(field.Hidden) }/>
|
|
<input name={ fields.FieldName(field, "", "field_type") } type="hidden" value={ fmt.Sprint(field.Type) }/>
|
|
<div class="pt-3">
|
|
<label for={ fields.FieldName(field, "", "label") } class="pr-1">
|
|
Field label
|
|
</label>
|
|
@fields.RequiredFieldIndicator()
|
|
</div>
|
|
<input
|
|
id={ fields.FieldName(field, "", "label") }
|
|
name={ fields.FieldName(field, "", "label") }
|
|
type="text"
|
|
class="w-full rounded-md"
|
|
value={ field.Label }
|
|
autocomplete="off"
|
|
_={ fmt.Sprintf("on keyup debounced at 600ms trigger '%s'", FieldsFormUpdateEvent) }
|
|
/>
|
|
switch field.Type {
|
|
case types.FormFieldTypeTextSingle, types.FormFieldTypeTextMultiple:
|
|
<div class="pt-3">
|
|
<label for={ fields.FieldName(field, "", "placeholder") } class="pr-1">
|
|
Placeholder
|
|
</label>
|
|
</div>
|
|
<input
|
|
id={ fields.FieldName(field, "", "placeholder") }
|
|
name={ fields.FieldName(field, "", "placeholder") }
|
|
type="text"
|
|
class="w-full rounded-md"
|
|
value={ field.Placeholder }
|
|
autocomplete="off"
|
|
_={ fmt.Sprintf("on keyup debounced at 600ms trigger '%s'", FieldsFormUpdateEvent) }
|
|
/>
|
|
case types.FormFieldTypeMultiSelect, types.FormFieldTypeSingleSelect, types.FormFieldTypeSingleChoice:
|
|
<div class="pt-3">
|
|
<label for={ fields.FieldName(field, "", "options") } class="pr-1">
|
|
Options
|
|
</label>
|
|
</div>
|
|
@selector.Selector(selector.SelectArgs{
|
|
ID: fields.FieldName(field, "", "options"),
|
|
Name: fields.FieldName(field, "", "options"),
|
|
Placeholder: "Add, remove, or create new options",
|
|
Multiple: true,
|
|
EditItems: true,
|
|
Options: fields.ToSelectorOpts(field.Options, true),
|
|
SelectionChangeEvent: FieldsFormUpdateEvent,
|
|
})
|
|
if field.Type == types.FormFieldTypeSingleChoice {
|
|
<div class="pt-3">
|
|
<label for={ fields.FieldName(field, "", "option_labels") } class="pr-1">
|
|
Option labels
|
|
</label>
|
|
</div>
|
|
@selector.Selector(selector.SelectArgs{
|
|
ID: fields.FieldName(field, "", "option_labels"),
|
|
Name: fields.FieldName(field, "", "option_labels"),
|
|
Placeholder: "Create labels for your options",
|
|
Multiple: true,
|
|
EditItems: true,
|
|
Options: fields.ToSelectorOptsStr(field.OptionLabels, true),
|
|
SelectionChangeEvent: FieldsFormUpdateEvent,
|
|
})
|
|
}
|
|
}
|
|
<div class="pt-3">
|
|
<label for={ fields.FieldName(field, "", "required") } class="pr-1">
|
|
Required
|
|
</label>
|
|
</div>
|
|
<input
|
|
id={ fields.FieldName(field, "", "required") }
|
|
name={ fields.FieldName(field, "", "required") }
|
|
type="checkbox"
|
|
class="checkbox checkbox-primary"
|
|
if field.Required {
|
|
checked
|
|
}
|
|
_={ fmt.Sprintf(`
|
|
on click
|
|
if my.checked
|
|
set <input[name='%s']/>'s checked to false
|
|
end
|
|
then trigger '%s'`,
|
|
fields.FieldName(field, "","hidden"), FieldsFormUpdateEvent) }
|
|
/>
|
|
<div class="pt-3">
|
|
<label for={ fields.FieldName(field, "", "hidden") } class="pr-1">
|
|
Hidden
|
|
</label>
|
|
</div>
|
|
<input
|
|
id={ fields.FieldName(field, "", "hidden") }
|
|
name={ fields.FieldName(field, "", "hidden") }
|
|
type="checkbox"
|
|
class="checkbox checkbox-primary"
|
|
if field.Hidden {
|
|
checked
|
|
}
|
|
_={ fmt.Sprintf(`
|
|
on click
|
|
if my.checked
|
|
set <input[name='%s']/>'s checked to false
|
|
end
|
|
then trigger '%s'`,
|
|
fields.FieldName(field, "","required"), FieldsFormUpdateEvent) }
|
|
/>
|
|
<div class="py-3 divide-y">
|
|
<label for="delete-field" class="pr-1">
|
|
Danger zone
|
|
</label>
|
|
</div>
|
|
@ui.Button(ui.ButtonArgs{Type: "button", Label: "Delete field"}, templ.Attributes{
|
|
"data-hx-delete": ui.FormUrl[string](ctx, form, fmt.Sprintf("/fields/%s", field.ID)),
|
|
"data-hx-trigger": "click",
|
|
"data-hx-confirm": "Are you sure?",
|
|
})
|
|
</div>
|
|
}
|
|
|
|
templ fieldLogicConfiguration(form frm.Form, field types.FormField) {
|
|
<div id={ fmt.Sprintf("field-%s-logic", field.ID.String()) } class="flex flex-col gap-5 hidden">
|
|
<div>
|
|
<div
|
|
data-hx-get={ frm.BuilderPathFormField(ctx, form.ID, field.ID.String(), "/logic/choices") }
|
|
data-hx-trigger={ LogicConfiguratorTargetFieldSelected }
|
|
data-hx-swap="innerHTML"
|
|
data-hx-target={ fmt.Sprintf("#logic-field-value-chooser-%s", field.ID.String()) }
|
|
data-hx-on:htmx:config-request="event.detail.parameters['id'] = event.detail.triggeringEvent.detail.value"
|
|
>
|
|
@selector.Selector(selector.SelectArgs{
|
|
ID: fmt.Sprintf("field-%s-logic-config-field-chooser", field.ID.String()),
|
|
Name: fields.FieldName(field, FieldGroupLogic, FieldLogicTargetFieldID),
|
|
Placeholder: "Choose a field",
|
|
Options: fields.FieldsAsSelectorOptions(form, field.ID),
|
|
SearchDisabled: true,
|
|
SelectionChangeEvent: LogicConfiguratorTargetFieldSelected,
|
|
})
|
|
</div>
|
|
</div>
|
|
<div>
|
|
@selector.Selector(selector.SelectArgs{
|
|
ID: fmt.Sprintf("field-%s-logic-config-condition-chooser", field.ID.String()),
|
|
Name: fields.FieldName(field, FieldGroupLogic, FieldLogicComparator),
|
|
Placeholder: "Choose a condition",
|
|
Options: comparatorOptionsFor(field),
|
|
SearchDisabled: true,
|
|
SelectionChangeEvent: FieldsFormUpdateEvent,
|
|
})
|
|
</div>
|
|
<div>
|
|
<div
|
|
id={ fmt.Sprintf("logic-field-value-chooser-%s", field.ID.String()) }
|
|
>
|
|
if field.Logic != nil && field.Logic.TargetFieldID != uuid.Nil {
|
|
@LogicConfiguratorStepThree(form, field, form.Fields[field.Logic.TargetFieldID.String()])
|
|
}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<p class="pb-3">Choose an action</p>
|
|
<input
|
|
id={ fmt.Sprintf("%s-logic-action", field.ID.String()) }
|
|
type="checkbox"
|
|
class="checkbox"
|
|
name={ fields.FieldName(field, FieldGroupLogic, types.FieldLogicTriggerShow.String()) }
|
|
if field.Logic != nil && field.Logic.TriggerActions.Contains(types.FieldLogicTriggerShow) {
|
|
checked
|
|
}
|
|
_={ fmt.Sprintf(`
|
|
on click
|
|
trigger '%s'`,
|
|
FieldsFormUpdateEvent) }
|
|
/>
|
|
<label for={ fmt.Sprintf("%s-logic-action", field.ID.String()) }>Show this field</label>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
// comparatorOptionsFor returns the comparators available for a logic field given its Type
|
|
func comparatorOptionsFor(field types.FormField) (options selector.FieldOptions) {
|
|
switch field.Type {
|
|
default:
|
|
options = selector.FieldOptions{
|
|
selector.Option{
|
|
Value: fmt.Sprint(types.FieldLogicComparatorContains),
|
|
Label: "Contains",
|
|
Selected: field.Logic != nil && field.Logic.TriggerComparator == types.FieldLogicComparatorContains,
|
|
},
|
|
selector.Option{
|
|
Value: fmt.Sprint(types.FieldLogicComparatorEqual),
|
|
Label: "Equal to =",
|
|
Selected: field.Logic != nil && field.Logic.TriggerComparator == types.FieldLogicComparatorEqual,
|
|
},
|
|
selector.Option{
|
|
Value: fmt.Sprint(types.FieldLogicComparatorNot),
|
|
Label: "NOT",
|
|
Selected: field.Logic != nil && field.Logic.TriggerComparator == types.FieldLogicComparatorNot,
|
|
},
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// LogicConfiguratorStepThree returns HTML input elments appropriate for choosing values for `targetField` in the logic configurator.
|
|
//
|
|
// field: the field being configured
|
|
// targetField: the target field chosen as the logic target
|
|
templ LogicConfiguratorStepThree(form frm.Form, field types.FormField, targetField types.FormField) {
|
|
switch targetField.Type {
|
|
case types.FormFieldTypeMultiSelect, types.FormFieldTypeSingleSelect, types.FormFieldTypeSingleChoice:
|
|
@selector.Selector(selector.SelectArgs{
|
|
ID: fmt.Sprintf("%s-logic-chosen-field-value", field.ID.String()),
|
|
Label: "",
|
|
Name: fields.FieldName(field, FieldGroupLogic, FieldLogicTargetFieldValue),
|
|
Options: ui.FieldOptionsAsSelectorOptions(form, targetField),
|
|
Placeholder: "Choose a value",
|
|
SelectionChangeEvent: FieldsFormUpdateEvent,
|
|
})
|
|
case types.FormFieldTypeTextSingle, types.FormFieldTypeTextMultiple:
|
|
<input
|
|
id={ fmt.Sprintf("%s-logic-chosen-field-value", field.ID.String()) }
|
|
name={ fields.FieldName(field, FieldGroupLogic, FieldLogicTargetFieldValue) }
|
|
type="text"
|
|
class="bg-gray-50"
|
|
placeholder="Enter a value"
|
|
if field.Logic != nil && len(field.Logic.TriggerValues) > 0 {
|
|
value={ field.Logic.TriggerValues[0] }
|
|
}
|
|
_={ fmt.Sprintf("on keyup debounced at 600ms trigger '%s'", FieldsFormUpdateEvent) }
|
|
/>
|
|
}
|
|
}
|