mirror of
https://github.com/acaloiaro/frm
synced 2026-07-21 18:29:12 +00:00
feat: add toast when form is saved
This commit is contained in:
parent
c51226ecdd
commit
f33e5926c0
14 changed files with 1076 additions and 381 deletions
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/acaloiaro/frm/internal"
|
||||
"github.com/acaloiaro/frm/static"
|
||||
"github.com/acaloiaro/frm/types"
|
||||
"github.com/acaloiaro/frm/ui"
|
||||
"github.com/acaloiaro/frm/ui/builder"
|
||||
"github.com/acaloiaro/frm/ui/collector"
|
||||
"github.com/google/uuid"
|
||||
|
|
@ -699,20 +700,34 @@ func PublishDraft(w http.ResponseWriter, r *http.Request) {
|
|||
f, err := frm.Instance(ctx)
|
||||
if err != nil {
|
||||
slog.Error("unable to publish draft", "error", err)
|
||||
ui.Toast(ui.ToastArgs{
|
||||
Position: ui.ToastPositionTop,
|
||||
Type: ui.ToastTypeError,
|
||||
Message: "Failed! Your form was not saved.",
|
||||
}).Render(ctx, w)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
draftID, err := formID(ctx, f)
|
||||
if err != nil {
|
||||
slog.Error("unable to publish draft", "error", err)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
ui.Toast(ui.ToastArgs{
|
||||
Position: ui.ToastPositionTop,
|
||||
Type: ui.ToastTypeError,
|
||||
Message: "Failed! Your form was not saved.",
|
||||
}).Render(ctx, w)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
tx, err := internal.Tx(ctx, f.DBArgs)
|
||||
if err != nil {
|
||||
slog.Error("unable to publish draft", "error", err)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
ui.Toast(ui.ToastArgs{
|
||||
Position: ui.ToastPositionTop,
|
||||
Type: ui.ToastTypeError,
|
||||
Message: "Failed! Your form was not saved.",
|
||||
}).Render(ctx, w)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
|
|
@ -723,6 +738,11 @@ func PublishDraft(w http.ResponseWriter, r *http.Request) {
|
|||
_, err = q.PublishDraft(ctx, *draftID)
|
||||
if err != nil {
|
||||
slog.Error("unable to publish draft", "error", err)
|
||||
ui.Toast(ui.ToastArgs{
|
||||
Position: ui.ToastPositionTop,
|
||||
Type: ui.ToastTypeError,
|
||||
Message: "Failed! Your form was not saved.",
|
||||
}).Render(ctx, w)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
|
@ -733,13 +753,32 @@ func PublishDraft(w http.ResponseWriter, r *http.Request) {
|
|||
})
|
||||
if err != nil {
|
||||
slog.Error("unable to publish draft", "error", err)
|
||||
ui.Toast(ui.ToastArgs{
|
||||
Position: ui.ToastPositionTop,
|
||||
Type: ui.ToastTypeError,
|
||||
Message: "Failed! Your form was not saved.",
|
||||
}).Render(ctx, w)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
slog.Error("unable to publish draft", "error", err)
|
||||
ui.Toast(ui.ToastArgs{
|
||||
Position: ui.ToastPositionTop,
|
||||
Type: ui.ToastTypeError,
|
||||
Message: "Failed! Your form was not saved.",
|
||||
}).Render(ctx, w)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
ui.Toast(ui.ToastArgs{
|
||||
Position: ui.ToastPositionTop,
|
||||
Type: ui.ToastTypeSuccess,
|
||||
Message: "Success! Your form has been saved.",
|
||||
}).Render(ctx, w)
|
||||
}
|
||||
|
||||
// DeleteForm deletes forms
|
||||
|
|
|
|||
|
|
@ -1152,6 +1152,57 @@ html {
|
|||
content: var(--tw-content);
|
||||
}
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: var(--rounded-box, 1rem);
|
||||
}
|
||||
|
||||
.card:focus {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.card figure {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card.image-full {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.card.image-full:before {
|
||||
position: relative;
|
||||
content: "";
|
||||
z-index: 10;
|
||||
border-radius: var(--rounded-box, 1rem);
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.card.image-full:before,
|
||||
.card.image-full > * {
|
||||
grid-column-start: 1;
|
||||
grid-row-start: 1;
|
||||
}
|
||||
|
||||
.card.image-full > figure img {
|
||||
height: 100%;
|
||||
-o-object-fit: cover;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.card.image-full > .card-body {
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
flex-shrink: 0;
|
||||
--chkbg: var(--fallback-bc,oklch(var(--bc)/1));
|
||||
|
|
@ -1611,6 +1662,17 @@ input.tab:checked + .tab-content,
|
|||
background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));
|
||||
}
|
||||
|
||||
.toast {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
min-width: -moz-fit-content;
|
||||
min-width: fit-content;
|
||||
flex-direction: column;
|
||||
white-space: nowrap;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
flex-shrink: 0;
|
||||
--tglbg: var(--fallback-b1,oklch(var(--b1)/1));
|
||||
|
|
@ -1738,6 +1800,44 @@ input.tab:checked + .tab-content,
|
|||
}
|
||||
}
|
||||
|
||||
.card :where(figure:first-child) {
|
||||
overflow: hidden;
|
||||
border-start-start-radius: inherit;
|
||||
border-start-end-radius: inherit;
|
||||
border-end-start-radius: unset;
|
||||
border-end-end-radius: unset;
|
||||
}
|
||||
|
||||
.card :where(figure:last-child) {
|
||||
overflow: hidden;
|
||||
border-start-start-radius: unset;
|
||||
border-start-end-radius: unset;
|
||||
border-end-start-radius: inherit;
|
||||
border-end-end-radius: inherit;
|
||||
}
|
||||
|
||||
.card:focus-visible {
|
||||
outline: 2px solid currentColor;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.card.bordered {
|
||||
border-width: 1px;
|
||||
--tw-border-opacity: 1;
|
||||
border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));
|
||||
}
|
||||
|
||||
.card.compact .card-body {
|
||||
padding: 1rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.card.image-full :where(figure) {
|
||||
overflow: hidden;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.checkbox:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
|
@ -2320,6 +2420,10 @@ input.tab:checked + .tab-content,
|
|||
--tw-placeholder-opacity: 0.2;
|
||||
}
|
||||
|
||||
.toast > * {
|
||||
animation: toast-pop 0.25s ease-out;
|
||||
}
|
||||
|
||||
@keyframes toast-pop {
|
||||
0% {
|
||||
transform: scale(0.9);
|
||||
|
|
@ -2582,6 +2686,63 @@ input.tab:checked + .tab-content,
|
|||
--tab-padding: 0.5rem;
|
||||
}
|
||||
|
||||
:where(.toast) {
|
||||
bottom: 0px;
|
||||
inset-inline-end: 0px;
|
||||
inset-inline-start: auto;
|
||||
top: auto;
|
||||
--tw-translate-x: 0px;
|
||||
--tw-translate-y: 0px;
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
.toast:where(.toast-start) {
|
||||
inset-inline-end: auto;
|
||||
inset-inline-start: 0px;
|
||||
--tw-translate-x: 0px;
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
.toast:where(.toast-center) {
|
||||
inset-inline-end: 50%;
|
||||
inset-inline-start: 50%;
|
||||
--tw-translate-x: -50%;
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
.toast:where(.toast-center):where([dir="rtl"], [dir="rtl"] *) {
|
||||
--tw-translate-x: 50%;
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
.toast:where(.toast-end) {
|
||||
inset-inline-end: 0px;
|
||||
inset-inline-start: auto;
|
||||
--tw-translate-x: 0px;
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
.toast:where(.toast-bottom) {
|
||||
bottom: 0px;
|
||||
top: auto;
|
||||
--tw-translate-y: 0px;
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
.toast:where(.toast-middle) {
|
||||
bottom: auto;
|
||||
top: 50%;
|
||||
--tw-translate-y: -50%;
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
.toast:where(.toast-top) {
|
||||
bottom: auto;
|
||||
top: 0px;
|
||||
--tw-translate-y: 0px;
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
|
@ -2746,6 +2907,10 @@ input.tab:checked + .tab-content,
|
|||
bottom: 0px;
|
||||
}
|
||||
|
||||
.z-30 {
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
.z-50 {
|
||||
z-index: 50;
|
||||
}
|
||||
|
|
@ -3149,16 +3314,31 @@ input.tab:checked + .tab-content,
|
|||
background-color: rgb(246 246 246 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
|
||||
.bg-orange-400 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(251 146 60 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
|
||||
.bg-primary-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(206 241 255 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
|
||||
.bg-primary-400 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(38 198 255 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
|
||||
.bg-primary-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 159 255 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
|
||||
.bg-red-400 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 153 153 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 119 119 / var(--tw-bg-opacity, 1));
|
||||
|
|
@ -3298,6 +3478,10 @@ input.tab:checked + .tab-content,
|
|||
padding-right: 0.25rem;
|
||||
}
|
||||
|
||||
.pt-12 {
|
||||
padding-top: 3rem;
|
||||
}
|
||||
|
||||
.pt-20 {
|
||||
padding-top: 5rem;
|
||||
}
|
||||
|
|
@ -3446,6 +3630,12 @@ input.tab:checked + .tab-content,
|
|||
opacity: 0;
|
||||
}
|
||||
|
||||
.shadow-lg {
|
||||
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.shadow-sm {
|
||||
--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
|
||||
|
|
@ -3460,6 +3650,14 @@ input.tab:checked + .tab-content,
|
|||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
.transition {
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
|
||||
.transition-colors {
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ templ FormBuilderNavTitle(form frm.Form) {
|
|||
|
||||
// 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-builder-nav" data-hx-swap-oob="true" class="fixed w-full p-2 flex gap-x-2 items-center bg-white z-30">
|
||||
<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{
|
||||
|
|
@ -100,7 +100,7 @@ templ FormBuilderNav(form frm.Form) {
|
|||
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"
|
||||
data-hx-target="#messages"
|
||||
>
|
||||
<!---->
|
||||
<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 <!---->
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ func FormBuilderNav(form frm.Form) templ.Component {
|
|||
templ_7745c5c3_Var5 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<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\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<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-30\"><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\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -240,7 +240,7 @@ func FormBuilderNav(form frm.Form) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" 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>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" data-hx-trigger=\"click\" data-hx-target=\"#messages\"><!----><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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
|
|||
82
ui/builder/builder_templ.txt
Normal file
82
ui/builder/builder_templ.txt
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
<section id=\"app-container h-2/3 max-h-1/3\">
|
||||
<section id=\"builder-main\" class=\"flex pt-20\"><!-- Left column -->
|
||||
<!-- Middle Column -->
|
||||
<!-- Right Column -->
|
||||
</section></section>
|
||||
<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\">
|
||||
</h3></div>
|
||||
<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-30\"><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\">
|
||||
</div></div>
|
||||
<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=\"
|
||||
\" data-hx-trigger=\"click\" data-hx-target=\"#messages\"><!----><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>
|
||||
<div id=\"settings-main\" class=\"hidden\"><form id=\"settings-form\" data-hx-put=\"
|
||||
\" data-hx-trigger=\"
|
||||
\" data-hx-swap-oob=\"true\"><div class=\"pt-3\"><label for=\"form-name-field\" class=\"pr-1\">Form name</label>
|
||||
</div><input id=\"form-name-field\" name=\"name\" type=\"text\" class=\"w-full rounded-md\" value=\"
|
||||
\" placeholder=\"Enter a good name for your form\" autocomplete=\"off\" _=\"
|
||||
\"></form></div>
|
||||
<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\">
|
||||
</section>
|
||||
<div id=\"form-fields\" class=\"active-section\">
|
||||
</div>
|
||||
<div id=\"form-fields-form\" data-hx-swap-oob=\"true\" class=\"flex flex-col gap-3 w-full\">
|
||||
<div class=\"w-full border-b pb-2\"></div><form data-hx-put=\"
|
||||
\" 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\">
|
||||
<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=\"
|
||||
\"><div class=\"group flex items-center gap-x-0.5 py-1.5 pr-1\"><!-- field item --><a href=\"#\" class=\"w-full\" _=\"
|
||||
\"><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\">
|
||||
</p></div></div></a>
|
||||
<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\">
|
||||
</div></div></div>
|
||||
</div></form></div>
|
||||
<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>
|
||||
<div class=\"group flex items-center my-1.5 pr-1\"><a href=\"#\" class=\"w-full\"><div class=\"flex flex-col\"><div tabindex=\"
|
||||
\" 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=\"
|
||||
\" data-hx-trigger=\"click\" data-hx-vals=\"
|
||||
\" data-hx-swap=\"none\">
|
||||
</div></div></a></div>
|
||||
</div>
|
||||
</section>
|
||||
<form id=\"fields-form\" data-hx-put=\"
|
||||
\" data-hx-trigger=\"
|
||||
\" data-hx-swap=\"none\" data-hx-swap-oob=\"true\">
|
||||
<div id=\"
|
||||
\" class=\"hidden\"><div class=\"mx-auto w-full border-gray-300 transition-colors pb-3\">
|
||||
</div><div id=\"
|
||||
\" 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\">
|
||||
</div></div><!-- Form fields settings configurations -->
|
||||
<!-- Form fields logic configurations -->
|
||||
</div>
|
||||
</form>
|
||||
<div id=\"
|
||||
\" class=\"active-configurator-section\"><input name=\"
|
||||
\" type=\"hidden\" value=\"
|
||||
\"> <input name=\"
|
||||
\" type=\"hidden\" value=\"
|
||||
\"> <input name=\"
|
||||
\" type=\"hidden\" value=\"
|
||||
\">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class=\"pt-9 pb-3 divide-y\"><label for=\"delete-field\" class=\"pr-1\">Danger zone</label></div>
|
||||
</div>
|
||||
<div id=\"
|
||||
\" class=\"flex flex-col gap-5 hidden\"><div><div data-hx-get=\"
|
||||
\" data-hx-trigger=\"
|
||||
\" data-hx-swap=\"innerHTML\" data-hx-target=\"
|
||||
\" data-hx-on:htmx:config-request=\"event.detail.parameters['id'] = event.detail.triggeringEvent.detail.value\">
|
||||
</div></div><div>
|
||||
</div><div><div id=\"
|
||||
\">
|
||||
</div></div><div><p class=\"pb-3\">Choose action(s)</p>
|
||||
</div></div>
|
||||
<input id=\"
|
||||
\" name=\"
|
||||
\" type=\"text\" class=\"bg-gray-50\" placeholder=\"Enter a value\"
|
||||
value=\"
|
||||
\"
|
||||
_=\"
|
||||
\">
|
||||
24
ui/collector/collector_templ.txt
Normal file
24
ui/collector/collector_templ.txt
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<div class=\"bg-sky-100 h-full\"><section id=\"app-container\" class=\"container mx-auto\">
|
||||
</section></div>
|
||||
<section id=\"builder-main-middle-col\" class=\"w-full h-full overflow-y-scroll overscroll-auto p-4 bg-gray-50 border-y-1 max-h-9/10\"><div class=\"mockup-browser border-base-300 border overflow-visible\"><div class=\"mockup-browser-toolbar\"><div class=\"input border-base-300 border\">https://your-form-domain.com/form</div></div><div class=\"flex border-base-300 bg-sky-100 border-t px-4 py-8\">
|
||||
</div></div></section>
|
||||
<div id=\"form-viewer\"
|
||||
class=\"flex flex-col w-full mx-auto justify-top\"
|
||||
class=\"flex flex-col w-full md:w-2/3 lg:w-1/2 mx-auto justify-top py-6 lg:py-12 px-3 lg:px-0\"
|
||||
data-hx-swap-oob=\"true\"><div id=\"form-metadata\" data-data=\"
|
||||
\"></div><h1 class=\"rounded relative mb-8 font-black text-slate-700 text-2xl md:text-4xl lg:text-5xl\">
|
||||
</h1><div id=\"form-input\" hx-ext=\"response-targets\"><div id=\"errors\"></div><form _=\"on field_change(field_id, value) formValueChanged(field_id, value)\" class=\"flex flex-col gap-3\"
|
||||
data-hx-post=\"
|
||||
\" data-hx-target-400=\"#errors\"
|
||||
data-hx-disabled-elt=\"find #submit_button\" data-hx-indicator=\"#spinner\">
|
||||
<input name=\"short_code\" type=\"hidden\" value=\"
|
||||
\">
|
||||
<div class=\"py-3\"></div><button id=\"submit_button\" class=\"btn bg-primary-500 hover:bg-primary-400 cursor-pointer justify-center uppercase disabled:bg-gray-200 disabled:cursor-not-allowed\"
|
||||
disabled
|
||||
>Submit <img id=\"spinner\" class=\"htmx-indicator\" src=\"
|
||||
\"></button></form></div>
|
||||
<div id=\"collector_footer\" class=\"pt-6 text-center\">
|
||||
</div>
|
||||
</div>
|
||||
<div class=\"bg-sky-100 h-screen\"><section id=\"app-container\" class=\"container mx-auto\"><p class=\"text-4xl pt-9\">Thank you!</p></section></div>
|
||||
<p class=\"underline\"></p>
|
||||
|
|
@ -13,6 +13,60 @@ import (
|
|||
|
||||
var loadDependenciesOnce = templ.NewOnceHandle()
|
||||
|
||||
type ToastType string
|
||||
type ToastPosition string
|
||||
|
||||
const (
|
||||
ToastPositionTop = "top"
|
||||
ToastPositionBottom = "bottom"
|
||||
)
|
||||
|
||||
const (
|
||||
ToastTypeSuccess = "success"
|
||||
ToastTypeWarning = "warning"
|
||||
ToastTypeError = "error"
|
||||
)
|
||||
|
||||
type ToastArgs struct {
|
||||
Message string
|
||||
Position string
|
||||
Type ToastType
|
||||
}
|
||||
|
||||
// Toast shows a toast message on screen
|
||||
templ Toast(arg ToastArgs) {
|
||||
<div
|
||||
class="toast toast-top toast-center pt-12 z-50 opacity-0"
|
||||
_="on intersection(intersecting) transition my opacity from 0 to 1 over 500 milliseconds
|
||||
then wait 3s
|
||||
then transition my opacity from 1 to 0 over 500 milliseconds"
|
||||
>
|
||||
switch arg.Type {
|
||||
case ToastTypeSuccess:
|
||||
<div
|
||||
class="card rounded-md bg-base-100 shadow-lg p-2 text-white bg-primary-400"
|
||||
_="on click add .hidden to #messages"
|
||||
>
|
||||
<span>{ arg.Message }</span>
|
||||
</div>
|
||||
case ToastTypeWarning:
|
||||
<div
|
||||
class="card rounded-md bg-base-100 shadow-lg p-2 text-white bg-orange-400"
|
||||
_="on click add .hidden to #messages"
|
||||
>
|
||||
<span>{ arg.Message }</span>
|
||||
</div>
|
||||
case ToastTypeError:
|
||||
<div
|
||||
class="card rounded-md bg-base-100 shadow-lg p-2 text-white bg-red-400"
|
||||
_="on click add .hidden to #messages"
|
||||
>
|
||||
<span>{ arg.Message }</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
templ HeroIcon(style string, name string) {
|
||||
<svg data-src={ fmt.Sprintf("https://unpkg.com/heroicons/20/%s/%s.svg", style, name) } class="h-5 w-5"></svg>
|
||||
}
|
||||
|
|
@ -322,6 +376,7 @@ templ App(pageTitle string) {
|
|||
<html lang="eng">
|
||||
@Head(pageTitle)
|
||||
<body>
|
||||
<div id="messages"></div>
|
||||
<main>
|
||||
{ children... }
|
||||
</main>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
84
ui/common_templ.txt
Normal file
84
ui/common_templ.txt
Normal file
File diff suppressed because one or more lines are too long
3
ui/errors_templ.txt
Normal file
3
ui/errors_templ.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<div id=\"
|
||||
\" data-hx-swap-oob=\"true\" class=\"text-red-400\">
|
||||
</div>
|
||||
46
ui/fields/fields_templ.txt
Normal file
46
ui/fields/fields_templ.txt
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<span class=\"text-red-500 required-dot\">*</span>
|
||||
<label for=\"
|
||||
\" class=\"text-slate-700 text-xl pb-5\">
|
||||
|
||||
</label>
|
||||
<label for=\"
|
||||
\" class=\"text-slate-700 text-xl\"><div class=\"pb-5\"><span>
|
||||
</span> <span class=\"invisible group-has-[:required]:visible text-red-500 required-dot\">*</span></div>
|
||||
</label><div id=\"
|
||||
\" class=\"text-red-400\"></div>
|
||||
<div class=\"p-1 rounded-md flex items-center justify-center bg-blue-100 text-blue-900 ml-2\">
|
||||
<!-- hericons: bards-3-bottom-left --> <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"size-6\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25H12\"></path></svg>
|
||||
<!-- heroicons: chevron-up-down --> <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"size-6\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M8.25 15 12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9\"></path></svg>
|
||||
<!-- heroicons: outline star --> <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"size-6\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z\"></path></svg>
|
||||
</div>
|
||||
<div id=\"
|
||||
\"
|
||||
class=\"group hidden\"
|
||||
class=\"group\"
|
||||
><div class=\"flex flex-col bg-sky-200 rounded-xl p-6 border-red-500 group-has-[:user-invalid]:border-2\">
|
||||
</div></div>
|
||||
<input id=\"
|
||||
\" name=\"
|
||||
\" placeholder=\"
|
||||
\" type=\"text\" autocomplete=\"off\"
|
||||
required
|
||||
_=\"
|
||||
\" class=\"flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm focus:outline-none focus:ring-2 focus:border-2 focus:ring-opacity-100 px-4 py-2 text-base resize-y block rounded-xl bg-sky-50\"><div hx-swap-oob=\"true\" id=\"
|
||||
\"></div>
|
||||
<textarea id=\"
|
||||
\" name=\"
|
||||
\" class=\"flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100 px-4 py-2 text-base resize-y block rounded-xl bg-sky-50\" placeholder=\"
|
||||
\" autocomplete=\"off\"
|
||||
required
|
||||
_=\"
|
||||
\" rows=\"3\"></textarea><div id=\"
|
||||
\" class=\"text-red-400\"></div>
|
||||
<div class=\"flex gap-3\">
|
||||
<label class=\"w-full cursor-pointer truncate\">
|
||||
Single-line text
|
||||
Multi-line text
|
||||
Single select
|
||||
Multi select
|
||||
Single Choice
|
||||
Single Choice (spaced)
|
||||
</label></div>
|
||||
28
ui/fields/single_choice_templ.txt
Normal file
28
ui/fields/single_choice_templ.txt
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<div class=\"w-full\"><div class=\"join w-full\">
|
||||
<label class=\"join-item h-auto flex-grow shrink btn has-[:checked]:bg-sky-400 first:border-x-0 last:border-r-0 border-y-0 border-black bg-sky-50 py-3\"><!-- the input field is hidden with opacity, width, and height, but available for HTML validation when 'required' --><input class=\"opacity-0 w-0 h-0\" type=\"radio\" id=\"
|
||||
\" name=\"
|
||||
\" aria-label=\"
|
||||
\" value=\"
|
||||
\"
|
||||
required
|
||||
_=\"
|
||||
\">
|
||||
</label>
|
||||
</div>
|
||||
<div class=\"flex flex-grow w-full text-center text-gray-400\">
|
||||
<div class=\"w-full first:text-left last:text-right\">
|
||||
</div>
|
||||
</div>
|
||||
</div><div id=\"
|
||||
\" class=\"text-red-400\"></div>
|
||||
<div class=\"w-full\"><div class=\"grid grid-cols-1 xl:grid-cols-2 gap-8\">
|
||||
<label class=\"flex gap-2 w-auto h-auto btn bg-sky-50 has-[:checked]:bg-sky-400 has-[:checked]:border-sky-500 py-3\"><span>
|
||||
</span> <span class=\"grow\"></span> <input type=\"radio\" class=\"peer opacity-0 w-0 h-0\" name=\"
|
||||
\" aria-label=\"
|
||||
\" value=\"
|
||||
\"
|
||||
required
|
||||
_=\"
|
||||
\"> <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\" class=\"size-7 invisible peer-checked:visible bg-sky-200 rounded-full p-1\"><path fill-rule=\"evenodd\" d=\"M19.916 4.626a.75.75 0 0 1 .208 1.04l-9 13.5a.75.75 0 0 1-1.154.114l-6-6a.75.75 0 0 1 1.06-1.06l5.353 5.353 8.493-12.74a.75.75 0 0 1 1.04-.207Z\" clip-rule=\"evenodd\"></path></svg></label>
|
||||
</div></div><div id=\"
|
||||
\" class=\"text-red-400\"></div>
|
||||
25
ui/selector/selector_templ.txt
Normal file
25
ui/selector/selector_templ.txt
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<label for=\"
|
||||
\" class=\"
|
||||
\">
|
||||
|
||||
<span class=\"text-red-500 required-dot\">*</span>
|
||||
</label>
|
||||
<select id=\"
|
||||
\" name=\"
|
||||
\"
|
||||
multiple
|
||||
required=\"true\"
|
||||
data-placeholder=\"
|
||||
\"
|
||||
_=\"
|
||||
\"
|
||||
><option></option></select><div id=\"
|
||||
\" data-args=\"
|
||||
\" _=\"
|
||||
\"></div><!-- when OptionsContent\n are present, their components render inside of this element when chosen -->
|
||||
<!-- content displayed when specific options are selected --> <div id=\"selected-content\">
|
||||
<div id=\"
|
||||
\" class=\"hidden\">
|
||||
</div>
|
||||
</div>
|
||||
<script>\n\t\t/**\n\t\t * Function to wait for predicates.\n\t\t * @param {function() : Promise.<Boolean> | function() : Boolean} predicate\n\t\t * - A function that returns or resolves a bool\n\t\t * @param {Number} [timeout] - Optional maximum waiting time in ms after rejected\n\t\t */\n\t\tfunction waitFor(predicate, timeout) {\n\t\t return new Promise((resolve, reject) => {\n\t\t let running = true;\n\n\t\t const check = async () => {\n\t\t const res = await predicate();\n\t\t if (res) return resolve(res);\n\t\t if (running) setTimeout(check, 100);\n\t\t };\n\n\t\t check();\n\n\t\t if (!timeout) return;\n\t\t setTimeout(() => {\n\t\t running = false;\n\t\t reject();\n\t\t }, timeout);\n\t\t });\n\t\t}\n\n\t\tasync function initializeSelect(dataElementID) {\n\t\t\t// wait 10 seconds for choices.js to load \n\t\t\ttry {\n\t\t\t\tawait waitFor(() => typeof Choices !== 'undefined', 10000);\n\t\t\t} catch {\n\t\t\t\tconsole.log(\"unable to load choices!\")\n\t\t\t}\n\n\t\t\tconst argsElement = document.getElementById(dataElementID);\n\t\t\tconst args = JSON.parse(argsElement.getAttribute('data-args'));\n\t\t\tif (args.Options != null) {\n\t\t\t\targs.Options.map((option) => {\n\t\t\t\t\tif (option.value === \"\") {\n\t\t\t\t\t\tconsole.error(`option id: (${option.id}) label: (${option.label}) should have a value! ensure every option provided to SelectorArgs has a 'Value'`)\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\tvar searchEnabled = true\n\t\t\tif (args.SearchDisabled) {\n\t\t\t\tsearchEnabled = false\n\t\t\t}\n\n\t\t\tvar element = document.getElementById(args.ID)\n\t\t\tvar choices = new Choices(element, {\n\t\t\t\tchoices: args.Options != null ? args.Options : [],\n\t\t\t\tduplicateItemsAllowed: false,\n\t\t\t\tplaceholder: true,\n\t\t\t\tplaceholderValue: args.Placeholder,\n\t\t\t\tsearchEnabled: searchEnabled,\n\t\t\t\teditItems: args.EditItems,\n\t\t\t\tremoveItems: true,\n\t\t\t\tremoveItemButton: true,\n\t\t\t\taddChoices: true,\n\t\t\t\taddItems: true,\n\t\t\t\titemSelectText: \"\",\n\t\t\t\tshouldSort: false,\n\t\t\t});\n\t\t\telement._choices = choices;\n\t\t\n\t\t\t/**\n\t\t\t* When the user's selection(s) have changed, notify a DOM element with an event\n\t\t\t*/\n\t\t\tfunction notifySelected(event) {\n\t\t\t\tif (args.SelectionChangeEvent == \"\") {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tvar element = document.getElementById(args.ID)\n\t\t\t\tif (element == null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\telement.dispatchEvent(new CustomEvent(args.SelectionChangeEvent, {bubbles: true, detail: { id: args.ID, value: event.detail.value}}))\n\t\t\t}\n\t\t\t\n\t\t\telement.addEventListener('change', notifySelected, false);\n\t\t}\n\t</script>
|
||||
1
ui/trigger_rebuild_templ.txt
Normal file
1
ui/trigger_rebuild_templ.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
<div>000164</div>
|
||||
Loading…
Reference in a new issue