bank20baht-ui 0.11.0 → 0.12.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.
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">`). 58 elements:
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 58 elements | read `dist/custom-elements.json` ([CEM standard](https://github.com/webcomponents/custom-elements-manifest)) |
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
 
@@ -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). Both
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 g } from "lit";
2
- import { customElement as D } from "lit/decorators.js";
3
- import { ifDefined as k } from "lit/directives/if-defined.js";
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 C, parseIsoDateTime as v, parseDisabledEntry as _ } from "../../core/dates.js";
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 I } from "./icons.js";
8
- var B = Object.getOwnPropertyDescriptor, O = (t, e, a, s) => {
9
- for (var i = s > 1 ? void 0 : s ? B(e, a) : e, n = t.length - 1, r; n >= 0; n--)
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, x;
14
- function E() {
15
- return x ?? (x = import("flatpickr").then((t) => f = t.default));
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 g`<span class="baht-datepicker-field">
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=${k(this.field.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">${I("calendar")}</span>
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 = E().catch((i) => {
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) => F(s, t));
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) => y(s, e)) || t.setDate([...t.selectedDates, e], !0);
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 ? v(a) : null) ?? l(a);
168
- return C(t).map(e).filter((a) => a !== null);
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 = O([
184
- D("baht-datepicker")
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 y(t, e) {
190
- return c(t) === c(e);
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
- if (t instanceof Date) return y(t, e);
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 = _(s);
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
- /** false = radio behavior (max one active; clicking it again clears). */
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 p, customElement as c } from "lit/decorators.js";
3
- import { boolOff as d, BahtElement as b } from "../base.js";
4
- var u = Object.defineProperty, f = Object.getOwnPropertyDescriptor, i = (t, s, l, o) => {
5
- for (var e = o > 1 ? void 0 : o ? f(s, l) : s, a = t.length - 1, n; a >= 0; a--)
6
- (n = t[a]) && (e = (o ? n(s, l, e) : n(e)) || e);
7
- return o && e && u(s, l, e), e;
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 r = class extends b {
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(t) {
14
- const s = this.selected.includes(t);
15
- let l;
16
- this.multiple ? (l = s ? this.selected.filter((e) => e !== t) : [...this.selected, t], l = this.options.map((e) => e.key).filter((e) => l.includes(e))) : l = s ? [] : [t], this.emit("baht-change", { selected: l });
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
- return h`<div class="baht-filter" role="group" aria-label=${this.label}>
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
- i([
34
- p({ attribute: !1 })
35
- ], r.prototype, "options", 2);
36
- i([
37
- p({ attribute: !1 })
38
- ], r.prototype, "selected", 2);
39
- i([
40
- p({ converter: d })
41
- ], r.prototype, "multiple", 2);
42
- i([
43
- p({ type: Boolean })
44
- ], r.prototype, "disabled", 2);
45
- i([
46
- p()
47
- ], r.prototype, "label", 2);
48
- r = i([
49
- c("baht-filter")
50
- ], r);
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
- r as BahtFilter
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.
@@ -1,13 +1,13 @@
1
- import { nothing as a, html as h } from "lit";
2
- import { property as c, query as f, state as m, customElement as b } from "lit/decorators.js";
1
+ import { nothing as r, html as h } from "lit";
2
+ import { property as c, query as m, state as f, customElement as b } from "lit/decorators.js";
3
3
  import { boolOff as y, BahtElement as v, hasDom as p, adoptLightChildren as u } from "../base.js";
4
4
  import { afterExit as g } from "../motion.js";
5
5
  import { t as $ } from "../../core/i18n.js";
6
6
  import { renderIllustration as k } from "./illustrations.js";
7
- var C = Object.defineProperty, S = Object.getOwnPropertyDescriptor, r = (t, i, e, n) => {
8
- for (var o = n > 1 ? void 0 : n ? S(i, e) : i, l = t.length - 1, d; l >= 0; l--)
9
- (d = t[l]) && (o = (n ? d(i, e, o) : d(o)) || o);
10
- return n && o && C(i, e, o), o;
7
+ var C = Object.defineProperty, S = Object.getOwnPropertyDescriptor, l = (t, o, e, n) => {
8
+ for (var i = n > 1 ? void 0 : n ? S(o, e) : o, a = t.length - 1, d; a >= 0; a--)
9
+ (d = t[a]) && (i = (n ? d(o, e, i) : d(i)) || i);
10
+ return n && i && C(o, e, i), i;
11
11
  };
12
12
  const E = {
13
13
  fromAttribute: (t) => t !== null && t !== "false",
@@ -15,7 +15,7 @@ const E = {
15
15
  };
16
16
  let s = class extends v {
17
17
  constructor() {
18
- super(...arguments), this.open = !1, this.heading = "", this.description = "", this.showClose = !0, this.closeOnScrim = !1, this.closing = !1, this.closeToken = 0, this.previousFocus = null, this.onKeydown = (t) => {
18
+ super(...arguments), this.open = !1, this.heading = "", this.description = "", this.showClose = !0, this.placement = "center", this.closeOnScrim = !1, this.closing = !1, this.closeToken = 0, this.previousFocus = null, this.onKeydown = (t) => {
19
19
  !this.open || s.openStack.at(-1) !== this || (t.key === "Escape" ? this.requestClose("escape") : t.key === "Tab" && this.trapFocus(t));
20
20
  };
21
21
  }
@@ -35,17 +35,17 @@ let s = class extends v {
35
35
  }
36
36
  /** Keep Tab cycling inside the dialog (no keyboard escape to the page behind). */
37
37
  trapFocus(t) {
38
- const i = this.focusables(), e = this.querySelector(".baht-modal");
39
- if (i.length === 0) {
38
+ const o = this.focusables(), e = this.querySelector(".baht-modal");
39
+ if (o.length === 0) {
40
40
  e == null || e.focus(), t.preventDefault();
41
41
  return;
42
42
  }
43
- const n = i[0], o = i[i.length - 1], l = document.activeElement;
44
- if (!this.contains(l)) {
43
+ const n = o[0], i = o[o.length - 1], a = document.activeElement;
44
+ if (!this.contains(a)) {
45
45
  n.focus(), t.preventDefault();
46
46
  return;
47
47
  }
48
- t.shiftKey && (l === n || l === e) ? (o.focus(), t.preventDefault()) : !t.shiftKey && l === o && (n.focus(), t.preventDefault());
48
+ t.shiftKey && (a === n || a === e) ? (i.focus(), t.preventDefault()) : !t.shiftKey && a === i && (n.focus(), t.preventDefault());
49
49
  }
50
50
  connectedCallback() {
51
51
  super.connectedCallback(), p() && document.addEventListener("keydown", this.onKeydown);
@@ -66,14 +66,14 @@ let s = class extends v {
66
66
  // already under `.baht-modal-body`) and keeps that content inside the body
67
67
  // instead of leaking it to render outside the modal entirely.
68
68
  updated(t) {
69
- var i;
69
+ var o;
70
70
  if (u(this, this.bodyEl), t.has("open") && (s.openStack = s.openStack.filter((e) => e !== this), this.open && s.openStack.push(this)), t.has("open") && !this.open && this.closing) {
71
- const e = ++this.closeToken, n = this.querySelector(".baht-modal-scrim"), o = () => {
71
+ const e = ++this.closeToken, n = this.querySelector(".baht-modal-scrim"), i = () => {
72
72
  e === this.closeToken && !this.open && (this.closing = !1);
73
73
  };
74
- n ? g(n, o) : o();
74
+ n ? g(n, i) : i();
75
75
  }
76
- if (t.has("open") && this.open && (this.previousFocus = document.activeElement, (i = this.querySelector(".baht-modal")) == null || i.focus()), t.has("open") && !this.open && t.get("open") === !0) {
76
+ if (t.has("open") && this.open && (this.previousFocus = document.activeElement, (o = this.querySelector(".baht-modal")) == null || o.focus()), t.has("open") && !this.open && t.get("open") === !0) {
77
77
  const e = this.previousFocus;
78
78
  this.previousFocus = null, e != null && e.isConnected && (!document.activeElement || document.activeElement === document.body || this.contains(document.activeElement)) && e.focus();
79
79
  }
@@ -83,59 +83,64 @@ let s = class extends v {
83
83
  this.emit("baht-close", { reason: t });
84
84
  }
85
85
  render() {
86
+ const t = this.placement === "sheet" || this.placement === "side" ? this.placement : void 0;
86
87
  return h`<div
87
- class="baht-modal-scrim${this.closing ? " baht-modal-scrim--closing" : ""}"
88
- style=${this.open || this.closing ? a : "display:none"}
89
- @click=${(t) => {
90
- this.closeOnScrim && t.target === t.currentTarget && this.requestClose("scrim");
88
+ class="baht-modal-scrim${t ? ` baht-modal-scrim--${t}` : ""}${this.closing ? " baht-modal-scrim--closing" : ""}"
89
+ style=${this.open || this.closing ? r : "display:none"}
90
+ @click=${(o) => {
91
+ this.closeOnScrim && o.target === o.currentTarget && this.requestClose("scrim");
91
92
  }}
92
93
  >
93
94
  <div
94
- class="baht-modal"
95
+ class="baht-modal${t ? ` baht-modal--${t}` : ""}"
95
96
  role="dialog"
96
97
  aria-modal="true"
97
- aria-labelledby=${this.heading ? this.titleId : a}
98
- aria-label=${this.heading ? a : "Dialog"}
99
- aria-describedby=${this.description ? this.descriptionId : a}
98
+ aria-labelledby=${this.heading ? this.titleId : r}
99
+ aria-label=${this.heading ? r : "Dialog"}
100
+ aria-describedby=${this.description ? this.descriptionId : r}
100
101
  tabindex="-1"
101
102
  >
103
+ ${t === "sheet" ? h`<div class="baht-modal-handle" aria-hidden="true"></div>` : r}
102
104
  ${this.heading || this.description || this.showClose ? h`<header class="baht-modal-header">
103
105
  <div class="baht-modal-heading">
104
106
  <div class="baht-modal-title" id=${this.titleId}>${this.heading}</div>
105
- ${this.description ? h`<p class="baht-modal-description" id=${this.descriptionId}>${this.description}</p>` : a}
107
+ ${this.description ? h`<p class="baht-modal-description" id=${this.descriptionId}>${this.description}</p>` : r}
106
108
  </div>
107
109
  ${this.showClose ? h`<button type="button" class="baht-modal-close" aria-label=${$("close")} @click=${() => {
108
110
  this.requestClose("button");
109
- }}>${k("cross-box")}</button>` : a}
110
- </header>` : a}
111
+ }}>${k("cross-box")}</button>` : r}
112
+ </header>` : r}
111
113
  <div class="baht-modal-body"></div>
112
114
  </div>
113
115
  </div>`;
114
116
  }
115
117
  };
116
118
  s.openStack = [];
117
- r([
119
+ l([
118
120
  c({ type: Boolean, reflect: !0 })
119
121
  ], s.prototype, "open", 2);
120
- r([
122
+ l([
121
123
  c()
122
124
  ], s.prototype, "heading", 2);
123
- r([
125
+ l([
124
126
  c()
125
127
  ], s.prototype, "description", 2);
126
- r([
128
+ l([
127
129
  c({ attribute: "show-close", converter: y })
128
130
  ], s.prototype, "showClose", 2);
129
- r([
131
+ l([
132
+ c()
133
+ ], s.prototype, "placement", 2);
134
+ l([
130
135
  c({ attribute: "close-on-scrim", converter: E })
131
136
  ], s.prototype, "closeOnScrim", 2);
132
- r([
133
- f(".baht-modal-body")
137
+ l([
138
+ m(".baht-modal-body")
134
139
  ], s.prototype, "bodyEl", 2);
135
- r([
136
- m()
140
+ l([
141
+ f()
137
142
  ], s.prototype, "closing", 2);
138
- s = r([
143
+ s = l([
139
144
  b("baht-modal")
140
145
  ], s);
141
146
  export {
@@ -13,6 +13,10 @@ import { BahtFieldBase } from '../field-base.js';
13
13
  * and moves on, typing jumps to the first option starting with those letters
14
14
  * (while closed it picks it directly, like a native select).
15
15
  *
16
+ * An option's `description` renders as a muted second line under its label in
17
+ * the list, and after the label on the closed trigger while it is selected
18
+ * (e.g. label "Food", description "฿580 left").
19
+ *
16
20
  * Value contract unchanged: option `key`, or `null` for the placeholder row.
17
21
  */
18
22
  export declare class BahtSelect extends BahtFieldBase {