@douglasneuroinformatics/libui 6.2.1 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ComboBox/ComboBox.d.ts +27 -0
- package/dist/components/ComboBox/ComboBox.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBox.js +37 -0
- package/dist/components/ComboBox/ComboBox.js.map +1 -0
- package/dist/components/ComboBox/ComboBoxChips.d.ts +9 -0
- package/dist/components/ComboBox/ComboBoxChips.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBoxChips.js +17 -0
- package/dist/components/ComboBox/ComboBoxChips.js.map +1 -0
- package/dist/components/ComboBox/ComboBoxClear.d.ts +4 -0
- package/dist/components/ComboBox/ComboBoxClear.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBoxClear.js +10 -0
- package/dist/components/ComboBox/ComboBoxClear.js.map +1 -0
- package/dist/components/ComboBox/ComboBoxCollection.d.ts +4 -0
- package/dist/components/ComboBox/ComboBoxCollection.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBoxCollection.js +7 -0
- package/dist/components/ComboBox/ComboBoxCollection.js.map +1 -0
- package/dist/components/ComboBox/ComboBoxContent.d.ts +4 -0
- package/dist/components/ComboBox/ComboBoxContent.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBoxContent.js +8 -0
- package/dist/components/ComboBox/ComboBoxContent.js.map +1 -0
- package/dist/components/ComboBox/ComboBoxEmpty.d.ts +4 -0
- package/dist/components/ComboBox/ComboBoxEmpty.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBoxEmpty.js +8 -0
- package/dist/components/ComboBox/ComboBoxEmpty.js.map +1 -0
- package/dist/components/ComboBox/ComboBoxGroup.d.ts +4 -0
- package/dist/components/ComboBox/ComboBoxGroup.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBoxGroup.js +8 -0
- package/dist/components/ComboBox/ComboBoxGroup.js.map +1 -0
- package/dist/components/ComboBox/ComboBoxInput.d.ts +7 -0
- package/dist/components/ComboBox/ComboBoxInput.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBoxInput.js +17 -0
- package/dist/components/ComboBox/ComboBoxInput.js.map +1 -0
- package/dist/components/ComboBox/ComboBoxItem.d.ts +4 -0
- package/dist/components/ComboBox/ComboBoxItem.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBoxItem.js +9 -0
- package/dist/components/ComboBox/ComboBoxItem.js.map +1 -0
- package/dist/components/ComboBox/ComboBoxLabel.d.ts +4 -0
- package/dist/components/ComboBox/ComboBoxLabel.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBoxLabel.js +8 -0
- package/dist/components/ComboBox/ComboBoxLabel.js.map +1 -0
- package/dist/components/ComboBox/ComboBoxList.d.ts +4 -0
- package/dist/components/ComboBox/ComboBoxList.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBoxList.js +8 -0
- package/dist/components/ComboBox/ComboBoxList.js.map +1 -0
- package/dist/components/ComboBox/ComboBoxSeparator.d.ts +4 -0
- package/dist/components/ComboBox/ComboBoxSeparator.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBoxSeparator.js +8 -0
- package/dist/components/ComboBox/ComboBoxSeparator.js.map +1 -0
- package/dist/components/ComboBox/ComboBoxTrigger.d.ts +4 -0
- package/dist/components/ComboBox/ComboBoxTrigger.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBoxTrigger.js +9 -0
- package/dist/components/ComboBox/ComboBoxTrigger.js.map +1 -0
- package/dist/components/ComboBox/ComboBoxValue.d.ts +4 -0
- package/dist/components/ComboBox/ComboBoxValue.d.ts.map +1 -0
- package/dist/components/ComboBox/ComboBoxValue.js +7 -0
- package/dist/components/ComboBox/ComboBoxValue.js.map +1 -0
- package/dist/components/InputGroup/InputGroup.d.ts +4 -0
- package/dist/components/InputGroup/InputGroup.d.ts.map +1 -0
- package/dist/components/InputGroup/InputGroup.js +8 -0
- package/dist/components/InputGroup/InputGroup.js.map +1 -0
- package/dist/components/InputGroup/InputGroupAddon.d.ts +8 -0
- package/dist/components/InputGroup/InputGroupAddon.d.ts.map +1 -0
- package/dist/components/InputGroup/InputGroupAddon.js +23 -0
- package/dist/components/InputGroup/InputGroupAddon.js.map +1 -0
- package/dist/components/InputGroup/InputGroupButton.d.ts +9 -0
- package/dist/components/InputGroup/InputGroupButton.d.ts.map +1 -0
- package/dist/components/InputGroup/InputGroupButton.js +29 -0
- package/dist/components/InputGroup/InputGroupButton.js.map +1 -0
- package/dist/components/InputGroup/InputGroupInput.d.ts +4 -0
- package/dist/components/InputGroup/InputGroupInput.d.ts.map +1 -0
- package/dist/components/InputGroup/InputGroupInput.js +9 -0
- package/dist/components/InputGroup/InputGroupInput.js.map +1 -0
- package/dist/components/InputGroup/InputGroupText.d.ts +4 -0
- package/dist/components/InputGroup/InputGroupText.d.ts.map +1 -0
- package/dist/components/InputGroup/InputGroupText.js +8 -0
- package/dist/components/InputGroup/InputGroupText.js.map +1 -0
- package/dist/components/InputGroup/InputGroupTextArea.d.ts +4 -0
- package/dist/components/InputGroup/InputGroupTextArea.d.ts.map +1 -0
- package/dist/components/InputGroup/InputGroupTextArea.js +9 -0
- package/dist/components/InputGroup/InputGroupTextArea.js.map +1 -0
- package/package.json +2 -1
- package/src/components/ComboBox/ComboBox.stories.tsx +31 -0
- package/src/components/ComboBox/ComboBox.tsx +41 -0
- package/src/components/ComboBox/ComboBoxChips.tsx +69 -0
- package/src/components/ComboBox/ComboBoxClear.tsx +23 -0
- package/src/components/ComboBox/ComboBoxCollection.tsx +7 -0
- package/src/components/ComboBox/ComboBoxContent.tsx +39 -0
- package/src/components/ComboBox/ComboBoxEmpty.tsx +18 -0
- package/src/components/ComboBox/ComboBoxGroup.tsx +9 -0
- package/src/components/ComboBox/ComboBoxInput.tsx +51 -0
- package/src/components/ComboBox/ComboBoxItem.tsx +28 -0
- package/src/components/ComboBox/ComboBoxLabel.tsx +15 -0
- package/src/components/ComboBox/ComboBoxList.tsx +18 -0
- package/src/components/ComboBox/ComboBoxSeparator.tsx +15 -0
- package/src/components/ComboBox/ComboBoxTrigger.tsx +23 -0
- package/src/components/ComboBox/ComboBoxValue.tsx +7 -0
- package/src/components/InputGroup/InputGroup.stories.tsx +27 -0
- package/src/components/InputGroup/InputGroup.tsx +19 -0
- package/src/components/InputGroup/InputGroupAddon.tsx +42 -0
- package/src/components/InputGroup/InputGroupButton.tsx +50 -0
- package/src/components/InputGroup/InputGroupInput.tsx +20 -0
- package/src/components/InputGroup/InputGroupText.tsx +17 -0
- package/src/components/InputGroup/InputGroupTextArea.tsx +20 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InputGroupText.d.ts","sourceRoot":"","sources":["../../../src/components/InputGroup/InputGroupText.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,QAAA,MAAM,cAAc,GAAI,yBAAyB,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,4CAU5E,CAAC;AAEF,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { cn } from '#utils';
|
|
4
|
+
const InputGroupText = ({ className, ...props }) => {
|
|
5
|
+
return (_jsx("span", { className: cn("text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4", className), ...props }));
|
|
6
|
+
};
|
|
7
|
+
export { InputGroupText };
|
|
8
|
+
//# sourceMappingURL=InputGroupText.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InputGroupText.js","sourceRoot":"","sources":["../../../src/components/InputGroup/InputGroupText.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE5B,MAAM,cAAc,GAAG,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAgC,EAAE,EAAE;IAC/E,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,wHAAwH,EACxH,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAC;AACJ,CAAC,CAAC;AAEF,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InputGroupTextArea.d.ts","sourceRoot":"","sources":["../../../src/components/InputGroup/InputGroupTextArea.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,QAAA,MAAM,kBAAkB,GAAI,yBAAyB,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,4CAWpF,CAAC;AAEF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { cn } from '#utils';
|
|
4
|
+
import { TextArea } from "../TextArea/TextArea.js";
|
|
5
|
+
const InputGroupTextArea = ({ className, ...props }) => {
|
|
6
|
+
return (_jsx(TextArea, { className: cn('flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent', className), "data-slot": "input-group-control", ...props }));
|
|
7
|
+
};
|
|
8
|
+
export { InputGroupTextArea };
|
|
9
|
+
//# sourceMappingURL=InputGroupTextArea.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InputGroupTextArea.js","sourceRoot":"","sources":["../../../src/components/InputGroup/InputGroupTextArea.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAA0B,CAAC;AAEpD,MAAM,kBAAkB,GAAG,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAoC,EAAE,EAAE;IACvF,OAAO,CACL,KAAC,QAAQ,IACP,SAAS,EAAE,EAAE,CACX,mMAAmM,EACnM,SAAS,CACV,eACS,qBAAqB,KAC3B,KAAK,GACT,CACH,CAAC;AACJ,CAAC,CAAC;AAEF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@douglasneuroinformatics/libui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "6.
|
|
4
|
+
"version": "6.3.0",
|
|
5
5
|
"packageManager": "pnpm@10.7.1",
|
|
6
6
|
"description": "Generic UI components for DNP projects, built using React and Tailwind CSS",
|
|
7
7
|
"author": "Joshua Unrau",
|
|
@@ -97,6 +97,7 @@
|
|
|
97
97
|
"zod": "^3.25.x"
|
|
98
98
|
},
|
|
99
99
|
"dependencies": {
|
|
100
|
+
"@base-ui/react": "^1.2.0",
|
|
100
101
|
"@douglasneuroinformatics/libjs": "^3.0.2",
|
|
101
102
|
"@douglasneuroinformatics/libui-form-types": "^0.11.0",
|
|
102
103
|
"@radix-ui/react-accordion": "^1.2.3",
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { ComboBox } from './ComboBox.tsx';
|
|
4
|
+
|
|
5
|
+
type Story = StoryObj<typeof ComboBox>;
|
|
6
|
+
|
|
7
|
+
const frameworks: string[] = ['Next.js', 'SvelteKit', 'Nuxt.js', 'Remix', 'Astro'];
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
args: {
|
|
11
|
+
children: (
|
|
12
|
+
<ComboBox items={frameworks}>
|
|
13
|
+
<ComboBox.Input placeholder="Select a framework" />
|
|
14
|
+
<ComboBox.Content>
|
|
15
|
+
<ComboBox.Empty>No items found.</ComboBox.Empty>
|
|
16
|
+
<ComboBox.List>
|
|
17
|
+
{(item: string) => (
|
|
18
|
+
<ComboBox.Item key={item} value={item}>
|
|
19
|
+
{item}
|
|
20
|
+
</ComboBox.Item>
|
|
21
|
+
)}
|
|
22
|
+
</ComboBox.List>
|
|
23
|
+
</ComboBox.Content>
|
|
24
|
+
</ComboBox>
|
|
25
|
+
)
|
|
26
|
+
},
|
|
27
|
+
component: ComboBox,
|
|
28
|
+
tags: ['autodocs']
|
|
29
|
+
} as Meta<typeof ComboBox>;
|
|
30
|
+
|
|
31
|
+
export const Default: Story = {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react';
|
|
4
|
+
|
|
5
|
+
import { ComboboxChip, ComboboxChips, ComboboxChipsInput } from './ComboBoxChips.tsx';
|
|
6
|
+
import { ComboboxClear } from './ComboBoxClear.tsx';
|
|
7
|
+
import { ComboboxCollection } from './ComboBoxCollection.tsx';
|
|
8
|
+
import { ComboboxContent } from './ComboBoxContent.tsx';
|
|
9
|
+
import { ComboboxEmpty } from './ComboBoxEmpty.tsx';
|
|
10
|
+
import { ComboboxGroup } from './ComboBoxGroup.tsx';
|
|
11
|
+
import { ComboboxInput } from './ComboBoxInput.tsx';
|
|
12
|
+
import { ComboboxItem } from './ComboBoxItem.tsx';
|
|
13
|
+
import { ComboboxLabel } from './ComboBoxLabel.tsx';
|
|
14
|
+
import { ComboboxList } from './ComboBoxList.tsx';
|
|
15
|
+
import { ComboboxSeparator } from './ComboBoxSeparator.tsx';
|
|
16
|
+
import { ComboboxTrigger } from './ComboBoxTrigger.tsx';
|
|
17
|
+
import { ComboboxValue } from './ComboBoxValue.tsx';
|
|
18
|
+
|
|
19
|
+
function useComboboxAnchor() {
|
|
20
|
+
return React.useRef<HTMLDivElement | null>(null);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { useComboboxAnchor };
|
|
24
|
+
|
|
25
|
+
export const ComboBox = Object.assign(ComboboxPrimitive.Root.bind(null), {
|
|
26
|
+
Chip: ComboboxChip,
|
|
27
|
+
Chips: ComboboxChips,
|
|
28
|
+
ChipsInput: ComboboxChipsInput,
|
|
29
|
+
Clear: ComboboxClear,
|
|
30
|
+
Collection: ComboboxCollection,
|
|
31
|
+
Content: ComboboxContent,
|
|
32
|
+
Empty: ComboboxEmpty,
|
|
33
|
+
Group: ComboboxGroup,
|
|
34
|
+
Input: ComboboxInput,
|
|
35
|
+
Item: ComboboxItem,
|
|
36
|
+
Label: ComboboxLabel,
|
|
37
|
+
List: ComboboxList,
|
|
38
|
+
Separator: ComboboxSeparator,
|
|
39
|
+
Trigger: ComboboxTrigger,
|
|
40
|
+
Value: ComboboxValue
|
|
41
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react';
|
|
4
|
+
import { XIcon } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
import { cn } from '#utils';
|
|
7
|
+
|
|
8
|
+
import { Button } from '../Button/Button.tsx';
|
|
9
|
+
|
|
10
|
+
const ComboboxChips = ({
|
|
11
|
+
className,
|
|
12
|
+
...props
|
|
13
|
+
}: ComboboxPrimitive.Chips.Props & React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips>) => {
|
|
14
|
+
return (
|
|
15
|
+
<ComboboxPrimitive.Chips
|
|
16
|
+
className={cn(
|
|
17
|
+
'dark:bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive dark:has-aria-invalid:border-destructive/50 flex min-h-8 flex-wrap items-center gap-1 rounded-lg border bg-transparent bg-clip-padding px-2.5 py-1 text-sm transition-colors focus-within:ring-3 has-aria-invalid:ring-3 has-data-[slot=combobox-chip]:px-1',
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
data-slot="combobox-chips"
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const ComboboxChip = ({
|
|
27
|
+
children,
|
|
28
|
+
className,
|
|
29
|
+
showRemove = true,
|
|
30
|
+
...props
|
|
31
|
+
}: ComboboxPrimitive.Chip.Props & {
|
|
32
|
+
showRemove?: boolean;
|
|
33
|
+
}) => {
|
|
34
|
+
return (
|
|
35
|
+
<ComboboxPrimitive.Chip
|
|
36
|
+
className={cn(
|
|
37
|
+
'bg-muted text-foreground flex h-[calc(--spacing(5.25))] w-fit items-center justify-center gap-1 rounded-sm px-1.5 text-xs font-medium whitespace-nowrap has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0',
|
|
38
|
+
className
|
|
39
|
+
)}
|
|
40
|
+
data-slot="combobox-chip"
|
|
41
|
+
{...props}
|
|
42
|
+
>
|
|
43
|
+
{children}
|
|
44
|
+
{showRemove && (
|
|
45
|
+
<ComboboxPrimitive.ChipRemove
|
|
46
|
+
className="-ml-1 opacity-50 hover:opacity-100"
|
|
47
|
+
data-slot="combobox-chip-remove"
|
|
48
|
+
render={
|
|
49
|
+
<Button size="sm" variant="ghost">
|
|
50
|
+
<XIcon className="pointer-events-none" />
|
|
51
|
+
</Button>
|
|
52
|
+
}
|
|
53
|
+
/>
|
|
54
|
+
)}
|
|
55
|
+
</ComboboxPrimitive.Chip>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const ComboboxChipsInput = ({ className, ...props }: ComboboxPrimitive.Input.Props) => {
|
|
60
|
+
return (
|
|
61
|
+
<ComboboxPrimitive.Input
|
|
62
|
+
className={cn('min-w-16 flex-1 outline-none', className)}
|
|
63
|
+
data-slot="combobox-chip-input"
|
|
64
|
+
{...props}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export { ComboboxChip, ComboboxChips, ComboboxChipsInput };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react';
|
|
2
|
+
import { XIcon } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
import { cn } from '#utils';
|
|
5
|
+
|
|
6
|
+
import { InputGroupButton } from '../InputGroup/InputGroupButton.tsx';
|
|
7
|
+
|
|
8
|
+
const ComboboxClear = ({ className, ...props }: ComboboxPrimitive.Clear.Props) => {
|
|
9
|
+
return (
|
|
10
|
+
<ComboboxPrimitive.Clear
|
|
11
|
+
className={cn(className)}
|
|
12
|
+
data-slot="combobox-clear"
|
|
13
|
+
{...props}
|
|
14
|
+
render={
|
|
15
|
+
<InputGroupButton size="icon-xs" variant="ghost">
|
|
16
|
+
<XIcon className="pointer-events-none" />
|
|
17
|
+
</InputGroupButton>
|
|
18
|
+
}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export { ComboboxClear };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react';
|
|
2
|
+
|
|
3
|
+
const ComboboxCollection = ({ ...props }: ComboboxPrimitive.Collection.Props) => {
|
|
4
|
+
return <ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} />;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export { ComboboxCollection };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '#utils';
|
|
4
|
+
|
|
5
|
+
const ComboboxContent = ({
|
|
6
|
+
align = 'start',
|
|
7
|
+
alignOffset = 0,
|
|
8
|
+
anchor,
|
|
9
|
+
className,
|
|
10
|
+
side = 'bottom',
|
|
11
|
+
sideOffset = 6,
|
|
12
|
+
...props
|
|
13
|
+
}: ComboboxPrimitive.Popup.Props &
|
|
14
|
+
Pick<ComboboxPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'anchor' | 'side' | 'sideOffset'>) => {
|
|
15
|
+
return (
|
|
16
|
+
<ComboboxPrimitive.Portal>
|
|
17
|
+
<ComboboxPrimitive.Positioner
|
|
18
|
+
align={align}
|
|
19
|
+
alignOffset={alignOffset}
|
|
20
|
+
anchor={anchor}
|
|
21
|
+
className="isolate z-50"
|
|
22
|
+
side={side}
|
|
23
|
+
sideOffset={sideOffset}
|
|
24
|
+
>
|
|
25
|
+
<ComboboxPrimitive.Popup
|
|
26
|
+
className={cn(
|
|
27
|
+
'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:border-input/30 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-lg shadow-md ring-1 duration-100 data-[chips=true]:min-w-(--anchor-width) *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:shadow-none',
|
|
28
|
+
className
|
|
29
|
+
)}
|
|
30
|
+
data-chips={!!anchor}
|
|
31
|
+
data-slot="combobox-content"
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
</ComboboxPrimitive.Positioner>
|
|
35
|
+
</ComboboxPrimitive.Portal>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export { ComboboxContent };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '#utils';
|
|
4
|
+
|
|
5
|
+
const ComboboxEmpty = ({ className, ...props }: ComboboxPrimitive.Empty.Props) => {
|
|
6
|
+
return (
|
|
7
|
+
<ComboboxPrimitive.Empty
|
|
8
|
+
className={cn(
|
|
9
|
+
'text-muted-foreground hidden w-full justify-center py-2 text-center text-sm group-data-empty/combobox-content:flex',
|
|
10
|
+
className
|
|
11
|
+
)}
|
|
12
|
+
data-slot="combobox-empty"
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export { ComboboxEmpty };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '#utils';
|
|
4
|
+
|
|
5
|
+
const ComboboxGroup = ({ className, ...props }: ComboboxPrimitive.Group.Props) => {
|
|
6
|
+
return <ComboboxPrimitive.Group className={cn(className)} data-slot="combobox-group" {...props} />;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export { ComboboxGroup };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react';
|
|
2
|
+
import { ChevronDownIcon } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
import { cn } from '#utils';
|
|
5
|
+
|
|
6
|
+
import { InputGroup } from '../InputGroup/InputGroup.tsx';
|
|
7
|
+
import { InputGroupAddon } from '../InputGroup/InputGroupAddon.tsx';
|
|
8
|
+
import { InputGroupButton } from '../InputGroup/InputGroupButton.tsx';
|
|
9
|
+
import { InputGroupInput } from '../InputGroup/InputGroupInput.tsx';
|
|
10
|
+
import { ComboboxClear } from './ComboBoxClear.tsx';
|
|
11
|
+
import { ComboboxTrigger } from './ComboBoxTrigger.tsx';
|
|
12
|
+
|
|
13
|
+
const ComboboxInput = ({
|
|
14
|
+
children,
|
|
15
|
+
className,
|
|
16
|
+
disabled = false,
|
|
17
|
+
showClear = false,
|
|
18
|
+
showTrigger = true,
|
|
19
|
+
...props
|
|
20
|
+
}: ComboboxPrimitive.Input.Props & {
|
|
21
|
+
showClear?: boolean;
|
|
22
|
+
showTrigger?: boolean;
|
|
23
|
+
}) => {
|
|
24
|
+
return (
|
|
25
|
+
<InputGroup className={cn('w-auto', className)}>
|
|
26
|
+
<ComboboxPrimitive.Input render={<InputGroupInput disabled={disabled} />} {...props} />
|
|
27
|
+
<InputGroupAddon align="inline-end">
|
|
28
|
+
{showTrigger && (
|
|
29
|
+
<ComboboxTrigger
|
|
30
|
+
disabled={disabled}
|
|
31
|
+
/* Now this works because ComboboxTrigger is expecting 'render' */
|
|
32
|
+
render={
|
|
33
|
+
<InputGroupButton
|
|
34
|
+
className="group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent"
|
|
35
|
+
data-slot="input-group-button"
|
|
36
|
+
size="icon-xs"
|
|
37
|
+
variant="ghost"
|
|
38
|
+
>
|
|
39
|
+
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-4" />
|
|
40
|
+
</InputGroupButton>
|
|
41
|
+
}
|
|
42
|
+
/>
|
|
43
|
+
)}
|
|
44
|
+
{showClear && <ComboboxClear disabled={disabled} />}
|
|
45
|
+
</InputGroupAddon>
|
|
46
|
+
{children}
|
|
47
|
+
</InputGroup>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export { ComboboxInput };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react';
|
|
2
|
+
import { CheckIcon } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
import { cn } from '#utils';
|
|
5
|
+
|
|
6
|
+
const ComboboxItem = ({ children, className, ...props }: ComboboxPrimitive.Item.Props) => {
|
|
7
|
+
return (
|
|
8
|
+
<ComboboxPrimitive.Item
|
|
9
|
+
className={cn(
|
|
10
|
+
"data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground relative flex w-full cursor-default items-center gap-2 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
11
|
+
className
|
|
12
|
+
)}
|
|
13
|
+
data-slot="combobox-item"
|
|
14
|
+
{...props}
|
|
15
|
+
>
|
|
16
|
+
{children}
|
|
17
|
+
<ComboboxPrimitive.ItemIndicator
|
|
18
|
+
render={
|
|
19
|
+
<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center">
|
|
20
|
+
<CheckIcon className="pointer-events-none" />
|
|
21
|
+
</span>
|
|
22
|
+
}
|
|
23
|
+
/>
|
|
24
|
+
</ComboboxPrimitive.Item>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export { ComboboxItem };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '#utils';
|
|
4
|
+
|
|
5
|
+
const ComboboxLabel = ({ className, ...props }: ComboboxPrimitive.GroupLabel.Props) => {
|
|
6
|
+
return (
|
|
7
|
+
<ComboboxPrimitive.GroupLabel
|
|
8
|
+
className={cn('text-muted-foreground px-2 py-1.5 text-xs', className)}
|
|
9
|
+
data-slot="combobox-label"
|
|
10
|
+
{...props}
|
|
11
|
+
/>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export { ComboboxLabel };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '#utils';
|
|
4
|
+
|
|
5
|
+
const ComboboxList = ({ className, ...props }: ComboboxPrimitive.List.Props) => {
|
|
6
|
+
return (
|
|
7
|
+
<ComboboxPrimitive.List
|
|
8
|
+
className={cn(
|
|
9
|
+
'no-scrollbar max-h-[min(calc(--spacing(72)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto overscroll-contain p-1 data-empty:p-0',
|
|
10
|
+
className
|
|
11
|
+
)}
|
|
12
|
+
data-slot="combobox-list"
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export { ComboboxList };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '#utils';
|
|
4
|
+
|
|
5
|
+
const ComboboxSeparator = ({ className, ...props }: ComboboxPrimitive.Separator.Props) => {
|
|
6
|
+
return (
|
|
7
|
+
<ComboboxPrimitive.Separator
|
|
8
|
+
className={cn('bg-border -mx-1 my-1 h-px', className)}
|
|
9
|
+
data-slot="combobox-separator"
|
|
10
|
+
{...props}
|
|
11
|
+
/>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export { ComboboxSeparator };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react';
|
|
2
|
+
import { ChevronDownIcon } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
import { cn } from '#utils';
|
|
5
|
+
|
|
6
|
+
const ComboboxTrigger = ({ children, className, render, ...props }: ComboboxPrimitive.Trigger.Props) => {
|
|
7
|
+
return (
|
|
8
|
+
<ComboboxPrimitive.Trigger
|
|
9
|
+
className={cn("[&_svg:not([class*='size-'])]:size-4", className)}
|
|
10
|
+
data-slot="combobox-trigger"
|
|
11
|
+
render={render} // This allows it to "become" the InputGroupButton
|
|
12
|
+
{...props}
|
|
13
|
+
>
|
|
14
|
+
{children}
|
|
15
|
+
{/* We move the icon here so it's always included,
|
|
16
|
+
unless you prefer passing it as children manually.
|
|
17
|
+
*/}
|
|
18
|
+
{!render && <ChevronDownIcon className="text-muted-foreground pointer-events-none size-4" />}
|
|
19
|
+
</ComboboxPrimitive.Trigger>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export { ComboboxTrigger };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { SearchIcon } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
import { InputGroup } from './InputGroup.tsx';
|
|
5
|
+
import { InputGroupAddon } from './InputGroupAddon.tsx';
|
|
6
|
+
import { InputGroupInput } from './InputGroupInput.tsx';
|
|
7
|
+
|
|
8
|
+
type Story = StoryObj<typeof InputGroup>;
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
args: {
|
|
12
|
+
children: (
|
|
13
|
+
<>
|
|
14
|
+
<InputGroup>
|
|
15
|
+
<InputGroupInput placeholder="Search..." />
|
|
16
|
+
<InputGroupAddon>
|
|
17
|
+
<SearchIcon />
|
|
18
|
+
</InputGroupAddon>
|
|
19
|
+
</InputGroup>
|
|
20
|
+
</>
|
|
21
|
+
)
|
|
22
|
+
},
|
|
23
|
+
component: InputGroup,
|
|
24
|
+
tags: ['autodocs']
|
|
25
|
+
} as Meta<typeof InputGroup>;
|
|
26
|
+
|
|
27
|
+
export const Default: Story = {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '#utils';
|
|
4
|
+
|
|
5
|
+
const InputGroup = ({ className, ...props }: React.ComponentProps<'div'>) => {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
className={cn(
|
|
9
|
+
'border-input dark:bg-input/30 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-disabled:bg-input/50 dark:has-disabled:bg-input/80 group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-3 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5',
|
|
10
|
+
className
|
|
11
|
+
)}
|
|
12
|
+
data-slot="input-group"
|
|
13
|
+
role="group"
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export { InputGroup };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cva } from 'class-variance-authority';
|
|
4
|
+
import type { VariantProps } from 'class-variance-authority';
|
|
5
|
+
|
|
6
|
+
import { cn } from '#utils';
|
|
7
|
+
|
|
8
|
+
const inputGroupAddonVariants = cva(
|
|
9
|
+
"text-muted-foreground h-auto gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4 flex cursor-text items-center justify-center select-none",
|
|
10
|
+
{
|
|
11
|
+
defaultVariants: {
|
|
12
|
+
align: 'inline-start'
|
|
13
|
+
},
|
|
14
|
+
variants: {
|
|
15
|
+
align: {
|
|
16
|
+
'block-end': 'px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2 order-last w-full justify-start',
|
|
17
|
+
'block-start':
|
|
18
|
+
'px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2 order-first w-full justify-start',
|
|
19
|
+
'inline-end': 'pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem] order-last',
|
|
20
|
+
'inline-start': 'pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem] order-first'
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
//ensure that Group encapsulates the input and the addon
|
|
27
|
+
const InputGroupAddon = ({
|
|
28
|
+
align = 'inline-start',
|
|
29
|
+
className,
|
|
30
|
+
...props
|
|
31
|
+
}: React.ComponentProps<'div'> & VariantProps<typeof inputGroupAddonVariants>) => {
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
className={cn(inputGroupAddonVariants({ align }), className)}
|
|
35
|
+
data-align={align}
|
|
36
|
+
data-slot="input-group-addon"
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export { InputGroupAddon };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cva } from 'class-variance-authority';
|
|
4
|
+
import type { VariantProps } from 'class-variance-authority';
|
|
5
|
+
|
|
6
|
+
import { cn } from '#utils';
|
|
7
|
+
|
|
8
|
+
import { Button } from '../Button/Button.tsx';
|
|
9
|
+
|
|
10
|
+
const inputGroupButtonVariants = cva('gap-2 text-sm flex items-center shadow-none', {
|
|
11
|
+
defaultVariants: {
|
|
12
|
+
size: 'xs'
|
|
13
|
+
},
|
|
14
|
+
variants: {
|
|
15
|
+
size: {
|
|
16
|
+
'icon-sm': 'size-8 p-0 has-[>svg]:p-0',
|
|
17
|
+
'icon-xs': 'size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0',
|
|
18
|
+
sm: '',
|
|
19
|
+
xs: "h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const buttonSizeMap = {
|
|
25
|
+
'icon-sm': 'icon',
|
|
26
|
+
'icon-xs': 'icon',
|
|
27
|
+
sm: 'sm',
|
|
28
|
+
xs: 'sm'
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
const InputGroupButton = ({
|
|
32
|
+
className,
|
|
33
|
+
size = 'xs',
|
|
34
|
+
type = 'button',
|
|
35
|
+
variant = 'ghost',
|
|
36
|
+
...props
|
|
37
|
+
}: Omit<React.ComponentProps<typeof Button>, 'size'> & VariantProps<typeof inputGroupButtonVariants>) => {
|
|
38
|
+
return (
|
|
39
|
+
<Button
|
|
40
|
+
className={cn(inputGroupButtonVariants({ size }), className)}
|
|
41
|
+
data-size={size}
|
|
42
|
+
size={buttonSizeMap[size ?? 'xs']}
|
|
43
|
+
type={type}
|
|
44
|
+
variant={variant}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export { InputGroupButton };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '#utils';
|
|
4
|
+
|
|
5
|
+
import { Input } from '../Input/Input.tsx';
|
|
6
|
+
|
|
7
|
+
const InputGroupInput = ({ className, ...props }: React.ComponentProps<'input'>) => {
|
|
8
|
+
return (
|
|
9
|
+
<Input
|
|
10
|
+
className={cn(
|
|
11
|
+
'flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent',
|
|
12
|
+
className
|
|
13
|
+
)}
|
|
14
|
+
data-slot="input-group-control"
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { InputGroupInput };
|