bank20baht-ui 0.10.0 → 0.11.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
@@ -154,7 +154,7 @@ el.value = JSON.parse(localStorage.getItem(key) ?? '{}'); // restore on mount
154
154
  // clear the key after the server accepts the submission
155
155
  ```
156
156
 
157
- See `apps/web`'s render page for a working restore/discard/clear-on-submit flow.
157
+ The `/builder` page of the docs site (`bun run --filter docs dev`) shows the same flow live.
158
158
 
159
159
  ### i18n — Thai (or any) built-in strings
160
160
 
@@ -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">`). 35+ components:
264
+ (`<html data-baht-theme="dark">`). 58 elements:
265
265
 
266
266
  ```html
267
267
  <baht-button variant="secondary" size="sm">Cancel</baht-button>
@@ -275,10 +275,13 @@ fonts yourself), borders-over-shadows, "paper" easing — plus an opt-in dark th
275
275
  - **Feedback** — `baht-modal` · `baht-toast` · `baht-alert` · `baht-tooltip` · `baht-progress` ·
276
276
  `baht-spinner` · `baht-skeleton`
277
277
  - **Inputs** — `baht-rating` · `baht-range` · `baht-otp` · `baht-typeahead` · `baht-reference-picker` ·
278
- `baht-keyvalue-editor` (+ the 11 form field primitives). Typed fields debounce keystrokes (150 ms,
279
- `debounce` / `options.inputDebounce`); `dropdown` renders its own listbox, never the OS picker.
280
- - **Structure & nav** — `baht-card` · `baht-fieldset` · `baht-timeline` · `baht-accordion` ·
281
- `baht-link` · `baht-breadcrumbs`
278
+ `baht-keyvalue-editor` · `baht-upload` (+ the 15 form field primitives). Typed fields debounce keystrokes
279
+ (150 ms, `debounce`); `dropdown` renders its own listbox, never the OS picker.
280
+ - **Stats & charts** — `baht-stat` · `baht-stat-chart` · `baht-meter` · `baht-sparkline` · `baht-bar-chart` ·
281
+ `baht-line-chart` · `baht-donut-chart` · `baht-heatmap` · `baht-progress-ring`
282
+ - **Structure & nav** — `baht-card` · `baht-fieldset` · `baht-timeline` · `baht-accordion` · `baht-steps` ·
283
+ `baht-link` · `baht-breadcrumbs` · `baht-avatar` · `baht-code` · `baht-empty-state` · `baht-error-state` ·
284
+ `baht-loading-overlay`
282
285
 
283
286
  ## Theming — design tokens + light DOM
284
287
 
@@ -305,7 +308,7 @@ Full reference: [docs/theming.md](./docs/theming.md).
305
308
  // 1. per-element subpath — one module per tag, plus only what it imports
306
309
  import 'bank20baht-ui/components/button'; // <baht-button>
307
310
  import { BahtTable } from 'bank20baht-ui/components/table';
308
- import 'bank20baht-ui/components/form'; // <baht-form> + the 14 field primitives
311
+ import 'bank20baht-ui/components/form'; // <baht-form> + the 15 field primitives
309
312
  import 'bank20baht-ui/components/workflow';
310
313
 
311
314
  // 2. by tag list — each tag is a dynamic import, fetched on demand
@@ -344,7 +347,7 @@ no hand-written type declarations in your app:
344
347
  | **Angular** | template binding on properties/events | `CUSTOM_ELEMENTS_SCHEMA`, see below |
345
348
  | **Vue 3** | keep Vue from treating `baht-*` as components | `isCustomElement`, see below |
346
349
  | **Phoenix / Rails / plain HTML** | it's just HTML — works as-is | none |
347
- | **Tooling** (Storybook, docgen, custom) | machine-readable API of all 36 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 58 elements | read `dist/custom-elements.json` ([CEM standard](https://github.com/webcomponents/custom-elements-manifest)) |
348
351
 
349
352
  **VS Code** — `.vscode/settings.json`:
350
353
 
@@ -400,21 +403,20 @@ import 'bank20baht-ui';
400
403
  `@lit-labs/ssr` is an optional peer dependency (server only). Full guide, limitations
401
404
  (light-DOM children, ids, i18n locale) and troubleshooting: [docs/ssr.md](./docs/ssr.md).
402
405
 
403
- ## Docs, Storybook, examples
406
+ ## Docs
404
407
 
405
- - **Per-component docs**: [`docs/`](./docs/README.md) — one .md per component + framework
406
- guides (vanilla / React / Angular).
407
- - **Storybook**: `pnpm --filter storybook-bahtui dev` → http://localhost:6006.
408
- - **Copy-out starters**: `examples/vanilla`, `examples/react`, `examples/angular` at the
409
- repo root — same workflow JSON in three hosts.
408
+ - **Per-component docs**: [`docs/`](./docs/README.md) — one .md per element + framework
409
+ guides (vanilla / React / Angular) + theming + SSR.
410
+ - **Docs site with live specimens**: https://bahtui-docs.pages.dev (source: `apps/docs`).
411
+ - **For agents**: the `bahtui-mcp` server (`apps/mcp`) serves these docs and runs the engine on your JSON.
410
412
 
411
- ## Field types (13)
413
+ ## Field types (17)
412
414
 
413
- `text · textarea · number · email · password · dropdown · multiselect · checkbox · radio · toggle · date ·
414
- file (capture only, no upload) · label (static)`
415
+ `text · textarea · number · currency · email · password · dropdown · multiselect · tags · checkbox · radio ·
416
+ toggle · date · file · repeater (one level of rows) · label (static) · section (divider)`
415
417
 
416
418
  Each field descriptor: `{ order, type, key, label, placeholder, width: '1/4'|'1/2'|'3/4'|'full',
417
- value, disabled, required, options, max_length, min, max, validators[], formatter }`.
419
+ value, disabled, required, options | options_from, max_length, min, max, validators[], formatter, flag }`.
418
420
 
419
421
  ## Visual Builder
420
422
 
@@ -17,6 +17,7 @@ import "./primitives/baht-number.js";
17
17
  import "./primitives/baht-currency.js";
18
18
  import "./primitives/baht-select.js";
19
19
  import "./primitives/baht-multiselect.js";
20
+ import "./primitives/baht-tags.js";
20
21
  import "./primitives/baht-checkbox.js";
21
22
  import "./primitives/baht-radio.js";
22
23
  import "./primitives/baht-toggle.js";
@@ -0,0 +1,95 @@
1
+ import { PropertyValues, TemplateResult } from 'lit';
2
+ import { BahtFieldBase } from '../field-base.js';
3
+ /**
4
+ * Creatable chip input: value is `string[]` (unique, trimmed, in entry
5
+ * order). Type text + Enter or comma to add a chip; Backspace on an empty
6
+ * input removes the last chip; pasting "a, b, c" adds three at once.
7
+ *
8
+ * Suggestions come from `field.options` — the SAME list every other
9
+ * option-bearing field reads, already resolved by `<baht-form>` when the
10
+ * descriptor uses `options_from` (spec 01 §1: dynamic options are merged
11
+ * into `field.options` before the primitive ever sees them, so this
12
+ * component needs no fetcher of its own). They filter as you type
13
+ * (case-insensitive, prefix matches first) and exclude tags already chosen.
14
+ * Free text becomes a chip unless `field.allow_new === false`, in which case
15
+ * only a suggestion's `key` (matched case-insensitively) can become one.
16
+ *
17
+ * el.field = { type: 'tags', key: 'labels', options: [...], max_items: 5 };
18
+ * el.addEventListener('baht-input', (e) => (draft.labels = e.detail.value));
19
+ *
20
+ * ARIA: APG editable combobox with list autocomplete, same pattern as
21
+ * <baht-typeahead> — the input is the combobox, the chips sit before it in
22
+ * the same control, `aria-activedescendant` walks the listbox.
23
+ */
24
+ export declare class BahtTags extends BahtFieldBase {
25
+ private inputText;
26
+ private open;
27
+ private activeIndex;
28
+ /** Transient reason the last chip attempt was refused (max_length /
29
+ * max_items / allow_new / duplicate) — cleared on the next successful
30
+ * add or edit, never persisted, never blocks anything by itself. */
31
+ private hint;
32
+ private inputEl;
33
+ private readonly onDocMousedown;
34
+ disconnectedCallback(): void;
35
+ protected willUpdate(changed: PropertyValues<this>): void;
36
+ /** The host value as canonical tags — a store/host can hand in anything
37
+ * (`[1, null]`), and a non-string entry must not crash the render. */
38
+ private get tags();
39
+ /** A chip shows the matching option's label when the tag happens to be one
40
+ * (case-insensitive) — same fallback the readonly display (`displayValue`)
41
+ * already applies; free text with no match shows as typed. */
42
+ private chipLabel;
43
+ private get maxLength();
44
+ private get allowNew();
45
+ private get suggestions();
46
+ /** Same id the base label's `for` points at — clicking the field label
47
+ * focuses the input natively, no click handler of our own needed. */
48
+ private get inputId();
49
+ private get listboxId();
50
+ private get hintId();
51
+ /** Base `describedBy` (error/help) plus the transient refusal hint, when
52
+ * one is showing. */
53
+ private get controlDescribedBy();
54
+ private optionId;
55
+ /** Why `trimmed` can't join `current` right now, or null when it can —
56
+ * same rules bank20baht-validator re-checks server-side. */
57
+ private rejectionFor;
58
+ /**
59
+ * `current` + one more chip, or the reason it was refused — null only for
60
+ * blank input (nothing worth reporting). Pure (no emit) so `addMany` can
61
+ * fold several candidates into ONE next array before telling the host,
62
+ * instead of racing `this.value` across several back-to-back emits.
63
+ */
64
+ private nextWithTag;
65
+ private hintFor;
66
+ /** Attempt to add one chip; returns whether it landed. A refusal shows a
67
+ * brief inline hint instead of failing silently. */
68
+ private addTag;
69
+ private removeTag;
70
+ /** Split pasted / bulk text on commas (and newlines) into separate chips,
71
+ * emitted as ONE array (see `nextWithTag`). Reports the last refusal only
72
+ * when NOTHING from the paste landed. */
73
+ private addMany;
74
+ private listen;
75
+ private close;
76
+ private highlight;
77
+ private commitTyped;
78
+ private pick;
79
+ private onInput;
80
+ private onEnter;
81
+ private onBackspace;
82
+ private onArrowDown;
83
+ private onArrowUp;
84
+ private onEscape;
85
+ private onKeydown;
86
+ private onPaste;
87
+ private onBlur;
88
+ protected updated(): void;
89
+ protected renderControl(): TemplateResult;
90
+ }
91
+ declare global {
92
+ interface HTMLElementTagNameMap {
93
+ 'baht-tags': BahtTags;
94
+ }
95
+ }
@@ -0,0 +1,319 @@
1
+ import { nothing as l, html as p } from "lit";
2
+ import { state as d, query as f, customElement as m } from "lit/decorators.js";
3
+ import { live as b } from "lit/directives/live.js";
4
+ import { BahtFieldBase as v } from "../field-base.js";
5
+ import { unlistenOutside as u, listenOutside as x, scrollOptionIntoView as w } from "./listbox.js";
6
+ import { t as r } from "../../core/i18n.js";
7
+ import { normalizeTagsValue as g, optionKeyLookup as I } from "../../core/validators.js";
8
+ var $ = Object.defineProperty, y = Object.getOwnPropertyDescriptor, c = (t, e, i, s) => {
9
+ for (var o = s > 1 ? void 0 : s ? y(e, i) : e, n = t.length - 1, a; n >= 0; n--)
10
+ (a = t[n]) && (o = (s ? a(e, i, o) : a(o)) || o);
11
+ return s && o && $(e, i, o), o;
12
+ };
13
+ const T = 8;
14
+ let h = class extends v {
15
+ constructor() {
16
+ super(...arguments), this.inputText = "", this.open = !1, this.activeIndex = -1, this.hint = null, this.onDocMousedown = (t) => {
17
+ this.contains(t.target) || this.close();
18
+ };
19
+ }
20
+ disconnectedCallback() {
21
+ u(this.onDocMousedown), super.disconnectedCallback();
22
+ }
23
+ willUpdate(t) {
24
+ super.willUpdate(t);
25
+ const e = t.get("field");
26
+ t.has("field") && e !== void 0 && e.key !== this.field.key && (this.inputText = "", this.close());
27
+ }
28
+ /** The host value as canonical tags — a store/host can hand in anything
29
+ * (`[1, null]`), and a non-string entry must not crash the render. */
30
+ get tags() {
31
+ return g(this.value);
32
+ }
33
+ /** A chip shows the matching option's label when the tag happens to be one
34
+ * (case-insensitive) — same fallback the readonly display (`displayValue`)
35
+ * already applies; free text with no match shows as typed. */
36
+ chipLabel(t) {
37
+ const e = t.toLowerCase(), i = (this.field.options ?? []).find((s) => s.key.toLowerCase() === e);
38
+ return (i == null ? void 0 : i.label) ?? t;
39
+ }
40
+ get maxLength() {
41
+ return this.field.max_length ?? 32;
42
+ }
43
+ get allowNew() {
44
+ return this.field.allow_new !== !1;
45
+ }
46
+ get suggestions() {
47
+ const t = this.field.options ?? [], e = new Set(this.tags.map((n) => n.toLowerCase())), i = this.inputText.trim().toLowerCase(), s = [], o = [];
48
+ for (const n of t) {
49
+ if (e.has(n.key.toLowerCase())) continue;
50
+ if (!i) {
51
+ s.push(n);
52
+ continue;
53
+ }
54
+ const a = n.label.toLowerCase();
55
+ a.startsWith(i) || n.key.toLowerCase().startsWith(i) ? s.push(n) : (a.includes(i) || n.key.toLowerCase().includes(i)) && o.push(n);
56
+ }
57
+ return [...s, ...o].slice(0, T);
58
+ }
59
+ /** Same id the base label's `for` points at — clicking the field label
60
+ * focuses the input natively, no click handler of our own needed. */
61
+ get inputId() {
62
+ return this.controlId;
63
+ }
64
+ get listboxId() {
65
+ return `${this.inputId}-list`;
66
+ }
67
+ get hintId() {
68
+ return `${this.inputId}-hint`;
69
+ }
70
+ /** Base `describedBy` (error/help) plus the transient refusal hint, when
71
+ * one is showing. */
72
+ get controlDescribedBy() {
73
+ const t = this.describedBy, e = [t === l ? "" : t, this.hint ? this.hintId : ""].filter(Boolean);
74
+ return e.length > 0 ? e.join(" ") : l;
75
+ }
76
+ optionId(t) {
77
+ return `${this.listboxId}-${t}`;
78
+ }
79
+ /** Why `trimmed` can't join `current` right now, or null when it can —
80
+ * same rules bank20baht-validator re-checks server-side. */
81
+ rejectionFor(t, e, i) {
82
+ return e.length > this.maxLength ? "tooLong" : t.some((s) => s.toLowerCase() === i) ? "duplicate" : !this.allowNew && !new Set((this.field.options ?? []).map((o) => o.key.toLowerCase())).has(i) ? "notAllowed" : this.field.max_items !== void 0 && t.length >= this.field.max_items ? "maxItems" : null;
83
+ }
84
+ /**
85
+ * `current` + one more chip, or the reason it was refused — null only for
86
+ * blank input (nothing worth reporting). Pure (no emit) so `addMany` can
87
+ * fold several candidates into ONE next array before telling the host,
88
+ * instead of racing `this.value` across several back-to-back emits.
89
+ */
90
+ nextWithTag(t, e) {
91
+ const i = e.trim();
92
+ if (!i) return null;
93
+ const s = i.toLowerCase(), o = this.field.options ?? [], n = I(o).get(s) ?? i, a = this.rejectionFor(t, n, s);
94
+ return a ? { reason: a } : { tags: g([...t, n], o) };
95
+ }
96
+ hintFor(t) {
97
+ switch (t) {
98
+ case "tooLong":
99
+ return r("tagTooLong", { n: this.maxLength });
100
+ case "duplicate":
101
+ return r("tagDuplicate");
102
+ case "notAllowed":
103
+ return r("tagNotAllowed");
104
+ case "maxItems":
105
+ return r("maxItems", { n: this.field.max_items });
106
+ }
107
+ }
108
+ /** Attempt to add one chip; returns whether it landed. A refusal shows a
109
+ * brief inline hint instead of failing silently. */
110
+ addTag(t) {
111
+ const e = this.nextWithTag(this.tags, t);
112
+ return e ? "reason" in e ? (this.hint = this.hintFor(e.reason), !1) : (this.hint = null, this.emitInput(e.tags), !0) : !1;
113
+ }
114
+ removeTag(t) {
115
+ var e;
116
+ this.emitInput(this.tags.filter((i) => i !== t)), (e = this.inputEl) == null || e.focus();
117
+ }
118
+ /** Split pasted / bulk text on commas (and newlines) into separate chips,
119
+ * emitted as ONE array (see `nextWithTag`). Reports the last refusal only
120
+ * when NOTHING from the paste landed. */
121
+ addMany(t) {
122
+ let e = this.tags, i = !1, s = null;
123
+ for (const o of t.split(/[,\n]/)) {
124
+ const n = this.nextWithTag(e, o);
125
+ if (n) {
126
+ if ("reason" in n) {
127
+ s = n.reason;
128
+ continue;
129
+ }
130
+ e = n.tags, i = !0;
131
+ }
132
+ }
133
+ i ? (this.hint = null, this.emitInput(e)) : s && (this.hint = this.hintFor(s));
134
+ }
135
+ listen() {
136
+ x(this.onDocMousedown);
137
+ }
138
+ close() {
139
+ u(this.onDocMousedown), this.open = !1, this.activeIndex = -1;
140
+ }
141
+ highlight(t) {
142
+ this.activeIndex !== t && (this.activeIndex = t);
143
+ }
144
+ commitTyped() {
145
+ this.addTag(this.inputText) && (this.inputText = ""), this.close();
146
+ }
147
+ pick(t) {
148
+ var e;
149
+ this.addTag(t.key), this.inputText = "", this.close(), (e = this.inputEl) == null || e.focus();
150
+ }
151
+ onInput(t) {
152
+ this.inputText = t.target.value, this.activeIndex = -1, this.open = !0, this.hint = null, this.listen();
153
+ }
154
+ onEnter(t) {
155
+ t.preventDefault();
156
+ const e = this.open && this.activeIndex >= 0 ? this.suggestions[this.activeIndex] : void 0;
157
+ e ? this.pick(e) : this.commitTyped();
158
+ }
159
+ onBackspace(t) {
160
+ this.inputText !== "" || this.tags.length === 0 || (t.preventDefault(), this.removeTag(this.tags[this.tags.length - 1]));
161
+ }
162
+ onArrowDown(t) {
163
+ if (t.preventDefault(), !this.open) {
164
+ this.open = !0, this.listen();
165
+ return;
166
+ }
167
+ const e = this.suggestions.length;
168
+ e > 0 && (this.activeIndex = (this.activeIndex + 1) % e);
169
+ }
170
+ onArrowUp(t) {
171
+ t.preventDefault();
172
+ const e = this.suggestions.length;
173
+ this.open && e > 0 && (this.activeIndex = (this.activeIndex - 1 + e) % e);
174
+ }
175
+ onEscape(t) {
176
+ this.open && (t.preventDefault(), this.close());
177
+ }
178
+ onKeydown(t) {
179
+ if (!(t.isComposing || t.keyCode === 229))
180
+ switch (t.key) {
181
+ case ",":
182
+ t.preventDefault(), this.commitTyped();
183
+ break;
184
+ case "Enter":
185
+ this.onEnter(t);
186
+ break;
187
+ case "Backspace":
188
+ this.onBackspace(t);
189
+ break;
190
+ case "ArrowDown":
191
+ this.onArrowDown(t);
192
+ break;
193
+ case "ArrowUp":
194
+ this.onArrowUp(t);
195
+ break;
196
+ case "Escape":
197
+ this.onEscape(t);
198
+ break;
199
+ }
200
+ }
201
+ onPaste(t) {
202
+ var a;
203
+ const e = ((a = t.clipboardData) == null ? void 0 : a.getData("text")) ?? "";
204
+ if (!e.includes(",") && !e.includes(`
205
+ `)) return;
206
+ t.preventDefault();
207
+ const i = t.target, s = i.value, o = i.selectionStart ?? s.length, n = i.selectionEnd ?? o;
208
+ this.addMany(s.slice(0, o) + e + s.slice(n)), this.inputText = "";
209
+ }
210
+ onBlur() {
211
+ this.commitTyped();
212
+ }
213
+ updated() {
214
+ w(this, this.open, this.activeIndex, this.optionId(this.activeIndex));
215
+ }
216
+ renderControl() {
217
+ const t = this.open && this.suggestions.length > 0, e = this.disabled || this.readonly;
218
+ return p`<div class="baht-tags${this.open ? " baht-tags--open" : ""}">
219
+ <div class="baht-control baht-tags-control">
220
+ ${this.tags.map(
221
+ (i) => p`<span class="baht-tag-chip">
222
+ <span class="baht-tag-chip-label">${this.chipLabel(i)}</span>
223
+ ${e ? l : p`<button
224
+ type="button"
225
+ class="baht-tag-chip-remove"
226
+ aria-label=${r("removeTag", { tag: this.chipLabel(i) })}
227
+ title=${r("removeTag", { tag: this.chipLabel(i) })}
228
+ @click=${() => {
229
+ this.removeTag(i);
230
+ }}
231
+ >
232
+ ×
233
+ </button>`}
234
+ </span>`
235
+ )}
236
+ <input
237
+ class="baht-tags-input"
238
+ id=${this.inputId}
239
+ type="text"
240
+ autocomplete="off"
241
+ role="combobox"
242
+ aria-labelledby=${this.field.label ? this.labelId : l}
243
+ aria-expanded=${t ? "true" : "false"}
244
+ aria-controls=${this.listboxId}
245
+ aria-autocomplete="list"
246
+ aria-activedescendant=${t && this.activeIndex >= 0 ? this.optionId(this.activeIndex) : l}
247
+ aria-invalid=${this.invalid}
248
+ aria-describedby=${this.controlDescribedBy}
249
+ placeholder=${this.tags.length === 0 ? this.field.placeholder ?? "" : ""}
250
+ .value=${b(this.inputText)}
251
+ ?disabled=${e}
252
+ @input=${(i) => {
253
+ this.onInput(i);
254
+ }}
255
+ @keydown=${(i) => {
256
+ this.onKeydown(i);
257
+ }}
258
+ @paste=${(i) => {
259
+ this.onPaste(i);
260
+ }}
261
+ @blur=${() => {
262
+ this.onBlur();
263
+ }}
264
+ />
265
+ </div>
266
+ ${this.hint ? p`<div class="baht-tags-hint" id=${this.hintId} role="status">${this.hint}</div>` : l}
267
+ <div
268
+ class="baht-listbox baht-tags-list"
269
+ id=${this.listboxId}
270
+ role="listbox"
271
+ aria-label=${this.field.label ?? ""}
272
+ ?hidden=${!t}
273
+ >
274
+ ${this.suggestions.map(
275
+ // eslint-disable-next-line lit-a11y/click-events-have-key-events -- keyboard lives on the combobox input (aria-activedescendant pattern)
276
+ (i, s) => p`<div
277
+ class="baht-listbox-option${s === this.activeIndex ? " baht-listbox-option--active" : ""}"
278
+ id=${this.optionId(s)}
279
+ role="option"
280
+ aria-selected=${s === this.activeIndex ? "true" : "false"}
281
+ data-key=${i.key}
282
+ @mousedown=${(o) => {
283
+ o.preventDefault();
284
+ }}
285
+ @mousemove=${() => {
286
+ this.highlight(s);
287
+ }}
288
+ @click=${() => {
289
+ this.pick(i);
290
+ }}
291
+ >
292
+ ${i.label}
293
+ </div>`
294
+ )}
295
+ </div>
296
+ </div>`;
297
+ }
298
+ };
299
+ c([
300
+ d()
301
+ ], h.prototype, "inputText", 2);
302
+ c([
303
+ d()
304
+ ], h.prototype, "open", 2);
305
+ c([
306
+ d()
307
+ ], h.prototype, "activeIndex", 2);
308
+ c([
309
+ d()
310
+ ], h.prototype, "hint", 2);
311
+ c([
312
+ f("input")
313
+ ], h.prototype, "inputEl", 2);
314
+ h = c([
315
+ m("baht-tags")
316
+ ], h);
317
+ export {
318
+ h as BahtTags
319
+ };