@donotdev/components 0.0.11 → 0.0.12

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 (45) hide show
  1. package/dist/atomic/Button/index.d.ts.map +1 -1
  2. package/dist/atomic/Button/index.js +5 -2
  3. package/dist/atomic/Calendar/index.js +1 -1
  4. package/dist/atomic/Collapsible/index.d.ts.map +1 -1
  5. package/dist/atomic/Collapsible/index.js +1 -5
  6. package/dist/atomic/Combobox/index.d.ts +9 -47
  7. package/dist/atomic/Combobox/index.d.ts.map +1 -1
  8. package/dist/atomic/Combobox/index.js +135 -87
  9. package/dist/atomic/Dialog/index.d.ts.map +1 -1
  10. package/dist/atomic/Dialog/index.js +1 -1
  11. package/dist/atomic/DropdownMenu/index.js +1 -1
  12. package/dist/atomic/Input/index.d.ts +1 -1
  13. package/dist/atomic/Input/index.d.ts.map +1 -1
  14. package/dist/atomic/Input/index.js +6 -22
  15. package/dist/atomic/Label/FloatingLabel.d.ts +31 -0
  16. package/dist/atomic/Label/FloatingLabel.d.ts.map +1 -0
  17. package/dist/atomic/Label/FloatingLabel.js +34 -0
  18. package/dist/atomic/Label/index.d.ts +5 -1
  19. package/dist/atomic/Label/index.d.ts.map +1 -1
  20. package/dist/atomic/Label/index.js +8 -2
  21. package/dist/atomic/PasswordInput/index.d.ts +9 -10
  22. package/dist/atomic/PasswordInput/index.d.ts.map +1 -1
  23. package/dist/atomic/PasswordInput/index.js +10 -30
  24. package/dist/atomic/Section/index.d.ts.map +1 -1
  25. package/dist/atomic/Section/index.js +3 -4
  26. package/dist/atomic/Select/index.d.ts +3 -1
  27. package/dist/atomic/Select/index.d.ts.map +1 -1
  28. package/dist/atomic/Select/index.js +13 -24
  29. package/dist/atomic/Skeleton/index.d.ts.map +1 -1
  30. package/dist/atomic/Skeleton/index.js +6 -3
  31. package/dist/atomic/Switch/index.d.ts +18 -4
  32. package/dist/atomic/Switch/index.d.ts.map +1 -1
  33. package/dist/atomic/Switch/index.js +25 -6
  34. package/dist/atomic/Table/index.d.ts +17 -7
  35. package/dist/atomic/Table/index.d.ts.map +1 -1
  36. package/dist/atomic/Table/index.js +13 -3
  37. package/dist/atomic/index.d.ts +2 -3
  38. package/dist/atomic/index.d.ts.map +1 -1
  39. package/dist/atomic/index.js +1 -2
  40. package/dist/index.js +4 -4
  41. package/dist/styles/index.css +335 -56
  42. package/package.json +1 -1
  43. package/dist/atomic/Combobox/ComboboxPrimitive.d.ts +0 -18
  44. package/dist/atomic/Combobox/ComboboxPrimitive.d.ts.map +0 -1
  45. package/dist/atomic/Combobox/ComboboxPrimitive.js +0 -14
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/atomic/Button/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAK1D,OAAO,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5E;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;CAKjB,CAAC;AAEX,QAAA,MAAM,cAAc;;8EAmBlB,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC,OAAO,cAAc,CAAC,CAAC,SAAS,CAAC,CAAC;AAE3E;;;;;;GAMG;AACH,qEAAqE;AACrE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,WACf,SACE,oBAAoB,CAAC,iBAAiB,CAAC,EACvC,YAAY,CAAC,OAAO,cAAc,CAAC;IACrC;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC;IACjD;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC;IACjD,yDAAyD;IACzD,IAAI,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,MAAM,GAAG,SAAS,CAAC;IACvD;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,QAAA,MAAM,MAAM,GAAI,wKAcb,WAAW,4UAmFb,CAAC;AAEF,eAAe,MAAM,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/atomic/Button/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAK1D,OAAO,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5E;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;CAKjB,CAAC;AAEX,QAAA,MAAM,cAAc;;8EAmBlB,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC,OAAO,cAAc,CAAC,CAAC,SAAS,CAAC,CAAC;AAE3E;;;;;;GAMG;AACH,qEAAqE;AACrE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,WACf,SACE,oBAAoB,CAAC,iBAAiB,CAAC,EACvC,YAAY,CAAC,OAAO,cAAc,CAAC;IACrC;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC;IACjD;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC;IACjD,yDAAyD;IACzD,IAAI,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,MAAM,GAAG,SAAS,CAAC;IACvD;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,QAAA,MAAM,MAAM,GAAI,wKAcb,WAAW,4UAuFb,CAAC;AAEF,eAAe,MAAM,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,CAAC"}
@@ -91,6 +91,9 @@ const Button = ({ className, variant, render, display = DISPLAY.AUTO, icon, icon
91
91
  // Map 'default' variant to 'primary' for data-variant (same styling)
92
92
  const variantForDataAttr = variant === THEME_VARIANT.DEFAULT ? THEME_VARIANT.PRIMARY : variant;
93
93
  const variantAttrs = getVariantDataAttrs({ variant: variantForDataAttr });
94
+ // Extract type from props before spreading (to ensure it's not overridden)
95
+ const buttonType = props.type || 'button';
96
+ const { type: _type, ...restProps } = props;
94
97
  // Build all props that should be passed to element (button or render prop)
95
98
  const elementProps = {
96
99
  children: buttonContent,
@@ -101,10 +104,10 @@ const Button = ({ className, variant, render, display = DISPLAY.AUTO, icon, icon
101
104
  ...(floating && { 'data-floating': 'true' }),
102
105
  ...(fullWidth && { 'data-full-width': 'true' }),
103
106
  ...(effectiveDisplay && { 'data-display': effectiveDisplay }),
104
- ...props,
107
+ ...restProps,
105
108
  };
106
109
  // Render prop pattern - no cloneElement needed (React 19 compatible)
107
- const buttonElement = render ? (render(elementProps)) : (_jsx("button", { type: props.type || 'button', role: "button", tabIndex: 0, ...elementProps, children: buttonContent }));
110
+ const buttonElement = render ? (render(elementProps)) : (_jsx("button", { type: buttonType, role: "button", tabIndex: 0, ...elementProps, children: buttonContent }));
108
111
  // Tooltip: COMPACT/AUTO (may be icon-only when collapsed), others only if explicit
109
112
  // Don't specify side - let CSS --tooltip-side take priority (RTL-aware in sidebars)
110
113
  if (effectiveDisplay === DISPLAY.COMPACT ||
@@ -13,7 +13,7 @@ function CalendarDropdown({ options = [], value, onChange, 'aria-label': ariaLab
13
13
  const gridStyle = {
14
14
  display: 'grid',
15
15
  gridTemplateColumns: isMonth ? 'repeat(3, 1fr)' : 'repeat(5, 1fr)',
16
- gap: 'var(--gap-xs)',
16
+ gap: 'var(--gap-sm)',
17
17
  padding: '0',
18
18
  };
19
19
  const handleSelect = (newValue) => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/atomic/Collapsible/index.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,mBAAmB,CAAC;AAE3B,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,uCAAuC;IACvC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,wCAAwC;IACxC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACrC,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,iBAAS,WAAW,CAAC,EACnB,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,QAAgB,EAChB,SAAwB,EACxB,SAAS,GACV,EAAE,gBAAgB,2CAmElB;AAED,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/atomic/Collapsible/index.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,mBAAmB,CAAC;AAE3B,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,uCAAuC;IACvC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,wCAAwC;IACxC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACrC,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,iBAAS,WAAW,CAAC,EACnB,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,QAAgB,EAChB,SAAwB,EACxB,SAAS,GACV,EAAE,gBAAgB,2CA+DlB;AAED,eAAe,WAAW,CAAC"}
@@ -42,11 +42,7 @@ function Collapsible({ trigger, children, open, onOpenChange, defaultOpen, disab
42
42
  if (!trigger)
43
43
  return null;
44
44
  if (showIcon) {
45
- const IconComponent = iconStyle === 'chevron'
46
- ? ChevronDown
47
- : isOpen
48
- ? Minus
49
- : Plus;
45
+ const IconComponent = iconStyle === 'chevron' ? ChevronDown : isOpen ? Minus : Plus;
50
46
  return (_jsx(CollapsibleTrigger, { asChild: true, children: _jsxs(Stack, { as: "div", direction: "row", align: "center", justify: "between", className: "dndev-collapsible-trigger", children: [trigger, iconStyle === 'chevron' ? (_jsx(ChevronDown, { className: "dndev-collapsible-icon dndev-collapsible-chevron", "aria-hidden": "true" })) : (_jsx(IconComponent, { className: "dndev-collapsible-icon", "aria-hidden": "true" }))] }) }));
51
47
  }
52
48
  return _jsx(CollapsibleTrigger, { asChild: true, children: trigger });
@@ -1,12 +1,3 @@
1
- /**
2
- * @fileoverview Combobox component
3
- * @description Accessible combobox with search/filter functionality. Provides a prop-based implementation.
4
- *
5
- * @version 0.0.1
6
- * @since 0.0.1
7
- * @author AMBROISE PARK Consulting
8
- */
9
- import { type ReactNode } from 'react';
10
1
  import { CONTROL_VARIANT, type ControlVariant } from '../../utils/constants';
11
2
  import './Combobox.css';
12
3
  export interface ComboboxOption {
@@ -14,57 +5,28 @@ export interface ComboboxOption {
14
5
  label: string;
15
6
  disabled?: boolean;
16
7
  description?: string;
17
- content?: ReactNode;
8
+ content?: React.ReactNode;
18
9
  }
19
10
  export interface ComboboxProps {
20
- /** Selected value (string for single, array for multiple) */
21
11
  value?: string | string[];
22
- /** Change handler */
23
12
  onValueChange?: (value: string | string[]) => void;
24
- /** Placeholder text for trigger */
25
13
  placeholder?: string;
26
- /** Array of options */
27
14
  options?: ComboboxOption[];
28
- /** Custom trigger element */
29
- trigger?: ReactNode;
30
- /** Whether the combobox is disabled */
31
15
  disabled?: boolean;
32
- /** Whether the combobox is in a loading state */
33
16
  isLoading?: boolean;
34
- /** Variant for styling */
35
17
  variant?: ControlVariant;
36
- /** Placeholder for search input */
37
- searchPlaceholder?: string;
38
- /** Message when no results found */
39
18
  emptyMessage?: string;
40
- /** Custom filter function */
41
- filterFn?: (option: ComboboxOption, searchTerm: string) => boolean;
42
- /** Enable multiple selection */
43
19
  multiple?: boolean;
44
- /** Label for floating label - automatically floats when both label and placeholder are provided */
45
20
  label?: string;
21
+ creatable?: boolean;
22
+ createLabel?: string;
23
+ clearable?: boolean;
24
+ onOpenChange?: (open: boolean) => void;
25
+ onSearchChange?: (search: string) => void;
26
+ /** Whether the field is required - shows asterisk indicator */
27
+ required?: boolean;
46
28
  }
47
- /**
48
- * Accessible combobox component with search/filter functionality.
49
- * Provides a prop-based implementation to abstract complexity.
50
- *
51
- * @component
52
- * @example
53
- * ```tsx
54
- * <Combobox
55
- * value={value}
56
- * onValueChange={setValue}
57
- * placeholder="Select option..."
58
- * options={[
59
- * { value: 'option1', label: 'Option 1' },
60
- * { value: 'option2', label: 'Option 2' }
61
- * ]}
62
- * />
63
- * ```
64
- * @param {ComboboxProps} props - The props for the combobox
65
- * @returns {JSX.Element} The rendered combobox
66
- */
67
- declare const Combobox: ({ value, onValueChange, placeholder, searchPlaceholder, emptyMessage, options, trigger, disabled, isLoading, variant, filterFn, multiple, label, }: ComboboxProps) => import("react/jsx-runtime").JSX.Element;
29
+ declare const Combobox: ({ value, onValueChange, placeholder, emptyMessage, options, disabled, isLoading, variant, multiple, label, creatable, createLabel, clearable, onOpenChange, onSearchChange, required, }: ComboboxProps) => import("react/jsx-runtime").JSX.Element;
68
30
  export default Combobox;
69
31
  export { CONTROL_VARIANT as COMBOBOX_VARIANT };
70
32
  export type { ControlVariant as ComboboxVariant };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/atomic/Combobox/index.tsx"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,EAML,KAAK,SAAS,EAEf,MAAM,OAAO,CAAC;AAGf,OAAO,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAW7E,OAAO,gBAAgB,CAAC;AAExB,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,qBAAqB;IACrB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;IACnD,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,6BAA6B;IAC7B,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,mCAAmC;IACnC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;IACnE,gCAAgC;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mGAAmG;IACnG,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAYD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,QAAA,MAAM,QAAQ,GAAI,oJAcf,aAAa,4CA2Pf,CAAC;AAEF,eAAe,QAAQ,CAAC;AACxB,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,CAAC;AAC/C,YAAY,EAAE,cAAc,IAAI,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/atomic/Combobox/index.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG7E,OAAO,gBAAgB,CAAC;AAExB,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,QAAA,MAAM,QAAQ,GAAI,yLAiBf,aAAa,4CA4Uf,CAAC;AAEF,eAAe,QAAQ,CAAC;AACxB,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,CAAC;AAC/C,YAAY,EAAE,cAAc,IAAI,eAAe,EAAE,CAAC"}
@@ -1,77 +1,64 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  // packages/components/src/atomic/Combobox/index.tsx
3
- /**
4
- * @fileoverview Combobox component
5
- * @description Accessible combobox with search/filter functionality. Provides a prop-based implementation.
6
- *
7
- * @version 0.0.1
8
- * @since 0.0.1
9
- * @author AMBROISE PARK Consulting
10
- */
11
3
  import { useState, useMemo, useRef, useEffect, useId, } from 'react';
12
- import { Check, ChevronDown, X } from 'lucide-react';
4
+ import { Check, ChevronDown, Plus, X } from 'lucide-react';
5
+ import Spinner from '../Spinner';
6
+ import ScrollArea from '../ScrollArea';
13
7
  import { cn } from '../../utils/helpers';
14
8
  import { CONTROL_VARIANT } from '../../utils/constants';
15
- import { ComboboxRootPrimitive, ComboboxTriggerPrimitive, ComboboxContentPrimitive, ComboboxAnchorPrimitive, } from './ComboboxPrimitive';
16
9
  import Input from '../Input';
17
- import Button from '../Button';
18
- import Label from '../Label';
19
10
  import './Combobox.css';
20
- /**
21
- * Default filter function - case-insensitive label search
22
- */
23
- const defaultFilterFn = (option, searchTerm) => {
24
- return option.label.toLowerCase().includes(searchTerm.toLowerCase());
25
- };
26
- /**
27
- * Accessible combobox component with search/filter functionality.
28
- * Provides a prop-based implementation to abstract complexity.
29
- *
30
- * @component
31
- * @example
32
- * ```tsx
33
- * <Combobox
34
- * value={value}
35
- * onValueChange={setValue}
36
- * placeholder="Select option..."
37
- * options={[
38
- * { value: 'option1', label: 'Option 1' },
39
- * { value: 'option2', label: 'Option 2' }
40
- * ]}
41
- * />
42
- * ```
43
- * @param {ComboboxProps} props - The props for the combobox
44
- * @returns {JSX.Element} The rendered combobox
45
- */
46
- const Combobox = ({ value, onValueChange, placeholder = 'Select...', searchPlaceholder = 'Search...', emptyMessage = 'No results found', options = [], trigger, disabled, isLoading, variant, filterFn = defaultFilterFn, multiple = false, label, }) => {
11
+ const Combobox = ({ value, onValueChange, placeholder = 'Select...', emptyMessage = 'No results found', options = [], disabled, isLoading, variant, multiple = false, label, creatable = false, createLabel = 'Create', clearable = false, onOpenChange, onSearchChange, required, }) => {
47
12
  const id = useId();
48
13
  const [open, setOpen] = useState(false);
49
14
  const [search, setSearch] = useState('');
50
15
  const [highlightedIndex, setHighlightedIndex] = useState(0);
16
+ const containerRef = useRef(null);
51
17
  const inputRef = useRef(null);
52
18
  const listRef = useRef(null);
53
- // Normalize value to array for easier handling
54
19
  const selectedValues = useMemo(() => {
55
20
  if (!value)
56
21
  return [];
57
22
  return Array.isArray(value) ? value : [value];
58
23
  }, [value]);
59
- // Filter options based on search
60
24
  const filteredOptions = useMemo(() => {
61
25
  if (!search)
62
26
  return options;
63
- return options.filter((option) => filterFn(option, search));
64
- }, [options, search, filterFn]);
65
- // Reset search and highlighted index when opening
27
+ const searchLower = search.toLowerCase();
28
+ return options.filter((option) => {
29
+ const labelMatch = option.label.toLowerCase().includes(searchLower);
30
+ const descMatch = option.description?.toLowerCase().includes(searchLower);
31
+ return labelMatch || descMatch;
32
+ });
33
+ }, [options, search]);
34
+ const showCreateOption = useMemo(() => {
35
+ if (!creatable || !search.trim())
36
+ return false;
37
+ const exactMatch = options.some((opt) => opt.value.toLowerCase() === search.toLowerCase() ||
38
+ opt.label.toLowerCase() === search.toLowerCase());
39
+ return !exactMatch;
40
+ }, [creatable, search, options]);
41
+ const displayOptions = useMemo(() => {
42
+ if (showCreateOption) {
43
+ return [
44
+ ...filteredOptions,
45
+ {
46
+ value: search.trim(),
47
+ label: `${createLabel} "${search.trim()}"`,
48
+ isCreateOption: true,
49
+ },
50
+ ];
51
+ }
52
+ return filteredOptions;
53
+ }, [filteredOptions, showCreateOption, search, createLabel]);
66
54
  useEffect(() => {
67
55
  if (open) {
68
- setSearch('');
69
56
  setHighlightedIndex(0);
70
- // Focus input when opened
71
- setTimeout(() => inputRef.current?.focus(), 0);
72
57
  }
73
- }, [open]);
74
- // Scroll highlighted item into view
58
+ else if (!multiple) {
59
+ setSearch('');
60
+ }
61
+ }, [open, multiple]);
75
62
  useEffect(() => {
76
63
  if (open && listRef.current) {
77
64
  const highlightedElement = listRef.current.children[highlightedIndex];
@@ -80,6 +67,21 @@ const Combobox = ({ value, onValueChange, placeholder = 'Select...', searchPlace
80
67
  }
81
68
  }
82
69
  }, [highlightedIndex, open]);
70
+ useEffect(() => {
71
+ setHighlightedIndex(0);
72
+ }, [displayOptions.length]);
73
+ useEffect(() => {
74
+ const handleClickOutside = (e) => {
75
+ if (containerRef.current &&
76
+ !containerRef.current.contains(e.target)) {
77
+ setOpen(false);
78
+ }
79
+ };
80
+ if (open) {
81
+ document.addEventListener('mousedown', handleClickOutside);
82
+ return () => document.removeEventListener('mousedown', handleClickOutside);
83
+ }
84
+ }, [open]);
83
85
  const handleSelect = (optionValue) => {
84
86
  if (multiple) {
85
87
  const newValues = selectedValues.includes(optionValue)
@@ -90,30 +92,43 @@ const Combobox = ({ value, onValueChange, placeholder = 'Select...', searchPlace
90
92
  else {
91
93
  onValueChange?.(optionValue);
92
94
  setOpen(false);
95
+ setSearch('');
96
+ inputRef.current?.blur();
93
97
  }
94
98
  };
95
99
  const handleKeyDown = (e) => {
96
- if (!open)
97
- return;
98
100
  switch (e.key) {
99
101
  case 'ArrowDown':
100
102
  e.preventDefault();
101
- setHighlightedIndex((prev) => (prev + 1) % filteredOptions.length);
103
+ if (!open) {
104
+ setOpen(true);
105
+ }
106
+ else {
107
+ setHighlightedIndex((prev) => (prev + 1) % displayOptions.length);
108
+ }
102
109
  break;
103
110
  case 'ArrowUp':
104
111
  e.preventDefault();
105
- setHighlightedIndex((prev) => (prev - 1 + filteredOptions.length) % filteredOptions.length);
112
+ if (open) {
113
+ setHighlightedIndex((prev) => (prev - 1 + displayOptions.length) % displayOptions.length);
114
+ }
106
115
  break;
107
116
  case 'Enter':
108
117
  e.preventDefault();
109
- if (filteredOptions[highlightedIndex] &&
110
- !filteredOptions[highlightedIndex].disabled) {
111
- handleSelect(filteredOptions[highlightedIndex].value);
118
+ if (open &&
119
+ displayOptions[highlightedIndex] &&
120
+ !displayOptions[highlightedIndex].disabled) {
121
+ handleSelect(displayOptions[highlightedIndex].value);
122
+ }
123
+ else if (!open) {
124
+ setOpen(true);
112
125
  }
113
126
  break;
114
127
  case 'Escape':
115
128
  e.preventDefault();
116
129
  setOpen(false);
130
+ setSearch('');
131
+ inputRef.current?.blur();
117
132
  break;
118
133
  }
119
134
  };
@@ -121,43 +136,76 @@ const Combobox = ({ value, onValueChange, placeholder = 'Select...', searchPlace
121
136
  e.stopPropagation();
122
137
  onValueChange?.(multiple ? [] : '');
123
138
  };
124
- // Get display text for selected values
125
- const getDisplayText = () => {
139
+ const getDisplayValue = () => {
140
+ if (open && search)
141
+ return search;
126
142
  if (selectedValues.length === 0)
127
- return placeholder;
128
- if (multiple) {
129
- return `${selectedValues.length} selected`;
130
- }
143
+ return '';
144
+ if (multiple)
145
+ return '';
131
146
  const selectedOption = options.find((opt) => opt.value === selectedValues[0]);
132
- return selectedOption?.label || placeholder;
147
+ return selectedOption?.label || (creatable ? selectedValues[0] : '');
133
148
  };
134
- const displayText = getDisplayText();
149
+ const displayValue = getDisplayValue();
135
150
  const hasSelection = selectedValues.length > 0;
136
- const hasLabel = !!label;
137
- return (_jsxs(ComboboxRootPrimitive, { open: open, onOpenChange: setOpen, children: [_jsx(ComboboxAnchorPrimitive, { asChild: true, children: trigger ? (_jsx(ComboboxTriggerPrimitive, { asChild: true, disabled: disabled, children: trigger })) : (_jsxs("div", { className: "dndev-relative", children: [hasLabel && (_jsx(Label, { htmlFor: id, style: {
138
- position: 'absolute',
139
- left: 'var(--gap-md)',
140
- top: 'calc(-1 * var(--font-size-xs) / 2 - 1px)',
141
- fontSize: 'var(--font-size-xs)',
142
- fontWeight: 500,
143
- color: disabled
144
- ? 'var(--muted-foreground)'
145
- : 'var(--foreground)',
146
- pointerEvents: 'none',
147
- zIndex: 1,
148
- backgroundColor: 'var(--background)',
149
- padding: '0 var(--gap-xs)',
150
- lineHeight: 1,
151
- }, children: label })), _jsx(ComboboxTriggerPrimitive, { id: id, className: "dndev-input dndev-combobox-trigger", disabled: disabled, asChild: true, children: _jsxs("button", { type: "button", children: [isLoading ? (_jsxs("div", { className: "dndev-combobox-loading-container", children: [_jsx("div", { className: "dndev-animate-spin dndev-combobox-loading-spinner" }), _jsx("span", { children: "Loading..." })] })) : (_jsx("span", { className: cn(!hasSelection && 'dndev-combobox-placeholder'), children: displayText })), _jsxs("div", { className: "dndev-combobox-trigger-icons", children: [hasSelection && !disabled && (_jsx("span", { role: "button", tabIndex: 0, onClick: handleClear, onKeyDown: (e) => {
152
- if (e.key === 'Enter' || e.key === ' ') {
153
- e.preventDefault();
154
- handleClear(e);
155
- }
156
- }, className: "dndev-combobox-clear", "aria-label": "Clear selection", children: _jsx(X, {}) })), _jsx(ChevronDown, { className: "dndev-combobox-chevron" })] })] }) })] })) }), _jsxs(ComboboxContentPrimitive, { className: "dndev-floating dndev-menu-content dndev-z-tooltip dndev-combobox-content", sideOffset: 4, align: "start", "data-glow": "blank", onOpenAutoFocus: (e) => e.preventDefault(), children: [_jsx("div", { className: "dndev-combobox-search-container", children: _jsx(Input, { ref: inputRef, value: search, onChange: (e) => setSearch(e.target.value), onKeyDown: handleKeyDown, placeholder: searchPlaceholder, className: "dndev-combobox-search-input" }) }), _jsx("div", { className: "dndev-combobox-list", ref: listRef, children: filteredOptions.length === 0 ? (_jsx("div", { className: "dndev-combobox-empty", children: emptyMessage })) : (filteredOptions.map((option, index) => {
151
+ const handleInputChange = (e) => {
152
+ const newValue = e.target.value;
153
+ setSearch(newValue);
154
+ onSearchChange?.(newValue);
155
+ if (!open)
156
+ setOpen(true);
157
+ };
158
+ const handleInputFocus = () => {
159
+ if (!disabled && !isLoading) {
160
+ setOpen(true);
161
+ }
162
+ };
163
+ const handleInputClick = () => {
164
+ if (!disabled && !isLoading) {
165
+ setOpen(true);
166
+ inputRef.current?.focus();
167
+ }
168
+ };
169
+ const handleOpenChange = (isOpen) => {
170
+ setOpen(isOpen);
171
+ onOpenChange?.(isOpen);
172
+ };
173
+ return (_jsxs("div", { ref: containerRef, className: cn('dndev-relative', open && 'dndev-combobox-open'), style: { width: '100%' }, children: [_jsxs("div", { className: "dndev-relative", children: [_jsx(Input, { ref: inputRef, id: id, label: label, value: displayValue, onChange: handleInputChange, onFocus: handleInputFocus, onClick: handleInputClick, onKeyDown: handleKeyDown, placeholder: placeholder, disabled: disabled || isLoading, icon: isLoading ? _jsx(Spinner, {}) : ChevronDown, iconEnd: true, required: required, "data-variant": variant, style: {
174
+ '--chevron-rotation': open ? '180deg' : '0deg',
175
+ } }), clearable && hasSelection && !disabled && (_jsx("button", { type: "button", onClick: handleClear, className: "dndev-absolute", style: {
176
+ insetInlineEnd: 'calc(var(--gap-md) + var(--icon-md) + var(--gap-sm))',
177
+ top: '50%',
178
+ transform: 'translateY(-50%)',
179
+ background: 'none',
180
+ border: 'none',
181
+ cursor: 'pointer',
182
+ padding: 'var(--gap-tight)',
183
+ display: 'flex',
184
+ alignItems: 'center',
185
+ opacity: 0.5,
186
+ zIndex: 1,
187
+ }, "aria-label": "Clear", children: _jsx(X, { style: {
188
+ width: 'var(--size-icon-sm)',
189
+ height: 'var(--size-icon-sm)',
190
+ } }) }))] }), open && (_jsx("div", { className: "dndev-floating dndev-menu-content dndev-z-tooltip dndev-combobox-content", style: {
191
+ position: 'absolute',
192
+ top: '100%',
193
+ insetInlineStart: 0,
194
+ insetInlineEnd: 0,
195
+ width: '100%',
196
+ marginTop: 'var(--gap-tight)',
197
+ padding: 'var(--gap-tight)',
198
+ textAlign: 'start',
199
+ }, "data-glow": "blank", children: _jsx(ScrollArea, { className: "dndev-menu-scroll-area", end: true, children: _jsx("div", { ref: listRef, style: {
200
+ display: 'flex',
201
+ flexDirection: 'column',
202
+ gap: 'var(--gap-none)',
203
+ }, children: displayOptions.length === 0 ? (_jsx("div", { className: "dndev-combobox-empty", style: { textAlign: 'start' }, children: emptyMessage })) : (displayOptions.map((option, index) => {
157
204
  const isSelected = selectedValues.includes(option.value);
158
205
  const isHighlighted = index === highlightedIndex;
159
- return (_jsx("button", { type: "button", onClick: () => !option.disabled && handleSelect(option.value), disabled: option.disabled, className: cn('dndev-interactive dndev-combobox-option', isHighlighted && 'dndev-combobox-option-highlighted', isSelected && 'dndev-combobox-option-selected'), onMouseEnter: () => setHighlightedIndex(index), children: option.content ? (option.content) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "dndev-combobox-option-content", children: [_jsx("span", { className: "dndev-combobox-option-label", children: option.label }), option.description && (_jsx("span", { className: "dndev-combobox-option-description", children: option.description }))] }), isSelected && (_jsx(Check, { className: "dndev-combobox-option-check" }))] })) }, option.value));
160
- })) })] })] }));
206
+ const isCreateOption = option.isCreateOption;
207
+ return (_jsx("button", { type: "button", onClick: () => !option.disabled && handleSelect(option.value), disabled: option.disabled, className: cn('dndev-interactive dndev-combobox-option', isHighlighted && 'dndev-combobox-option-highlighted', isSelected && 'dndev-combobox-option-selected', isCreateOption && 'dndev-combobox-option-create'), onMouseEnter: () => setHighlightedIndex(index), children: option.content ? (option.content) : (_jsxs(_Fragment, { children: [isCreateOption && (_jsx(Plus, { className: "dndev-combobox-option-create-icon" })), _jsxs("div", { className: "dndev-combobox-option-content", children: [_jsx("span", { className: "dndev-combobox-option-label", children: option.label }), option.description && (_jsx("span", { className: "dndev-combobox-option-description", children: option.description }))] }), isSelected && !isCreateOption && (_jsx(Check, { className: "dndev-combobox-option-check" }))] })) }, isCreateOption ? `create-${option.value}` : option.value));
208
+ })) }) }) }))] }));
161
209
  };
162
210
  export default Combobox;
163
211
  export { CONTROL_VARIANT as COMBOBOX_VARIANT };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/atomic/Dialog/index.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG7E,OAAe,EAAkB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAGL,aAAa,IAAI,gBAAgB,EAQlC,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAU/E,QAAA,MAAM,aAAa,GAAI,0EAOpB,cAAc,CAAC,OAAO,gBAAgB,CAAC,GAAG;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IACrD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC,4CAyBA,CAAC;AAiCF,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AAEtE,MAAM,WAAW,WAAY,SAAQ,IAAI,CACvC,kBAAkB,EAClB,UAAU,GAAG,OAAO,CACrB;IACC,wCAAwC;IACxC,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,mBAAmB;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,qBAAqB;IACrB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,uCAAuC;IACvC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,uCAAuC;IACvC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sIAAsI;IACtI,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,sDAAsD;IACtD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kDAAkD;IAClD,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE;YACP,KAAK,EAAE,MAAM,CAAC;YACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;SACtB,CAAC;QACF,OAAO,CAAC,EAAE;YACR,KAAK,EAAE,MAAM,CAAC;YACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;YACrB,OAAO,CAAC,EAAE,aAAa,CAAC;SACzB,CAAC;KACH,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,QAAA,MAAM,MAAM,GAAI,yIAcb,WAAW,4CA2Eb,CAAC;AAEF,eAAe,MAAM,CAAC;AACtB,OAAO,EAAE,eAAe,IAAI,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC1E,YAAY,EAAE,cAAc,IAAI,aAAa,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/atomic/Dialog/index.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG7E,OAAe,EAAkB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAGL,aAAa,IAAI,gBAAgB,EAQlC,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAU/E,QAAA,MAAM,aAAa,GAAI,0EAOpB,cAAc,CAAC,OAAO,gBAAgB,CAAC,GAAG;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IACrD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC,4CAyBA,CAAC;AAiCF,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AAEtE,MAAM,WAAW,WAAY,SAAQ,IAAI,CACvC,kBAAkB,EAClB,UAAU,GAAG,OAAO,CACrB;IACC,wCAAwC;IACxC,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,mBAAmB;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,qBAAqB;IACrB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,uCAAuC;IACvC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,uCAAuC;IACvC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sIAAsI;IACtI,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,sDAAsD;IACtD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kDAAkD;IAClD,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE;YACP,KAAK,EAAE,MAAM,CAAC;YACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;SACtB,CAAC;QACF,OAAO,CAAC,EAAE;YACR,KAAK,EAAE,MAAM,CAAC;YACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;YACrB,OAAO,CAAC,EAAE,aAAa,CAAC;SACzB,CAAC;KACH,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,QAAA,MAAM,MAAM,GAAI,yIAcb,WAAW,4CAyEb,CAAC;AAEF,eAAe,MAAM,CAAC;AACtB,OAAO,EAAE,eAAe,IAAI,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC1E,YAAY,EAAE,cAAc,IAAI,aAAa,EAAE,MAAM,uBAAuB,CAAC"}
@@ -57,7 +57,7 @@ const Dialog = ({ trigger, title, description, children, footer, open, onOpenCha
57
57
  const variantAttrs = getVariantDataAttrs({
58
58
  variant: variant !== SURFACE_VARIANT.DEFAULT ? variant : undefined,
59
59
  });
60
- return (_jsxs(Root, { open: open, onOpenChange: onOpenChange, children: [trigger && _jsx(Trigger, { asChild: true, children: trigger }), _jsxs(DialogContent, { className: cn('dndev-surface', className), noSwipe: noSwipe, closeButtonRef: closeButtonRef, onOpenChange: onOpenChange, ...variantAttrs, ...contentProps, children: [title ? (_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: title }), description && (_jsx(DialogDescription, { className: "dndev-sr-only", children: description }))] })) : (_jsxs(_Fragment, { children: [_jsx(DialogTitle, { className: "dndev-sr-only", children: "Dialog" }), _jsx(DialogDescription, { className: "dndev-sr-only", children: description || 'Dialog content' })] })), _jsx("div", { className: "dndev-modal-body", children: children }), (footer || actions) && (_jsxs(DialogFooter, { children: [footer, actions && (_jsxs(_Fragment, { children: [actions.cancel && (_jsx(Close, { asChild: true, children: _jsx(Button, { variant: BUTTON_VARIANT.OUTLINE, onClick: actions.cancel.onClick, children: actions.cancel.label }) })), actions.confirm && (_jsx(Button, { variant: actions.confirm.variant || BUTTON_VARIANT.PRIMARY, onClick: actions.confirm.onClick, children: actions.confirm.label }))] }))] })), showClose && (_jsx(Close, { ref: closeButtonRef, asChild: true, children: _jsx(Button, { variant: BUTTON_VARIANT.GHOST, icon: X, className: "dndev-modal-close", "aria-label": "Close" }) }))] })] }));
60
+ return (_jsxs(Root, { open: open, onOpenChange: onOpenChange, children: [trigger && _jsx(Trigger, { asChild: true, children: trigger }), _jsxs(DialogContent, { className: cn('dndev-surface dndev-modal-content', className), noSwipe: noSwipe, closeButtonRef: closeButtonRef, onOpenChange: onOpenChange, ...variantAttrs, ...contentProps, children: [title ? (_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { className: "dndev-sr-only", children: description || 'Dialog content' })] })) : (_jsxs(_Fragment, { children: [_jsx(DialogTitle, { className: "dndev-sr-only", children: "Dialog" }), _jsx(DialogDescription, { className: "dndev-sr-only", children: description || 'Dialog content' })] })), _jsx("div", { className: "dndev-modal-body", children: children }), (footer || actions) && (_jsxs(DialogFooter, { children: [footer, actions && (_jsxs(_Fragment, { children: [actions.cancel && (_jsx(Close, { asChild: true, children: _jsx(Button, { variant: BUTTON_VARIANT.OUTLINE, onClick: actions.cancel.onClick, children: actions.cancel.label }) })), actions.confirm && (_jsx(Button, { variant: actions.confirm.variant || BUTTON_VARIANT.PRIMARY, onClick: actions.confirm.onClick, children: actions.confirm.label }))] }))] })), showClose && (_jsx(Close, { ref: closeButtonRef, asChild: true, children: _jsx(Button, { variant: BUTTON_VARIANT.GHOST, icon: X, className: "dndev-modal-close", "aria-label": "Close" }) }))] })] }));
61
61
  };
62
62
  export default Dialog;
63
63
  export { SURFACE_VARIANT as DIALOG_VARIANT } from '../../utils/constants';
@@ -112,6 +112,6 @@ const DropdownMenu = ({ trigger, items = [], children, contentWidth, contentAlig
112
112
  'data-state': 'checked',
113
113
  }), children: normalItem.children ? (_jsxs(_Fragment, { children: [normalItem.children, IconEnd && (_jsx(IconEnd, { className: "dndev-dropdown-menu-trailing" }))] })) : (_jsxs(_Fragment, { children: [Icon && _jsx(Icon, {}), _jsx("span", { children: normalItem.label }), IconEnd && (_jsx(IconEnd, { className: "dndev-dropdown-menu-trailing" })), normalItem.checked && (_jsx(Check, { className: "dndev-dropdown-menu-checkmark dndev-dropdown-menu-trailing" }))] })) }, index));
114
114
  })] }));
115
- return (_jsxs(DropdownMenuPrimitive, { open: open, onOpenChange: onOpenChange, children: [_jsx(DropdownMenuTriggerPrimitive, { asChild: true, children: trigger }), _jsx(DropdownMenuContentPrimitive, { align: contentAlign, className: "dndev-floating dndev-menu-content dndev-z-tooltip", style: contentWidth ? { width: contentWidth } : undefined, children: _jsx(ScrollArea, { className: "dndev-dropdown-scroll-area", end: true, children: content }) })] }));
115
+ return (_jsxs(DropdownMenuPrimitive, { open: open, onOpenChange: onOpenChange, children: [_jsx(DropdownMenuTriggerPrimitive, { asChild: true, children: trigger }), _jsx(DropdownMenuContentPrimitive, { align: contentAlign, className: "dndev-floating dndev-menu-content dndev-z-tooltip", style: contentWidth ? { width: contentWidth } : undefined, children: _jsx(ScrollArea, { className: "dndev-menu-scroll-area", end: true, children: content }) })] }));
116
116
  };
117
117
  export default DropdownMenu;
@@ -32,6 +32,6 @@ export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
32
32
  * @param {InputProps} props - The props for the input
33
33
  * @returns {JSX.Element} The rendered input
34
34
  */
35
- declare const Input: ({ className, type, icon, iconEnd, label, value, onFocus, onBlur, ...props }: InputProps) => import("react/jsx-runtime").JSX.Element;
35
+ declare const Input: ({ className, type, icon, iconEnd, label, value, onFocus, onBlur, required, ...props }: InputProps) => import("react/jsx-runtime").JSX.Element;
36
36
  export default Input;
37
37
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/atomic/Input/index.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAEtD,MAAM,WAAW,UAAW,SAAQ,mBAAmB,CAAC,gBAAgB,CAAC;IACvE,gDAAgD;IAChD,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,GAAG,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,QAAA,MAAM,KAAK,GAAI,6EAUZ,UAAU,4CA4EZ,CAAC;AAEF,eAAe,KAAK,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/atomic/Input/index.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAEtD,MAAM,WAAW,UAAW,SAAQ,mBAAmB,CAAC,gBAAgB,CAAC;IACvE,gDAAgD;IAChD,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,GAAG,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,QAAA,MAAM,KAAK,GAAI,uFAWZ,UAAU,4CA+DZ,CAAC;AAEF,eAAe,KAAK,CAAC"}
@@ -11,7 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  import { useState, useId } from 'react';
12
12
  import { cn } from '../../utils/helpers';
13
13
  import Icon from '../Icons/Icon';
14
- import Label from '../Label';
14
+ import { FloatingLabel } from '../Label';
15
15
  import './Input.css';
16
16
  /**
17
17
  * Accessible input component with mobile-friendly touch targets.
@@ -31,36 +31,20 @@ import './Input.css';
31
31
  * @param {InputProps} props - The props for the input
32
32
  * @returns {JSX.Element} The rendered input
33
33
  */
34
- const Input = ({ className, type, icon, iconEnd = false, label, value, onFocus, onBlur, ...props }) => {
34
+ const Input = ({ className, type, icon, iconEnd = false, label, value, onFocus, onBlur, required, ...props }) => {
35
35
  const id = useId();
36
36
  const hasIcon = !!icon;
37
37
  const isLeading = !iconEnd;
38
38
  const isTrailing = iconEnd;
39
39
  const hasLabel = !!label;
40
40
  const inputClasses = cn('dndev-input', hasIcon && isLeading && 'dndev-input-with-leading-icon', hasIcon && isTrailing && 'dndev-input-with-trailing-icon', className);
41
- const inputElement = (_jsx("input", { id: id, type: type, value: value, className: inputClasses, ...props }));
41
+ // Explicitly exclude children to prevent void element error (input is a void element)
42
+ const { children: _children, ...inputProps } = props;
43
+ const inputElement = (_jsx("input", { id: id, type: type, value: value, required: required, className: inputClasses, ...inputProps }));
42
44
  const needsWrapper = hasIcon || hasLabel;
43
45
  if (!needsWrapper) {
44
46
  return inputElement;
45
47
  }
46
- return (_jsxs("div", { className: "dndev-relative", children: [hasLabel && (_jsx(Label, { htmlFor: id, style: {
47
- position: 'absolute',
48
- left: 'var(--gap-md)',
49
- top: 'calc(-1 * var(--font-size-xs) / 2 - 1px)',
50
- fontSize: 'var(--font-size-xs)',
51
- fontWeight: 500,
52
- color: props.disabled
53
- ? 'var(--muted-foreground)'
54
- : 'var(--foreground)',
55
- pointerEvents: 'none',
56
- zIndex: 1,
57
- backgroundColor: 'var(--background)',
58
- padding: '0 var(--gap-xs)',
59
- lineHeight: 1,
60
- maxWidth: 'calc(100% - var(--gap-md) * 2)',
61
- overflow: 'hidden',
62
- textOverflow: 'ellipsis',
63
- whiteSpace: 'nowrap',
64
- }, children: label })), inputElement, hasIcon && (_jsx("div", { className: cn('dndev-input-icon', isLeading && 'dndev-input-icon-leading', isTrailing && 'dndev-input-icon-trailing'), children: _jsx(Icon, { icon: icon, className: "dndev-size-md", ariaHidden: true }) }))] }));
48
+ return (_jsxs("div", { className: "dndev-relative", children: [hasLabel && (_jsx(FloatingLabel, { htmlFor: id, disabled: props.disabled, required: required, children: label })), inputElement, hasIcon && (_jsx("div", { className: cn('dndev-input-icon', isLeading && 'dndev-input-icon-leading', isTrailing && 'dndev-input-icon-trailing'), children: _jsx(Icon, { icon: icon, className: "dndev-size-md", ariaHidden: true }) }))] }));
65
49
  };
66
50
  export default Input;
@@ -0,0 +1,31 @@
1
+ import './Label.css';
2
+ import type { ReactNode } from 'react';
3
+ export interface FloatingLabelProps {
4
+ /** HTML id to associate label with input */
5
+ htmlFor: string;
6
+ /** Label text or content */
7
+ children: ReactNode;
8
+ /** Whether the input is disabled */
9
+ disabled?: boolean;
10
+ /** Whether to truncate long labels with ellipsis */
11
+ truncate?: boolean;
12
+ /** Whether the field is required - shows asterisk indicator */
13
+ required?: boolean;
14
+ }
15
+ /**
16
+ * FloatingLabel - Shared floating label component
17
+ *
18
+ * Positioned absolutely at the top of input fields.
19
+ * Uses transparent background to adapt to any parent container (dropdowns, cards, etc.)
20
+ *
21
+ * @component
22
+ * @example
23
+ * ```tsx
24
+ * <FloatingLabel htmlFor="email" disabled={false} truncate>
25
+ * Email Address
26
+ * </FloatingLabel>
27
+ * ```
28
+ */
29
+ export declare function FloatingLabel({ htmlFor, children, disabled, truncate, required, }: FloatingLabelProps): import("react/jsx-runtime").JSX.Element;
30
+ export default FloatingLabel;
31
+ //# sourceMappingURL=FloatingLabel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FloatingLabel.d.ts","sourceRoot":"","sources":["../../../src/atomic/Label/FloatingLabel.tsx"],"names":[],"mappings":"AAaA,OAAO,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,WAAW,kBAAkB;IACjC,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,EACR,QAAgB,EAChB,QAAgB,EAChB,QAAgB,GACjB,EAAE,kBAAkB,2CAsBpB;AAED,eAAe,aAAa,CAAC"}