@echojs-ecosystem/ui 0.1.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 (58) hide show
  1. package/README.md +102 -0
  2. package/dist/button.d.ts +20 -0
  3. package/dist/button.js +559 -0
  4. package/dist/button.js.map +1 -0
  5. package/dist/button.types-B0SrEYRV.d.ts +27 -0
  6. package/dist/checkbox.d.ts +42 -0
  7. package/dist/checkbox.js +388 -0
  8. package/dist/checkbox.js.map +1 -0
  9. package/dist/cn-rGlUI2mx.d.ts +16 -0
  10. package/dist/core.d.ts +16 -0
  11. package/dist/core.js +294 -0
  12. package/dist/core.js.map +1 -0
  13. package/dist/field.d.ts +72 -0
  14. package/dist/field.js +479 -0
  15. package/dist/field.js.map +1 -0
  16. package/dist/field.types-BSyhu0Cf.d.ts +24 -0
  17. package/dist/icon-button.d.ts +15 -0
  18. package/dist/icon-button.js +606 -0
  19. package/dist/icon-button.js.map +1 -0
  20. package/dist/index-BhC5sVej.d.ts +35 -0
  21. package/dist/index.d.ts +87 -0
  22. package/dist/index.js +1604 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/input-mask.d.ts +5 -0
  25. package/dist/input-mask.js +568 -0
  26. package/dist/input-mask.js.map +1 -0
  27. package/dist/input-otp.d.ts +16 -0
  28. package/dist/input-otp.js +475 -0
  29. package/dist/input-otp.js.map +1 -0
  30. package/dist/input-tags.d.ts +16 -0
  31. package/dist/input-tags.js +549 -0
  32. package/dist/input-tags.js.map +1 -0
  33. package/dist/input.d.ts +14 -0
  34. package/dist/input.js +466 -0
  35. package/dist/input.js.map +1 -0
  36. package/dist/label.d.ts +17 -0
  37. package/dist/label.js +345 -0
  38. package/dist/label.js.map +1 -0
  39. package/dist/primitives.d.ts +23 -0
  40. package/dist/primitives.js +301 -0
  41. package/dist/primitives.js.map +1 -0
  42. package/dist/provider.d.ts +19 -0
  43. package/dist/provider.js +124 -0
  44. package/dist/provider.js.map +1 -0
  45. package/dist/slots-D2y1YgGz.d.ts +58 -0
  46. package/dist/textarea.d.ts +27 -0
  47. package/dist/textarea.js +361 -0
  48. package/dist/textarea.js.map +1 -0
  49. package/dist/theme-context-CyY95LK0.d.ts +39 -0
  50. package/dist/theme.d.ts +20 -0
  51. package/dist/theme.js +155 -0
  52. package/dist/theme.js.map +1 -0
  53. package/dist/types-CSHcGa_F.d.ts +13 -0
  54. package/dist/utils.d.ts +36 -0
  55. package/dist/utils.js +101 -0
  56. package/dist/utils.js.map +1 -0
  57. package/dist/variants-Bj38CFcH.d.ts +51 -0
  58. package/package.json +134 -0
package/dist/index.js ADDED
@@ -0,0 +1,1604 @@
1
+ import { h } from '@echojs-ecosystem/hyperdom';
2
+
3
+ // src/theme/default-theme.ts
4
+ var defaultTheme = {
5
+ prefix: "echo",
6
+ headless: false,
7
+ components: {
8
+ button: {
9
+ defaultVariants: {
10
+ variant: "primary",
11
+ size: "md",
12
+ radius: "full"
13
+ }
14
+ },
15
+ input: {
16
+ defaultVariants: {
17
+ variant: "outline",
18
+ size: "md"
19
+ }
20
+ },
21
+ inputMask: {
22
+ defaultVariants: {
23
+ variant: "outline",
24
+ size: "md"
25
+ }
26
+ },
27
+ inputOtp: {
28
+ defaultVariants: {
29
+ variant: "outline",
30
+ size: "md"
31
+ }
32
+ },
33
+ inputTags: {
34
+ defaultVariants: {
35
+ variant: "outline",
36
+ size: "md"
37
+ }
38
+ },
39
+ textarea: {
40
+ defaultVariants: {
41
+ variant: "outline",
42
+ size: "md",
43
+ resize: "vertical"
44
+ }
45
+ },
46
+ checkbox: {
47
+ defaultVariants: {
48
+ size: "md"
49
+ }
50
+ },
51
+ iconButton: {
52
+ defaultVariants: {
53
+ variant: "ghost",
54
+ size: "md"
55
+ }
56
+ }
57
+ }
58
+ };
59
+
60
+ // src/theme/create-theme.ts
61
+ var mergeComponentConfig = (base, next) => {
62
+ if (!base && !next) return void 0;
63
+ if (!base) return next ? { ...next } : void 0;
64
+ if (!next) return { ...base };
65
+ return {
66
+ baseClass: next.baseClass ?? base.baseClass,
67
+ className: next.className ?? base.className,
68
+ defaultProps: { ...base.defaultProps, ...next.defaultProps },
69
+ defaultVariants: { ...base.defaultVariants, ...next.defaultVariants },
70
+ variants: { ...base.variants, ...next.variants }
71
+ };
72
+ };
73
+ var mergeComponents = (base, next) => {
74
+ const keys = /* @__PURE__ */ new Set([...Object.keys(base ?? {}), ...Object.keys(next ?? {})]);
75
+ const merged = {};
76
+ for (const key of keys) {
77
+ const slice = mergeComponentConfig(base?.[key], next?.[key]);
78
+ if (slice) merged[key] = slice;
79
+ }
80
+ return merged;
81
+ };
82
+ var createTheme = (input, base = defaultTheme) => ({
83
+ prefix: input.prefix ?? base.prefix,
84
+ headless: input.headless ?? base.headless,
85
+ components: mergeComponents(base.components, input.components)
86
+ });
87
+
88
+ // src/theme/theme-context.ts
89
+ var stack = [];
90
+ var getUIContext = () => stack[stack.length - 1];
91
+ var getUIContextOrDefault = () => getUIContext() ?? createUIContextValue({});
92
+ var createUIContextValue = (input, parent = getUIContext()) => {
93
+ const parentTheme = parent?.theme ?? defaultTheme;
94
+ const theme = createTheme(input.theme ?? {}, parentTheme);
95
+ const headless = input.headless ?? theme.headless ?? parent?.headless ?? false;
96
+ return {
97
+ theme: { ...theme, headless },
98
+ headless
99
+ };
100
+ };
101
+ var runWithUIContext = (value, fn) => {
102
+ stack.push(value);
103
+ try {
104
+ return fn();
105
+ } finally {
106
+ stack.pop();
107
+ }
108
+ };
109
+ var resetUIContextStack = () => {
110
+ stack.length = 0;
111
+ };
112
+
113
+ // src/providers/UIProvider.ts
114
+ var resolveChildren = (children) => {
115
+ if (typeof children === "function") {
116
+ return children();
117
+ }
118
+ return children;
119
+ };
120
+ var UIProvider = (props) => {
121
+ const value = createUIContextValue({
122
+ theme: props.theme,
123
+ headless: props.headless
124
+ });
125
+ return runWithUIContext(value, () => resolveChildren(props.children));
126
+ };
127
+
128
+ // src/plugin/ui-plugin.ts
129
+ var createUiProvider = (options = {}) => ({
130
+ name: "ui",
131
+ wrapRoot(inner) {
132
+ return () => UIProvider({ ...options, children: inner });
133
+ }
134
+ });
135
+ var createUiPlugin = createUiProvider;
136
+ var uiPlugin = createUiProvider;
137
+
138
+ // src/theme/variants.ts
139
+ var tv = (config) => {
140
+ const resolve = (options) => {
141
+ const parts = [];
142
+ if (config.base) parts.push(config.base);
143
+ const resolved = { ...config.defaultVariants ?? {}, ...options ?? {} };
144
+ const variants = config.variants ?? {};
145
+ for (const [variantName, map] of Object.entries(variants)) {
146
+ const value = resolved[variantName];
147
+ if (value === void 0) continue;
148
+ const key = typeof value === "boolean" ? value ? "true" : "false" : String(value);
149
+ const cls = map[key];
150
+ if (cls) parts.push(cls);
151
+ }
152
+ for (const compound of config.compoundVariants ?? []) {
153
+ const { class: compoundClass, ...conditions } = compound;
154
+ const matches = Object.entries(conditions).every(([name, value]) => resolved[name] === value);
155
+ if (matches && compoundClass) parts.push(compoundClass);
156
+ }
157
+ return parts.filter(Boolean).join(" ");
158
+ };
159
+ if (config.slots) {
160
+ const slotFns = {};
161
+ for (const [slot, slotBase] of Object.entries(config.slots)) {
162
+ slotFns[slot] = (slotOptions) => {
163
+ const composed = [slotBase, resolve(slotOptions)].filter(Boolean).join(" ");
164
+ return composed.trim();
165
+ };
166
+ }
167
+ slotFns._config = config;
168
+ return slotFns;
169
+ }
170
+ const fn = (options) => resolve(options);
171
+ fn._config = config;
172
+ return fn;
173
+ };
174
+ var createComponentVariants = tv;
175
+ var resolveVariantClasses = (variantFn, options, headless) => {
176
+ if (headless || !variantFn) return void 0;
177
+ return variantFn(options);
178
+ };
179
+
180
+ // src/core/variant-keys.ts
181
+ var getVariantKeysFromFn = (variantsFn) => {
182
+ const variants = variantsFn?._config?.variants;
183
+ if (!variants) return [];
184
+ return Object.keys(variants);
185
+ };
186
+ var resolveVariantOptions = (variantsFn, themeDefaults, props) => {
187
+ const keys = [
188
+ .../* @__PURE__ */ new Set([
189
+ ...getVariantKeysFromFn(variantsFn),
190
+ ...Object.keys(variantsFn?._config?.defaultVariants ?? {}),
191
+ ...Object.keys(themeDefaults ?? {})
192
+ ])
193
+ ];
194
+ const picked = {};
195
+ for (const key of keys) {
196
+ if (props[key] !== void 0) picked[key] = props[key];
197
+ }
198
+ return {
199
+ ...variantsFn?._config?.defaultVariants,
200
+ ...themeDefaults,
201
+ ...picked
202
+ };
203
+ };
204
+
205
+ // src/utils/cn.ts
206
+ var defaultClassNameMerger = (...values) => {
207
+ const out = [];
208
+ const walk = (value) => {
209
+ if (value == null || value === false) return;
210
+ if (typeof value === "string") {
211
+ if (value) out.push(value);
212
+ return;
213
+ }
214
+ if (Array.isArray(value)) {
215
+ for (const item of value) walk(item);
216
+ return;
217
+ }
218
+ for (const [key, enabled] of Object.entries(value)) {
219
+ if (enabled) out.push(key);
220
+ }
221
+ };
222
+ for (const value of values) walk(value);
223
+ return out.join(" ");
224
+ };
225
+ var classNameMerger = defaultClassNameMerger;
226
+ var setClassNameMerger = (merger) => {
227
+ classNameMerger = merger;
228
+ };
229
+ var resetClassNameMerger = () => {
230
+ classNameMerger = defaultClassNameMerger;
231
+ };
232
+ var cn = (...values) => classNameMerger(...values);
233
+
234
+ // src/utils/compose-event-handlers.ts
235
+ var composeEventHandlers = (userHandler, internalHandler, options = {}) => {
236
+ if (!userHandler && !internalHandler) return void 0;
237
+ if (!userHandler) return internalHandler;
238
+ if (!internalHandler) return userHandler;
239
+ const { checkDefaultPrevented = false } = options;
240
+ return (event) => {
241
+ userHandler(event);
242
+ if (checkDefaultPrevented && event?.defaultPrevented) return;
243
+ internalHandler(event);
244
+ };
245
+ };
246
+
247
+ // src/utils/merge-props.ts
248
+ var CLASS_KEYS = /* @__PURE__ */ new Set(["class", "className"]);
249
+ var isEventHandlerKey = (key) => key.startsWith("on") || key.startsWith("on:");
250
+ var mergeClassValues = (...values) => {
251
+ const merged = cn(...values);
252
+ return merged || void 0;
253
+ };
254
+ var mergePair = (target, source) => {
255
+ if (!source) return;
256
+ for (const [key, value] of Object.entries(source)) {
257
+ if (value === void 0) continue;
258
+ if (CLASS_KEYS.has(key)) {
259
+ const mergedClass = mergeClassValues(target.className, value);
260
+ if (mergedClass !== void 0) {
261
+ target.className = mergedClass;
262
+ target.class = mergedClass;
263
+ }
264
+ continue;
265
+ }
266
+ if (isEventHandlerKey(key)) {
267
+ const existing = target[key];
268
+ target[key] = composeEventHandlers(
269
+ value,
270
+ existing,
271
+ { checkDefaultPrevented: true }
272
+ );
273
+ continue;
274
+ }
275
+ target[key] = value;
276
+ }
277
+ };
278
+ var mergeProps = (defaultProps2, providerProps, componentProps) => {
279
+ const merged = {};
280
+ mergePair(merged, defaultProps2);
281
+ mergePair(merged, providerProps);
282
+ mergePair(merged, componentProps);
283
+ return merged;
284
+ };
285
+
286
+ // src/core/component.ts
287
+ var themeKeyFromName = (name) => name.charAt(0).toLowerCase() + name.slice(1);
288
+ var createUIComponent = (config) => {
289
+ const themeKey = themeKeyFromName(config.name);
290
+ return (props) => {
291
+ const ctx = getUIContextOrDefault();
292
+ const themeSlice = ctx.theme.components?.[themeKey];
293
+ const headless = props.headless ?? ctx.headless ?? ctx.theme.headless ?? false;
294
+ const providerProps = {
295
+ ...themeSlice?.defaultProps,
296
+ ...themeSlice?.defaultVariants
297
+ };
298
+ if (themeSlice?.className) {
299
+ providerProps.className = themeSlice.className;
300
+ }
301
+ const merged = mergeProps(
302
+ config.defaultProps,
303
+ providerProps,
304
+ props
305
+ );
306
+ const variantOptions = resolveVariantOptions(
307
+ config.variants,
308
+ themeSlice?.defaultVariants,
309
+ merged
310
+ );
311
+ const variantClass = resolveVariantClasses(config.variants, variantOptions, headless);
312
+ const visualClass = headless ? void 0 : cn(themeSlice?.baseClass, themeSlice?.className, variantClass, merged.className, merged.class);
313
+ const finalProps = {
314
+ ...merged,
315
+ className: visualClass,
316
+ class: visualClass
317
+ };
318
+ if (config.render) {
319
+ return config.render({
320
+ props: finalProps,
321
+ headless,
322
+ className: visualClass,
323
+ ctx
324
+ });
325
+ }
326
+ const { children, as, ...rest } = finalProps;
327
+ const tag = as ?? config.defaultTag;
328
+ return h(tag, rest, children);
329
+ };
330
+ };
331
+
332
+ // src/utils/merge-refs.ts
333
+ var mergeRefs = (...refs) => {
334
+ const filtered = refs.filter(Boolean);
335
+ if (filtered.length === 0) return void 0;
336
+ if (filtered.length === 1) return filtered[0];
337
+ return (instance) => {
338
+ for (const ref of filtered) ref(instance);
339
+ };
340
+ };
341
+
342
+ // src/utils/data-attributes.ts
343
+ var dataState = (value) => ({
344
+ "data-state": value
345
+ });
346
+ var dataDisabled = (disabled) => disabled ? { "data-disabled": "" } : {};
347
+ var dataInvalid = (invalid) => invalid ? { "data-invalid": "" } : {};
348
+ var dataPending = (pending) => pending ? { "data-pending": "" } : {};
349
+
350
+ // src/core/aria.ts
351
+ var ariaBool = (value) => value ? "true" : "false";
352
+
353
+ // src/core/ids.ts
354
+ var idCounter = 0;
355
+ var createId = (prefix) => {
356
+ const ctx = getUIContextOrDefault();
357
+ const base = prefix ?? ctx.theme.prefix ?? "echo";
358
+ idCounter += 1;
359
+ return `${base}-${idCounter}`;
360
+ };
361
+ var useId = (prefix) => createId(prefix);
362
+
363
+ // src/core/slots.ts
364
+ var renderSlot = (props) => props.children;
365
+ var defaultProps = {
366
+ as: "span"
367
+ };
368
+ var VisuallyHidden = createUIComponent({
369
+ name: "VisuallyHidden",
370
+ defaultTag: "span",
371
+ defaultProps: {
372
+ ...defaultProps,
373
+ className: "sr-only"
374
+ },
375
+ render: ({ props, headless, className }) => {
376
+ const { as = "span", children, ...rest } = props;
377
+ const classes = headless ? void 0 : className ?? "sr-only";
378
+ return h(as, { ...rest, className: classes, class: classes }, children);
379
+ }
380
+ });
381
+
382
+ // src/primitives/portal.ts
383
+ var appendChild = (host, child) => {
384
+ if (child == null || child === false || child === true) return;
385
+ if (typeof child === "string" || typeof child === "number") {
386
+ host.appendChild(document.createTextNode(String(child)));
387
+ return;
388
+ }
389
+ if (child instanceof Node) {
390
+ host.appendChild(child);
391
+ return;
392
+ }
393
+ if (Array.isArray(child)) {
394
+ for (const item of child) appendChild(host, item);
395
+ }
396
+ };
397
+ var Portal = (props) => {
398
+ const container = props.container ?? document.body;
399
+ const host = document.createElement("div");
400
+ container.appendChild(host);
401
+ if (props.children !== void 0) {
402
+ appendChild(host, props.children);
403
+ }
404
+ return host;
405
+ };
406
+ var spinnerSizeClass = {
407
+ xs: "size-3.5 border",
408
+ sm: "size-4 border",
409
+ md: "size-4 border-2",
410
+ lg: "size-5 border-2",
411
+ xl: "size-5 border-2"
412
+ };
413
+ var defaultButtonSpinner = (size = "md") => h(
414
+ "span",
415
+ {
416
+ "data-slot": "spinner",
417
+ "data-btn-icon": "",
418
+ className: [
419
+ "inline-block shrink-0 animate-spin rounded-full border-current border-t-transparent",
420
+ spinnerSizeClass[size]
421
+ ].join(" "),
422
+ "aria-hidden": "true"
423
+ }
424
+ );
425
+
426
+ // src/components/button/button.styles.ts
427
+ var buttonStyles = tv({
428
+ base: [
429
+ "relative isolate inline-flex w-fit origin-center items-center justify-center gap-2",
430
+ "font-medium whitespace-nowrap select-none outline-none",
431
+ "rounded-3xl",
432
+ "transition-[transform,background-color,box-shadow,color] duration-200 ease-out",
433
+ "motion-reduce:transition-none",
434
+ "cursor-pointer",
435
+ "bg-[var(--button-bg)] text-[var(--button-fg)]",
436
+ "hover:bg-[var(--button-bg-hover)]",
437
+ "active:scale-[0.97] active:bg-[var(--button-bg-pressed)]",
438
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-zinc-400",
439
+ "disabled:opacity-50 disabled:pointer-events-none",
440
+ "data-[disabled]:opacity-50 data-[disabled]:pointer-events-none",
441
+ "data-[pending]:pointer-events-none data-[pending]:cursor-wait",
442
+ "[&_svg:not([data-slot=spinner]_svg)]:pointer-events-none [&_svg:not([data-slot=spinner]_svg)]:shrink-0",
443
+ "[&_[data-btn-icon]]:inline-flex [&_[data-btn-icon]]:shrink-0"
444
+ ].join(" "),
445
+ variants: {
446
+ variant: {
447
+ primary: [
448
+ "[--button-bg:var(--color-accent)]",
449
+ "[--button-bg-hover:var(--color-accent-hover)]",
450
+ "[--button-bg-pressed:var(--color-accent-hover)]",
451
+ "[--button-fg:var(--color-accent-foreground)]",
452
+ "focus-visible:ring-zinc-900"
453
+ ].join(" "),
454
+ secondary: [
455
+ "[--button-bg:var(--color-default)]",
456
+ "[--button-bg-hover:var(--color-default-hover)]",
457
+ "[--button-bg-pressed:var(--color-default-hover)]",
458
+ "[--button-fg:var(--color-default-foreground)]"
459
+ ].join(" "),
460
+ tertiary: [
461
+ "[--button-bg:var(--color-default)]",
462
+ "[--button-bg-hover:var(--color-default-hover)]",
463
+ "[--button-bg-pressed:var(--color-default-hover)]",
464
+ "[--button-fg:var(--color-zinc-700)]"
465
+ ].join(" "),
466
+ outline: [
467
+ "border border-[var(--color-border)]",
468
+ "[--button-bg:transparent]",
469
+ "[--button-bg-hover:color-mix(in_srgb,var(--color-default)_60%,transparent)]",
470
+ "[--button-bg-pressed:var(--color-default)]",
471
+ "[--button-fg:var(--color-default-foreground)]"
472
+ ].join(" "),
473
+ ghost: [
474
+ "[--button-bg:transparent]",
475
+ "[--button-bg-hover:var(--color-default)]",
476
+ "[--button-bg-pressed:var(--color-default)]",
477
+ "[--button-fg:var(--color-default-foreground)]"
478
+ ].join(" "),
479
+ danger: [
480
+ "[--button-bg:var(--color-danger)]",
481
+ "[--button-bg-hover:var(--color-danger-hover)]",
482
+ "[--button-bg-pressed:var(--color-danger-hover)]",
483
+ "[--button-fg:var(--color-danger-foreground)]",
484
+ "focus-visible:ring-red-600"
485
+ ].join(" "),
486
+ dangerSoft: [
487
+ "[--button-bg:var(--color-danger-soft)]",
488
+ "[--button-bg-hover:var(--color-danger-soft-hover)]",
489
+ "[--button-bg-pressed:var(--color-danger-soft-hover)]",
490
+ "[--button-fg:var(--color-danger-soft-foreground)]",
491
+ "focus-visible:ring-red-400"
492
+ ].join(" "),
493
+ /** @deprecated Use `danger`. */
494
+ destructive: [
495
+ "[--button-bg:var(--color-danger)]",
496
+ "[--button-bg-hover:var(--color-danger-hover)]",
497
+ "[--button-bg-pressed:var(--color-danger-hover)]",
498
+ "[--button-fg:var(--color-danger-foreground)]",
499
+ "focus-visible:ring-red-600"
500
+ ].join(" "),
501
+ link: [
502
+ "h-auto min-h-0 w-auto gap-1 rounded-none px-0",
503
+ "[--button-bg:transparent] [--button-bg-hover:transparent] [--button-bg-pressed:transparent]",
504
+ "[--button-fg:var(--color-zinc-900)]",
505
+ "underline-offset-4 hover:underline active:scale-100"
506
+ ].join(" ")
507
+ },
508
+ size: {
509
+ xs: "h-8 gap-1.5 px-2.5 text-xs md:h-7 [&_[data-btn-icon]]:size-3.5 active:scale-[0.98]",
510
+ sm: "h-9 gap-2 px-3 text-sm md:h-8 [&_[data-btn-icon]]:size-4 active:scale-[0.98]",
511
+ md: "h-10 gap-2 px-4 text-sm md:h-9 [&_[data-btn-icon]]:size-4",
512
+ lg: "h-11 gap-2 px-4 text-base md:h-10 [&_[data-btn-icon]]:size-5 active:scale-[0.96]",
513
+ xl: "h-12 gap-2.5 px-5 text-base [&_[data-btn-icon]]:size-5 active:scale-[0.96]"
514
+ },
515
+ radius: {
516
+ none: "!rounded-none",
517
+ sm: "!rounded-sm",
518
+ md: "!rounded-md",
519
+ lg: "!rounded-lg",
520
+ full: "!rounded-full"
521
+ },
522
+ fullWidth: {
523
+ true: "w-full",
524
+ false: ""
525
+ },
526
+ iconOnly: {
527
+ true: "aspect-square p-0 [&_[data-btn-icon]]:mx-0",
528
+ false: ""
529
+ },
530
+ pending: {
531
+ true: "",
532
+ false: ""
533
+ },
534
+ /** @deprecated Alias for `pending` (variant key resolution). */
535
+ loading: {
536
+ true: "",
537
+ false: ""
538
+ }
539
+ },
540
+ compoundVariants: [
541
+ { iconOnly: true, size: "xs", class: "size-8 md:size-7" },
542
+ { iconOnly: true, size: "sm", class: "size-9 md:size-8" },
543
+ { iconOnly: true, size: "md", class: "size-10 md:size-9" },
544
+ { iconOnly: true, size: "lg", class: "size-11 md:size-10" },
545
+ { iconOnly: true, size: "xl", class: "size-12" },
546
+ { variant: "link", iconOnly: true, class: "size-auto aspect-auto p-0" }
547
+ ],
548
+ defaultVariants: {
549
+ variant: "primary",
550
+ size: "md",
551
+ radius: "full",
552
+ fullWidth: false,
553
+ iconOnly: false,
554
+ pending: false
555
+ }
556
+ });
557
+
558
+ // src/components/button/button.ts
559
+ var INTERNAL_KEYS = /* @__PURE__ */ new Set([
560
+ "variant",
561
+ "size",
562
+ "radius",
563
+ "fullWidth",
564
+ "iconOnly",
565
+ "pending",
566
+ "loading",
567
+ "disabled",
568
+ "leftIcon",
569
+ "rightIcon",
570
+ "spinner",
571
+ "headless",
572
+ "children",
573
+ "onClick"
574
+ ]);
575
+ var pickDomProps = (props) => {
576
+ const out = {};
577
+ for (const [key, value] of Object.entries(props)) {
578
+ if (!INTERNAL_KEYS.has(key)) out[key] = value;
579
+ }
580
+ return out;
581
+ };
582
+ var wrapIcon = (node) => h(
583
+ "span",
584
+ { "data-btn-icon": "", className: "inline-flex shrink-0", "aria-hidden": "true" },
585
+ node
586
+ );
587
+ var buildContent = (options) => {
588
+ const { pending, size, leftIcon, rightIcon, spinner, children } = options;
589
+ const parts = [];
590
+ if (pending) {
591
+ parts.push(spinner ?? defaultButtonSpinner(size));
592
+ } else if (leftIcon) {
593
+ parts.push(wrapIcon(leftIcon));
594
+ }
595
+ if (children !== void 0 && children !== null && children !== false) {
596
+ parts.push(children);
597
+ }
598
+ if (!pending && rightIcon) {
599
+ parts.push(wrapIcon(rightIcon));
600
+ }
601
+ if (parts.length === 1) return parts[0];
602
+ return parts;
603
+ };
604
+ var Button = createUIComponent({
605
+ name: "Button",
606
+ defaultTag: "button",
607
+ defaultProps: {
608
+ variant: "primary",
609
+ size: "md",
610
+ radius: "full",
611
+ type: "button",
612
+ fullWidth: false,
613
+ iconOnly: false,
614
+ pending: false,
615
+ loading: false,
616
+ disabled: false
617
+ },
618
+ variants: buttonStyles,
619
+ render: ({ props, className, headless }) => {
620
+ const {
621
+ pending: pendingProp,
622
+ loading: loadingProp = false,
623
+ disabled: disabledProp = false,
624
+ type,
625
+ leftIcon,
626
+ rightIcon,
627
+ spinner,
628
+ children,
629
+ size = "md",
630
+ onClick,
631
+ ...rest
632
+ } = props;
633
+ const pending = Boolean(pendingProp || loadingProp);
634
+ const inactive = Boolean(disabledProp || pending);
635
+ const content = buildContent({
636
+ pending,
637
+ size,
638
+ leftIcon,
639
+ rightIcon,
640
+ spinner,
641
+ children
642
+ });
643
+ const domProps = pickDomProps(rest);
644
+ const visualClass = headless ? void 0 : className;
645
+ const handleClick = composeEventHandlers(
646
+ onClick,
647
+ inactive ? (event) => event.preventDefault() : void 0,
648
+ { checkDefaultPrevented: true }
649
+ );
650
+ return h(
651
+ "button",
652
+ {
653
+ ...domProps,
654
+ type: type ?? "button",
655
+ disabled: inactive,
656
+ className: visualClass,
657
+ class: visualClass,
658
+ ...dataDisabled(inactive),
659
+ ...dataPending(pending),
660
+ "aria-disabled": inactive ? "true" : void 0,
661
+ "aria-busy": pending ? "true" : void 0,
662
+ onClick: inactive ? handleClick : onClick
663
+ },
664
+ content
665
+ );
666
+ }
667
+ });
668
+
669
+ // src/components/icon-button/icon-button.styles.ts
670
+ var iconButtonStyles = tv({
671
+ base: "shrink-0 p-0",
672
+ variants: {
673
+ size: {
674
+ xs: "h-7 w-7",
675
+ sm: "h-8 w-8",
676
+ md: "h-10 w-10",
677
+ lg: "h-12 w-12"
678
+ }
679
+ },
680
+ defaultVariants: {
681
+ size: "md"
682
+ }
683
+ });
684
+
685
+ // src/components/icon-button/icon-button.ts
686
+ var isDev = () => {
687
+ const env = globalThis.process?.env?.NODE_ENV;
688
+ return env !== "production";
689
+ };
690
+ var IconButton = createUIComponent({
691
+ name: "IconButton",
692
+ defaultTag: "button",
693
+ defaultProps: {
694
+ variant: "ghost",
695
+ size: "md"
696
+ },
697
+ variants: (options) => iconButtonStyles(options),
698
+ render: ({ props, className, headless }) => {
699
+ const ariaLabel = props["aria-label"];
700
+ if (isDev() && (!ariaLabel || String(ariaLabel).trim().length === 0)) {
701
+ throw new Error(
702
+ 'IconButton: prop "aria-label" is required (e.g. IconButton({ "aria-label": "Close", children: ... })).'
703
+ );
704
+ }
705
+ const { size: _iconSize, children, ...rest } = props;
706
+ return Button({
707
+ ...rest,
708
+ headless,
709
+ size: "md",
710
+ className,
711
+ children
712
+ });
713
+ }
714
+ });
715
+
716
+ // src/components/input-shared/input.styles.ts
717
+ var inputStyles = tv({
718
+ slots: {
719
+ wrapper: [
720
+ "flex items-center gap-2",
721
+ "transition-colors",
722
+ "focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-zinc-400",
723
+ "data-[disabled]:opacity-50 data-[disabled]:pointer-events-none",
724
+ "data-[invalid]:ring-2 data-[invalid]:ring-red-500 data-[invalid]:ring-offset-2"
725
+ ].join(" "),
726
+ input: [
727
+ "w-full bg-transparent outline-none",
728
+ "placeholder:text-zinc-400",
729
+ "disabled:cursor-not-allowed"
730
+ ].join(" "),
731
+ start: "text-zinc-500",
732
+ end: "text-zinc-500"
733
+ },
734
+ variants: {
735
+ variant: {
736
+ outline: "border border-zinc-300 bg-white text-zinc-900",
737
+ filled: "bg-zinc-100 text-zinc-900",
738
+ ghost: "bg-transparent text-zinc-900"
739
+ },
740
+ size: {
741
+ sm: "h-8 px-3 text-sm rounded-md",
742
+ md: "h-10 px-3 text-sm rounded-md",
743
+ lg: "h-12 px-4 text-base rounded-md"
744
+ }
745
+ },
746
+ defaultVariants: {
747
+ variant: "outline",
748
+ size: "md"
749
+ }
750
+ });
751
+ var INPUT_INTERNAL_KEYS = /* @__PURE__ */ new Set([
752
+ "variant",
753
+ "size",
754
+ "startContent",
755
+ "endContent",
756
+ "invalid",
757
+ "disabled",
758
+ "readonly",
759
+ "readOnly",
760
+ "required",
761
+ "headless",
762
+ "children",
763
+ "className",
764
+ "class"
765
+ ]);
766
+ var pickInputDomProps = (props) => {
767
+ const out = {};
768
+ for (const [key, value] of Object.entries(props)) {
769
+ if (!INPUT_INTERNAL_KEYS.has(key)) out[key] = value;
770
+ }
771
+ return out;
772
+ };
773
+ var renderInputField = (options) => {
774
+ const {
775
+ headless,
776
+ className,
777
+ variant = "outline",
778
+ size = "md",
779
+ disabled,
780
+ invalid,
781
+ readonly,
782
+ required,
783
+ describedBy,
784
+ startContent,
785
+ endContent,
786
+ inputProps,
787
+ wrapperProps,
788
+ inputRef
789
+ } = options;
790
+ const ariaInvalid = invalid ? "true" : inputProps["aria-invalid"];
791
+ const dataReadonly = readonly ? { "data-readonly": "" } : {};
792
+ const slotOptions = { variant, size };
793
+ const slots = inputStyles;
794
+ const nativeInputProps = {
795
+ ...inputProps,
796
+ disabled,
797
+ readonly,
798
+ required,
799
+ "aria-invalid": ariaInvalid,
800
+ "aria-describedby": describedBy ?? inputProps["aria-describedby"],
801
+ ref: inputRef ?? inputProps.ref
802
+ };
803
+ if (!startContent && !endContent) {
804
+ const visualClass = headless ? void 0 : className;
805
+ return h("input", {
806
+ ...nativeInputProps,
807
+ ...dataDisabled(Boolean(disabled)),
808
+ ...dataInvalid(Boolean(invalid)),
809
+ ...dataReadonly,
810
+ className: visualClass,
811
+ class: visualClass
812
+ });
813
+ }
814
+ const wrapperClass = headless ? void 0 : cn(slots.wrapper(slotOptions), className);
815
+ const inputClass = headless ? void 0 : slots.input(slotOptions);
816
+ return h(
817
+ "div",
818
+ {
819
+ ...wrapperProps,
820
+ className: wrapperClass,
821
+ class: wrapperClass,
822
+ ...dataDisabled(Boolean(disabled)),
823
+ ...dataInvalid(Boolean(invalid)),
824
+ ...dataReadonly
825
+ },
826
+ [
827
+ startContent ? h(
828
+ "span",
829
+ { "data-input-slot": "start", className: headless ? void 0 : slots.start() },
830
+ startContent
831
+ ) : null,
832
+ h("input", {
833
+ ...nativeInputProps,
834
+ className: inputClass,
835
+ class: inputClass
836
+ }),
837
+ endContent ? h("span", { "data-input-slot": "end", className: headless ? void 0 : slots.end() }, endContent) : null
838
+ ]
839
+ );
840
+ };
841
+
842
+ // src/components/input/input.ts
843
+ var Input = createUIComponent({
844
+ name: "Input",
845
+ defaultTag: "input",
846
+ defaultProps: {
847
+ type: "text",
848
+ variant: "outline",
849
+ size: "md"
850
+ },
851
+ variants: (options) => inputStyles.wrapper(options),
852
+ render: ({ props, headless, className }) => {
853
+ const {
854
+ startContent,
855
+ endContent,
856
+ invalid,
857
+ disabled,
858
+ readonly: readonlyProp,
859
+ readOnly,
860
+ required,
861
+ variant,
862
+ size,
863
+ ...rest
864
+ } = props;
865
+ const readonly = readonlyProp ?? readOnly;
866
+ const domProps = pickInputDomProps(rest);
867
+ return renderInputField({
868
+ headless,
869
+ className,
870
+ variant,
871
+ size,
872
+ disabled,
873
+ invalid,
874
+ readonly,
875
+ required,
876
+ startContent,
877
+ endContent,
878
+ inputProps: domProps
879
+ });
880
+ }
881
+ });
882
+
883
+ // src/utils/input-mask/format-mask.ts
884
+ var slotPattern = (kind) => {
885
+ switch (kind.kind) {
886
+ case "9":
887
+ return /\d/;
888
+ case "a":
889
+ return /[A-Za-z]/;
890
+ case "*":
891
+ return /[A-Za-z0-9]/;
892
+ }
893
+ };
894
+ var extractSlotChars = (raw, tokens) => {
895
+ const slots = tokens.filter((t) => t.type === "slot");
896
+ const out = [];
897
+ for (const char of raw) {
898
+ if (out.length >= slots.length) break;
899
+ const slot = slots[out.length];
900
+ if (slotPattern(slot).test(char)) out.push(char);
901
+ }
902
+ return out;
903
+ };
904
+ var formatMaskedValue = (raw, tokens) => {
905
+ const slotChars = extractSlotChars(raw, tokens);
906
+ let slotIndex = 0;
907
+ let masked = "";
908
+ const unmaskedParts = [];
909
+ for (const token of tokens) {
910
+ if (token.type === "literal") {
911
+ masked += token.char;
912
+ continue;
913
+ }
914
+ const char = slotChars[slotIndex];
915
+ if (char === void 0) break;
916
+ masked += char;
917
+ unmaskedParts.push(char);
918
+ slotIndex += 1;
919
+ }
920
+ return {
921
+ masked,
922
+ unmasked: unmaskedParts.join("")
923
+ };
924
+ };
925
+
926
+ // src/utils/input-mask/parse-mask.ts
927
+ var SLOT_KINDS = /* @__PURE__ */ new Set(["9", "a", "*"]);
928
+ var parseMaskPattern = (pattern) => {
929
+ const tokens = [];
930
+ for (const char of pattern) {
931
+ if (SLOT_KINDS.has(char)) {
932
+ tokens.push({ type: "slot", kind: char });
933
+ continue;
934
+ }
935
+ tokens.push({ type: "literal", char });
936
+ }
937
+ return tokens;
938
+ };
939
+
940
+ // src/utils/input-mask/presets.ts
941
+ var MASK_PRESETS = {
942
+ phone: "(99) 99999-9999",
943
+ cpf: "999.999.999-99",
944
+ cnpj: "99.999.999/9999-99",
945
+ date: "99/99/9999",
946
+ cep: "99999-999",
947
+ time: "99:99",
948
+ "credit-card": "9999 9999 9999 9999"
949
+ };
950
+ var resolveMaskPattern = (mask) => mask in MASK_PRESETS ? MASK_PRESETS[mask] : mask;
951
+
952
+ // src/utils/input-mask/attach-input-mask.ts
953
+ var attachInputMask = (input, options) => {
954
+ const pattern = resolveMaskPattern(options.mask);
955
+ const tokens = parseMaskPattern(pattern);
956
+ const apply = (raw, notify = true) => {
957
+ const result = formatMaskedValue(raw, tokens);
958
+ if (input.value !== result.masked) {
959
+ input.value = result.masked;
960
+ }
961
+ if (notify) options.onValueChange?.(result);
962
+ };
963
+ const onInput = () => apply(input.value);
964
+ const onBlur = () => apply(input.value);
965
+ input.addEventListener("input", onInput);
966
+ input.addEventListener("blur", onBlur);
967
+ if (input.value) apply(input.value, true);
968
+ return () => {
969
+ input.removeEventListener("input", onInput);
970
+ input.removeEventListener("blur", onBlur);
971
+ };
972
+ };
973
+
974
+ // src/components/input-mask/input-mask.ts
975
+ var InputMask = createUIComponent({
976
+ name: "InputMask",
977
+ defaultTag: "input",
978
+ defaultProps: {
979
+ mask: "phone",
980
+ variant: "outline",
981
+ size: "md"
982
+ },
983
+ variants: (options) => inputStyles.wrapper(options),
984
+ render: ({ props, headless, className }) => {
985
+ const p = props;
986
+ const mask = p.mask;
987
+ const onMaskValueChange = p.onMaskValueChange;
988
+ const onInput = p.onInput;
989
+ const domProps = pickInputDomProps(p);
990
+ const readonly = Boolean(p.readonly ?? p.readOnly);
991
+ let disposeMask;
992
+ const inputRef = (element) => {
993
+ disposeMask?.();
994
+ disposeMask = void 0;
995
+ if (!element) return;
996
+ disposeMask = attachInputMask(element, {
997
+ mask,
998
+ onValueChange: onMaskValueChange
999
+ });
1000
+ const userRef = domProps.ref;
1001
+ if (typeof userRef === "function") userRef(element);
1002
+ };
1003
+ const { ref: _ref, ...inputRest } = domProps;
1004
+ return renderInputField({
1005
+ headless,
1006
+ className,
1007
+ variant: p.variant,
1008
+ size: p.size,
1009
+ disabled: p.disabled,
1010
+ invalid: p.invalid,
1011
+ readonly,
1012
+ required: p.required,
1013
+ startContent: p.startContent,
1014
+ endContent: p.endContent,
1015
+ inputRef,
1016
+ inputProps: {
1017
+ ...inputRest,
1018
+ type: "text",
1019
+ inputMode: "numeric",
1020
+ onInput
1021
+ }
1022
+ });
1023
+ }
1024
+ });
1025
+ var OTP_PROP_KEYS = /* @__PURE__ */ new Set(["length", "onValueChange", "value", "defaultValue"]);
1026
+ var pickOtpDomProps = (props) => {
1027
+ const out = {};
1028
+ for (const [key, value] of Object.entries(props)) {
1029
+ if (OTP_PROP_KEYS.has(key)) continue;
1030
+ out[key] = value;
1031
+ }
1032
+ return pickInputDomProps(out);
1033
+ };
1034
+ var normalizeOtp = (value, length) => value.replace(/\D/g, "").slice(0, length);
1035
+ var InputOtp = createUIComponent({
1036
+ name: "InputOtp",
1037
+ defaultTag: "div",
1038
+ defaultProps: {
1039
+ length: 6,
1040
+ variant: "outline",
1041
+ size: "md",
1042
+ autocomplete: "one-time-code"
1043
+ },
1044
+ variants: (options) => inputStyles.wrapper(options),
1045
+ render: ({ props, headless, className }) => {
1046
+ const {
1047
+ length = 6,
1048
+ value = "",
1049
+ invalid,
1050
+ disabled,
1051
+ readonly: readonlyProp,
1052
+ readOnly,
1053
+ required,
1054
+ variant = "outline",
1055
+ size = "md",
1056
+ autocomplete = "one-time-code",
1057
+ onValueChange,
1058
+ onInput,
1059
+ ...rest
1060
+ } = props;
1061
+ const readonly = readonlyProp ?? readOnly;
1062
+ const domProps = pickOtpDomProps(rest);
1063
+ const digits = normalizeOtp(String(value), length);
1064
+ const slotOptions = { variant, size };
1065
+ const cellClass = headless ? void 0 : cn(
1066
+ inputStyles.input(slotOptions),
1067
+ "w-10 text-center px-0",
1068
+ size === "sm" && "w-8",
1069
+ size === "lg" && "w-12"
1070
+ );
1071
+ const groupClass = headless ? "flex gap-2" : cn("flex gap-2", inputStyles.wrapper(slotOptions), className);
1072
+ const emitChange = (next, input) => {
1073
+ const normalized = normalizeOtp(next, length);
1074
+ onValueChange?.(normalized);
1075
+ if (input) {
1076
+ input.dispatchEvent(new Event("input", { bubbles: true }));
1077
+ }
1078
+ };
1079
+ const cells = Array.from({ length }, (_, index) => {
1080
+ const char = digits[index] ?? "";
1081
+ return h("input", {
1082
+ type: "text",
1083
+ inputMode: "numeric",
1084
+ pattern: "[0-9]*",
1085
+ maxlength: 1,
1086
+ value: char,
1087
+ disabled,
1088
+ readonly,
1089
+ required,
1090
+ autocomplete: index === 0 ? autocomplete : "off",
1091
+ "aria-label": `Digit ${index + 1} of ${length}`,
1092
+ className: cellClass,
1093
+ class: cellClass,
1094
+ onInput: (event) => {
1095
+ const input = event.currentTarget;
1096
+ const digit = normalizeOtp(input.value, 1).slice(-1);
1097
+ input.value = digit;
1098
+ const chars = [...digits];
1099
+ while (chars.length < length) chars.push("");
1100
+ chars[index] = digit;
1101
+ emitChange(chars.join(""), input);
1102
+ if (digit && index < length - 1) {
1103
+ const group = input.parentElement;
1104
+ const nextInput = group?.querySelectorAll("input")[index + 1];
1105
+ nextInput?.focus();
1106
+ }
1107
+ onInput?.(event);
1108
+ },
1109
+ onKeyDown: (event) => {
1110
+ const input = event.currentTarget;
1111
+ if (event.key === "Backspace" && !input.value && index > 0) {
1112
+ const group = input.parentElement;
1113
+ const prevInput = group?.querySelectorAll("input")[index - 1];
1114
+ prevInput?.focus();
1115
+ const chars = digits.split("");
1116
+ chars[index - 1] = "";
1117
+ emitChange(chars.join(""), input);
1118
+ }
1119
+ },
1120
+ onPaste: (event) => {
1121
+ event.preventDefault();
1122
+ const text = event.clipboardData?.getData("text") ?? "";
1123
+ emitChange(text, event.currentTarget);
1124
+ }
1125
+ });
1126
+ });
1127
+ return h(
1128
+ "div",
1129
+ {
1130
+ ...domProps,
1131
+ role: "group",
1132
+ "aria-label": domProps["aria-label"] ?? "One-time code",
1133
+ className: groupClass,
1134
+ class: groupClass,
1135
+ ...dataDisabled(Boolean(disabled)),
1136
+ ...dataInvalid(Boolean(invalid)),
1137
+ ...readonly ? { "data-readonly": "" } : {}
1138
+ },
1139
+ cells
1140
+ );
1141
+ }
1142
+ });
1143
+ var TAG_PROP_KEYS = /* @__PURE__ */ new Set(["value", "onValueChange", "separators", "allowDuplicates", "maxTags"]);
1144
+ var pickTagsDomProps = (props) => {
1145
+ const out = {};
1146
+ for (const [key, value] of Object.entries(props)) {
1147
+ if (TAG_PROP_KEYS.has(key)) continue;
1148
+ out[key] = value;
1149
+ }
1150
+ return pickInputDomProps(out);
1151
+ };
1152
+ var normalizeTag = (raw) => raw.trim();
1153
+ var canAddTag = (tags, tag, options) => {
1154
+ if (!tag) return false;
1155
+ if (options.maxTags !== void 0 && tags.length >= options.maxTags) return false;
1156
+ if (!options.allowDuplicates && tags.includes(tag)) return false;
1157
+ return true;
1158
+ };
1159
+ var InputTags = createUIComponent({
1160
+ name: "InputTags",
1161
+ defaultTag: "div",
1162
+ defaultProps: {
1163
+ value: [],
1164
+ separators: [",", "Enter"],
1165
+ allowDuplicates: false,
1166
+ variant: "outline",
1167
+ size: "md",
1168
+ placeholder: "Add tag\u2026"
1169
+ },
1170
+ variants: (options) => inputStyles.wrapper(options),
1171
+ render: ({ props, headless, className }) => {
1172
+ const {
1173
+ value = [],
1174
+ separators = [",", "Enter"],
1175
+ allowDuplicates = false,
1176
+ maxTags,
1177
+ startContent,
1178
+ endContent,
1179
+ invalid,
1180
+ disabled,
1181
+ readonly: readonlyProp,
1182
+ readOnly,
1183
+ required,
1184
+ variant = "outline",
1185
+ size = "md",
1186
+ placeholder,
1187
+ onValueChange,
1188
+ onInput,
1189
+ ...rest
1190
+ } = props;
1191
+ const readonly = readonlyProp ?? readOnly;
1192
+ const domProps = pickTagsDomProps(rest);
1193
+ const tags = Array.isArray(value) ? value : [];
1194
+ const slotOptions = { variant, size };
1195
+ const addTag = (draft, input) => {
1196
+ const tag = normalizeTag(draft);
1197
+ if (!canAddTag(tags, tag, { allowDuplicates, maxTags })) return;
1198
+ onValueChange?.([...tags, tag]);
1199
+ input.value = "";
1200
+ };
1201
+ const removeTag = (index) => {
1202
+ onValueChange?.(tags.filter((_, i) => i !== index));
1203
+ };
1204
+ const tagChips = tags.map(
1205
+ (tag, index) => h(
1206
+ "span",
1207
+ {
1208
+ "data-tag": "",
1209
+ className: headless ? void 0 : "inline-flex items-center gap-1 rounded-md bg-zinc-100 px-2 py-0.5 text-sm text-zinc-800"
1210
+ },
1211
+ [
1212
+ tag,
1213
+ readonly || disabled ? null : h(
1214
+ "button",
1215
+ {
1216
+ type: "button",
1217
+ className: "text-zinc-500 hover:text-zinc-800",
1218
+ "aria-label": `Remove ${tag}`,
1219
+ onClick: () => removeTag(index)
1220
+ },
1221
+ "\xD7"
1222
+ )
1223
+ ]
1224
+ )
1225
+ );
1226
+ const innerInput = renderInputField({
1227
+ headless: true,
1228
+ variant,
1229
+ size,
1230
+ disabled,
1231
+ invalid,
1232
+ readonly,
1233
+ required,
1234
+ inputProps: {
1235
+ type: "text",
1236
+ className: "min-w-24 flex-1 border-0 bg-transparent shadow-none outline-none",
1237
+ placeholder: tags.length === 0 ? placeholder : void 0,
1238
+ onInput,
1239
+ onKeyDown: (event) => {
1240
+ const input = event.currentTarget;
1241
+ if (separators.includes(event.key)) {
1242
+ event.preventDefault();
1243
+ addTag(input.value, input);
1244
+ return;
1245
+ }
1246
+ if (event.key === "Backspace" && input.value === "" && tags.length > 0) {
1247
+ removeTag(tags.length - 1);
1248
+ }
1249
+ }
1250
+ }
1251
+ });
1252
+ const wrapperClass = headless ? cn("flex min-w-0 flex-1 flex-wrap items-center gap-1.5") : cn(inputStyles.wrapper(slotOptions), "min-h-10 flex-wrap items-center gap-1.5 py-1", className);
1253
+ return h(
1254
+ "div",
1255
+ {
1256
+ ...domProps,
1257
+ className: wrapperClass,
1258
+ class: wrapperClass,
1259
+ ...dataDisabled(Boolean(disabled)),
1260
+ ...dataInvalid(Boolean(invalid)),
1261
+ ...readonly ? { "data-readonly": "" } : {}
1262
+ },
1263
+ [
1264
+ startContent ? h(
1265
+ "span",
1266
+ { "data-input-slot": "start", className: headless ? void 0 : inputStyles.start() },
1267
+ startContent
1268
+ ) : null,
1269
+ ...tagChips,
1270
+ innerInput,
1271
+ endContent ? h("span", { "data-input-slot": "end", className: headless ? void 0 : inputStyles.end() }, endContent) : null
1272
+ ]
1273
+ );
1274
+ }
1275
+ });
1276
+
1277
+ // src/components/textarea/textarea.styles.ts
1278
+ var textareaStyles = tv({
1279
+ base: [
1280
+ "w-full",
1281
+ "transition-colors",
1282
+ "outline-none",
1283
+ "focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-zinc-400",
1284
+ "disabled:opacity-50 disabled:cursor-not-allowed",
1285
+ "data-[invalid]:ring-2 data-[invalid]:ring-red-500 data-[invalid]:ring-offset-2"
1286
+ ].join(" "),
1287
+ variants: {
1288
+ variant: {
1289
+ outline: "border border-zinc-300 bg-white text-zinc-900",
1290
+ filled: "bg-zinc-100 text-zinc-900",
1291
+ ghost: "bg-transparent text-zinc-900"
1292
+ },
1293
+ size: {
1294
+ sm: "min-h-20 px-3 py-2 text-sm rounded-md",
1295
+ md: "min-h-24 px-3 py-2 text-sm rounded-md",
1296
+ lg: "min-h-28 px-4 py-3 text-base rounded-md"
1297
+ },
1298
+ resize: {
1299
+ none: "resize-none",
1300
+ vertical: "resize-y",
1301
+ horizontal: "resize-x",
1302
+ both: "resize"
1303
+ }
1304
+ },
1305
+ defaultVariants: {
1306
+ variant: "outline",
1307
+ size: "md",
1308
+ resize: "vertical"
1309
+ }
1310
+ });
1311
+
1312
+ // src/components/textarea/textarea.ts
1313
+ var Textarea = createUIComponent({
1314
+ name: "Textarea",
1315
+ defaultTag: "textarea",
1316
+ defaultProps: {
1317
+ variant: "outline",
1318
+ size: "md",
1319
+ resize: "vertical"
1320
+ },
1321
+ variants: (options) => textareaStyles(options),
1322
+ render: ({ props, headless, className }) => {
1323
+ const { invalid, disabled, readonly, required, ...rest } = props;
1324
+ const ariaInvalid = invalid ? "true" : rest["aria-invalid"];
1325
+ const dataReadonly = readonly ? { "data-readonly": "" } : {};
1326
+ return h("textarea", {
1327
+ ...rest,
1328
+ disabled,
1329
+ readonly,
1330
+ required,
1331
+ "aria-invalid": ariaInvalid,
1332
+ ...dataDisabled(Boolean(disabled)),
1333
+ ...dataInvalid(Boolean(invalid)),
1334
+ ...dataReadonly,
1335
+ className: headless ? void 0 : className,
1336
+ class: headless ? void 0 : className
1337
+ });
1338
+ }
1339
+ });
1340
+
1341
+ // src/components/label/label.styles.ts
1342
+ var labelStyles = tv({
1343
+ base: [
1344
+ "text-sm font-medium leading-none",
1345
+ "data-[disabled]:opacity-50",
1346
+ "data-[invalid]:text-red-700"
1347
+ ].join(" ")
1348
+ });
1349
+
1350
+ // src/components/label/label.ts
1351
+ var Label = createUIComponent({
1352
+ name: "Label",
1353
+ defaultTag: "label",
1354
+ defaultProps: {
1355
+ requiredIndicator: "*"
1356
+ },
1357
+ variants: () => labelStyles({}),
1358
+ render: ({ props, headless, className }) => {
1359
+ const {
1360
+ for: htmlFor,
1361
+ required,
1362
+ disabled,
1363
+ invalid,
1364
+ requiredIndicator,
1365
+ optionalIndicator,
1366
+ children,
1367
+ ...rest
1368
+ } = props;
1369
+ const indicator = required === true ? requiredIndicator : required === false && optionalIndicator ? optionalIndicator : null;
1370
+ const visualClass = headless ? void 0 : cn(className);
1371
+ return h(
1372
+ "label",
1373
+ {
1374
+ ...rest,
1375
+ for: htmlFor,
1376
+ ...dataDisabled(Boolean(disabled)),
1377
+ ...dataInvalid(Boolean(invalid)),
1378
+ className: visualClass,
1379
+ class: visualClass
1380
+ },
1381
+ [
1382
+ children,
1383
+ indicator ? h("span", { "aria-hidden": "true", className: headless ? void 0 : "ml-1" }, indicator) : null
1384
+ ]
1385
+ );
1386
+ }
1387
+ });
1388
+
1389
+ // src/components/field/field.styles.ts
1390
+ var fieldStyles = tv({
1391
+ slots: {
1392
+ root: "grid gap-1.5",
1393
+ label: "",
1394
+ description: "text-sm text-zinc-500",
1395
+ error: "text-sm text-red-600"
1396
+ }
1397
+ });
1398
+
1399
+ // src/components/field/field-context.ts
1400
+ var stack2 = [];
1401
+ var getFieldContext = () => stack2[stack2.length - 1];
1402
+ var runWithFieldContext = (value, fn) => {
1403
+ stack2.push(value);
1404
+ try {
1405
+ return fn();
1406
+ } finally {
1407
+ stack2.pop();
1408
+ }
1409
+ };
1410
+
1411
+ // src/components/field/field.ts
1412
+ var isFnChild = (children) => typeof children === "function";
1413
+ var Field = (props) => {
1414
+ const ui = getUIContextOrDefault();
1415
+ const slice = ui.theme.components?.field;
1416
+ const headless = props.headless ?? ui.headless ?? ui.theme.headless ?? false;
1417
+ const themeDefaults = {
1418
+ ...slice?.defaultProps
1419
+ };
1420
+ if (!headless) {
1421
+ if (slice?.baseClass) themeDefaults.className = cn(themeDefaults.className, slice.baseClass);
1422
+ if (slice?.className) themeDefaults.className = cn(themeDefaults.className, slice.className);
1423
+ }
1424
+ const merged = mergeProps(void 0, themeDefaults, props);
1425
+ const id = merged.id ?? createId("field");
1426
+ const inputId = createId(`${id}-input`);
1427
+ const labelId = createId(`${id}-label`);
1428
+ const descriptionId = merged.description ? createId(`${id}-description`) : void 0;
1429
+ const errorId = merged.error ? createId(`${id}-error`) : void 0;
1430
+ const invalid = Boolean(merged.invalid || merged.error);
1431
+ const disabled = Boolean(merged.disabled);
1432
+ const required = Boolean(merged.required);
1433
+ const describedBy = [descriptionId, errorId].filter(Boolean).join(" ") || void 0;
1434
+ const ctx = {
1435
+ id,
1436
+ inputId,
1437
+ labelId,
1438
+ descriptionId,
1439
+ errorId,
1440
+ invalid,
1441
+ disabled,
1442
+ required,
1443
+ inputProps: {
1444
+ id: inputId,
1445
+ "aria-labelledby": labelId,
1446
+ "aria-describedby": describedBy,
1447
+ "aria-invalid": invalid ? "true" : void 0,
1448
+ "aria-required": required ? "true" : void 0,
1449
+ required: required || void 0,
1450
+ disabled: disabled || void 0,
1451
+ invalid: invalid || void 0
1452
+ },
1453
+ labelProps: {
1454
+ id: labelId,
1455
+ for: inputId,
1456
+ required: required || void 0,
1457
+ disabled: disabled || void 0,
1458
+ invalid: invalid || void 0
1459
+ },
1460
+ descriptionProps: descriptionId ? { id: descriptionId } : void 0,
1461
+ errorProps: errorId ? { id: errorId, role: "alert" } : void 0
1462
+ };
1463
+ const rootClass = headless ? void 0 : cn(fieldStyles.root(), merged.className, merged.class);
1464
+ const content = isFnChild(merged.children) ? merged.children(ctx) : merged.children;
1465
+ return runWithFieldContext(
1466
+ ctx,
1467
+ () => h(
1468
+ "div",
1469
+ {
1470
+ className: rootClass,
1471
+ class: rootClass,
1472
+ ...dataInvalid(invalid),
1473
+ ...dataDisabled(disabled),
1474
+ ...required ? { "data-required": "" } : {}
1475
+ },
1476
+ [
1477
+ merged.label ? Label({
1478
+ ...ctx.labelProps,
1479
+ headless,
1480
+ children: merged.label
1481
+ }) : null,
1482
+ content,
1483
+ merged.description ? h(
1484
+ "div",
1485
+ {
1486
+ id: descriptionId,
1487
+ className: headless ? void 0 : fieldStyles.description()
1488
+ },
1489
+ merged.description
1490
+ ) : null,
1491
+ merged.error ? h(
1492
+ "div",
1493
+ {
1494
+ id: errorId,
1495
+ role: "alert",
1496
+ className: headless ? void 0 : fieldStyles.error()
1497
+ },
1498
+ merged.error
1499
+ ) : null
1500
+ ]
1501
+ )
1502
+ );
1503
+ };
1504
+
1505
+ // src/components/field/merge-field-control-props.ts
1506
+ var mergeFieldControlProps = (base, overrides) => {
1507
+ const ctx = getFieldContext();
1508
+ if (!ctx) {
1509
+ return mergeProps(void 0, base, overrides ?? {});
1510
+ }
1511
+ return mergeProps(ctx.inputProps, base, overrides ?? {});
1512
+ };
1513
+
1514
+ // src/components/checkbox/checkbox.styles.ts
1515
+ var checkboxStyles = tv({
1516
+ base: [
1517
+ "shrink-0 cursor-pointer appearance-none rounded border border-zinc-400 bg-white",
1518
+ "transition-colors",
1519
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-zinc-500",
1520
+ "checked:border-zinc-900 checked:bg-zinc-900",
1521
+ "indeterminate:border-zinc-500 indeterminate:bg-zinc-500",
1522
+ "disabled:cursor-not-allowed disabled:opacity-50",
1523
+ "data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
1524
+ "data-[invalid]:border-red-500 data-[invalid]:ring-red-500",
1525
+ "data-[indeterminate]:border-zinc-500 data-[indeterminate]:bg-zinc-500"
1526
+ ].join(" "),
1527
+ variants: {
1528
+ size: {
1529
+ sm: "h-4 w-4",
1530
+ md: "h-5 w-5",
1531
+ lg: "h-6 w-6"
1532
+ }
1533
+ },
1534
+ defaultVariants: {
1535
+ size: "md"
1536
+ }
1537
+ });
1538
+
1539
+ // src/components/checkbox/checkbox.ts
1540
+ var INTERNAL_KEYS2 = /* @__PURE__ */ new Set([
1541
+ "size",
1542
+ "indeterminate",
1543
+ "invalid",
1544
+ "disabled",
1545
+ "checked",
1546
+ "defaultChecked",
1547
+ "required",
1548
+ "headless",
1549
+ "children",
1550
+ "className",
1551
+ "class"
1552
+ ]);
1553
+ var pickDomProps2 = (props) => {
1554
+ const out = {};
1555
+ for (const [key, value] of Object.entries(props)) {
1556
+ if (!INTERNAL_KEYS2.has(key)) out[key] = value;
1557
+ }
1558
+ return out;
1559
+ };
1560
+ var Checkbox = createUIComponent({
1561
+ name: "Checkbox",
1562
+ defaultTag: "input",
1563
+ defaultProps: {
1564
+ size: "md"
1565
+ },
1566
+ variants: checkboxStyles,
1567
+ render: ({ props, headless, className }) => {
1568
+ const {
1569
+ checked,
1570
+ defaultChecked,
1571
+ indeterminate = false,
1572
+ disabled,
1573
+ invalid,
1574
+ required,
1575
+ ...rest
1576
+ } = props;
1577
+ const domProps = pickDomProps2(rest);
1578
+ const visualClass = headless ? void 0 : className;
1579
+ return h(
1580
+ "input",
1581
+ {
1582
+ ...domProps,
1583
+ type: "checkbox",
1584
+ checked,
1585
+ defaultChecked,
1586
+ disabled,
1587
+ required,
1588
+ indeterminate,
1589
+ "aria-checked": indeterminate ? "mixed" : checked !== void 0 ? ariaBool(Boolean(checked)) : void 0,
1590
+ "aria-invalid": invalid ? "true" : void 0,
1591
+ "aria-required": required ? "true" : void 0,
1592
+ ...dataDisabled(Boolean(disabled)),
1593
+ ...dataInvalid(Boolean(invalid)),
1594
+ ...indeterminate ? { "data-indeterminate": "" } : {},
1595
+ className: visualClass,
1596
+ class: visualClass
1597
+ }
1598
+ );
1599
+ }
1600
+ });
1601
+
1602
+ export { Button, Checkbox, Field, IconButton, Input, InputMask, InputOtp, InputTags, Label, MASK_PRESETS, Portal, Textarea, UIProvider, VisuallyHidden, ariaBool, attachInputMask, buttonStyles, cn, composeEventHandlers, createComponentVariants, createId, createTheme, createUIComponent, createUIContextValue, createUiPlugin, createUiProvider, dataDisabled, dataInvalid, dataState, defaultButtonSpinner, defaultTheme, formatMaskedValue, getFieldContext, getUIContext, getUIContextOrDefault, inputStyles, mergeFieldControlProps, mergeProps, mergeRefs, parseMaskPattern, renderSlot, resetClassNameMerger, resetUIContextStack, resolveMaskPattern, resolveVariantClasses, runWithUIContext, setClassNameMerger, uiPlugin, useId };
1603
+ //# sourceMappingURL=index.js.map
1604
+ //# sourceMappingURL=index.js.map