@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.
- package/dist/atomic/Button/index.d.ts.map +1 -1
- package/dist/atomic/Button/index.js +5 -2
- package/dist/atomic/Calendar/index.js +1 -1
- package/dist/atomic/Collapsible/index.d.ts.map +1 -1
- package/dist/atomic/Collapsible/index.js +1 -5
- package/dist/atomic/Combobox/index.d.ts +9 -47
- package/dist/atomic/Combobox/index.d.ts.map +1 -1
- package/dist/atomic/Combobox/index.js +135 -87
- package/dist/atomic/Dialog/index.d.ts.map +1 -1
- package/dist/atomic/Dialog/index.js +1 -1
- package/dist/atomic/DropdownMenu/index.js +1 -1
- package/dist/atomic/Input/index.d.ts +1 -1
- package/dist/atomic/Input/index.d.ts.map +1 -1
- package/dist/atomic/Input/index.js +6 -22
- package/dist/atomic/Label/FloatingLabel.d.ts +31 -0
- package/dist/atomic/Label/FloatingLabel.d.ts.map +1 -0
- package/dist/atomic/Label/FloatingLabel.js +34 -0
- package/dist/atomic/Label/index.d.ts +5 -1
- package/dist/atomic/Label/index.d.ts.map +1 -1
- package/dist/atomic/Label/index.js +8 -2
- package/dist/atomic/PasswordInput/index.d.ts +9 -10
- package/dist/atomic/PasswordInput/index.d.ts.map +1 -1
- package/dist/atomic/PasswordInput/index.js +10 -30
- package/dist/atomic/Section/index.d.ts.map +1 -1
- package/dist/atomic/Section/index.js +3 -4
- package/dist/atomic/Select/index.d.ts +3 -1
- package/dist/atomic/Select/index.d.ts.map +1 -1
- package/dist/atomic/Select/index.js +13 -24
- package/dist/atomic/Skeleton/index.d.ts.map +1 -1
- package/dist/atomic/Skeleton/index.js +6 -3
- package/dist/atomic/Switch/index.d.ts +18 -4
- package/dist/atomic/Switch/index.d.ts.map +1 -1
- package/dist/atomic/Switch/index.js +25 -6
- package/dist/atomic/Table/index.d.ts +17 -7
- package/dist/atomic/Table/index.d.ts.map +1 -1
- package/dist/atomic/Table/index.js +13 -3
- package/dist/atomic/index.d.ts +2 -3
- package/dist/atomic/index.d.ts.map +1 -1
- package/dist/atomic/index.js +1 -2
- package/dist/index.js +4 -4
- package/dist/styles/index.css +335 -56
- package/package.json +1 -1
- package/dist/atomic/Combobox/ComboboxPrimitive.d.ts +0 -18
- package/dist/atomic/Combobox/ComboboxPrimitive.d.ts.map +0 -1
- 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,
|
|
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
|
-
...
|
|
107
|
+
...restProps,
|
|
105
108
|
};
|
|
106
109
|
// Render prop pattern - no cloneElement needed (React 19 compatible)
|
|
107
|
-
const buttonElement = render ? (render(elementProps)) : (_jsx("button", { type:
|
|
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-
|
|
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,
|
|
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":"
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
125
|
-
|
|
139
|
+
const getDisplayValue = () => {
|
|
140
|
+
if (open && search)
|
|
141
|
+
return search;
|
|
126
142
|
if (selectedValues.length === 0)
|
|
127
|
-
return
|
|
128
|
-
if (multiple)
|
|
129
|
-
return
|
|
130
|
-
}
|
|
143
|
+
return '';
|
|
144
|
+
if (multiple)
|
|
145
|
+
return '';
|
|
131
146
|
const selectedOption = options.find((opt) => opt.value === selectedValues[0]);
|
|
132
|
-
return selectedOption?.label ||
|
|
147
|
+
return selectedOption?.label || (creatable ? selectedValues[0] : '');
|
|
133
148
|
};
|
|
134
|
-
const
|
|
149
|
+
const displayValue = getDisplayValue();
|
|
135
150
|
const hasSelection = selectedValues.length > 0;
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
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,
|
|
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 }),
|
|
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-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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(
|
|
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"}
|