bank20baht-ui 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/baht-form.d.ts +17 -1
- package/dist/components/baht-form.js +178 -152
- package/dist/components/baht-workflow.d.ts +11 -0
- package/dist/components/baht-workflow.js +135 -101
- package/dist/components/base.d.ts +15 -1
- package/dist/components/base.js +52 -40
- package/dist/components/field-base.d.ts +7 -0
- package/dist/components/field-base.js +38 -23
- package/dist/components/primitives/baht-accordion.js +3 -1
- package/dist/components/primitives/baht-alert.js +3 -1
- package/dist/components/primitives/baht-bar-chart.d.ts +1 -12
- package/dist/components/primitives/baht-bar-chart.js +152 -137
- package/dist/components/primitives/baht-button.d.ts +13 -0
- package/dist/components/primitives/baht-button.js +13 -10
- package/dist/components/primitives/baht-checkbox.js +3 -1
- package/dist/components/primitives/baht-currency.js +20 -19
- package/dist/components/primitives/baht-datepicker.js +61 -55
- package/dist/components/primitives/baht-donut-chart.js +16 -15
- package/dist/components/primitives/baht-error-state.d.ts +1 -1
- package/dist/components/primitives/baht-error-state.js +3 -4
- package/dist/components/primitives/baht-file.js +9 -3
- package/dist/components/primitives/baht-filter.js +3 -1
- package/dist/components/primitives/baht-heatmap.js +61 -44
- package/dist/components/primitives/baht-input.js +11 -9
- package/dist/components/primitives/baht-keyvalue-editor.d.ts +1 -1
- package/dist/components/primitives/baht-keyvalue-editor.js +25 -20
- package/dist/components/primitives/baht-line-chart.js +70 -64
- package/dist/components/primitives/baht-list.js +3 -1
- package/dist/components/primitives/baht-meter.js +11 -12
- package/dist/components/primitives/baht-modal.js +19 -15
- package/dist/components/primitives/baht-multiselect.js +3 -1
- package/dist/components/primitives/baht-number.js +13 -13
- package/dist/components/primitives/baht-otp.js +9 -3
- package/dist/components/primitives/baht-pagination.js +15 -5
- package/dist/components/primitives/baht-radio.js +6 -2
- package/dist/components/primitives/baht-range.js +3 -1
- package/dist/components/primitives/baht-rating.js +12 -4
- package/dist/components/primitives/baht-reference-picker.d.ts +3 -2
- package/dist/components/primitives/baht-reference-picker.js +47 -34
- package/dist/components/primitives/baht-repeater.js +10 -3
- package/dist/components/primitives/baht-section.d.ts +2 -0
- package/dist/components/primitives/baht-section.js +15 -11
- package/dist/components/primitives/baht-select.js +28 -19
- package/dist/components/primitives/baht-sparkline.js +9 -9
- package/dist/components/primitives/baht-stat.js +4 -4
- package/dist/components/primitives/baht-table.d.ts +11 -11
- package/dist/components/primitives/baht-table.js +79 -45
- package/dist/components/primitives/baht-text.d.ts +2 -0
- package/dist/components/primitives/baht-text.js +21 -17
- package/dist/components/primitives/baht-textarea.js +10 -8
- package/dist/components/primitives/baht-toast.js +9 -3
- package/dist/components/primitives/baht-toggle.js +3 -1
- package/dist/components/primitives/baht-tooltip.d.ts +2 -2
- package/dist/components/primitives/baht-tooltip.js +8 -8
- package/dist/components/primitives/baht-typeahead.js +17 -7
- package/dist/components/primitives/baht-upload.d.ts +6 -3
- package/dist/components/primitives/baht-upload.js +116 -93
- package/dist/components/primitives/charts.js +34 -35
- package/dist/core/defaults.js +13 -3
- package/dist/core/display.d.ts +2 -2
- package/dist/core/display.js +17 -8
- package/dist/core/engine/conditions.js +51 -53
- package/dist/core/engine/evaluate.js +145 -136
- package/dist/core/engine/expressions.js +25 -22
- package/dist/core/engine/graph.d.ts +1 -1
- package/dist/core/engine/scripts.js +49 -36
- package/dist/core/flags.js +19 -16
- package/dist/core/types.d.ts +3 -3
- package/dist/core/validators.js +100 -98
- package/dist/css/components.css +57 -2
- package/dist/custom-elements.json +745 -185
- package/dist/hydrate.js +1 -1
- package/dist/overlay.js +33 -29
- package/dist/react.d.ts +4 -4
- package/dist/tokens.css +57 -2
- package/dist/vscode.html-custom-data.json +7 -2
- package/dist/web-types.json +17 -7
- package/package.json +4 -2
|
@@ -26,7 +26,7 @@ export declare class BahtForm extends BahtElement {
|
|
|
26
26
|
* but getData() still returns the FULL form — the server always validates
|
|
27
27
|
* the complete schema, views never change the submit payload.
|
|
28
28
|
*/
|
|
29
|
-
view?: string;
|
|
29
|
+
view?: string | undefined;
|
|
30
30
|
/**
|
|
31
31
|
* Feature-flag values (spec 01 §12) — fields whose `flag` is off behave as
|
|
32
32
|
* if they were never in the schema: no render, no validation, no payload.
|
|
@@ -58,6 +58,11 @@ export declare class BahtForm extends BahtElement {
|
|
|
58
58
|
private warnedViews;
|
|
59
59
|
/** applyFlags result, memoized on (form, flags) identity. */
|
|
60
60
|
private flagCache;
|
|
61
|
+
/**
|
|
62
|
+
* `form` as the host may actually have left it: a framework can connect the element
|
|
63
|
+
* before it binds the property, so internally it is optional until then.
|
|
64
|
+
*/
|
|
65
|
+
private get assignedForm();
|
|
61
66
|
private get effectiveForm();
|
|
62
67
|
protected willUpdate(changed: Map<PropertyKey, unknown>): void;
|
|
63
68
|
/**
|
|
@@ -73,6 +78,15 @@ export declare class BahtForm extends BahtElement {
|
|
|
73
78
|
*/
|
|
74
79
|
private clearDependents;
|
|
75
80
|
private get sortedFields();
|
|
81
|
+
/**
|
|
82
|
+
* Visibility as of the last completed render, by key. A field that was
|
|
83
|
+
* hidden there and is visible now gets `baht-field--revealed` for one
|
|
84
|
+
* render so the stylesheet can fade it in; fields visible from the first
|
|
85
|
+
* render never animate (a form should not shimmer on mount).
|
|
86
|
+
*/
|
|
87
|
+
private lastVisible;
|
|
88
|
+
private nextVisible;
|
|
89
|
+
private revealClass;
|
|
76
90
|
private stateFor;
|
|
77
91
|
private valueFor;
|
|
78
92
|
/** Debounced keystrokes still in a control land in `edits` before anyone reads them. */
|
|
@@ -116,6 +130,8 @@ export declare class BahtForm extends BahtElement {
|
|
|
116
130
|
* `renderField`/`.baht-error`). No-op for 'change'/'submit' modes. */
|
|
117
131
|
private onFieldBlur;
|
|
118
132
|
private renderField;
|
|
133
|
+
private uploaderFor;
|
|
134
|
+
private renderFieldMsg;
|
|
119
135
|
/**
|
|
120
136
|
* Resolve the descriptor at event time — it may have changed since the control
|
|
121
137
|
* was first rendered (the same node is reused across renders, see render()).
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { nothing as
|
|
2
|
-
import { property as
|
|
3
|
-
import { repeat as
|
|
4
|
-
import { html as
|
|
5
|
-
import { BahtElement as
|
|
6
|
-
import { TAG_FOR_TYPE as
|
|
1
|
+
import { nothing as g, html as p } from "lit";
|
|
2
|
+
import { property as d, state as m, customElement as x } from "lit/decorators.js";
|
|
3
|
+
import { repeat as E } from "lit/directives/repeat.js";
|
|
4
|
+
import { html as $ } from "lit/static-html.js";
|
|
5
|
+
import { BahtElement as P, hasDom as O } from "./base.js";
|
|
6
|
+
import { TAG_FOR_TYPE as T, WIDTH_CLASS as B, staticTag as I } from "./primitives/tag-map.js";
|
|
7
7
|
import { defaultValue as C } from "../core/defaults.js";
|
|
8
|
-
import { t as
|
|
9
|
-
import { validateField as
|
|
10
|
-
import { resolveViewFields as
|
|
11
|
-
import { applyFlags as
|
|
12
|
-
import { hasScripts as
|
|
13
|
-
import { displayValue as
|
|
8
|
+
import { t as D } from "../core/i18n.js";
|
|
9
|
+
import { validateField as _ } from "../core/validators.js";
|
|
10
|
+
import { resolveViewFields as M } from "../core/views.js";
|
|
11
|
+
import { applyFlags as j } from "../core/flags.js";
|
|
12
|
+
import { hasScripts as w, runScripts as F, createScriptContext as L, MAX_SCRIPT_DEPTH as V } from "../core/engine/scripts.js";
|
|
13
|
+
import { displayValue as A } from "../core/display.js";
|
|
14
14
|
import "./primitives/baht-input.js";
|
|
15
15
|
import "./primitives/baht-textarea.js";
|
|
16
16
|
import "./primitives/baht-number.js";
|
|
@@ -25,21 +25,26 @@ import "./primitives/baht-file.js";
|
|
|
25
25
|
import "./primitives/baht-text.js";
|
|
26
26
|
import "./primitives/baht-section.js";
|
|
27
27
|
import "./primitives/baht-repeater.js";
|
|
28
|
-
var
|
|
29
|
-
for (var r = i > 1 ? void 0 : i ?
|
|
30
|
-
(a = t[o]) && (r = (i ? a(
|
|
31
|
-
return i && r &&
|
|
28
|
+
var R = Object.defineProperty, U = Object.getOwnPropertyDescriptor, h = (t, s, e, i) => {
|
|
29
|
+
for (var r = i > 1 ? void 0 : i ? U(s, e) : s, o = t.length - 1, a; o >= 0; o--)
|
|
30
|
+
(a = t[o]) && (r = (i ? a(s, e, r) : a(r)) || r);
|
|
31
|
+
return i && r && R(s, e, r), r;
|
|
32
32
|
};
|
|
33
|
-
let n = class extends
|
|
33
|
+
let n = class extends P {
|
|
34
34
|
constructor() {
|
|
35
|
-
super(...arguments), this.value = {}, this.registry = {}, this.showOptional = !1, this.options = {}, this.fieldStates = {}, this.flags = {}, this.readonly = !1, this.edits = {}, this.errors = [], this.scriptStates = {}, this.fieldMessages = {}, this.formMessages = [], this.scriptContext = null, this.scriptContextForm = null, this.scriptDepth = 0, this.loadedForm = null, this.dynamicOptions = /* @__PURE__ */ new Map(), this.optionsSnapshots = /* @__PURE__ */ new Map(), this.optionsEpoch = /* @__PURE__ */ new Map(), this.warnedViews = /* @__PURE__ */ new Set(), this.flagCache = {};
|
|
35
|
+
super(...arguments), this.value = {}, this.registry = {}, this.showOptional = !1, this.options = {}, this.fieldStates = {}, this.flags = {}, this.readonly = !1, this.edits = {}, this.errors = [], this.scriptStates = {}, this.fieldMessages = {}, this.formMessages = [], this.scriptContext = null, this.scriptContextForm = null, this.scriptDepth = 0, this.loadedForm = null, this.dynamicOptions = /* @__PURE__ */ new Map(), this.optionsSnapshots = /* @__PURE__ */ new Map(), this.optionsEpoch = /* @__PURE__ */ new Map(), this.warnedViews = /* @__PURE__ */ new Set(), this.flagCache = {}, this.lastVisible = {}, this.nextVisible = {};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* `form` as the host may actually have left it: a framework can connect the element
|
|
39
|
+
* before it binds the property, so internally it is optional until then.
|
|
40
|
+
*/
|
|
41
|
+
get assignedForm() {
|
|
42
|
+
return this.form;
|
|
36
43
|
}
|
|
37
44
|
get effectiveForm() {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
flags: this.flags,
|
|
41
|
-
out: L(this.form, this.flags)
|
|
42
|
-
}), this.flagCache.out) : this.form;
|
|
45
|
+
const t = this.assignedForm;
|
|
46
|
+
if (t)
|
|
47
|
+
return (this.flagCache.form !== t || this.flagCache.flags !== this.flags) && (this.flagCache = { form: t, flags: this.flags, out: j(t, this.flags) }), this.flagCache.out;
|
|
43
48
|
}
|
|
44
49
|
willUpdate(t) {
|
|
45
50
|
t.has("value") && (this.edits = {}), (t.has("form") || t.has("flags")) && (this.dynamicOptions.clear(), this.optionsSnapshots.clear(), this.optionsEpoch.clear()), t.has("form") && (this.scriptStates = {}, this.fieldMessages = {}, this.formMessages = [], this.scriptContext = null), (t.has("form") || t.has("flags") || t.has("value") || t.has("registry")) && this.syncDynamicOptions();
|
|
@@ -50,16 +55,16 @@ let n = class extends T {
|
|
|
50
55
|
* dropped by epoch).
|
|
51
56
|
*/
|
|
52
57
|
syncDynamicOptions() {
|
|
53
|
-
var
|
|
58
|
+
var s, e;
|
|
54
59
|
const t = this.getData();
|
|
55
|
-
for (const i of ((
|
|
60
|
+
for (const i of ((s = this.effectiveForm) == null ? void 0 : s.fields) ?? []) {
|
|
56
61
|
if (!i.options_from) continue;
|
|
57
62
|
const r = JSON.stringify((i.options_deps ?? []).map((c) => t[c] ?? null));
|
|
58
63
|
if (this.optionsSnapshots.get(i.key) === r) continue;
|
|
59
64
|
this.optionsSnapshots.set(i.key, r);
|
|
60
65
|
const o = (this.optionsEpoch.get(i.key) ?? 0) + 1;
|
|
61
66
|
this.optionsEpoch.set(i.key, o);
|
|
62
|
-
const a = (
|
|
67
|
+
const a = (e = this.registry.options) == null ? void 0 : e[i.options_from];
|
|
63
68
|
if (!a) {
|
|
64
69
|
console.warn(`[bahtui] unknown options fetcher "${i.options_from}" — skipped`), this.dynamicOptions.set(i.key, []);
|
|
65
70
|
continue;
|
|
@@ -79,27 +84,31 @@ let n = class extends T {
|
|
|
79
84
|
*/
|
|
80
85
|
clearDependents(t) {
|
|
81
86
|
var i, r;
|
|
82
|
-
const
|
|
83
|
-
for (;
|
|
84
|
-
const o =
|
|
87
|
+
const s = [t], e = /* @__PURE__ */ new Set();
|
|
88
|
+
for (; s.length > 0; ) {
|
|
89
|
+
const o = s.shift();
|
|
85
90
|
for (const a of ((i = this.effectiveForm) == null ? void 0 : i.fields) ?? [])
|
|
86
|
-
!((r = a.options_deps) != null && r.includes(o)) ||
|
|
91
|
+
!((r = a.options_deps) != null && r.includes(o)) || e.has(a.key) || (e.add(a.key), this.edits = { ...this.edits, [a.key]: C(a) }, s.push(a.key));
|
|
87
92
|
}
|
|
88
93
|
}
|
|
89
94
|
get sortedFields() {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
const t = this.effectiveForm;
|
|
96
|
+
if (!t) return [];
|
|
97
|
+
const s = M(t, this.view);
|
|
98
|
+
return s || (this.view !== void 0 && !this.warnedViews.has(this.view) && (this.warnedViews.add(this.view), console.warn(`[bahtui] unknown view "${this.view}" — showing the full form`)), M(t) ?? []);
|
|
99
|
+
}
|
|
100
|
+
revealClass(t, s) {
|
|
101
|
+
return this.nextVisible[t] = s, s && this.lastVisible[t] === !1 ? " baht-field--revealed" : "";
|
|
93
102
|
}
|
|
94
103
|
stateFor(t) {
|
|
95
|
-
const
|
|
104
|
+
const s = this.fieldStates[t.key] ?? {
|
|
96
105
|
visible: !0,
|
|
97
106
|
enabled: !t.disabled,
|
|
98
107
|
readonly: !!t.readonly,
|
|
99
108
|
required: !!t.required,
|
|
100
109
|
computed: !1
|
|
101
|
-
},
|
|
102
|
-
return
|
|
110
|
+
}, e = this.scriptStates[t.key];
|
|
111
|
+
return e ? { ...s, ...e } : s;
|
|
103
112
|
}
|
|
104
113
|
valueFor(t) {
|
|
105
114
|
return Object.hasOwn(this.edits, t.key) ? this.edits[t.key] : Object.hasOwn(this.value, t.key) ? this.value[t.key] : C(t);
|
|
@@ -108,16 +117,16 @@ let n = class extends T {
|
|
|
108
117
|
/** Debounced keystrokes still in a control land in `edits` before anyone reads them. */
|
|
109
118
|
flushTyped() {
|
|
110
119
|
var t;
|
|
111
|
-
if (
|
|
112
|
-
for (const
|
|
113
|
-
(t =
|
|
120
|
+
if (O())
|
|
121
|
+
for (const s of this.querySelectorAll(":scope > .baht-form > .baht-field > *"))
|
|
122
|
+
(t = s.flushTyped) == null || t.call(s);
|
|
114
123
|
}
|
|
115
124
|
getData() {
|
|
116
|
-
var
|
|
125
|
+
var s;
|
|
117
126
|
this.flushTyped();
|
|
118
127
|
const t = {};
|
|
119
|
-
for (const
|
|
120
|
-
|
|
128
|
+
for (const e of ((s = this.effectiveForm) == null ? void 0 : s.fields) ?? [])
|
|
129
|
+
e.type === "label" || e.type === "section" || (t[e.key] = this.valueFor(e));
|
|
121
130
|
return t;
|
|
122
131
|
}
|
|
123
132
|
/**
|
|
@@ -126,20 +135,20 @@ let n = class extends T {
|
|
|
126
135
|
* already knows, and a store adapter would otherwise loop.
|
|
127
136
|
*/
|
|
128
137
|
setValue(t) {
|
|
129
|
-
const
|
|
138
|
+
const s = this.getData();
|
|
130
139
|
this.edits = { ...this.edits, ...t }, this.requestUpdate();
|
|
131
|
-
for (const
|
|
132
|
-
this.runChangeScripts(
|
|
140
|
+
for (const e of Object.keys(t))
|
|
141
|
+
this.runChangeScripts(e, s[e], t[e], !1);
|
|
133
142
|
}
|
|
134
143
|
/** Declarative + registry validators over the visible fields (no scripts). */
|
|
135
144
|
runValidators() {
|
|
136
|
-
const t = [],
|
|
137
|
-
for (const
|
|
138
|
-
if (
|
|
139
|
-
const i = this.stateFor(
|
|
145
|
+
const t = [], s = this.getData();
|
|
146
|
+
for (const e of this.sortedFields) {
|
|
147
|
+
if (e.type === "label" || e.type === "section") continue;
|
|
148
|
+
const i = this.stateFor(e);
|
|
140
149
|
if (!i.visible) continue;
|
|
141
|
-
const r = i.required === !!
|
|
142
|
-
t.push(...
|
|
150
|
+
const r = i.required === !!e.required ? e : { ...e, required: i.required };
|
|
151
|
+
t.push(..._(r, this.valueFor(e), this.registry, s));
|
|
143
152
|
}
|
|
144
153
|
return t;
|
|
145
154
|
}
|
|
@@ -149,23 +158,23 @@ let n = class extends T {
|
|
|
149
158
|
* `{ key: '$' }` errors so hosts see why.
|
|
150
159
|
*/
|
|
151
160
|
validate() {
|
|
152
|
-
const t = this.runValidators(),
|
|
153
|
-
if (
|
|
161
|
+
const t = this.runValidators(), s = this.effectiveForm;
|
|
162
|
+
if (s && w(s, "submit")) {
|
|
154
163
|
this.formMessages = [];
|
|
155
164
|
const i = F({
|
|
156
|
-
form:
|
|
165
|
+
form: s,
|
|
157
166
|
registry: this.registry,
|
|
158
|
-
g: this.g(
|
|
167
|
+
g: this.g(s),
|
|
159
168
|
on: "submit",
|
|
160
169
|
detail: { isLoading: !1, data: this.getData() }
|
|
161
170
|
}), r = this.formMessages.filter((o) => o.type === "error").map((o) => ({ key: "$", message: o.message }));
|
|
162
|
-
!i && r.length === 0 && r.push({ key: "$", message:
|
|
171
|
+
!i && r.length === 0 && r.push({ key: "$", message: D("scriptBlocked") }), t.push(...r);
|
|
163
172
|
}
|
|
164
173
|
this.errors = t;
|
|
165
|
-
const
|
|
174
|
+
const e = { valid: t.length === 0, errors: t };
|
|
166
175
|
return this.dispatchEvent(
|
|
167
|
-
new CustomEvent("baht-validity", { detail:
|
|
168
|
-
),
|
|
176
|
+
new CustomEvent("baht-validity", { detail: e, bubbles: !0, composed: !0 })
|
|
177
|
+
), e;
|
|
169
178
|
}
|
|
170
179
|
reset() {
|
|
171
180
|
this.edits = {}, this.value = {}, this.errors = [], this.scriptStates = {}, this.fieldMessages = {}, this.formMessages = [], this.requestUpdate();
|
|
@@ -173,29 +182,31 @@ let n = class extends T {
|
|
|
173
182
|
// ---- client scripts (spec 01 §14) ----
|
|
174
183
|
/** The `g` (g_form) for the current form — built lazily over this component's state. */
|
|
175
184
|
g(t) {
|
|
176
|
-
return this.scriptContext && this.scriptContextForm === t ? this.scriptContext : (this.scriptContextForm = t, this.scriptContext =
|
|
185
|
+
return this.scriptContext && this.scriptContextForm === t ? this.scriptContext : (this.scriptContextForm = t, this.scriptContext = L(t, {
|
|
177
186
|
getData: () => this.getData(),
|
|
178
|
-
setValue: (
|
|
179
|
-
|
|
187
|
+
setValue: (s, e) => {
|
|
188
|
+
this.applyScriptValue(s, e);
|
|
189
|
+
},
|
|
190
|
+
setState: (s, e) => {
|
|
180
191
|
this.scriptStates = {
|
|
181
192
|
...this.scriptStates,
|
|
182
|
-
[
|
|
193
|
+
[s]: { ...this.scriptStates[s], ...e }
|
|
183
194
|
};
|
|
184
195
|
},
|
|
185
|
-
resetState: (
|
|
186
|
-
if (
|
|
196
|
+
resetState: (s) => {
|
|
197
|
+
if (s === void 0) {
|
|
187
198
|
this.scriptStates = {};
|
|
188
199
|
return;
|
|
189
200
|
}
|
|
190
|
-
const s =
|
|
191
|
-
|
|
201
|
+
const { [s]: e, ...i } = this.scriptStates;
|
|
202
|
+
this.scriptStates = i;
|
|
192
203
|
},
|
|
193
|
-
setFieldMessage: (
|
|
194
|
-
const i =
|
|
195
|
-
|
|
204
|
+
setFieldMessage: (s, e) => {
|
|
205
|
+
const { [s]: i, ...r } = this.fieldMessages;
|
|
206
|
+
this.fieldMessages = e ? { ...r, [s]: e } : r;
|
|
196
207
|
},
|
|
197
|
-
addFormMessage: (
|
|
198
|
-
this.formMessages = [...this.formMessages,
|
|
208
|
+
addFormMessage: (s) => {
|
|
209
|
+
this.formMessages = [...this.formMessages, s];
|
|
199
210
|
},
|
|
200
211
|
clearFormMessages: () => {
|
|
201
212
|
this.formMessages = [];
|
|
@@ -203,22 +214,22 @@ let n = class extends T {
|
|
|
203
214
|
}), this.scriptContext);
|
|
204
215
|
}
|
|
205
216
|
/** A script wrote a value: same path as a user edit (emit + cascade), minus the formatter. */
|
|
206
|
-
applyScriptValue(t,
|
|
207
|
-
const
|
|
208
|
-
this.edits = { ...this.edits, [t]:
|
|
217
|
+
applyScriptValue(t, s) {
|
|
218
|
+
const e = this.getData()[t];
|
|
219
|
+
this.edits = { ...this.edits, [t]: s }, this.clearDependents(t), this.syncDynamicOptions(), this.dispatchEvent(
|
|
209
220
|
new CustomEvent("baht-change", {
|
|
210
|
-
detail: { form_name: this.form.form_name, key: t, value:
|
|
221
|
+
detail: { form_name: this.form.form_name, key: t, value: s, data: this.getData() },
|
|
211
222
|
bubbles: !0,
|
|
212
223
|
composed: !0
|
|
213
224
|
})
|
|
214
|
-
), this.runChangeScripts(t,
|
|
225
|
+
), this.runChangeScripts(t, e, s, !1);
|
|
215
226
|
}
|
|
216
|
-
runChangeScripts(t,
|
|
227
|
+
runChangeScripts(t, s, e, i) {
|
|
217
228
|
const r = this.effectiveForm;
|
|
218
|
-
if (!(!r || !
|
|
219
|
-
if (this.scriptDepth >=
|
|
229
|
+
if (!(!r || !w(r, "change", t))) {
|
|
230
|
+
if (this.scriptDepth >= V) {
|
|
220
231
|
console.warn(
|
|
221
|
-
`[bahtui] script cascade on "${r.form_name}.${t}" exceeded ${
|
|
232
|
+
`[bahtui] script cascade on "${r.form_name}.${t}" exceeded ${V} levels — stopped (setValue ping-pong between change scripts?)`
|
|
222
233
|
);
|
|
223
234
|
return;
|
|
224
235
|
}
|
|
@@ -230,7 +241,7 @@ let n = class extends T {
|
|
|
230
241
|
g: this.g(r),
|
|
231
242
|
on: "change",
|
|
232
243
|
key: t,
|
|
233
|
-
detail: { oldValue:
|
|
244
|
+
detail: { oldValue: s, newValue: e, isLoading: i, data: this.getData() }
|
|
234
245
|
});
|
|
235
246
|
} finally {
|
|
236
247
|
this.scriptDepth--;
|
|
@@ -243,13 +254,13 @@ let n = class extends T {
|
|
|
243
254
|
* workflow reuses this element across steps, so each step loads.
|
|
244
255
|
*/
|
|
245
256
|
runLoadScripts() {
|
|
246
|
-
var
|
|
257
|
+
var e;
|
|
247
258
|
const t = this.effectiveForm;
|
|
248
|
-
if (!((
|
|
249
|
-
const
|
|
259
|
+
if (!((e = t == null ? void 0 : t.scripts) != null && e.length)) return;
|
|
260
|
+
const s = this.getData();
|
|
250
261
|
for (const i of t.fields)
|
|
251
|
-
i.type === "label" || i.type === "section" || this.runChangeScripts(i.key, void 0,
|
|
252
|
-
|
|
262
|
+
i.type === "label" || i.type === "section" || this.runChangeScripts(i.key, void 0, s[i.key], !0);
|
|
263
|
+
w(t, "load") && F({
|
|
253
264
|
form: t,
|
|
254
265
|
registry: this.registry,
|
|
255
266
|
g: this.g(t),
|
|
@@ -258,117 +269,132 @@ let n = class extends T {
|
|
|
258
269
|
});
|
|
259
270
|
}
|
|
260
271
|
updated(t) {
|
|
261
|
-
super.updated(t),
|
|
272
|
+
if (super.updated(t), this.lastVisible = this.nextVisible, this.nextVisible = {}, !O()) return;
|
|
273
|
+
const s = this.assignedForm;
|
|
274
|
+
s && this.loadedForm !== s && (this.loadedForm = s, this.runLoadScripts());
|
|
262
275
|
}
|
|
263
276
|
// ---- internal ----
|
|
264
|
-
onFieldInput(t,
|
|
277
|
+
onFieldInput(t, s) {
|
|
265
278
|
var o;
|
|
266
|
-
|
|
267
|
-
let { value:
|
|
279
|
+
s.stopPropagation();
|
|
280
|
+
let { value: e } = s.detail;
|
|
268
281
|
const i = t.formatter ? (o = this.registry.formatters) == null ? void 0 : o[t.formatter] : void 0;
|
|
269
|
-
t.formatter && !i && console.warn(`[bahtui] unknown formatter "${t.formatter}" — skipped`), i && (
|
|
282
|
+
t.formatter && !i && console.warn(`[bahtui] unknown formatter "${t.formatter}" — skipped`), i && (e = i(e));
|
|
270
283
|
const r = this.valueFor(t);
|
|
271
|
-
this.edits = { ...this.edits, [t.key]:
|
|
284
|
+
this.edits = { ...this.edits, [t.key]: e }, this.clearDependents(t.key), this.syncDynamicOptions(), (this.options.validateOn ?? "change") === "change" && this.errors.length > 0 && (this.errors = this.runValidators()), this.dispatchEvent(
|
|
272
285
|
new CustomEvent("baht-change", {
|
|
273
|
-
detail: { form_name: this.form.form_name, key: t.key, value:
|
|
286
|
+
detail: { form_name: this.form.form_name, key: t.key, value: e, data: this.getData() },
|
|
274
287
|
bubbles: !0,
|
|
275
288
|
composed: !0
|
|
276
289
|
})
|
|
277
|
-
), this.runChangeScripts(t.key, r,
|
|
290
|
+
), this.runChangeScripts(t.key, r, e, !1);
|
|
278
291
|
}
|
|
279
292
|
/** Validate a single field against current form data (siblings included). */
|
|
280
293
|
validateOneField(t) {
|
|
281
|
-
const
|
|
282
|
-
if (!
|
|
283
|
-
const
|
|
284
|
-
return
|
|
294
|
+
const s = this.stateFor(t);
|
|
295
|
+
if (!s.visible) return [];
|
|
296
|
+
const e = this.getData(), i = s.required === !!t.required ? t : { ...t, required: s.required };
|
|
297
|
+
return _(i, this.valueFor(t), this.registry, e);
|
|
285
298
|
}
|
|
286
299
|
/** `validateOn: 'blur'` — validate just the field the user left, same as
|
|
287
300
|
* how errors are shown elsewhere (merged into `this.errors`, painted by
|
|
288
301
|
* `renderField`/`.baht-error`). No-op for 'change'/'submit' modes. */
|
|
289
302
|
onFieldBlur(t) {
|
|
290
303
|
if ((this.options.validateOn ?? "change") !== "blur") return;
|
|
291
|
-
const
|
|
292
|
-
this.errors = [...
|
|
304
|
+
const s = this.validateOneField(t), e = this.errors.filter((i) => i.key !== t.key);
|
|
305
|
+
this.errors = [...e, ...s];
|
|
293
306
|
}
|
|
294
307
|
renderField(t) {
|
|
295
|
-
var S
|
|
296
|
-
const
|
|
297
|
-
if (!
|
|
298
|
-
return console.warn(`[bahtui] unknown field type "${t.type}" — skipped`),
|
|
299
|
-
const
|
|
308
|
+
var S;
|
|
309
|
+
const s = T[t.type];
|
|
310
|
+
if (!s)
|
|
311
|
+
return console.warn(`[bahtui] unknown field type "${t.type}" — skipped`), g;
|
|
312
|
+
const e = this.stateFor(t), i = B[t.width ?? "full"] ?? "baht-field--w-full", r = ((S = this.errors.find((u) => u.key === t.key)) == null ? void 0 : S.message) ?? null;
|
|
300
313
|
if (this.readonly && t.type !== "label" && t.type !== "section")
|
|
301
|
-
return this.renderReadonlyField(t, i,
|
|
302
|
-
const o = t.options_from ? { ...t, options: this.dynamicOptions.get(t.key) ?? t.options ?? [] } : t, a = this.valueFor(t), l = !
|
|
314
|
+
return this.renderReadonlyField(t, i, e.visible);
|
|
315
|
+
const o = t.options_from ? { ...t, options: this.dynamicOptions.get(t.key) ?? t.options ?? [] } : t, a = this.valueFor(t), l = !e.enabled, c = this.readonly || e.readonly, b = this.options.inputDebounce, v = (u) => {
|
|
316
|
+
this.onControlInput(t.key, u);
|
|
317
|
+
}, f = I(s);
|
|
303
318
|
let y;
|
|
304
319
|
if (t.type === "repeater") {
|
|
305
|
-
const
|
|
306
|
-
y =
|
|
320
|
+
const u = this.errors.filter((q) => q.key.startsWith(`${t.key}[`));
|
|
321
|
+
y = $`<${f}
|
|
307
322
|
.field=${o}
|
|
308
323
|
.value=${a}
|
|
309
324
|
.disabled=${l}
|
|
310
325
|
.readonly=${c}
|
|
311
|
-
.required=${
|
|
326
|
+
.required=${e.required}
|
|
312
327
|
.showOptional=${this.showOptional}
|
|
313
328
|
.error=${r}
|
|
314
|
-
.debounce=${
|
|
315
|
-
.rowErrors=${
|
|
316
|
-
@baht-input=${
|
|
329
|
+
.debounce=${b}
|
|
330
|
+
.rowErrors=${u}
|
|
331
|
+
@baht-input=${v}
|
|
317
332
|
></${f}>`;
|
|
318
333
|
} else if (t.type === "file") {
|
|
319
|
-
|
|
320
|
-
|
|
334
|
+
const u = this.uploaderFor(t);
|
|
335
|
+
y = $`<${f}
|
|
321
336
|
.field=${o}
|
|
322
337
|
.value=${a}
|
|
323
338
|
.disabled=${l}
|
|
324
339
|
.readonly=${c}
|
|
325
|
-
.required=${
|
|
340
|
+
.required=${e.required}
|
|
326
341
|
.showOptional=${this.showOptional}
|
|
327
342
|
.error=${r}
|
|
328
|
-
.debounce=${
|
|
329
|
-
.uploader=${
|
|
330
|
-
@baht-input=${
|
|
343
|
+
.debounce=${b}
|
|
344
|
+
.uploader=${u}
|
|
345
|
+
@baht-input=${v}
|
|
331
346
|
></${f}>`;
|
|
332
347
|
} else
|
|
333
|
-
y =
|
|
348
|
+
y = $`<${f}
|
|
334
349
|
.field=${o}
|
|
335
350
|
.value=${a}
|
|
336
351
|
.disabled=${l}
|
|
337
352
|
.readonly=${c}
|
|
338
|
-
.required=${
|
|
353
|
+
.required=${e.required}
|
|
339
354
|
.showOptional=${this.showOptional}
|
|
340
355
|
.error=${r}
|
|
341
|
-
.debounce=${
|
|
342
|
-
@baht-input=${
|
|
356
|
+
.debounce=${b}
|
|
357
|
+
@baht-input=${v}
|
|
343
358
|
></${f}>`;
|
|
344
|
-
const
|
|
359
|
+
const k = this.fieldMessages[t.key];
|
|
345
360
|
return p`<div
|
|
346
|
-
class="baht-field ${i} ${
|
|
361
|
+
class="baht-field ${i} ${e.visible ? "" : "baht-field--hidden"}${this.revealClass(t.key, e.visible)}"
|
|
347
362
|
data-key=${t.key}
|
|
348
363
|
data-type=${t.type}
|
|
349
364
|
data-testid="${this.form.form_id}.${t.key}"
|
|
350
|
-
@focusout=${() =>
|
|
365
|
+
@focusout=${() => {
|
|
366
|
+
this.onFieldBlur(t);
|
|
367
|
+
}}
|
|
351
368
|
>
|
|
352
369
|
${y}
|
|
353
|
-
${
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
370
|
+
${k ? this.renderFieldMsg(k) : g}
|
|
371
|
+
</div>`;
|
|
372
|
+
}
|
|
373
|
+
uploaderFor(t) {
|
|
374
|
+
var e;
|
|
375
|
+
if (!t.uploader) return;
|
|
376
|
+
const s = (e = this.registry.uploaders) == null ? void 0 : e[t.uploader];
|
|
377
|
+
return s || console.warn(`[bahtui] unknown uploader "${t.uploader}" — capture only`), s;
|
|
378
|
+
}
|
|
379
|
+
renderFieldMsg(t) {
|
|
380
|
+
return p`<div
|
|
381
|
+
class="baht-field-msg baht-field-msg--${t.type}"
|
|
382
|
+
role=${t.type === "error" ? "alert" : "status"}
|
|
383
|
+
>
|
|
384
|
+
${t.message}
|
|
359
385
|
</div>`;
|
|
360
386
|
}
|
|
361
387
|
/**
|
|
362
388
|
* Resolve the descriptor at event time — it may have changed since the control
|
|
363
389
|
* was first rendered (the same node is reused across renders, see render()).
|
|
364
390
|
*/
|
|
365
|
-
onControlInput(t,
|
|
391
|
+
onControlInput(t, s) {
|
|
366
392
|
var i;
|
|
367
|
-
const
|
|
368
|
-
|
|
393
|
+
const e = (i = this.effectiveForm) == null ? void 0 : i.fields.find((r) => r.key === t);
|
|
394
|
+
e && this.onFieldInput(e, s);
|
|
369
395
|
}
|
|
370
|
-
renderReadonlyField(t,
|
|
371
|
-
const i = t.options_from ? this.dynamicOptions.get(t.key) ?? t.options ?? [] : t.options, r =
|
|
396
|
+
renderReadonlyField(t, s, e) {
|
|
397
|
+
const i = t.options_from ? this.dynamicOptions.get(t.key) ?? t.options ?? [] : t.options, r = A(t, this.valueFor(t), i), o = r.kind === "empty" ? p`<span class="baht-readonly-value baht-readonly-value--empty">${D("emptyValue")}</span>` : r.kind === "text" ? p`<span class="baht-readonly-value">${r.text}</span>` : r.kind === "chips" ? p`<span class="baht-readonly-value baht-readonly-chips"
|
|
372
398
|
>${r.items.map((a) => p`<span class="baht-readonly-chip">${a}</span>`)}</span
|
|
373
399
|
>` : p`<div class="baht-readonly-rows">
|
|
374
400
|
${r.rows.map(
|
|
@@ -383,12 +409,12 @@ let n = class extends T {
|
|
|
383
409
|
)}
|
|
384
410
|
</div>`;
|
|
385
411
|
return p`<div
|
|
386
|
-
class="baht-field ${
|
|
412
|
+
class="baht-field ${s} baht-field--readonly ${e ? "" : "baht-field--hidden"}${this.revealClass(t.key, e)}"
|
|
387
413
|
data-key=${t.key}
|
|
388
414
|
data-type=${t.type}
|
|
389
415
|
data-testid="${this.form.form_id}.${t.key}"
|
|
390
416
|
>
|
|
391
|
-
${t.label ? p`<span class="baht-label">${t.label}</span>` :
|
|
417
|
+
${t.label ? p`<span class="baht-label">${t.label}</span>` : g}
|
|
392
418
|
${o}
|
|
393
419
|
</div>`;
|
|
394
420
|
}
|
|
@@ -403,8 +429,8 @@ let n = class extends T {
|
|
|
403
429
|
${t.message}
|
|
404
430
|
</div>`
|
|
405
431
|
)}
|
|
406
|
-
</div>` :
|
|
407
|
-
${
|
|
432
|
+
</div>` : g}
|
|
433
|
+
${E(
|
|
408
434
|
this.sortedFields,
|
|
409
435
|
(t) => t.key,
|
|
410
436
|
(t) => this.renderField(t)
|
|
@@ -413,31 +439,31 @@ let n = class extends T {
|
|
|
413
439
|
}
|
|
414
440
|
};
|
|
415
441
|
h([
|
|
416
|
-
|
|
442
|
+
d({ attribute: !1 })
|
|
417
443
|
], n.prototype, "form", 2);
|
|
418
444
|
h([
|
|
419
|
-
|
|
445
|
+
d({ attribute: !1 })
|
|
420
446
|
], n.prototype, "value", 2);
|
|
421
447
|
h([
|
|
422
|
-
|
|
448
|
+
d({ attribute: !1 })
|
|
423
449
|
], n.prototype, "registry", 2);
|
|
424
450
|
h([
|
|
425
|
-
|
|
451
|
+
d({ type: Boolean, attribute: "show-optional" })
|
|
426
452
|
], n.prototype, "showOptional", 2);
|
|
427
453
|
h([
|
|
428
|
-
|
|
454
|
+
d({ attribute: !1 })
|
|
429
455
|
], n.prototype, "options", 2);
|
|
430
456
|
h([
|
|
431
|
-
|
|
457
|
+
d({ attribute: !1 })
|
|
432
458
|
], n.prototype, "fieldStates", 2);
|
|
433
459
|
h([
|
|
434
|
-
|
|
460
|
+
d({ type: String })
|
|
435
461
|
], n.prototype, "view", 2);
|
|
436
462
|
h([
|
|
437
|
-
|
|
463
|
+
d({ attribute: !1 })
|
|
438
464
|
], n.prototype, "flags", 2);
|
|
439
465
|
h([
|
|
440
|
-
|
|
466
|
+
d({ type: Boolean })
|
|
441
467
|
], n.prototype, "readonly", 2);
|
|
442
468
|
h([
|
|
443
469
|
m()
|
|
@@ -455,7 +481,7 @@ h([
|
|
|
455
481
|
m()
|
|
456
482
|
], n.prototype, "formMessages", 2);
|
|
457
483
|
n = h([
|
|
458
|
-
|
|
484
|
+
x("baht-form")
|
|
459
485
|
], n);
|
|
460
486
|
export {
|
|
461
487
|
n as BahtForm
|
|
@@ -21,6 +21,12 @@ export declare class BahtWorkflow extends BahtElement {
|
|
|
21
21
|
submitLabel?: string;
|
|
22
22
|
backLabel?: string;
|
|
23
23
|
showBack: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Which way the last navigation went. Stamped on the step wrapper as
|
|
26
|
+
* `baht-workflow-step--forward|--back` so the stylesheet can slide the new
|
|
27
|
+
* step in from the matching side; null on first render (nothing to replay).
|
|
28
|
+
*/
|
|
29
|
+
private direction;
|
|
24
30
|
/** Feature-flag values (spec 01 §12): flag-off steps/fields/rules vanish. */
|
|
25
31
|
flags: FeatureFlags;
|
|
26
32
|
/** Render a <baht-steps> progress indicator above the current form. */
|
|
@@ -35,6 +41,11 @@ export declare class BahtWorkflow extends BahtElement {
|
|
|
35
41
|
get currentFormName(): string | null;
|
|
36
42
|
/** applyFlags result, memoized on (workflow, flags) identity. */
|
|
37
43
|
private flagCache;
|
|
44
|
+
/**
|
|
45
|
+
* `workflow` as the host may actually have left it: a framework can connect the
|
|
46
|
+
* element before it binds the property, so internally it is optional until then.
|
|
47
|
+
*/
|
|
48
|
+
private get assignedWorkflow();
|
|
38
49
|
private get effectiveWorkflow();
|
|
39
50
|
protected willUpdate(changed: Map<PropertyKey, unknown>): void;
|
|
40
51
|
private reevaluate;
|