bank20baht-ui 0.11.0 → 0.12.1
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/README.md +3 -3
- package/dist/components/baht-form.d.ts +2 -0
- package/dist/components/baht-form.js +34 -24
- package/dist/components/primitives/baht-datepicker.d.ts +4 -1
- package/dist/components/primitives/baht-datepicker.js +29 -26
- package/dist/components/primitives/baht-filter.d.ts +12 -1
- package/dist/components/primitives/baht-filter.js +42 -32
- package/dist/components/primitives/baht-modal.d.ts +14 -0
- package/dist/components/primitives/baht-modal.js +42 -37
- package/dist/components/primitives/baht-repeater.d.ts +6 -1
- package/dist/components/primitives/baht-repeater.js +62 -55
- package/dist/components/primitives/baht-select.d.ts +4 -0
- package/dist/components/primitives/baht-select.js +50 -46
- package/dist/components/primitives/baht-swipe-row.d.ts +62 -0
- package/dist/components/primitives/baht-swipe-row.js +174 -0
- package/dist/components/primitives/index.d.ts +3 -2
- package/dist/core/engine/conditions.d.ts +21 -3
- package/dist/core/engine/conditions.js +62 -44
- package/dist/core/engine/evaluate.js +203 -111
- package/dist/core/i18n.d.ts +1 -0
- package/dist/core/i18n.js +2 -0
- package/dist/core/index.d.ts +2 -1
- package/dist/core/types.d.ts +3 -1
- package/dist/core/validators.d.ts +7 -2
- package/dist/core/validators.js +82 -74
- package/dist/css/components.css +205 -0
- package/dist/custom-elements.json +478 -6
- package/dist/engine.js +25 -23
- package/dist/index.js +168 -164
- package/dist/react.d.ts +10 -1
- package/dist/register.d.ts +1 -0
- package/dist/register.js +1 -0
- package/dist/ssr.js +3 -2
- package/dist/tokens.css +205 -0
- package/dist/vscode.html-custom-data.json +33 -5
- package/dist/web-types.json +71 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -261,7 +261,7 @@ near-black ink (`#171717`) on white, hairline neutral borders, 44px controls, on
|
|
|
261
261
|
(`#444FBF`), status as tint pills, hand-drawn koboyo spot illustrations for empty /
|
|
262
262
|
error / upload states, IBM Plex Sans Thai / IBM Plex Mono (load the
|
|
263
263
|
fonts yourself), borders-over-shadows, "paper" easing — plus an opt-in dark theme
|
|
264
|
-
(`<html data-baht-theme="dark">`).
|
|
264
|
+
(`<html data-baht-theme="dark">`). 59 elements:
|
|
265
265
|
|
|
266
266
|
```html
|
|
267
267
|
<baht-button variant="secondary" size="sm">Cancel</baht-button>
|
|
@@ -269,7 +269,7 @@ fonts yourself), borders-over-shadows, "paper" easing — plus an opt-in dark th
|
|
|
269
269
|
<baht-table></baht-table> <baht-modal></baht-modal> <baht-toast></baht-toast> …
|
|
270
270
|
```
|
|
271
271
|
|
|
272
|
-
- **Data (server-side paging)** — `baht-table` · `baht-list` · `baht-pagination` · `baht-filter`.
|
|
272
|
+
- **Data (server-side paging)** — `baht-table` · `baht-list` · `baht-swipe-row` · `baht-pagination` · `baht-filter`.
|
|
273
273
|
`baht-table` ships a ServiceNow-style column picker with dot-walking into reference columns (`requester.department`) and emits `baht-columns-change` so the host can persist the layout.
|
|
274
274
|
`items` is one page; `baht-page-change` asks your app to fetch the next one.
|
|
275
275
|
- **Feedback** — `baht-modal` · `baht-toast` · `baht-alert` · `baht-tooltip` · `baht-progress` ·
|
|
@@ -347,7 +347,7 @@ no hand-written type declarations in your app:
|
|
|
347
347
|
| **Angular** | template binding on properties/events | `CUSTOM_ELEMENTS_SCHEMA`, see below |
|
|
348
348
|
| **Vue 3** | keep Vue from treating `baht-*` as components | `isCustomElement`, see below |
|
|
349
349
|
| **Phoenix / Rails / plain HTML** | it's just HTML — works as-is | none |
|
|
350
|
-
| **Tooling** (Storybook, docgen, custom) | machine-readable API of all
|
|
350
|
+
| **Tooling** (Storybook, docgen, custom) | machine-readable API of all 59 elements | read `dist/custom-elements.json` ([CEM standard](https://github.com/webcomponents/custom-elements-manifest)) |
|
|
351
351
|
|
|
352
352
|
**VS Code** — `.vscode/settings.json`:
|
|
353
353
|
|
|
@@ -88,6 +88,8 @@ export declare class BahtForm extends BahtElement {
|
|
|
88
88
|
private nextVisible;
|
|
89
89
|
private revealClass;
|
|
90
90
|
private stateFor;
|
|
91
|
+
/** The row sub-field states of one repeater: `fieldStates` keys starting `key[`. */
|
|
92
|
+
private rowStatesFor;
|
|
91
93
|
private valueFor;
|
|
92
94
|
/** Debounced keystrokes still in a control land in `edits` before anyone reads them. */
|
|
93
95
|
private flushTyped;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { nothing as g, html as p } from "lit";
|
|
2
|
-
import { property as d, state as m, customElement as
|
|
3
|
-
import { repeat as
|
|
2
|
+
import { property as d, state as m, customElement as E } from "lit/decorators.js";
|
|
3
|
+
import { repeat as P } from "lit/directives/repeat.js";
|
|
4
4
|
import { html as $ } from "lit/static-html.js";
|
|
5
|
-
import { BahtElement as
|
|
6
|
-
import { TAG_FOR_TYPE as
|
|
5
|
+
import { BahtElement as T, hasDom as O } from "./base.js";
|
|
6
|
+
import { TAG_FOR_TYPE as B, WIDTH_CLASS as I, staticTag as j } from "./primitives/tag-map.js";
|
|
7
7
|
import { defaultValue as C } from "../core/defaults.js";
|
|
8
8
|
import { t as D } from "../core/i18n.js";
|
|
9
9
|
import { validateField as _ } from "../core/validators.js";
|
|
10
10
|
import { resolveViewFields as M } from "../core/views.js";
|
|
11
|
-
import { applyFlags as
|
|
12
|
-
import { hasScripts as w, runScripts as F, createScriptContext as
|
|
13
|
-
import { displayValue as
|
|
11
|
+
import { applyFlags as L } from "../core/flags.js";
|
|
12
|
+
import { hasScripts as w, runScripts as F, createScriptContext as A, MAX_SCRIPT_DEPTH as x } from "../core/engine/scripts.js";
|
|
13
|
+
import { displayValue as R } 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";
|
|
@@ -26,12 +26,12 @@ import "./primitives/baht-file.js";
|
|
|
26
26
|
import "./primitives/baht-text.js";
|
|
27
27
|
import "./primitives/baht-section.js";
|
|
28
28
|
import "./primitives/baht-repeater.js";
|
|
29
|
-
var
|
|
30
|
-
for (var r = i > 1 ? void 0 : i ?
|
|
29
|
+
var U = Object.defineProperty, W = Object.getOwnPropertyDescriptor, h = (t, s, e, i) => {
|
|
30
|
+
for (var r = i > 1 ? void 0 : i ? W(s, e) : s, o = t.length - 1, a; o >= 0; o--)
|
|
31
31
|
(a = t[o]) && (r = (i ? a(s, e, r) : a(r)) || r);
|
|
32
|
-
return i && r &&
|
|
32
|
+
return i && r && U(s, e, r), r;
|
|
33
33
|
};
|
|
34
|
-
let n = class extends
|
|
34
|
+
let n = class extends T {
|
|
35
35
|
constructor() {
|
|
36
36
|
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 = {};
|
|
37
37
|
}
|
|
@@ -45,7 +45,7 @@ let n = class extends P {
|
|
|
45
45
|
get effectiveForm() {
|
|
46
46
|
const t = this.assignedForm;
|
|
47
47
|
if (t)
|
|
48
|
-
return (this.flagCache.form !== t || this.flagCache.flags !== this.flags) && (this.flagCache = { form: t, flags: this.flags, out:
|
|
48
|
+
return (this.flagCache.form !== t || this.flagCache.flags !== this.flags) && (this.flagCache = { form: t, flags: this.flags, out: L(t, this.flags) }), this.flagCache.out;
|
|
49
49
|
}
|
|
50
50
|
willUpdate(t) {
|
|
51
51
|
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();
|
|
@@ -111,6 +111,13 @@ let n = class extends P {
|
|
|
111
111
|
}, e = this.scriptStates[t.key];
|
|
112
112
|
return e ? { ...s, ...e } : s;
|
|
113
113
|
}
|
|
114
|
+
/** The row sub-field states of one repeater: `fieldStates` keys starting `key[`. */
|
|
115
|
+
rowStatesFor(t) {
|
|
116
|
+
const s = `${t}[`, e = {};
|
|
117
|
+
for (const [i, r] of Object.entries(this.fieldStates))
|
|
118
|
+
i.startsWith(s) && (e[i] = r);
|
|
119
|
+
return e;
|
|
120
|
+
}
|
|
114
121
|
valueFor(t) {
|
|
115
122
|
return Object.hasOwn(this.edits, t.key) ? this.edits[t.key] : Object.hasOwn(this.value, t.key) ? this.value[t.key] : C(t);
|
|
116
123
|
}
|
|
@@ -149,7 +156,9 @@ let n = class extends P {
|
|
|
149
156
|
const i = this.stateFor(e);
|
|
150
157
|
if (!i.visible) continue;
|
|
151
158
|
const r = i.required === !!e.required ? e : { ...e, required: i.required };
|
|
152
|
-
t.push(
|
|
159
|
+
t.push(
|
|
160
|
+
..._(r, this.valueFor(e), this.registry, s, this.fieldStates)
|
|
161
|
+
);
|
|
153
162
|
}
|
|
154
163
|
return t;
|
|
155
164
|
}
|
|
@@ -183,7 +192,7 @@ let n = class extends P {
|
|
|
183
192
|
// ---- client scripts (spec 01 §14) ----
|
|
184
193
|
/** The `g` (g_form) for the current form — built lazily over this component's state. */
|
|
185
194
|
g(t) {
|
|
186
|
-
return this.scriptContext && this.scriptContextForm === t ? this.scriptContext : (this.scriptContextForm = t, this.scriptContext =
|
|
195
|
+
return this.scriptContext && this.scriptContextForm === t ? this.scriptContext : (this.scriptContextForm = t, this.scriptContext = A(t, {
|
|
187
196
|
getData: () => this.getData(),
|
|
188
197
|
setValue: (s, e) => {
|
|
189
198
|
this.applyScriptValue(s, e);
|
|
@@ -228,9 +237,9 @@ let n = class extends P {
|
|
|
228
237
|
runChangeScripts(t, s, e, i) {
|
|
229
238
|
const r = this.effectiveForm;
|
|
230
239
|
if (!(!r || !w(r, "change", t))) {
|
|
231
|
-
if (this.scriptDepth >=
|
|
240
|
+
if (this.scriptDepth >= x) {
|
|
232
241
|
console.warn(
|
|
233
|
-
`[bahtui] script cascade on "${r.form_name}.${t}" exceeded ${
|
|
242
|
+
`[bahtui] script cascade on "${r.form_name}.${t}" exceeded ${x} levels — stopped (setValue ping-pong between change scripts?)`
|
|
234
243
|
);
|
|
235
244
|
return;
|
|
236
245
|
}
|
|
@@ -295,7 +304,7 @@ let n = class extends P {
|
|
|
295
304
|
const s = this.stateFor(t);
|
|
296
305
|
if (!s.visible) return [];
|
|
297
306
|
const e = this.getData(), i = s.required === !!t.required ? t : { ...t, required: s.required };
|
|
298
|
-
return _(i, this.valueFor(t), this.registry, e);
|
|
307
|
+
return _(i, this.valueFor(t), this.registry, e, this.fieldStates);
|
|
299
308
|
}
|
|
300
309
|
/** `validateOn: 'blur'` — validate just the field the user left, same as
|
|
301
310
|
* how errors are shown elsewhere (merged into `this.errors`, painted by
|
|
@@ -307,18 +316,18 @@ let n = class extends P {
|
|
|
307
316
|
}
|
|
308
317
|
renderField(t) {
|
|
309
318
|
var S;
|
|
310
|
-
const s =
|
|
319
|
+
const s = B[t.type];
|
|
311
320
|
if (!s)
|
|
312
321
|
return console.warn(`[bahtui] unknown field type "${t.type}" — skipped`), g;
|
|
313
|
-
const e = this.stateFor(t), i =
|
|
322
|
+
const e = this.stateFor(t), i = I[t.width ?? "full"] ?? "baht-field--w-full", r = ((S = this.errors.find((u) => u.key === t.key)) == null ? void 0 : S.message) ?? null;
|
|
314
323
|
if (this.readonly && t.type !== "label" && t.type !== "section")
|
|
315
324
|
return this.renderReadonlyField(t, i, e.visible);
|
|
316
325
|
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) => {
|
|
317
326
|
this.onControlInput(t.key, u);
|
|
318
|
-
}, f =
|
|
327
|
+
}, f = j(s);
|
|
319
328
|
let y;
|
|
320
329
|
if (t.type === "repeater") {
|
|
321
|
-
const u = this.errors.filter((q) => q.key.startsWith(`${t.key}[`));
|
|
330
|
+
const u = this.errors.filter((q) => q.key.startsWith(`${t.key}[`)), V = this.rowStatesFor(t.key);
|
|
322
331
|
y = $`<${f}
|
|
323
332
|
.field=${o}
|
|
324
333
|
.value=${a}
|
|
@@ -329,6 +338,7 @@ let n = class extends P {
|
|
|
329
338
|
.error=${r}
|
|
330
339
|
.debounce=${b}
|
|
331
340
|
.rowErrors=${u}
|
|
341
|
+
.rowStates=${V}
|
|
332
342
|
@baht-input=${v}
|
|
333
343
|
></${f}>`;
|
|
334
344
|
} else if (t.type === "file") {
|
|
@@ -395,7 +405,7 @@ let n = class extends P {
|
|
|
395
405
|
e && this.onFieldInput(e, s);
|
|
396
406
|
}
|
|
397
407
|
renderReadonlyField(t, s, e) {
|
|
398
|
-
const i = t.options_from ? this.dynamicOptions.get(t.key) ?? t.options ?? [] : t.options, r =
|
|
408
|
+
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">${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"
|
|
399
409
|
>${r.items.map((a) => p`<span class="baht-readonly-chip">${a}</span>`)}</span
|
|
400
410
|
>` : p`<div class="baht-readonly-rows">
|
|
401
411
|
${r.rows.map(
|
|
@@ -431,7 +441,7 @@ let n = class extends P {
|
|
|
431
441
|
</div>`
|
|
432
442
|
)}
|
|
433
443
|
</div>` : g}
|
|
434
|
-
${
|
|
444
|
+
${P(
|
|
435
445
|
this.sortedFields,
|
|
436
446
|
(t) => t.key,
|
|
437
447
|
(t) => this.renderField(t)
|
|
@@ -482,7 +492,7 @@ h([
|
|
|
482
492
|
m()
|
|
483
493
|
], n.prototype, "formMessages", 2);
|
|
484
494
|
n = h([
|
|
485
|
-
|
|
495
|
+
E("baht-form")
|
|
486
496
|
], n);
|
|
487
497
|
export {
|
|
488
498
|
n as BahtForm
|
|
@@ -15,7 +15,10 @@ import { BahtFieldBase } from '../field-base.js';
|
|
|
15
15
|
* Give a clipping ancestor (`overflow: hidden`) room, or it hides the popup.
|
|
16
16
|
*
|
|
17
17
|
* A footer under the grid carries **Today** (picks today — greyed out when
|
|
18
|
-
* min/max/disabled rules exclude it) and **Clear** (empties the value).
|
|
18
|
+
* min/max/disabled rules exclude it) and **Clear** (empties the value). With
|
|
19
|
+
* `field.enable_time` the first button reads **Now** (i18n key `now`) and picks
|
|
20
|
+
* today at the current time, to the minute (seconds dropped), under the same
|
|
21
|
+
* min/max/disabled rules; like Today it closes the calendar. Both
|
|
19
22
|
* `await el.updateComplete` resolves only once the calendar is attached (the
|
|
20
23
|
* lazy flatpickr load is folded into it), so hosts and tests need no extra wait.
|
|
21
24
|
* Today / Clear go through flatpickr, so they emit exactly like a click on a day. `disableMobile`
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { html as
|
|
2
|
-
import { customElement as
|
|
3
|
-
import { ifDefined as
|
|
1
|
+
import { html as y } from "lit";
|
|
2
|
+
import { customElement as g } from "lit/decorators.js";
|
|
3
|
+
import { ifDefined as D } from "lit/directives/if-defined.js";
|
|
4
4
|
import { BahtFieldBase as w } from "../field-base.js";
|
|
5
|
-
import { formatIsoDateTime as u, formatIsoDate as c, dateModeOf as h, parseIsoDate as l, isoDaysOf as
|
|
5
|
+
import { formatIsoDateTime as u, formatIsoDate as c, dateModeOf as h, parseIsoDate as l, isoDaysOf as k, parseIsoDateTime as C, parseDisabledEntry as v } from "../../core/dates.js";
|
|
6
6
|
import { t as p } from "../../core/i18n.js";
|
|
7
|
-
import { renderIcon as
|
|
8
|
-
var
|
|
9
|
-
for (var i = s > 1 ? void 0 : s ?
|
|
7
|
+
import { renderIcon as _ } from "./icons.js";
|
|
8
|
+
var I = Object.getOwnPropertyDescriptor, B = (t, e, a, s) => {
|
|
9
|
+
for (var i = s > 1 ? void 0 : s ? I(e, a) : e, n = t.length - 1, r; n >= 0; n--)
|
|
10
10
|
(r = t[n]) && (i = r(i) || i);
|
|
11
11
|
return i;
|
|
12
12
|
};
|
|
13
|
-
let f,
|
|
14
|
-
function
|
|
15
|
-
return
|
|
13
|
+
let f, O;
|
|
14
|
+
function x() {
|
|
15
|
+
return O ?? (O = import("flatpickr").then((t) => f = t.default));
|
|
16
16
|
}
|
|
17
17
|
let m = class extends w {
|
|
18
18
|
constructor() {
|
|
@@ -25,20 +25,20 @@ let m = class extends w {
|
|
|
25
25
|
return t;
|
|
26
26
|
}
|
|
27
27
|
renderControl() {
|
|
28
|
-
return
|
|
28
|
+
return y`<span class="baht-datepicker-field">
|
|
29
29
|
<input
|
|
30
30
|
class="baht-control baht-control--date"
|
|
31
31
|
id=${this.controlId}
|
|
32
32
|
type="text"
|
|
33
33
|
autocomplete="off"
|
|
34
|
-
placeholder=${
|
|
34
|
+
placeholder=${D(this.field.placeholder)}
|
|
35
35
|
?disabled=${this.disabled}
|
|
36
36
|
?readonly=${this.readonly}
|
|
37
37
|
?required=${this.required}
|
|
38
38
|
aria-invalid=${this.invalid}
|
|
39
39
|
aria-describedby=${this.describedBy}
|
|
40
40
|
/>
|
|
41
|
-
<span class="baht-datepicker-field-icon" aria-hidden="true">${
|
|
41
|
+
<span class="baht-datepicker-field-icon" aria-hidden="true">${_("calendar")}</span>
|
|
42
42
|
</span>`;
|
|
43
43
|
}
|
|
44
44
|
connectedCallback() {
|
|
@@ -56,7 +56,7 @@ let m = class extends w {
|
|
|
56
56
|
}
|
|
57
57
|
if (((s = this.fp) == null ? void 0 : s.input) !== e || t.has("field") || t.has("disabled") || t.has("readonly")) {
|
|
58
58
|
if (this.teardown(), !f) {
|
|
59
|
-
this.pendingLoad ?? (this.pendingLoad =
|
|
59
|
+
this.pendingLoad ?? (this.pendingLoad = x().catch((i) => {
|
|
60
60
|
console.warn("[bahtui] <baht-datepicker> needs the `flatpickr` peer dependency", i);
|
|
61
61
|
}).then(() => {
|
|
62
62
|
this.pendingLoad = void 0, this.isConnected && this.requestUpdate();
|
|
@@ -119,7 +119,7 @@ let m = class extends w {
|
|
|
119
119
|
const e = document.createElement("div");
|
|
120
120
|
e.className = "baht-datepicker-footer";
|
|
121
121
|
const a = document.createElement("button");
|
|
122
|
-
a.type = "button", a.className = "baht-button baht-button--sm baht-datepicker-today", a.textContent = p("today"), a.addEventListener("click", () => {
|
|
122
|
+
a.type = "button", a.className = "baht-button baht-button--sm baht-datepicker-today", a.textContent = p(this.field.enable_time ? "now" : "today"), a.addEventListener("click", () => {
|
|
123
123
|
this.pickToday();
|
|
124
124
|
});
|
|
125
125
|
const s = document.createElement("button");
|
|
@@ -136,18 +136,18 @@ let m = class extends w {
|
|
|
136
136
|
}
|
|
137
137
|
todayAllowed() {
|
|
138
138
|
const t = o(/* @__PURE__ */ new Date()), e = l(this.field.min), a = l(this.field.max);
|
|
139
|
-
return e && t < o(e) || a && t > o(a) ? !1 : !(b(this.field) ?? []).some((s) =>
|
|
139
|
+
return e && t < o(e) || a && t > o(a) ? !1 : !(b(this.field) ?? []).some((s) => L(s, t));
|
|
140
140
|
}
|
|
141
141
|
pickToday() {
|
|
142
142
|
const t = this.fp;
|
|
143
143
|
if (!t || !this.todayAllowed()) return;
|
|
144
|
-
const e = o(/* @__PURE__ */ new Date());
|
|
144
|
+
const e = this.field.enable_time ? E(/* @__PURE__ */ new Date()) : o(/* @__PURE__ */ new Date());
|
|
145
145
|
switch (h(this.field)) {
|
|
146
146
|
case "range":
|
|
147
147
|
t.setDate([e, e], !0);
|
|
148
148
|
break;
|
|
149
149
|
case "multiple": {
|
|
150
|
-
t.selectedDates.some((s) =>
|
|
150
|
+
t.selectedDates.some((s) => this.sameInstants([s], [e])) || t.setDate([...t.selectedDates, e], !0);
|
|
151
151
|
break;
|
|
152
152
|
}
|
|
153
153
|
case "single":
|
|
@@ -164,8 +164,8 @@ let m = class extends w {
|
|
|
164
164
|
* a host may not always have a time to feed back (an edited record that
|
|
165
165
|
* predates this field, for one). */
|
|
166
166
|
datesOf(t) {
|
|
167
|
-
const e = (a) => (this.field.enable_time ?
|
|
168
|
-
return
|
|
167
|
+
const e = (a) => (this.field.enable_time ? C(a) : null) ?? l(a);
|
|
168
|
+
return k(t).map(e).filter((a) => a !== null);
|
|
169
169
|
}
|
|
170
170
|
/** flatpickr's selection -> the value shape of the field's mode. */
|
|
171
171
|
toValue(t) {
|
|
@@ -180,17 +180,20 @@ let m = class extends w {
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
};
|
|
183
|
-
m =
|
|
184
|
-
|
|
183
|
+
m = B([
|
|
184
|
+
g("baht-datepicker")
|
|
185
185
|
], m);
|
|
186
186
|
function o(t) {
|
|
187
187
|
return new Date(t.getFullYear(), t.getMonth(), t.getDate());
|
|
188
188
|
}
|
|
189
|
-
function
|
|
190
|
-
return
|
|
189
|
+
function E(t) {
|
|
190
|
+
return new Date(t.getFullYear(), t.getMonth(), t.getDate(), t.getHours(), t.getMinutes());
|
|
191
191
|
}
|
|
192
192
|
function F(t, e) {
|
|
193
|
-
|
|
193
|
+
return c(t) === c(e);
|
|
194
|
+
}
|
|
195
|
+
function L(t, e) {
|
|
196
|
+
if (t instanceof Date) return F(t, e);
|
|
194
197
|
if (typeof t == "function") return t(e);
|
|
195
198
|
if (typeof t == "object" && "from" in t) {
|
|
196
199
|
const a = o(new Date(t.from)), s = o(new Date(t.to));
|
|
@@ -201,7 +204,7 @@ function F(t, e) {
|
|
|
201
204
|
function b(t) {
|
|
202
205
|
const e = [];
|
|
203
206
|
for (const s of t.disabled_dates ?? []) {
|
|
204
|
-
const i =
|
|
207
|
+
const i = v(s);
|
|
205
208
|
i && e.push(i[0].getTime() === i[1].getTime() ? i[0] : { from: i[0], to: i[1] });
|
|
206
209
|
}
|
|
207
210
|
const a = t.disabled_weekdays;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TemplateResult } from 'lit';
|
|
2
2
|
import { BahtElement } from '../base.js';
|
|
3
3
|
import { FieldOption } from '../../core/types.js';
|
|
4
|
+
export type FilterVariant = 'chips' | 'segmented';
|
|
4
5
|
/**
|
|
5
6
|
* Filter chip group. Controlled: emits `baht-change { selected }` (array of
|
|
6
7
|
* option keys), host re-feeds `selected`. Typical use: above an <baht-table>,
|
|
@@ -10,11 +11,21 @@ import { FieldOption } from '../../core/types.js';
|
|
|
10
11
|
* f.options = [{ key: 'running', label: 'Running' }, { key: 'failed', label: 'Failed' }];
|
|
11
12
|
* f.selected = ['running'];
|
|
12
13
|
* f.addEventListener('baht-change', (e) => refetch({ status: e.detail.selected }));
|
|
14
|
+
*
|
|
15
|
+
* `variant="segmented"` draws the same options as one joined segmented
|
|
16
|
+
* control (a single bordered group, active segment filled with the ink
|
|
17
|
+
* colour) — for a period switch like Week / Month / Year. Pair it with
|
|
18
|
+
* `multiple="false"`: a segmented control always holds one value, so clicking
|
|
19
|
+
* the active segment does nothing (no `baht-change`) instead of clearing it.
|
|
13
20
|
*/
|
|
14
21
|
export declare class BahtFilter extends BahtElement {
|
|
15
22
|
options: FieldOption[];
|
|
16
23
|
selected: string[];
|
|
17
|
-
/**
|
|
24
|
+
/** Look: `'chips'` (default) = separate pill chips; `'segmented'` = one joined
|
|
25
|
+
* segmented control. With `multiple=false` a segmented control never clears. */
|
|
26
|
+
variant: FilterVariant;
|
|
27
|
+
/** false = radio behavior (max one active; clicking it again clears — except
|
|
28
|
+
* with `variant="segmented"`, where the active segment stays selected). */
|
|
18
29
|
multiple: boolean;
|
|
19
30
|
disabled: boolean;
|
|
20
31
|
/** Accessible name for the chip group. */
|
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
import { html as h } from "lit";
|
|
2
|
-
import { property as
|
|
3
|
-
import { boolOff as
|
|
4
|
-
var
|
|
5
|
-
for (var e =
|
|
6
|
-
(n =
|
|
7
|
-
return
|
|
2
|
+
import { property as o, customElement as d } from "lit/decorators.js";
|
|
3
|
+
import { boolOff as c, BahtElement as b } from "../base.js";
|
|
4
|
+
var m = Object.defineProperty, u = Object.getOwnPropertyDescriptor, l = (s, t, r, a) => {
|
|
5
|
+
for (var e = a > 1 ? void 0 : a ? u(t, r) : t, p = s.length - 1, n; p >= 0; p--)
|
|
6
|
+
(n = s[p]) && (e = (a ? n(t, r, e) : n(e)) || e);
|
|
7
|
+
return a && e && m(t, r, e), e;
|
|
8
8
|
};
|
|
9
|
-
let
|
|
9
|
+
let i = class extends b {
|
|
10
10
|
constructor() {
|
|
11
|
-
super(...arguments), this.options = [], this.selected = [], this.multiple = !0, this.disabled = !1, this.label = "Filters";
|
|
11
|
+
super(...arguments), this.options = [], this.selected = [], this.variant = "chips", this.multiple = !0, this.disabled = !1, this.label = "Filters";
|
|
12
12
|
}
|
|
13
|
-
toggle(
|
|
14
|
-
const
|
|
15
|
-
let
|
|
16
|
-
|
|
13
|
+
toggle(s) {
|
|
14
|
+
const t = this.selected.includes(s);
|
|
15
|
+
let r;
|
|
16
|
+
if (this.multiple)
|
|
17
|
+
r = t ? this.selected.filter((e) => e !== s) : [...this.selected, s], r = this.options.map((e) => e.key).filter((e) => r.includes(e));
|
|
18
|
+
else {
|
|
19
|
+
if (t && this.variant === "segmented") return;
|
|
20
|
+
r = t ? [] : [s];
|
|
21
|
+
}
|
|
22
|
+
this.emit("baht-change", { selected: r });
|
|
17
23
|
}
|
|
18
24
|
render() {
|
|
19
|
-
|
|
25
|
+
const s = this.variant === "segmented" ? " baht-filter--segmented" : "";
|
|
26
|
+
return h`<div class="baht-filter${s}" role="group" aria-label=${this.label}>
|
|
20
27
|
${this.options.map(
|
|
21
28
|
(t) => h`<button
|
|
22
29
|
type="button"
|
|
@@ -30,24 +37,27 @@ let r = class extends b {
|
|
|
30
37
|
</div>`;
|
|
31
38
|
}
|
|
32
39
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
],
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
],
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
],
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
],
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
],
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
],
|
|
40
|
+
l([
|
|
41
|
+
o({ attribute: !1 })
|
|
42
|
+
], i.prototype, "options", 2);
|
|
43
|
+
l([
|
|
44
|
+
o({ attribute: !1 })
|
|
45
|
+
], i.prototype, "selected", 2);
|
|
46
|
+
l([
|
|
47
|
+
o()
|
|
48
|
+
], i.prototype, "variant", 2);
|
|
49
|
+
l([
|
|
50
|
+
o({ converter: c })
|
|
51
|
+
], i.prototype, "multiple", 2);
|
|
52
|
+
l([
|
|
53
|
+
o({ type: Boolean })
|
|
54
|
+
], i.prototype, "disabled", 2);
|
|
55
|
+
l([
|
|
56
|
+
o()
|
|
57
|
+
], i.prototype, "label", 2);
|
|
58
|
+
i = l([
|
|
59
|
+
d("baht-filter")
|
|
60
|
+
], i);
|
|
51
61
|
export {
|
|
52
|
-
|
|
62
|
+
i as BahtFilter
|
|
53
63
|
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { TemplateResult } from 'lit';
|
|
2
2
|
import { BahtElement } from '../base.js';
|
|
3
|
+
/** Where the dialog panel sits. */
|
|
4
|
+
export type ModalPlacement = 'center' | 'sheet' | 'side';
|
|
3
5
|
/**
|
|
4
6
|
* Modal dialog (20baht: 0.4 ink scrim, no blur; float shadow; 20px radius).
|
|
5
7
|
* Controlled: the host flips `open`. Children become the body.
|
|
@@ -9,6 +11,15 @@ import { BahtElement } from '../base.js';
|
|
|
9
11
|
* </baht-modal>
|
|
10
12
|
* modal.open = true;
|
|
11
13
|
* modal.addEventListener('baht-close', (e) => (modal.open = false));
|
|
14
|
+
*
|
|
15
|
+
* `placement` moves the panel: `'center'` (default) is the dialog above;
|
|
16
|
+
* `'sheet'` docks it to the bottom as a bottom sheet below 600px viewport
|
|
17
|
+
* width (full width, rounded top corners, a grab handle, max 90dvh with the
|
|
18
|
+
* body scrolling, slides up) and is a centered dialog from 600px up; `'side'`
|
|
19
|
+
* is a full-height drawer at the inline end, `min(420px, 100vw)` wide, sliding
|
|
20
|
+
* in from that edge. Scrim, focus trap/restore and `baht-close` are the same in
|
|
21
|
+
* every placement. Modifier classes: `.baht-modal--sheet` / `.baht-modal--side`
|
|
22
|
+
* on the panel, `.baht-modal-scrim--sheet` / `.baht-modal-scrim--side` on the scrim.
|
|
12
23
|
*/
|
|
13
24
|
export declare class BahtModal extends BahtElement {
|
|
14
25
|
open: boolean;
|
|
@@ -16,6 +27,9 @@ export declare class BahtModal extends BahtElement {
|
|
|
16
27
|
/** One muted line under the heading; also becomes the dialog's aria-describedby. */
|
|
17
28
|
description: string;
|
|
18
29
|
showClose: boolean;
|
|
30
|
+
/** `'center'` (default) dialog, `'sheet'` bottom sheet below 600px (centered
|
|
31
|
+
* from 600px up), or `'side'` full-height drawer at the inline end. */
|
|
32
|
+
placement: ModalPlacement;
|
|
19
33
|
/**
|
|
20
34
|
* Scrim click requests close. Off by default: a stray click outside the panel
|
|
21
35
|
* must not throw away what the user typed — close via ×, Escape or your buttons.
|