@classytic/fluid 0.1.1 → 0.2.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.
@@ -0,0 +1,986 @@
1
+ import { cn } from './chunk-GUHK2DTW.js';
2
+ import { Controller } from 'react-hook-form';
3
+ import { Input } from '@/components/ui/input';
4
+ import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupTextarea } from '@/components/ui/input-group';
5
+ import { createContext, memo, useId, useMemo, useContext, useState, useRef } from 'react';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+ import { Textarea } from '@/components/ui/textarea';
8
+ import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select';
9
+ import { Minus, Plus, X, Check, Wand2 } from 'lucide-react';
10
+ import { Button } from '@/components/ui/button';
11
+ import { Badge } from '@/components/ui/badge';
12
+ import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover';
13
+
14
+ var FieldContext = createContext(null);
15
+ var Root = memo(function FieldRoot({
16
+ children,
17
+ className,
18
+ disabled = false,
19
+ invalid = false
20
+ }) {
21
+ const id = useId();
22
+ const value = useMemo(
23
+ () => ({
24
+ id,
25
+ disabled,
26
+ invalid
27
+ }),
28
+ [id, disabled, invalid]
29
+ );
30
+ return /* @__PURE__ */ jsx(FieldContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { className: cn("w-full pt-3", className), children }) });
31
+ });
32
+ var Label = memo(function FieldLabel({
33
+ children,
34
+ className
35
+ }) {
36
+ const ctx = useContext(FieldContext);
37
+ return /* @__PURE__ */ jsx(
38
+ "label",
39
+ {
40
+ htmlFor: ctx?.id,
41
+ className: cn(
42
+ "absolute top-0 -translate-y-1/2 left-3 z-10",
43
+ "bg-card px-1.5",
44
+ "text-[11px] font-medium leading-tight text-muted-foreground",
45
+ "pointer-events-none select-none whitespace-nowrap",
46
+ "transition-colors",
47
+ "group-focus-within:text-primary",
48
+ ctx?.disabled && "opacity-60",
49
+ ctx?.invalid && "text-destructive group-focus-within:text-destructive",
50
+ className
51
+ ),
52
+ children
53
+ }
54
+ );
55
+ });
56
+ var Error = memo(function FieldError({
57
+ children,
58
+ className
59
+ }) {
60
+ if (!children) return null;
61
+ return /* @__PURE__ */ jsx("p", { className: cn("text-[11px] text-destructive mt-1.5 pl-3", className), children });
62
+ });
63
+ var Description = memo(function FieldDescription({
64
+ children,
65
+ className
66
+ }) {
67
+ if (!children) return null;
68
+ return /* @__PURE__ */ jsx("p", { className: cn("text-[11px] text-muted-foreground mt-1.5 pl-3", className), children });
69
+ });
70
+ var Icon = memo(function FieldIcon({
71
+ children,
72
+ className
73
+ }) {
74
+ return /* @__PURE__ */ jsx(
75
+ "div",
76
+ {
77
+ className: cn(
78
+ "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none z-10",
79
+ "text-muted-foreground",
80
+ className
81
+ ),
82
+ children
83
+ }
84
+ );
85
+ });
86
+ var Field = {
87
+ Root,
88
+ Label,
89
+ Error,
90
+ Description,
91
+ Icon
92
+ };
93
+ function CompactInput({
94
+ // Form integration
95
+ control,
96
+ name,
97
+ description,
98
+ required,
99
+ // Basic props
100
+ label,
101
+ placeholder,
102
+ disabled,
103
+ type = "text",
104
+ // InputGroup addons
105
+ addonLeft,
106
+ addonRight,
107
+ // Styling
108
+ className,
109
+ inputClassName,
110
+ // Events
111
+ onValueChange,
112
+ // Direct usage
113
+ value,
114
+ onChange,
115
+ error,
116
+ // Valid HTML input attributes
117
+ autoComplete,
118
+ autoFocus,
119
+ maxLength,
120
+ minLength,
121
+ max,
122
+ min,
123
+ pattern,
124
+ readOnly,
125
+ step,
126
+ inputMode,
127
+ enterKeyHint,
128
+ ref,
129
+ ...props
130
+ }) {
131
+ const hasInputGroup = addonLeft || addonRight;
132
+ const renderInput = (fieldValue, fieldOnChange, isDisabled, fieldError) => {
133
+ const inputProps = {
134
+ ref,
135
+ id: name,
136
+ name,
137
+ type,
138
+ disabled: isDisabled,
139
+ placeholder,
140
+ value: fieldValue || "",
141
+ onChange: (e) => {
142
+ const newValue = e.target.value;
143
+ fieldOnChange?.(newValue);
144
+ onValueChange?.(newValue);
145
+ },
146
+ className: cn(
147
+ "h-11 text-sm",
148
+ fieldError && "border-destructive focus-visible:ring-destructive/20",
149
+ inputClassName
150
+ ),
151
+ autoComplete,
152
+ autoFocus,
153
+ maxLength,
154
+ minLength,
155
+ max,
156
+ min,
157
+ pattern,
158
+ readOnly,
159
+ step,
160
+ inputMode,
161
+ enterKeyHint,
162
+ ...props
163
+ };
164
+ if (hasInputGroup) {
165
+ return /* @__PURE__ */ jsxs(InputGroup, { className: "h-11", children: [
166
+ addonLeft && /* @__PURE__ */ jsx(InputGroupAddon, { align: "inline-start", children: addonLeft }),
167
+ /* @__PURE__ */ jsx(InputGroupInput, { ...inputProps }),
168
+ addonRight && /* @__PURE__ */ jsx(InputGroupAddon, { align: "inline-end", children: addonRight })
169
+ ] });
170
+ }
171
+ return /* @__PURE__ */ jsx(Input, { ...inputProps });
172
+ };
173
+ if (control && name) {
174
+ return /* @__PURE__ */ jsx(
175
+ Controller,
176
+ {
177
+ name,
178
+ control,
179
+ render: ({ field, fieldState }) => /* @__PURE__ */ jsxs(Field.Root, { className, disabled, invalid: !!fieldState?.error, children: [
180
+ /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
181
+ label && /* @__PURE__ */ jsx(Field.Label, { children: label }),
182
+ renderInput(field.value, field.onChange, disabled, fieldState?.error?.message)
183
+ ] }),
184
+ fieldState?.error && /* @__PURE__ */ jsx(Field.Error, { children: fieldState.error.message }),
185
+ /* @__PURE__ */ jsx(Field.Description, { children: description })
186
+ ] })
187
+ }
188
+ );
189
+ }
190
+ const handleDirectChange = (newValue) => {
191
+ onChange?.({ target: { value: newValue } });
192
+ onValueChange?.(newValue);
193
+ };
194
+ return /* @__PURE__ */ jsxs(Field.Root, { className, disabled, invalid: !!error, children: [
195
+ /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
196
+ label && /* @__PURE__ */ jsx(Field.Label, { children: label }),
197
+ renderInput(value, handleDirectChange, disabled, error)
198
+ ] }),
199
+ error && /* @__PURE__ */ jsx(Field.Error, { children: error }),
200
+ /* @__PURE__ */ jsx(Field.Description, { children: description })
201
+ ] });
202
+ }
203
+ function CompactTextarea({
204
+ // Form integration
205
+ control,
206
+ name,
207
+ description,
208
+ required,
209
+ // Basic props
210
+ label,
211
+ placeholder,
212
+ disabled,
213
+ rows = 3,
214
+ // InputGroup addons
215
+ addonLeft,
216
+ addonRight,
217
+ // Styling
218
+ className,
219
+ inputClassName,
220
+ // Events
221
+ onValueChange,
222
+ // Direct usage
223
+ value,
224
+ onChange,
225
+ error,
226
+ // Valid HTML textarea attributes
227
+ cols,
228
+ wrap,
229
+ autoComplete,
230
+ autoFocus,
231
+ maxLength,
232
+ minLength,
233
+ readOnly,
234
+ spellCheck,
235
+ ref,
236
+ ...props
237
+ }) {
238
+ const hasInputGroup = addonLeft || addonRight;
239
+ const renderTextarea = (fieldValue, fieldOnChange, isDisabled, fieldError) => {
240
+ const currentCharCount = fieldValue?.length || 0;
241
+ const textareaProps = {
242
+ ref,
243
+ id: name,
244
+ name,
245
+ disabled: isDisabled,
246
+ placeholder,
247
+ rows,
248
+ value: fieldValue || "",
249
+ onChange: (e) => {
250
+ const newValue = e.target.value;
251
+ fieldOnChange?.(newValue);
252
+ onValueChange?.(newValue);
253
+ },
254
+ className: cn(
255
+ "resize-none pt-3 text-sm",
256
+ fieldError && "border-destructive focus-visible:ring-destructive/20",
257
+ inputClassName
258
+ ),
259
+ cols,
260
+ wrap,
261
+ autoComplete,
262
+ autoFocus,
263
+ maxLength,
264
+ minLength,
265
+ readOnly,
266
+ spellCheck,
267
+ ...props
268
+ };
269
+ if (hasInputGroup) {
270
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
271
+ /* @__PURE__ */ jsxs(InputGroup, { children: [
272
+ addonLeft && /* @__PURE__ */ jsx(InputGroupAddon, { align: "block-start", children: addonLeft }),
273
+ /* @__PURE__ */ jsx(InputGroupTextarea, { ...textareaProps }),
274
+ addonRight && /* @__PURE__ */ jsx(InputGroupAddon, { align: "block-end", children: addonRight })
275
+ ] }),
276
+ maxLength && currentCharCount > 0 && /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground mt-1.5 text-right", children: [
277
+ currentCharCount,
278
+ "/",
279
+ maxLength
280
+ ] })
281
+ ] });
282
+ }
283
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
284
+ /* @__PURE__ */ jsx(Textarea, { ...textareaProps }),
285
+ maxLength && currentCharCount > 0 && /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground mt-1.5 text-right", children: [
286
+ currentCharCount,
287
+ "/",
288
+ maxLength
289
+ ] })
290
+ ] });
291
+ };
292
+ if (control && name) {
293
+ return /* @__PURE__ */ jsx(
294
+ Controller,
295
+ {
296
+ name,
297
+ control,
298
+ render: ({ field, fieldState }) => /* @__PURE__ */ jsxs(Field.Root, { className, disabled, invalid: !!fieldState?.error, children: [
299
+ /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
300
+ label && /* @__PURE__ */ jsx(Field.Label, { children: label }),
301
+ renderTextarea(field.value, field.onChange, disabled, fieldState?.error?.message)
302
+ ] }),
303
+ fieldState?.error && /* @__PURE__ */ jsx(Field.Error, { children: fieldState.error.message }),
304
+ /* @__PURE__ */ jsx(Field.Description, { children: description })
305
+ ] })
306
+ }
307
+ );
308
+ }
309
+ const handleDirectChange = (newValue) => {
310
+ onChange?.({ target: { value: newValue } });
311
+ onValueChange?.(newValue);
312
+ };
313
+ return /* @__PURE__ */ jsxs(Field.Root, { className, disabled, invalid: !!error, children: [
314
+ /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
315
+ label && /* @__PURE__ */ jsx(Field.Label, { children: label }),
316
+ renderTextarea(value, handleDirectChange, disabled, error)
317
+ ] }),
318
+ error && /* @__PURE__ */ jsx(Field.Error, { children: error }),
319
+ /* @__PURE__ */ jsx(Field.Description, { children: description })
320
+ ] });
321
+ }
322
+ function CompactSelect({
323
+ // Form integration
324
+ control,
325
+ name,
326
+ description,
327
+ required,
328
+ // Basic props
329
+ label,
330
+ placeholder = "Select option",
331
+ disabled,
332
+ items = [],
333
+ // Styling
334
+ className,
335
+ // Events
336
+ onValueChange,
337
+ // Direct usage
338
+ value,
339
+ error,
340
+ ref,
341
+ ...props
342
+ }) {
343
+ if (control && name) {
344
+ return /* @__PURE__ */ jsx(
345
+ Controller,
346
+ {
347
+ name,
348
+ control,
349
+ render: ({ field, fieldState }) => /* @__PURE__ */ jsxs(Field.Root, { disabled, invalid: !!fieldState?.error, children: [
350
+ /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
351
+ label && /* @__PURE__ */ jsx(Field.Label, { children: label }),
352
+ /* @__PURE__ */ jsxs(
353
+ Select,
354
+ {
355
+ value: field.value,
356
+ onValueChange: (val) => {
357
+ field.onChange(val);
358
+ onValueChange?.(val);
359
+ },
360
+ disabled,
361
+ ...props,
362
+ children: [
363
+ /* @__PURE__ */ jsx(
364
+ SelectTrigger,
365
+ {
366
+ ref,
367
+ id: name,
368
+ className: cn(
369
+ "w-full data-[size=default]:h-11 text-sm",
370
+ fieldState?.error && "border-destructive focus:ring-destructive/20",
371
+ className
372
+ ),
373
+ children: /* @__PURE__ */ jsx(SelectValue, { placeholder })
374
+ }
375
+ ),
376
+ /* @__PURE__ */ jsx(SelectContent, { children: items.map((item) => /* @__PURE__ */ jsx(SelectItem, { value: item.value, children: item.label }, item.value)) })
377
+ ]
378
+ }
379
+ )
380
+ ] }),
381
+ fieldState?.error && /* @__PURE__ */ jsx(Field.Error, { children: fieldState.error.message }),
382
+ /* @__PURE__ */ jsx(Field.Description, { children: description })
383
+ ] })
384
+ }
385
+ );
386
+ }
387
+ return /* @__PURE__ */ jsxs(Field.Root, { disabled, invalid: !!error, children: [
388
+ /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
389
+ label && /* @__PURE__ */ jsx(Field.Label, { children: label }),
390
+ /* @__PURE__ */ jsxs(
391
+ Select,
392
+ {
393
+ value,
394
+ onValueChange,
395
+ disabled,
396
+ ...props,
397
+ children: [
398
+ /* @__PURE__ */ jsx(
399
+ SelectTrigger,
400
+ {
401
+ ref,
402
+ className: cn(
403
+ "w-full data-[size=default]:h-11 text-sm",
404
+ error && "border-destructive focus:ring-destructive/20",
405
+ className
406
+ ),
407
+ children: /* @__PURE__ */ jsx(SelectValue, { placeholder })
408
+ }
409
+ ),
410
+ /* @__PURE__ */ jsx(SelectContent, { children: items.map((item) => /* @__PURE__ */ jsx(SelectItem, { value: item.value, children: item.label }, item.value)) })
411
+ ]
412
+ }
413
+ )
414
+ ] }),
415
+ error && /* @__PURE__ */ jsx(Field.Error, { children: error })
416
+ ] });
417
+ }
418
+ function CompactNumberInput({
419
+ // Form integration
420
+ control,
421
+ name,
422
+ description,
423
+ required,
424
+ // Basic props
425
+ label,
426
+ placeholder,
427
+ disabled,
428
+ // Number configuration
429
+ min = 0,
430
+ max,
431
+ step = 1,
432
+ // Display options
433
+ prefix,
434
+ suffix,
435
+ showButtons = false,
436
+ buttonVariant = "ghost",
437
+ // Styling
438
+ className,
439
+ inputClassName,
440
+ labelClassName,
441
+ containerClassName,
442
+ // Events
443
+ onValueChange,
444
+ // Direct usage
445
+ value,
446
+ defaultValue,
447
+ onChange,
448
+ error,
449
+ ref,
450
+ ...props
451
+ }) {
452
+ if (control && name) {
453
+ return /* @__PURE__ */ jsx(
454
+ Controller,
455
+ {
456
+ name,
457
+ control,
458
+ render: ({ field, fieldState }) => /* @__PURE__ */ jsx(
459
+ CompactNumberInputInternal,
460
+ {
461
+ ref,
462
+ label,
463
+ placeholder,
464
+ disabled,
465
+ min,
466
+ max,
467
+ step,
468
+ prefix,
469
+ suffix,
470
+ showButtons,
471
+ buttonVariant,
472
+ className,
473
+ inputClassName,
474
+ labelClassName,
475
+ containerClassName,
476
+ error: fieldState?.error?.message,
477
+ value: field.value ?? "",
478
+ onChange: (val) => {
479
+ field.onChange(val);
480
+ onValueChange?.(val);
481
+ },
482
+ ...props
483
+ }
484
+ )
485
+ }
486
+ );
487
+ }
488
+ return /* @__PURE__ */ jsx(
489
+ CompactNumberInputInternal,
490
+ {
491
+ ref,
492
+ label,
493
+ placeholder,
494
+ disabled,
495
+ min,
496
+ max,
497
+ step,
498
+ prefix,
499
+ suffix,
500
+ showButtons,
501
+ buttonVariant,
502
+ className,
503
+ inputClassName,
504
+ labelClassName,
505
+ containerClassName,
506
+ error,
507
+ value: value ?? "",
508
+ defaultValue,
509
+ onChange,
510
+ onValueChange,
511
+ ...props
512
+ }
513
+ );
514
+ }
515
+ function CompactNumberInputInternal({
516
+ label,
517
+ placeholder,
518
+ min = 0,
519
+ max,
520
+ step = 1,
521
+ prefix,
522
+ suffix,
523
+ showButtons,
524
+ buttonVariant,
525
+ error,
526
+ className,
527
+ inputClassName,
528
+ labelClassName,
529
+ containerClassName,
530
+ disabled,
531
+ value,
532
+ defaultValue,
533
+ onChange,
534
+ onValueChange,
535
+ ref,
536
+ ...props
537
+ }) {
538
+ const handleIncrement = () => {
539
+ const currentValue = Number(value) || 0;
540
+ const newValue = currentValue + step;
541
+ if (max === void 0 || newValue <= max) {
542
+ const finalValue = Number(newValue.toFixed(10));
543
+ onChange?.(finalValue);
544
+ onValueChange?.(finalValue);
545
+ }
546
+ };
547
+ const handleDecrement = () => {
548
+ const currentValue = Number(value) || 0;
549
+ const newValue = currentValue - step;
550
+ if (newValue >= min) {
551
+ const finalValue = Number(newValue.toFixed(10));
552
+ onChange?.(finalValue);
553
+ onValueChange?.(finalValue);
554
+ }
555
+ };
556
+ const handleInputChange = (e) => {
557
+ const val = e.target.value;
558
+ if (val === "") {
559
+ onChange?.("");
560
+ onValueChange?.("");
561
+ return;
562
+ }
563
+ const numVal = Number(val);
564
+ if (!isNaN(numVal)) {
565
+ if (min !== void 0 && numVal < min || max !== void 0 && numVal > max) {
566
+ return;
567
+ }
568
+ onChange?.(numVal);
569
+ onValueChange?.(numVal);
570
+ }
571
+ };
572
+ if (showButtons) {
573
+ return /* @__PURE__ */ jsxs(Field.Root, { className: containerClassName, disabled, invalid: !!error, children: [
574
+ /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
575
+ label && /* @__PURE__ */ jsx(Field.Label, { className: labelClassName, children: label }),
576
+ /* @__PURE__ */ jsxs(InputGroup, { className: "h-11", children: [
577
+ /* @__PURE__ */ jsx(InputGroupAddon, { align: "inline-start", children: /* @__PURE__ */ jsx(
578
+ InputGroupButton,
579
+ {
580
+ type: "button",
581
+ variant: buttonVariant,
582
+ size: "icon-sm",
583
+ onClick: handleDecrement,
584
+ disabled: disabled || Number(value) <= min,
585
+ children: /* @__PURE__ */ jsx(Minus, { className: "h-4 w-4" })
586
+ }
587
+ ) }),
588
+ /* @__PURE__ */ jsx(
589
+ InputGroupInput,
590
+ {
591
+ ref,
592
+ type: "number",
593
+ value,
594
+ defaultValue,
595
+ onChange: handleInputChange,
596
+ min,
597
+ max,
598
+ step,
599
+ disabled,
600
+ placeholder,
601
+ className: cn("text-center text-sm", inputClassName, className),
602
+ ...props
603
+ }
604
+ ),
605
+ /* @__PURE__ */ jsx(InputGroupAddon, { align: "inline-end", children: /* @__PURE__ */ jsx(
606
+ InputGroupButton,
607
+ {
608
+ type: "button",
609
+ variant: buttonVariant,
610
+ size: "icon-sm",
611
+ onClick: handleIncrement,
612
+ disabled: disabled || max !== void 0 && Number(value) >= max,
613
+ children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" })
614
+ }
615
+ ) })
616
+ ] })
617
+ ] }),
618
+ error && /* @__PURE__ */ jsx(Field.Error, { children: error })
619
+ ] });
620
+ }
621
+ if (prefix || suffix) {
622
+ return /* @__PURE__ */ jsxs(Field.Root, { className: containerClassName, disabled, invalid: !!error, children: [
623
+ /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
624
+ label && /* @__PURE__ */ jsx(Field.Label, { className: labelClassName, children: label }),
625
+ /* @__PURE__ */ jsxs(InputGroup, { className: "h-11", children: [
626
+ prefix && /* @__PURE__ */ jsx(InputGroupAddon, { align: "inline-start", children: typeof prefix === "string" ? /* @__PURE__ */ jsx("span", { className: "text-xs", children: prefix }) : prefix }),
627
+ /* @__PURE__ */ jsx(
628
+ InputGroupInput,
629
+ {
630
+ ref,
631
+ type: "number",
632
+ value,
633
+ defaultValue,
634
+ onChange: handleInputChange,
635
+ min,
636
+ max,
637
+ step,
638
+ disabled,
639
+ placeholder,
640
+ className: cn("text-sm", inputClassName, className),
641
+ ...props
642
+ }
643
+ ),
644
+ suffix && /* @__PURE__ */ jsx(InputGroupAddon, { align: "inline-end", children: typeof suffix === "string" ? /* @__PURE__ */ jsx("span", { className: "text-xs", children: suffix }) : suffix })
645
+ ] })
646
+ ] }),
647
+ error && /* @__PURE__ */ jsx(Field.Error, { children: error })
648
+ ] });
649
+ }
650
+ return /* @__PURE__ */ jsxs(Field.Root, { className: containerClassName, disabled, invalid: !!error, children: [
651
+ /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
652
+ label && /* @__PURE__ */ jsx(Field.Label, { className: labelClassName, children: label }),
653
+ /* @__PURE__ */ jsx(
654
+ Input,
655
+ {
656
+ ref,
657
+ type: "number",
658
+ value,
659
+ defaultValue,
660
+ onChange: handleInputChange,
661
+ min,
662
+ max,
663
+ step,
664
+ disabled,
665
+ placeholder,
666
+ className: cn(
667
+ "h-11 text-sm",
668
+ error && "border-destructive focus-visible:ring-destructive/20",
669
+ inputClassName,
670
+ className
671
+ ),
672
+ ...props
673
+ }
674
+ )
675
+ ] }),
676
+ error && /* @__PURE__ */ jsx(Field.Error, { children: error })
677
+ ] });
678
+ }
679
+ function CompactTagChoice({
680
+ // Form integration
681
+ control,
682
+ name,
683
+ // Display
684
+ label,
685
+ description,
686
+ placeholder = "Select options...",
687
+ required,
688
+ disabled,
689
+ // Data
690
+ choices = [],
691
+ maxSelections,
692
+ // Styling
693
+ className,
694
+ containerClassName,
695
+ labelClassName,
696
+ inputClassName,
697
+ // Direct usage
698
+ value: propValue = [],
699
+ onChange: propOnChange,
700
+ onValueChange,
701
+ error,
702
+ ref
703
+ }) {
704
+ if (control && name) {
705
+ return /* @__PURE__ */ jsx(
706
+ Controller,
707
+ {
708
+ name,
709
+ control,
710
+ render: ({ field, fieldState }) => /* @__PURE__ */ jsx(
711
+ CompactTagChoiceInternal,
712
+ {
713
+ ref,
714
+ label,
715
+ placeholder,
716
+ disabled,
717
+ error: fieldState?.error?.message,
718
+ value: field.value || [],
719
+ onChange: (vals) => {
720
+ field.onChange(vals);
721
+ onValueChange?.(vals);
722
+ },
723
+ choices,
724
+ maxSelections,
725
+ containerClassName: cn(className, containerClassName),
726
+ labelClassName,
727
+ inputClassName
728
+ }
729
+ )
730
+ }
731
+ );
732
+ }
733
+ return /* @__PURE__ */ jsx(
734
+ CompactTagChoiceInternal,
735
+ {
736
+ ref,
737
+ label,
738
+ placeholder,
739
+ disabled,
740
+ value: propValue,
741
+ onChange: (vals) => {
742
+ propOnChange?.(vals);
743
+ onValueChange?.(vals);
744
+ },
745
+ choices,
746
+ maxSelections,
747
+ containerClassName,
748
+ labelClassName,
749
+ inputClassName,
750
+ className,
751
+ error
752
+ }
753
+ );
754
+ }
755
+ function CompactTagChoiceInternal({
756
+ label,
757
+ placeholder,
758
+ disabled,
759
+ value = [],
760
+ onChange,
761
+ choices = [],
762
+ maxSelections,
763
+ error,
764
+ className,
765
+ containerClassName,
766
+ labelClassName,
767
+ inputClassName,
768
+ ref
769
+ }) {
770
+ const [open, setOpen] = useState(false);
771
+ const triggerRef = useRef(null);
772
+ const availableChoices = choices.filter((c) => !value.includes(c.value));
773
+ const addChoice = (choiceValue) => {
774
+ if (value.includes(choiceValue)) return;
775
+ if (maxSelections && value.length >= maxSelections) return;
776
+ const updated = [...value, choiceValue];
777
+ onChange?.(updated);
778
+ };
779
+ const removeChoice = (choiceValue) => {
780
+ const updated = value.filter((v) => v !== choiceValue);
781
+ onChange?.(updated);
782
+ };
783
+ return /* @__PURE__ */ jsxs(Field.Root, { className: containerClassName, disabled, invalid: !!error, children: [
784
+ /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
785
+ label && /* @__PURE__ */ jsx(Field.Label, { className: labelClassName, children: label }),
786
+ /* @__PURE__ */ jsxs(
787
+ "div",
788
+ {
789
+ className: cn(
790
+ "min-h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
791
+ "ring-offset-background focus-within:ring-1 focus-within:ring-primary/20",
792
+ "hover:border-primary/50 transition-all duration-200",
793
+ error && "border-destructive focus-within:ring-destructive/20",
794
+ disabled && "opacity-50 cursor-not-allowed",
795
+ inputClassName
796
+ ),
797
+ ref,
798
+ children: [
799
+ /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap gap-1", value.length > 0 && "mb-2"), children: value.map((val) => {
800
+ const choice = choices.find((c) => c.value === val);
801
+ return /* @__PURE__ */ jsxs(Badge, { variant: "secondary", className: "flex items-center gap-1 px-2 py-0.5 text-xs", children: [
802
+ /* @__PURE__ */ jsx("span", { children: choice?.label ?? val }),
803
+ !disabled && /* @__PURE__ */ jsx(
804
+ "button",
805
+ {
806
+ type: "button",
807
+ className: "text-muted-foreground hover:text-foreground ml-0.5",
808
+ onClick: (e) => {
809
+ e.preventDefault();
810
+ e.stopPropagation();
811
+ removeChoice(val);
812
+ },
813
+ children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" })
814
+ }
815
+ )
816
+ ] }, val);
817
+ }) }),
818
+ !disabled && availableChoices.length > 0 && (!maxSelections || value.length < maxSelections) && /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
819
+ /* @__PURE__ */ jsx(
820
+ PopoverTrigger,
821
+ {
822
+ render: /* @__PURE__ */ jsxs(
823
+ Button,
824
+ {
825
+ ref: triggerRef,
826
+ type: "button",
827
+ variant: "ghost",
828
+ className: cn(
829
+ "inline-flex items-center justify-center h-auto p-1 rounded-md text-xs font-medium transition-colors",
830
+ "text-muted-foreground hover:text-foreground hover:bg-accent",
831
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
832
+ ),
833
+ onClick: (e) => {
834
+ e.preventDefault();
835
+ e.stopPropagation();
836
+ setOpen(true);
837
+ },
838
+ children: [
839
+ /* @__PURE__ */ jsx(Plus, { className: "h-3 w-3 mr-1" }),
840
+ value.length === 0 ? placeholder : "Add more..."
841
+ ]
842
+ }
843
+ )
844
+ }
845
+ ),
846
+ /* @__PURE__ */ jsx(PopoverContent, { className: "w-[220px] p-0", align: "start", children: /* @__PURE__ */ jsx("div", { className: "p-1", children: availableChoices.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-2 py-3 text-sm text-muted-foreground text-center", children: "No options available" }) : /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: availableChoices.map((choice) => /* @__PURE__ */ jsxs(
847
+ "div",
848
+ {
849
+ onClick: (e) => {
850
+ e.preventDefault();
851
+ e.stopPropagation();
852
+ addChoice(choice.value);
853
+ },
854
+ className: "flex items-center justify-between px-2 py-1.5 text-sm rounded-sm hover:bg-accent hover:text-accent-foreground cursor-pointer",
855
+ children: [
856
+ /* @__PURE__ */ jsx("span", { children: choice.label }),
857
+ value.includes(choice.value) && /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" })
858
+ ]
859
+ },
860
+ choice.value
861
+ )) }) }) })
862
+ ] }),
863
+ maxSelections && /* @__PURE__ */ jsxs("div", { className: "text-[11px] text-muted-foreground mt-1", children: [
864
+ value.length,
865
+ "/",
866
+ maxSelections,
867
+ " selected"
868
+ ] })
869
+ ]
870
+ }
871
+ )
872
+ ] }),
873
+ error && /* @__PURE__ */ jsx(Field.Error, { children: error })
874
+ ] });
875
+ }
876
+ function generateSlug(text) {
877
+ if (!text) return "";
878
+ return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
879
+ }
880
+ function CompactSlugField({
881
+ // Form integration
882
+ control,
883
+ name,
884
+ description,
885
+ required,
886
+ // Basic props
887
+ label,
888
+ placeholder = "my-page-slug",
889
+ disabled,
890
+ // Icon support
891
+ icon,
892
+ // Slug generation
893
+ sourceValue,
894
+ onGenerate,
895
+ // Styling
896
+ className,
897
+ inputClassName,
898
+ // Events
899
+ onValueChange,
900
+ // Direct usage
901
+ value,
902
+ onChange,
903
+ error,
904
+ ref,
905
+ ...props
906
+ }) {
907
+ const handleGenerate = (currentValue, fieldOnChange) => {
908
+ const newSlug = onGenerate ? onGenerate(sourceValue || "") : generateSlug(sourceValue || "");
909
+ fieldOnChange(newSlug);
910
+ onValueChange?.(newSlug);
911
+ };
912
+ const renderInput = (fieldValue, fieldOnChange, isDisabled, fieldState) => {
913
+ const inputProps = {
914
+ ref,
915
+ id: name,
916
+ type: "text",
917
+ disabled: isDisabled,
918
+ placeholder,
919
+ value: fieldValue || "",
920
+ onChange: (e) => {
921
+ const newValue = e.target.value;
922
+ fieldOnChange(newValue);
923
+ onValueChange?.(newValue);
924
+ },
925
+ className: cn(
926
+ "h-11 text-sm",
927
+ fieldState?.error && "border-destructive focus-visible:ring-destructive/20",
928
+ inputClassName
929
+ ),
930
+ ...props
931
+ };
932
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
933
+ icon && /* @__PURE__ */ jsx(Field.Icon, { children: icon }),
934
+ /* @__PURE__ */ jsxs(InputGroup, { className: "h-11", children: [
935
+ /* @__PURE__ */ jsx(InputGroupInput, { ...inputProps, className: cn(inputProps.className, icon && "pl-9") }),
936
+ /* @__PURE__ */ jsx(InputGroupAddon, { align: "inline-end", children: /* @__PURE__ */ jsxs(
937
+ InputGroupButton,
938
+ {
939
+ type: "button",
940
+ size: "sm",
941
+ onClick: () => handleGenerate(fieldValue, fieldOnChange),
942
+ disabled: isDisabled || !sourceValue,
943
+ title: "Generate slug from name",
944
+ children: [
945
+ /* @__PURE__ */ jsx(Wand2, { className: "h-4 w-4" }),
946
+ "Generate"
947
+ ]
948
+ }
949
+ ) })
950
+ ] })
951
+ ] });
952
+ };
953
+ if (control && name) {
954
+ return /* @__PURE__ */ jsx(
955
+ Controller,
956
+ {
957
+ name,
958
+ control,
959
+ render: ({ field, fieldState }) => /* @__PURE__ */ jsxs(Field.Root, { className, disabled, invalid: !!fieldState?.error, children: [
960
+ /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
961
+ label && /* @__PURE__ */ jsx(Field.Label, { children: label }),
962
+ renderInput(field.value, field.onChange, disabled, fieldState)
963
+ ] }),
964
+ fieldState?.error && /* @__PURE__ */ jsx(Field.Error, { children: fieldState.error.message }),
965
+ /* @__PURE__ */ jsx(Field.Description, { children: description })
966
+ ] })
967
+ }
968
+ );
969
+ }
970
+ const handleDirectChange = (newValue) => {
971
+ onChange?.({ target: { value: newValue } });
972
+ onValueChange?.(newValue);
973
+ };
974
+ return /* @__PURE__ */ jsxs(Field.Root, { className, disabled, invalid: !!error, children: [
975
+ /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
976
+ label && /* @__PURE__ */ jsx(Field.Label, { children: label }),
977
+ renderInput(value, handleDirectChange, disabled, { error: error ? { message: error } : void 0 })
978
+ ] }),
979
+ error && /* @__PURE__ */ jsx(Field.Error, { children: error }),
980
+ /* @__PURE__ */ jsx(Field.Description, { children: description })
981
+ ] });
982
+ }
983
+
984
+ export { CompactInput, CompactNumberInput, CompactSelect, CompactSlugField, CompactTagChoice, CompactTextarea, generateSlug };
985
+ //# sourceMappingURL=compact.js.map
986
+ //# sourceMappingURL=compact.js.map