bank20baht-ui 0.5.7 → 0.6.0

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.
Files changed (102) hide show
  1. package/README.md +25 -0
  2. package/dist/components/baht-form.js +472 -0
  3. package/dist/components/baht-workflow.js +331 -0
  4. package/dist/{chunks/base-D1pT3J5T.js → components/base.js} +35 -35
  5. package/dist/components/motion.js +16 -0
  6. package/dist/components/primitives/baht-accordion.js +63 -0
  7. package/dist/components/primitives/baht-alert.js +59 -0
  8. package/dist/components/primitives/baht-avatar.d.ts +19 -0
  9. package/dist/components/primitives/baht-avatar.js +42 -0
  10. package/dist/components/primitives/baht-badge.js +37 -0
  11. package/dist/components/primitives/baht-bar-chart.js +345 -0
  12. package/dist/components/primitives/baht-breadcrumbs.js +43 -0
  13. package/dist/components/primitives/baht-button.js +56 -0
  14. package/dist/components/primitives/baht-card.js +41 -0
  15. package/dist/components/primitives/baht-checkbox.js +38 -0
  16. package/dist/components/primitives/baht-code.js +32 -0
  17. package/dist/components/primitives/baht-currency.js +64 -0
  18. package/dist/components/primitives/baht-datepicker.js +189 -0
  19. package/dist/components/primitives/baht-donut-chart.js +86 -0
  20. package/dist/components/primitives/baht-empty-state.js +36 -0
  21. package/dist/components/primitives/baht-error-state.js +44 -0
  22. package/dist/components/primitives/baht-fieldset.js +41 -0
  23. package/dist/components/primitives/baht-file.js +149 -0
  24. package/dist/components/primitives/baht-filter.js +51 -0
  25. package/dist/components/primitives/baht-heatmap.d.ts +14 -0
  26. package/dist/components/primitives/baht-heatmap.js +145 -0
  27. package/dist/components/primitives/baht-input.js +35 -0
  28. package/dist/components/primitives/baht-keyvalue-editor.js +100 -0
  29. package/dist/components/primitives/baht-line-chart.js +146 -0
  30. package/dist/components/primitives/baht-link.js +39 -0
  31. package/dist/components/primitives/baht-list.js +76 -0
  32. package/dist/components/primitives/baht-loading-overlay.d.ts +14 -0
  33. package/dist/components/primitives/baht-loading-overlay.js +31 -0
  34. package/dist/components/primitives/baht-meter.js +99 -0
  35. package/dist/components/primitives/baht-modal.js +135 -0
  36. package/dist/components/primitives/baht-multiselect.js +45 -0
  37. package/dist/components/primitives/baht-number.js +39 -0
  38. package/dist/components/primitives/baht-otp.js +102 -0
  39. package/dist/components/primitives/baht-pagination.js +75 -0
  40. package/dist/components/primitives/baht-progress-ring.d.ts +22 -0
  41. package/dist/components/primitives/baht-progress-ring.js +61 -0
  42. package/dist/components/primitives/baht-progress.js +47 -0
  43. package/dist/components/primitives/baht-radio.js +63 -0
  44. package/dist/components/primitives/baht-range.js +67 -0
  45. package/dist/components/primitives/baht-rating.js +80 -0
  46. package/dist/components/primitives/baht-reference-picker.js +193 -0
  47. package/dist/components/primitives/baht-repeater.js +142 -0
  48. package/dist/components/primitives/baht-section.js +24 -0
  49. package/dist/components/primitives/baht-select.js +180 -0
  50. package/dist/components/primitives/baht-skeleton.js +36 -0
  51. package/dist/components/primitives/baht-sparkline.js +55 -0
  52. package/dist/components/primitives/baht-spinner.js +29 -0
  53. package/dist/components/primitives/baht-stat-chart.js +37 -0
  54. package/dist/components/primitives/baht-stat.js +90 -0
  55. package/dist/components/primitives/baht-steps.js +48 -0
  56. package/dist/components/primitives/baht-table.js +528 -0
  57. package/dist/components/primitives/baht-text.js +24 -0
  58. package/dist/components/primitives/baht-textarea.js +35 -0
  59. package/dist/components/primitives/baht-timeline.js +41 -0
  60. package/dist/components/primitives/baht-toast.js +71 -0
  61. package/dist/components/primitives/baht-toggle.js +40 -0
  62. package/dist/components/primitives/baht-tooltip.js +65 -0
  63. package/dist/components/primitives/baht-typeahead.js +244 -0
  64. package/dist/components/primitives/baht-upload.js +289 -0
  65. package/dist/components/primitives/charts.js +176 -0
  66. package/dist/components/primitives/icons.js +114 -0
  67. package/dist/components/primitives/illustrations.js +78 -0
  68. package/dist/components/primitives/index.d.ts +3 -0
  69. package/dist/components/primitives/tag-map.js +21 -0
  70. package/dist/core/approval.js +9 -0
  71. package/dist/core/dates.js +74 -0
  72. package/dist/core/defaults.js +22 -0
  73. package/dist/core/display.js +45 -0
  74. package/dist/core/engine/conditions.js +71 -0
  75. package/dist/core/engine/evaluate.js +168 -0
  76. package/dist/core/engine/expressions.js +49 -0
  77. package/dist/core/engine/graph.js +41 -0
  78. package/dist/core/engine/scripts.js +69 -0
  79. package/dist/core/flags.js +34 -0
  80. package/dist/{chunks/i18n-CWv5lrun.js → core/i18n.js} +8 -8
  81. package/dist/core/legacy.js +44 -0
  82. package/dist/core/validators.js +120 -0
  83. package/dist/core/views.js +11 -0
  84. package/dist/css/components.css +3649 -0
  85. package/dist/css/datepicker.css +344 -0
  86. package/dist/css/tokens.css +431 -0
  87. package/dist/custom-elements.json +692 -45
  88. package/dist/engine.js +42 -70
  89. package/dist/hydrate.js +6 -6
  90. package/dist/index.js +179 -106
  91. package/dist/overlay.js +20 -17
  92. package/dist/react.d.ts +14 -1
  93. package/dist/register.d.ts +76 -0
  94. package/dist/register.js +69 -0
  95. package/dist/ssr.js +84 -26
  96. package/dist/tokens.css +137 -6
  97. package/dist/vscode.html-custom-data.json +60 -1
  98. package/dist/web-types.json +134 -2
  99. package/package.json +24 -4
  100. package/dist/chunks/baht-toast-DkZc1diO.js +0 -448
  101. package/dist/chunks/baht-workflow-CdiF0vxT.js +0 -5199
  102. package/dist/chunks/display-Cm3Pmp9U.js +0 -676
package/README.md CHANGED
@@ -296,6 +296,31 @@ Styling API: `.baht-form`, `.baht-field`, `.baht-field--w-1-2`, `.baht-field--hi
296
296
  `.baht-control`, `.baht-error`, `.baht-button`, `.baht-badge` (+ the token list in `tokens.css`).
297
297
  Full reference: [docs/theming.md](./docs/theming.md).
298
298
 
299
+ ## Import only what you use
300
+
301
+ `import 'bank20baht-ui'` registers every element (~230 KB min). Three lighter routes:
302
+
303
+ ```ts
304
+ // 1. per-element subpath — one module per tag, plus only what it imports
305
+ import 'bank20baht-ui/components/button'; // <baht-button>
306
+ import { BahtTable } from 'bank20baht-ui/components/table';
307
+ import 'bank20baht-ui/components/form'; // <baht-form> + the 14 field primitives
308
+ import 'bank20baht-ui/components/workflow';
309
+
310
+ // 2. by tag list — each tag is a dynamic import, fetched on demand
311
+ import { registerElements } from 'bank20baht-ui/register';
312
+ await registerElements(['baht-button', 'baht-avatar']);
313
+ ```
314
+
315
+ ```css
316
+ /* 3. split stylesheets instead of the all-in-one tokens.css (139 KB) */
317
+ @import 'bank20baht-ui/css/tokens.css'; /* custom properties only, 18 KB */
318
+ @import 'bank20baht-ui/css/components.css'; /* .baht-* classes */
319
+ @import 'bank20baht-ui/css/datepicker.css'; /* flatpickr base + re-skin — only with <baht-datepicker> / date fields */
320
+ ```
321
+
322
+ `bank20baht-ui/tokens.css` stays the one-import sheet (all three, flatpickr included).
323
+
299
324
  ## One install, typed everywhere
300
325
 
301
326
  `npm i bank20baht-ui` ships the runtime **and** the metadata every editor/framework needs —
@@ -0,0 +1,472 @@
1
+ import { nothing as b, html as p } from "lit";
2
+ import { property as u, state as m, customElement as T } from "lit/decorators.js";
3
+ import { repeat as P } from "lit/directives/repeat.js";
4
+ import { html as w, unsafeStatic as I } from "lit/static-html.js";
5
+ import { BahtElement as B, hasDom as D } from "./base.js";
6
+ import { TAG_FOR_TYPE as A } from "./primitives/tag-map.js";
7
+ import { defaultValue as C } from "../core/defaults.js";
8
+ import { t as _ } from "../core/i18n.js";
9
+ import { validateField as M } from "../core/validators.js";
10
+ import { resolveViewFields as q } from "../core/views.js";
11
+ import { applyFlags as j } from "../core/flags.js";
12
+ import { hasScripts as k, runScripts as F, createScriptContext as L, MAX_SCRIPT_DEPTH as E } from "../core/engine/scripts.js";
13
+ import { displayValue as R } from "../core/display.js";
14
+ import "./primitives/baht-input.js";
15
+ import "./primitives/baht-textarea.js";
16
+ import "./primitives/baht-number.js";
17
+ import "./primitives/baht-currency.js";
18
+ import "./primitives/baht-select.js";
19
+ import "./primitives/baht-multiselect.js";
20
+ import "./primitives/baht-checkbox.js";
21
+ import "./primitives/baht-radio.js";
22
+ import "./primitives/baht-toggle.js";
23
+ import "./primitives/baht-datepicker.js";
24
+ import "./primitives/baht-file.js";
25
+ import "./primitives/baht-text.js";
26
+ import "./primitives/baht-section.js";
27
+ import "./primitives/baht-repeater.js";
28
+ var U = Object.defineProperty, G = Object.getOwnPropertyDescriptor, h = (t, e, s, i) => {
29
+ for (var r = i > 1 ? void 0 : i ? G(e, s) : e, o = t.length - 1, a; o >= 0; o--)
30
+ (a = t[o]) && (r = (i ? a(e, s, r) : a(r)) || r);
31
+ return i && r && U(e, s, r), r;
32
+ };
33
+ const H = {
34
+ "1/4": "baht-field--w-1-4",
35
+ "1/2": "baht-field--w-1-2",
36
+ "3/4": "baht-field--w-3-4",
37
+ full: "baht-field--w-full"
38
+ }, x = /* @__PURE__ */ new Map();
39
+ function W(t) {
40
+ let e = x.get(t);
41
+ return e || (e = I(t), x.set(t, e)), e;
42
+ }
43
+ let n = class extends B {
44
+ constructor() {
45
+ 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 = {};
46
+ }
47
+ get effectiveForm() {
48
+ return this.form ? ((this.flagCache.form !== this.form || this.flagCache.flags !== this.flags) && (this.flagCache = {
49
+ form: this.form,
50
+ flags: this.flags,
51
+ out: j(this.form, this.flags)
52
+ }), this.flagCache.out) : this.form;
53
+ }
54
+ willUpdate(t) {
55
+ 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();
56
+ }
57
+ /**
58
+ * Refetch `options_from` options whose dep snapshot changed. Sync fetchers
59
+ * land immediately; async ones re-render on resolve (stale responses are
60
+ * dropped by epoch).
61
+ */
62
+ syncDynamicOptions() {
63
+ var e, s;
64
+ const t = this.getData();
65
+ for (const i of ((e = this.effectiveForm) == null ? void 0 : e.fields) ?? []) {
66
+ if (!i.options_from) continue;
67
+ const r = JSON.stringify((i.options_deps ?? []).map((c) => t[c] ?? null));
68
+ if (this.optionsSnapshots.get(i.key) === r) continue;
69
+ this.optionsSnapshots.set(i.key, r);
70
+ const o = (this.optionsEpoch.get(i.key) ?? 0) + 1;
71
+ this.optionsEpoch.set(i.key, o);
72
+ const a = (s = this.registry.options) == null ? void 0 : s[i.options_from];
73
+ if (!a) {
74
+ console.warn(`[bahtui] unknown options fetcher "${i.options_from}" — skipped`), this.dynamicOptions.set(i.key, []);
75
+ continue;
76
+ }
77
+ const l = a(t);
78
+ l instanceof Promise ? l.then((c) => {
79
+ this.optionsEpoch.get(i.key) === o && (this.dynamicOptions.set(i.key, c), this.requestUpdate());
80
+ }).catch((c) => {
81
+ console.warn(`[bahtui] options fetcher "${i.options_from}" failed`, c), this.optionsEpoch.get(i.key) === o && this.optionsSnapshots.delete(i.key);
82
+ }) : this.dynamicOptions.set(i.key, l);
83
+ }
84
+ }
85
+ /**
86
+ * A user edit invalidates every field whose options_deps include the edited
87
+ * key — their values reset to defaults, cascading down chains
88
+ * (province -> district -> subdistrict).
89
+ */
90
+ clearDependents(t) {
91
+ var i, r;
92
+ const e = [t], s = /* @__PURE__ */ new Set();
93
+ for (; e.length > 0; ) {
94
+ const o = e.shift();
95
+ for (const a of ((i = this.effectiveForm) == null ? void 0 : i.fields) ?? [])
96
+ !((r = a.options_deps) != null && r.includes(o)) || s.has(a.key) || (s.add(a.key), this.edits = { ...this.edits, [a.key]: C(a) }, e.push(a.key));
97
+ }
98
+ }
99
+ get sortedFields() {
100
+ if (!this.form) return [];
101
+ const t = q(this.effectiveForm, this.view);
102
+ return t || (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`)), q(this.effectiveForm) ?? []);
103
+ }
104
+ stateFor(t) {
105
+ const e = this.fieldStates[t.key] ?? {
106
+ visible: !0,
107
+ enabled: !t.disabled,
108
+ readonly: !!t.readonly,
109
+ required: !!t.required,
110
+ computed: !1
111
+ }, s = this.scriptStates[t.key];
112
+ return s ? { ...e, ...s } : e;
113
+ }
114
+ valueFor(t) {
115
+ return Object.hasOwn(this.edits, t.key) ? this.edits[t.key] : Object.hasOwn(this.value, t.key) ? this.value[t.key] : C(t);
116
+ }
117
+ // ---- public API (spec 01 §6) ----
118
+ /** Debounced keystrokes still in a control land in `edits` before anyone reads them. */
119
+ flushTyped() {
120
+ var t;
121
+ if (D())
122
+ for (const e of this.querySelectorAll(":scope > .baht-form > .baht-field > *"))
123
+ (t = e.flushTyped) == null || t.call(e);
124
+ }
125
+ getData() {
126
+ var e;
127
+ this.flushTyped();
128
+ const t = {};
129
+ for (const s of ((e = this.effectiveForm) == null ? void 0 : e.fields) ?? [])
130
+ s.type === "label" || s.type === "section" || (t[s.key] = this.valueFor(s));
131
+ return t;
132
+ }
133
+ /**
134
+ * Merge values programmatically. Runs each key's `change` scripts (like
135
+ * ServiceNow's g_form.setValue) but emits no `baht-change` — the host
136
+ * already knows, and a store adapter would otherwise loop.
137
+ */
138
+ setValue(t) {
139
+ const e = this.getData();
140
+ this.edits = { ...this.edits, ...t }, this.requestUpdate();
141
+ for (const s of Object.keys(t))
142
+ this.runChangeScripts(s, e[s], t[s], !1);
143
+ }
144
+ /** Declarative + registry validators over the visible fields (no scripts). */
145
+ runValidators() {
146
+ const t = [], e = this.getData();
147
+ for (const s of this.sortedFields) {
148
+ if (s.type === "label" || s.type === "section") continue;
149
+ const i = this.stateFor(s);
150
+ if (!i.visible) continue;
151
+ const r = i.required === !!s.required ? s : { ...s, required: i.required };
152
+ t.push(...M(r, this.valueFor(s), this.registry, e));
153
+ }
154
+ return t;
155
+ }
156
+ /**
157
+ * Full validation pass: validators, then `submit` scripts (spec 01 §14). A
158
+ * script returning `false` blocks; its `addErrorMessage()` calls become
159
+ * `{ key: '$' }` errors so hosts see why.
160
+ */
161
+ validate() {
162
+ const t = this.runValidators(), e = this.effectiveForm;
163
+ if (e && k(e, "submit")) {
164
+ this.formMessages = [];
165
+ const i = F({
166
+ form: e,
167
+ registry: this.registry,
168
+ g: this.g(e),
169
+ on: "submit",
170
+ detail: { isLoading: !1, data: this.getData() }
171
+ }), r = this.formMessages.filter((o) => o.type === "error").map((o) => ({ key: "$", message: o.message }));
172
+ !i && r.length === 0 && r.push({ key: "$", message: _("scriptBlocked") }), t.push(...r);
173
+ }
174
+ this.errors = t;
175
+ const s = { valid: t.length === 0, errors: t };
176
+ return this.dispatchEvent(
177
+ new CustomEvent("baht-validity", { detail: s, bubbles: !0, composed: !0 })
178
+ ), s;
179
+ }
180
+ reset() {
181
+ this.edits = {}, this.value = {}, this.errors = [], this.scriptStates = {}, this.fieldMessages = {}, this.formMessages = [], this.requestUpdate();
182
+ }
183
+ // ---- client scripts (spec 01 §14) ----
184
+ /** The `g` (g_form) for the current form — built lazily over this component's state. */
185
+ g(t) {
186
+ return this.scriptContext && this.scriptContextForm === t ? this.scriptContext : (this.scriptContextForm = t, this.scriptContext = L(t, {
187
+ getData: () => this.getData(),
188
+ setValue: (e, s) => this.applyScriptValue(e, s),
189
+ setState: (e, s) => {
190
+ this.scriptStates = {
191
+ ...this.scriptStates,
192
+ [e]: { ...this.scriptStates[e], ...s }
193
+ };
194
+ },
195
+ resetState: (e) => {
196
+ if (e === void 0) {
197
+ this.scriptStates = {};
198
+ return;
199
+ }
200
+ const s = { ...this.scriptStates };
201
+ delete s[e], this.scriptStates = s;
202
+ },
203
+ setFieldMessage: (e, s) => {
204
+ const i = { ...this.fieldMessages };
205
+ s ? i[e] = s : delete i[e], this.fieldMessages = i;
206
+ },
207
+ addFormMessage: (e) => {
208
+ this.formMessages = [...this.formMessages, e];
209
+ },
210
+ clearFormMessages: () => {
211
+ this.formMessages = [];
212
+ }
213
+ }), this.scriptContext);
214
+ }
215
+ /** A script wrote a value: same path as a user edit (emit + cascade), minus the formatter. */
216
+ applyScriptValue(t, e) {
217
+ const s = this.getData()[t];
218
+ this.edits = { ...this.edits, [t]: e }, this.clearDependents(t), this.syncDynamicOptions(), this.dispatchEvent(
219
+ new CustomEvent("baht-change", {
220
+ detail: { form_name: this.form.form_name, key: t, value: e, data: this.getData() },
221
+ bubbles: !0,
222
+ composed: !0
223
+ })
224
+ ), this.runChangeScripts(t, s, e, !1);
225
+ }
226
+ runChangeScripts(t, e, s, i) {
227
+ const r = this.effectiveForm;
228
+ if (!(!r || !k(r, "change", t))) {
229
+ if (this.scriptDepth >= E) {
230
+ console.warn(
231
+ `[bahtui] script cascade on "${r.form_name}.${t}" exceeded ${E} levels — stopped (setValue ping-pong between change scripts?)`
232
+ );
233
+ return;
234
+ }
235
+ this.scriptDepth++;
236
+ try {
237
+ F({
238
+ form: r,
239
+ registry: this.registry,
240
+ g: this.g(r),
241
+ on: "change",
242
+ key: t,
243
+ detail: { oldValue: e, newValue: s, isLoading: i, data: this.getData() }
244
+ });
245
+ } finally {
246
+ this.scriptDepth--;
247
+ }
248
+ }
249
+ }
250
+ /**
251
+ * Form load (ServiceNow order): every field's `change` scripts with
252
+ * isLoading=true, then the `load` scripts. Once per `form` identity — a
253
+ * workflow reuses this element across steps, so each step loads.
254
+ */
255
+ runLoadScripts() {
256
+ var s;
257
+ const t = this.effectiveForm;
258
+ if (!((s = t == null ? void 0 : t.scripts) != null && s.length)) return;
259
+ const e = this.getData();
260
+ for (const i of t.fields)
261
+ i.type === "label" || i.type === "section" || this.runChangeScripts(i.key, void 0, e[i.key], !0);
262
+ k(t, "load") && F({
263
+ form: t,
264
+ registry: this.registry,
265
+ g: this.g(t),
266
+ on: "load",
267
+ detail: { isLoading: !0, data: this.getData() }
268
+ });
269
+ }
270
+ updated(t) {
271
+ super.updated(t), D() && this.form && this.loadedForm !== this.form && (this.loadedForm = this.form, this.runLoadScripts());
272
+ }
273
+ // ---- internal ----
274
+ onFieldInput(t, e) {
275
+ var o;
276
+ e.stopPropagation();
277
+ let { value: s } = e.detail;
278
+ const i = t.formatter ? (o = this.registry.formatters) == null ? void 0 : o[t.formatter] : void 0;
279
+ t.formatter && !i && console.warn(`[bahtui] unknown formatter "${t.formatter}" — skipped`), i && (s = i(s));
280
+ const r = this.valueFor(t);
281
+ this.edits = { ...this.edits, [t.key]: s }, this.clearDependents(t.key), this.syncDynamicOptions(), (this.options.validateOn ?? "change") === "change" && this.errors.length > 0 && (this.errors = this.runValidators()), this.dispatchEvent(
282
+ new CustomEvent("baht-change", {
283
+ detail: { form_name: this.form.form_name, key: t.key, value: s, data: this.getData() },
284
+ bubbles: !0,
285
+ composed: !0
286
+ })
287
+ ), this.runChangeScripts(t.key, r, s, !1);
288
+ }
289
+ /** Validate a single field against current form data (siblings included). */
290
+ validateOneField(t) {
291
+ const e = this.stateFor(t);
292
+ if (!e.visible) return [];
293
+ const s = this.getData(), i = e.required === !!t.required ? t : { ...t, required: e.required };
294
+ return M(i, this.valueFor(t), this.registry, s);
295
+ }
296
+ /** `validateOn: 'blur'` — validate just the field the user left, same as
297
+ * how errors are shown elsewhere (merged into `this.errors`, painted by
298
+ * `renderField`/`.baht-error`). No-op for 'change'/'submit' modes. */
299
+ onFieldBlur(t) {
300
+ if ((this.options.validateOn ?? "change") !== "blur") return;
301
+ const e = this.validateOneField(t), s = this.errors.filter((i) => i.key !== t.key);
302
+ this.errors = [...s, ...e];
303
+ }
304
+ renderField(t) {
305
+ var S, O;
306
+ const e = A[t.type];
307
+ if (!e)
308
+ return console.warn(`[bahtui] unknown field type "${t.type}" — skipped`), b;
309
+ const s = this.stateFor(t), i = H[t.width ?? "full"] ?? "baht-field--w-full", r = ((S = this.errors.find((d) => d.key === t.key)) == null ? void 0 : S.message) ?? null;
310
+ if (this.readonly && t.type !== "label" && t.type !== "section")
311
+ return this.renderReadonlyField(t, i, s.visible);
312
+ const o = t.options_from ? { ...t, options: this.dynamicOptions.get(t.key) ?? t.options ?? [] } : t, a = this.valueFor(t), l = !s.enabled, c = this.readonly || s.readonly, v = this.options.inputDebounce, $ = (d) => this.onControlInput(t.key, d), f = W(e);
313
+ let y;
314
+ if (t.type === "repeater") {
315
+ const d = this.errors.filter((V) => V.key.startsWith(`${t.key}[`));
316
+ y = w`<${f}
317
+ .field=${o}
318
+ .value=${a}
319
+ .disabled=${l}
320
+ .readonly=${c}
321
+ .required=${s.required}
322
+ .showOptional=${this.showOptional}
323
+ .error=${r}
324
+ .debounce=${v}
325
+ .rowErrors=${d}
326
+ @baht-input=${$}
327
+ ></${f}>`;
328
+ } else if (t.type === "file") {
329
+ let d;
330
+ t.uploader && (d = (O = this.registry.uploaders) == null ? void 0 : O[t.uploader], d || console.warn(`[bahtui] unknown uploader "${t.uploader}" — capture only`)), y = w`<${f}
331
+ .field=${o}
332
+ .value=${a}
333
+ .disabled=${l}
334
+ .readonly=${c}
335
+ .required=${s.required}
336
+ .showOptional=${this.showOptional}
337
+ .error=${r}
338
+ .debounce=${v}
339
+ .uploader=${d}
340
+ @baht-input=${$}
341
+ ></${f}>`;
342
+ } else
343
+ y = w`<${f}
344
+ .field=${o}
345
+ .value=${a}
346
+ .disabled=${l}
347
+ .readonly=${c}
348
+ .required=${s.required}
349
+ .showOptional=${this.showOptional}
350
+ .error=${r}
351
+ .debounce=${v}
352
+ @baht-input=${$}
353
+ ></${f}>`;
354
+ const g = this.fieldMessages[t.key];
355
+ return p`<div
356
+ class="baht-field ${i} ${s.visible ? "" : "baht-field--hidden"}"
357
+ data-key=${t.key}
358
+ data-type=${t.type}
359
+ data-testid="${this.form.form_id}.${t.key}"
360
+ @focusout=${() => this.onFieldBlur(t)}
361
+ >
362
+ ${y}
363
+ ${g ? p`<div
364
+ class="baht-field-msg baht-field-msg--${g.type}"
365
+ role=${g.type === "error" ? "alert" : "status"}
366
+ >
367
+ ${g.message}
368
+ </div>` : b}
369
+ </div>`;
370
+ }
371
+ /**
372
+ * Resolve the descriptor at event time — it may have changed since the control
373
+ * was first rendered (the same node is reused across renders, see render()).
374
+ */
375
+ onControlInput(t, e) {
376
+ var i;
377
+ const s = (i = this.effectiveForm) == null ? void 0 : i.fields.find((r) => r.key === t);
378
+ s && this.onFieldInput(s, e);
379
+ }
380
+ renderReadonlyField(t, e, s) {
381
+ const i = t.options_from ? this.dynamicOptions.get(t.key) ?? t.options ?? [] : t.options, r = R(t, this.valueFor(t), i), o = r.kind === "empty" ? p`<span class="baht-readonly-value baht-readonly-value--empty">${_("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"
382
+ >${r.items.map((a) => p`<span class="baht-readonly-chip">${a}</span>`)}</span
383
+ >` : p`<div class="baht-readonly-rows">
384
+ ${r.rows.map(
385
+ (a) => p`<div class="baht-readonly-row">
386
+ ${a.map(
387
+ (l) => p`<span class="baht-readonly-cell"
388
+ ><span class="baht-readonly-cell-label">${l.label}</span
389
+ ><span class="baht-readonly-cell-value">${l.text}</span></span
390
+ >`
391
+ )}
392
+ </div>`
393
+ )}
394
+ </div>`;
395
+ return p`<div
396
+ class="baht-field ${e} baht-field--readonly ${s ? "" : "baht-field--hidden"}"
397
+ data-key=${t.key}
398
+ data-type=${t.type}
399
+ data-testid="${this.form.form_id}.${t.key}"
400
+ >
401
+ ${t.label ? p`<span class="baht-label">${t.label}</span>` : b}
402
+ ${o}
403
+ </div>`;
404
+ }
405
+ render() {
406
+ return p`<div class="baht-form ${this.readonly ? "baht-form--readonly" : ""}">
407
+ ${this.formMessages.length > 0 ? p`<div class="baht-form-messages">
408
+ ${this.formMessages.map(
409
+ (t) => p`<div
410
+ class="baht-form-msg baht-form-msg--${t.type}"
411
+ role=${t.type === "error" ? "alert" : "status"}
412
+ >
413
+ ${t.message}
414
+ </div>`
415
+ )}
416
+ </div>` : b}
417
+ ${P(
418
+ this.sortedFields,
419
+ (t) => t.key,
420
+ (t) => this.renderField(t)
421
+ )}
422
+ </div>`;
423
+ }
424
+ };
425
+ h([
426
+ u({ attribute: !1 })
427
+ ], n.prototype, "form", 2);
428
+ h([
429
+ u({ attribute: !1 })
430
+ ], n.prototype, "value", 2);
431
+ h([
432
+ u({ attribute: !1 })
433
+ ], n.prototype, "registry", 2);
434
+ h([
435
+ u({ type: Boolean, attribute: "show-optional" })
436
+ ], n.prototype, "showOptional", 2);
437
+ h([
438
+ u({ attribute: !1 })
439
+ ], n.prototype, "options", 2);
440
+ h([
441
+ u({ attribute: !1 })
442
+ ], n.prototype, "fieldStates", 2);
443
+ h([
444
+ u({ type: String })
445
+ ], n.prototype, "view", 2);
446
+ h([
447
+ u({ attribute: !1 })
448
+ ], n.prototype, "flags", 2);
449
+ h([
450
+ u({ type: Boolean })
451
+ ], n.prototype, "readonly", 2);
452
+ h([
453
+ m()
454
+ ], n.prototype, "edits", 2);
455
+ h([
456
+ m()
457
+ ], n.prototype, "errors", 2);
458
+ h([
459
+ m()
460
+ ], n.prototype, "scriptStates", 2);
461
+ h([
462
+ m()
463
+ ], n.prototype, "fieldMessages", 2);
464
+ h([
465
+ m()
466
+ ], n.prototype, "formMessages", 2);
467
+ n = h([
468
+ T("baht-form")
469
+ ], n);
470
+ export {
471
+ n as BahtForm
472
+ };