@shipfox/react-ui 0.13.0 → 0.15.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/.storybook/preview.tsx +7 -0
- package/.turbo/turbo-build.log +7 -7
- package/.turbo/turbo-check.log +2 -2
- package/.turbo/turbo-type.log +1 -1
- package/CHANGELOG.md +16 -0
- package/dist/components/avatar/avatar.js +1 -1
- package/dist/components/avatar/avatar.js.map +1 -1
- package/dist/components/button-group/button-group.d.ts +17 -0
- package/dist/components/button-group/button-group.d.ts.map +1 -0
- package/dist/components/button-group/button-group.js +74 -0
- package/dist/components/button-group/button-group.js.map +1 -0
- package/dist/components/button-group/button-group.stories.js +644 -0
- package/dist/components/button-group/button-group.stories.js.map +1 -0
- package/dist/components/button-group/index.d.ts +2 -0
- package/dist/components/button-group/index.d.ts.map +1 -0
- package/dist/components/button-group/index.js +3 -0
- package/dist/components/button-group/index.js.map +1 -0
- package/dist/components/code-block/code-block-footer.d.ts.map +1 -1
- package/dist/components/code-block/code-block-footer.js +13 -5
- package/dist/components/code-block/code-block-footer.js.map +1 -1
- package/dist/components/command/command.d.ts +28 -0
- package/dist/components/command/command.d.ts.map +1 -0
- package/dist/components/command/command.js +190 -0
- package/dist/components/command/command.js.map +1 -0
- package/dist/components/command/command.stories.js +228 -0
- package/dist/components/command/command.stories.js.map +1 -0
- package/dist/components/command/index.d.ts +2 -0
- package/dist/components/command/index.d.ts.map +1 -0
- package/dist/components/command/index.js +3 -0
- package/dist/components/command/index.js.map +1 -0
- package/dist/components/confetti/confetti.d.ts +21 -0
- package/dist/components/confetti/confetti.d.ts.map +1 -0
- package/dist/components/confetti/confetti.js +101 -0
- package/dist/components/confetti/confetti.js.map +1 -0
- package/dist/components/confetti/confetti.stories.js +41 -0
- package/dist/components/confetti/confetti.stories.js.map +1 -0
- package/dist/components/confetti/index.d.ts +2 -0
- package/dist/components/confetti/index.d.ts.map +1 -0
- package/dist/components/confetti/index.js +3 -0
- package/dist/components/confetti/index.js.map +1 -0
- package/dist/components/dashboard/components/analytics-content.d.ts +2 -0
- package/dist/components/dashboard/components/analytics-content.d.ts.map +1 -0
- package/dist/components/dashboard/components/analytics-content.js +180 -0
- package/dist/components/dashboard/components/analytics-content.js.map +1 -0
- package/dist/components/dashboard/components/animated-logo.d.ts +4 -0
- package/dist/components/dashboard/components/animated-logo.d.ts.map +1 -0
- package/dist/components/dashboard/components/animated-logo.js +23 -0
- package/dist/components/dashboard/components/animated-logo.js.map +1 -0
- package/dist/components/dashboard/components/complete-setup-button.d.ts +4 -0
- package/dist/components/dashboard/components/complete-setup-button.d.ts.map +1 -0
- package/dist/components/dashboard/components/complete-setup-button.js +28 -0
- package/dist/components/dashboard/components/complete-setup-button.js.map +1 -0
- package/dist/components/dashboard/components/jobs-content.d.ts +2 -0
- package/dist/components/dashboard/components/jobs-content.d.ts.map +1 -0
- package/dist/components/dashboard/components/jobs-content.js +69 -0
- package/dist/components/dashboard/components/jobs-content.js.map +1 -0
- package/dist/components/dashboard/components/mobile-menu.d.ts +2 -0
- package/dist/components/dashboard/components/mobile-menu.d.ts.map +1 -0
- package/dist/components/dashboard/components/mobile-menu.js +65 -0
- package/dist/components/dashboard/components/mobile-menu.js.map +1 -0
- package/dist/components/dashboard/components/organization-selector.d.ts +2 -0
- package/dist/components/dashboard/components/organization-selector.d.ts.map +1 -0
- package/dist/components/dashboard/components/organization-selector.js +92 -0
- package/dist/components/dashboard/components/organization-selector.js.map +1 -0
- package/dist/components/dashboard/components/top-menu.d.ts +5 -0
- package/dist/components/dashboard/components/top-menu.d.ts.map +1 -0
- package/dist/components/dashboard/components/top-menu.js +31 -0
- package/dist/components/dashboard/components/top-menu.js.map +1 -0
- package/dist/components/dashboard/components/topbar-button.d.ts +7 -0
- package/dist/components/dashboard/components/topbar-button.d.ts.map +1 -0
- package/dist/components/dashboard/components/topbar-button.js +18 -0
- package/dist/components/dashboard/components/topbar-button.js.map +1 -0
- package/dist/components/dashboard/components/topbar.d.ts +4 -0
- package/dist/components/dashboard/components/topbar.d.ts.map +1 -0
- package/dist/components/dashboard/components/topbar.js +62 -0
- package/dist/components/dashboard/components/topbar.js.map +1 -0
- package/dist/components/dashboard/components/user-profile.d.ts +2 -0
- package/dist/components/dashboard/components/user-profile.d.ts.map +1 -0
- package/dist/components/dashboard/components/user-profile.js +146 -0
- package/dist/components/dashboard/components/user-profile.js.map +1 -0
- package/dist/components/dashboard/dashboard.d.ts +2 -0
- package/dist/components/dashboard/dashboard.d.ts.map +1 -0
- package/dist/components/dashboard/dashboard.js +70 -0
- package/dist/components/dashboard/dashboard.js.map +1 -0
- package/dist/components/dashboard/dashboard.stories.js +23 -0
- package/dist/components/dashboard/dashboard.stories.js.map +1 -0
- package/dist/components/dashboard/index.d.ts +2 -0
- package/dist/components/dashboard/index.d.ts.map +1 -0
- package/dist/components/dashboard/index.js +3 -0
- package/dist/components/dashboard/index.js.map +1 -0
- package/dist/components/form/form.stories.js +6 -1
- package/dist/components/form/form.stories.js.map +1 -1
- package/dist/components/icon/icon.d.ts +3 -2
- package/dist/components/icon/icon.d.ts.map +1 -1
- package/dist/components/icon/icon.js +7 -2
- package/dist/components/icon/icon.js.map +1 -1
- package/dist/components/index.d.ts +9 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +9 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/kbd/index.d.ts +2 -0
- package/dist/components/kbd/index.d.ts.map +1 -0
- package/dist/components/kbd/index.js +3 -0
- package/dist/components/kbd/index.js.map +1 -0
- package/dist/components/kbd/kbd.d.ts +7 -0
- package/dist/components/kbd/kbd.d.ts.map +1 -0
- package/dist/components/kbd/kbd.js +18 -0
- package/dist/components/kbd/kbd.js.map +1 -0
- package/dist/components/kbd/kbd.stories.js +119 -0
- package/dist/components/kbd/kbd.stories.js.map +1 -0
- package/dist/components/modal/modal.stories.js +227 -168
- package/dist/components/modal/modal.stories.js.map +1 -1
- package/dist/components/search/index.d.ts +7 -0
- package/dist/components/search/index.d.ts.map +1 -0
- package/dist/components/search/index.js +8 -0
- package/dist/components/search/index.js.map +1 -0
- package/dist/components/search/search-context.d.ts +11 -0
- package/dist/components/search/search-context.d.ts.map +1 -0
- package/dist/components/search/search-context.js +56 -0
- package/dist/components/search/search-context.js.map +1 -0
- package/dist/components/search/search-inline.d.ts +9 -0
- package/dist/components/search/search-inline.d.ts.map +1 -0
- package/dist/components/search/search-inline.js +85 -0
- package/dist/components/search/search-inline.js.map +1 -0
- package/dist/components/search/search-modal.d.ts +25 -0
- package/dist/components/search/search-modal.d.ts.map +1 -0
- package/dist/components/search/search-modal.js +162 -0
- package/dist/components/search/search-modal.js.map +1 -0
- package/dist/components/search/search-trigger.d.ts +9 -0
- package/dist/components/search/search-trigger.d.ts.map +1 -0
- package/dist/components/search/search-trigger.js +37 -0
- package/dist/components/search/search-trigger.js.map +1 -0
- package/dist/components/search/search-variants.d.ts +14 -0
- package/dist/components/search/search-variants.d.ts.map +1 -0
- package/dist/components/search/search-variants.js +90 -0
- package/dist/components/search/search-variants.js.map +1 -0
- package/dist/components/search/search.d.ts +11 -0
- package/dist/components/search/search.d.ts.map +1 -0
- package/dist/components/search/search.js +35 -0
- package/dist/components/search/search.js.map +1 -0
- package/dist/components/search/search.stories.js +630 -0
- package/dist/components/search/search.stories.js.map +1 -0
- package/dist/components/select/index.d.ts +2 -0
- package/dist/components/select/index.d.ts.map +1 -0
- package/dist/components/select/index.js +3 -0
- package/dist/components/select/index.js.map +1 -0
- package/dist/components/select/select.d.ts +25 -0
- package/dist/components/select/select.d.ts.map +1 -0
- package/dist/components/select/select.js +153 -0
- package/dist/components/select/select.js.map +1 -0
- package/dist/components/select/select.stories.js +393 -0
- package/dist/components/select/select.stories.js.map +1 -0
- package/dist/components/shiny-text/index.d.ts +2 -0
- package/dist/components/shiny-text/index.d.ts.map +1 -0
- package/dist/components/shiny-text/index.js +3 -0
- package/dist/components/shiny-text/index.js.map +1 -0
- package/dist/components/shiny-text/shiny-text.d.ts +10 -0
- package/dist/components/shiny-text/shiny-text.d.ts.map +1 -0
- package/dist/components/shiny-text/shiny-text.js +17 -0
- package/dist/components/shiny-text/shiny-text.js.map +1 -0
- package/dist/components/skeleton/index.d.ts +2 -0
- package/dist/components/skeleton/index.d.ts.map +1 -0
- package/dist/components/skeleton/index.js +3 -0
- package/dist/components/skeleton/index.js.map +1 -0
- package/dist/components/skeleton/skeleton.d.ts +5 -0
- package/dist/components/skeleton/skeleton.d.ts.map +1 -0
- package/dist/components/skeleton/skeleton.js +11 -0
- package/dist/components/skeleton/skeleton.js.map +1 -0
- package/dist/components/skeleton/skeleton.stories.js +345 -0
- package/dist/components/skeleton/skeleton.stories.js.map +1 -0
- package/dist/components/table/data-table.d.ts +70 -0
- package/dist/components/table/data-table.d.ts.map +1 -0
- package/dist/components/table/data-table.js +159 -0
- package/dist/components/table/data-table.js.map +1 -0
- package/dist/components/table/index.d.ts +6 -0
- package/dist/components/table/index.d.ts.map +1 -0
- package/dist/components/table/index.js +6 -0
- package/dist/components/table/index.js.map +1 -0
- package/dist/components/table/table-column-header.d.ts +79 -0
- package/dist/components/table/table-column-header.d.ts.map +1 -0
- package/dist/components/table/table-column-header.js +99 -0
- package/dist/components/table/table-column-header.js.map +1 -0
- package/dist/components/table/table-pagination.d.ts +53 -0
- package/dist/components/table/table-pagination.d.ts.map +1 -0
- package/dist/components/table/table-pagination.js +139 -0
- package/dist/components/table/table-pagination.js.map +1 -0
- package/dist/components/table/table.d.ts +11 -0
- package/dist/components/table/table.d.ts.map +1 -0
- package/dist/components/table/table.js +64 -0
- package/dist/components/table/table.js.map +1 -0
- package/dist/components/table/table.stories.columns.d.ts +24 -0
- package/dist/components/table/table.stories.columns.d.ts.map +1 -0
- package/dist/components/table/table.stories.columns.js +310 -0
- package/dist/components/table/table.stories.columns.js.map +1 -0
- package/dist/components/table/table.stories.components.d.ts +14 -0
- package/dist/components/table/table.stories.components.d.ts.map +1 -0
- package/dist/components/table/table.stories.components.js +107 -0
- package/dist/components/table/table.stories.components.js.map +1 -0
- package/dist/components/table/table.stories.data.d.ts +54 -0
- package/dist/components/table/table.stories.data.d.ts.map +1 -0
- package/dist/components/table/table.stories.data.js +122 -0
- package/dist/components/table/table.stories.data.js.map +1 -0
- package/dist/components/table/table.stories.js +302 -0
- package/dist/components/table/table.stories.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/index.css +79 -0
- package/package.json +6 -2
- package/src/components/avatar/avatar.tsx +1 -1
- package/src/components/button-group/button-group.stories.tsx +361 -0
- package/src/components/button-group/button-group.tsx +111 -0
- package/src/components/button-group/index.ts +1 -0
- package/src/components/code-block/code-block-footer.tsx +19 -2
- package/src/components/command/command.stories.tsx +133 -0
- package/src/components/command/command.tsx +265 -0
- package/src/components/command/index.ts +1 -0
- package/src/components/confetti/confetti.stories.tsx +38 -0
- package/src/components/confetti/confetti.tsx +140 -0
- package/src/components/confetti/index.ts +1 -0
- package/src/components/dashboard/components/analytics-content.tsx +102 -0
- package/src/components/dashboard/components/animated-logo.tsx +25 -0
- package/src/components/dashboard/components/complete-setup-button.tsx +30 -0
- package/src/components/dashboard/components/jobs-content.tsx +51 -0
- package/src/components/dashboard/components/mobile-menu.tsx +50 -0
- package/src/components/dashboard/components/organization-selector.tsx +51 -0
- package/src/components/dashboard/components/top-menu.tsx +26 -0
- package/src/components/dashboard/components/topbar-button.tsx +27 -0
- package/src/components/dashboard/components/topbar.tsx +40 -0
- package/src/components/dashboard/components/user-profile.tsx +90 -0
- package/src/components/dashboard/dashboard.stories.tsx +25 -0
- package/src/components/dashboard/dashboard.tsx +61 -0
- package/src/components/dashboard/index.ts +1 -0
- package/src/components/form/form.stories.tsx +5 -0
- package/src/components/icon/icon.tsx +7 -3
- package/src/components/index.ts +9 -0
- package/src/components/kbd/index.ts +1 -0
- package/src/components/kbd/kbd.stories.tsx +64 -0
- package/src/components/kbd/kbd.tsx +32 -0
- package/src/components/modal/modal.stories.tsx +58 -4
- package/src/components/search/index.ts +28 -0
- package/src/components/search/search-context.tsx +78 -0
- package/src/components/search/search-inline.tsx +107 -0
- package/src/components/search/search-modal.tsx +198 -0
- package/src/components/search/search-trigger.tsx +47 -0
- package/src/components/search/search-variants.ts +88 -0
- package/src/components/search/search.stories.tsx +392 -0
- package/src/components/search/search.tsx +47 -0
- package/src/components/select/index.ts +1 -0
- package/src/components/select/select.stories.tsx +207 -0
- package/src/components/select/select.tsx +220 -0
- package/src/components/shiny-text/index.ts +1 -0
- package/src/components/shiny-text/shiny-text.tsx +21 -0
- package/src/components/skeleton/index.ts +1 -0
- package/src/components/skeleton/skeleton.stories.tsx +178 -0
- package/src/components/skeleton/skeleton.tsx +14 -0
- package/src/components/table/data-table.tsx +254 -0
- package/src/components/table/index.ts +5 -0
- package/src/components/table/table-column-header.tsx +141 -0
- package/src/components/table/table-pagination.tsx +161 -0
- package/src/components/table/table.stories.columns.tsx +198 -0
- package/src/components/table/table.stories.components.tsx +104 -0
- package/src/components/table/table.stories.data.ts +117 -0
- package/src/components/table/table.stories.tsx +256 -0
- package/src/components/table/table.tsx +95 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import type {Meta, StoryObj} from '@storybook/react';
|
|
2
|
+
import {useState} from 'react';
|
|
3
|
+
import {Button} from '../button';
|
|
4
|
+
import {Icon} from '../icon';
|
|
5
|
+
import {Input} from '../input';
|
|
6
|
+
import {Popover, PopoverContent, PopoverTrigger} from '../popover';
|
|
7
|
+
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '../select';
|
|
8
|
+
import {Textarea} from '../textarea';
|
|
9
|
+
import {Code, Header} from '../typography';
|
|
10
|
+
import {ButtonGroup, ButtonGroupSeparator, ButtonGroupText} from './button-group';
|
|
11
|
+
|
|
12
|
+
const meta = {
|
|
13
|
+
title: 'Components/ButtonGroup',
|
|
14
|
+
component: ButtonGroup,
|
|
15
|
+
tags: ['autodocs'],
|
|
16
|
+
parameters: {
|
|
17
|
+
docs: {
|
|
18
|
+
description: {
|
|
19
|
+
component:
|
|
20
|
+
'A container that groups related buttons together with consistent styling. Automatically styles Button, Input, Select, and Textarea children without requiring manual className overrides. Separators are recommended for visual hierarchy.',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
argTypes: {
|
|
25
|
+
orientation: {
|
|
26
|
+
control: 'select',
|
|
27
|
+
options: ['horizontal', 'vertical'],
|
|
28
|
+
description: 'The orientation of the button group',
|
|
29
|
+
defaultValue: 'horizontal',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
} satisfies Meta<typeof ButtonGroup>;
|
|
33
|
+
|
|
34
|
+
export default meta;
|
|
35
|
+
|
|
36
|
+
type Story = StoryObj<typeof meta>;
|
|
37
|
+
|
|
38
|
+
function SelectExample() {
|
|
39
|
+
const [currency, setCurrency] = useState('$');
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="inline-flex gap-8">
|
|
43
|
+
<ButtonGroup className="w-280" aria-label="Currency converter">
|
|
44
|
+
<Select value={currency} onValueChange={setCurrency}>
|
|
45
|
+
<SelectTrigger
|
|
46
|
+
className="w-80 font-mono text-foreground-neutral-subtle"
|
|
47
|
+
aria-label="Select currency"
|
|
48
|
+
>
|
|
49
|
+
<SelectValue />
|
|
50
|
+
</SelectTrigger>
|
|
51
|
+
<SelectContent align="start">
|
|
52
|
+
<SelectItem value="$">$ USD</SelectItem>
|
|
53
|
+
<SelectItem value="€">€ EUR</SelectItem>
|
|
54
|
+
<SelectItem value="£">£ GBP</SelectItem>
|
|
55
|
+
<SelectItem value="¥">¥ JPY</SelectItem>
|
|
56
|
+
</SelectContent>
|
|
57
|
+
</Select>
|
|
58
|
+
<ButtonGroupSeparator />
|
|
59
|
+
<Input placeholder="10.00" pattern="[0-9]*" aria-label="Amount" />
|
|
60
|
+
</ButtonGroup>
|
|
61
|
+
<ButtonGroup aria-label="Send action">
|
|
62
|
+
<Button variant="secondary" aria-label="Send">
|
|
63
|
+
<Icon name="arrowRightLine" className="size-16 text-foreground-neutral-subtle" />
|
|
64
|
+
</Button>
|
|
65
|
+
</ButtonGroup>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function PopoverExample() {
|
|
71
|
+
const [open, setOpen] = useState(false);
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<ButtonGroup aria-label="Copilot actions">
|
|
75
|
+
<ButtonGroupText>
|
|
76
|
+
<Icon name="sparklingLine" className="size-16" />
|
|
77
|
+
Copilot
|
|
78
|
+
</ButtonGroupText>
|
|
79
|
+
<ButtonGroupSeparator />
|
|
80
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
81
|
+
<PopoverTrigger asChild>
|
|
82
|
+
<Button
|
|
83
|
+
variant="secondary"
|
|
84
|
+
size="sm"
|
|
85
|
+
className="!text-foreground-neutral-subtle"
|
|
86
|
+
aria-label="Open Copilot options"
|
|
87
|
+
>
|
|
88
|
+
<Icon name="arrowDownSLine" className="size-16" />
|
|
89
|
+
</Button>
|
|
90
|
+
</PopoverTrigger>
|
|
91
|
+
<PopoverContent align="end" className="w-320 p-0 rounded-12">
|
|
92
|
+
<div className="px-16 py-12 border-b border-border-neutral-strong">
|
|
93
|
+
<Header variant="h4" className="text-sm font-medium">
|
|
94
|
+
Agent Tasks
|
|
95
|
+
</Header>
|
|
96
|
+
</div>
|
|
97
|
+
<div className="p-16 flex flex-col gap-12">
|
|
98
|
+
<Textarea
|
|
99
|
+
placeholder="Describe your task in natural language."
|
|
100
|
+
className="min-h-80 resize-none"
|
|
101
|
+
aria-label="Task description"
|
|
102
|
+
/>
|
|
103
|
+
<div className="flex flex-col gap-8">
|
|
104
|
+
<Code variant="label" className="font-medium text-foreground-neutral-base">
|
|
105
|
+
Start a new task with Copilot
|
|
106
|
+
</Code>
|
|
107
|
+
<Code variant="paragraph" className="text-foreground-neutral-subtle text-xs">
|
|
108
|
+
Describe your task in natural language. Copilot will work in the background and open
|
|
109
|
+
a pull request for your review.
|
|
110
|
+
</Code>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</PopoverContent>
|
|
114
|
+
</Popover>
|
|
115
|
+
</ButtonGroup>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const Default: Story = {
|
|
120
|
+
render: () => (
|
|
121
|
+
<div className="flex flex-col gap-24">
|
|
122
|
+
{/* Basic Button Group */}
|
|
123
|
+
<div className="flex flex-col gap-8">
|
|
124
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
125
|
+
Basic Button Group
|
|
126
|
+
</Code>
|
|
127
|
+
<ButtonGroup aria-label="Text alignment">
|
|
128
|
+
<Button variant="secondary" size="sm" aria-label="Align left">
|
|
129
|
+
<Icon name="rewindFill" className="size-14 text-foreground-neutral-subtle" />
|
|
130
|
+
</Button>
|
|
131
|
+
<ButtonGroupSeparator />
|
|
132
|
+
<Button variant="secondary" size="sm" aria-label="Align center">
|
|
133
|
+
<Icon name="playFill" className="size-14 text-foreground-neutral-subtle" />
|
|
134
|
+
</Button>
|
|
135
|
+
<ButtonGroupSeparator />
|
|
136
|
+
<Button variant="secondary" size="sm" aria-label="Align right">
|
|
137
|
+
<Icon name="speedFill" className="size-14 text-foreground-neutral-subtle" />
|
|
138
|
+
</Button>
|
|
139
|
+
</ButtonGroup>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
{/* Sizes */}
|
|
143
|
+
<div className="flex flex-col gap-12">
|
|
144
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
145
|
+
Sizes
|
|
146
|
+
</Code>
|
|
147
|
+
<div className="flex flex-col gap-8">
|
|
148
|
+
<ButtonGroup aria-label="Small buttons">
|
|
149
|
+
<Button variant="secondary" size="sm" aria-label="Cut">
|
|
150
|
+
<Icon name="rewindFill" className="size-14 text-foreground-neutral-subtle" />
|
|
151
|
+
</Button>
|
|
152
|
+
<ButtonGroupSeparator />
|
|
153
|
+
<Button variant="secondary" size="sm" aria-label="Copy">
|
|
154
|
+
<Icon name="playFill" className="size-14 text-foreground-neutral-subtle" />
|
|
155
|
+
</Button>
|
|
156
|
+
<ButtonGroupSeparator />
|
|
157
|
+
<Button variant="secondary" size="sm" aria-label="Paste">
|
|
158
|
+
<Icon name="speedFill" className="size-14 text-foreground-neutral-subtle" />
|
|
159
|
+
</Button>
|
|
160
|
+
</ButtonGroup>
|
|
161
|
+
<ButtonGroup aria-label="Medium buttons">
|
|
162
|
+
<Button variant="secondary" size="md" aria-label="Cut">
|
|
163
|
+
<Icon name="rewindFill" className="size-16 text-foreground-neutral-subtle" />
|
|
164
|
+
</Button>
|
|
165
|
+
<ButtonGroupSeparator />
|
|
166
|
+
<Button variant="secondary" size="md" aria-label="Copy">
|
|
167
|
+
<Icon name="playFill" className="size-16 text-foreground-neutral-subtle" />
|
|
168
|
+
</Button>
|
|
169
|
+
<ButtonGroupSeparator />
|
|
170
|
+
<Button variant="secondary" size="md" aria-label="Paste">
|
|
171
|
+
<Icon name="speedFill" className="size-16 text-foreground-neutral-subtle" />
|
|
172
|
+
</Button>
|
|
173
|
+
</ButtonGroup>
|
|
174
|
+
<ButtonGroup aria-label="Large buttons">
|
|
175
|
+
<Button variant="secondary" size="lg" aria-label="Cut">
|
|
176
|
+
<Icon name="rewindFill" className="size-16 text-foreground-neutral-subtle" />
|
|
177
|
+
</Button>
|
|
178
|
+
<ButtonGroupSeparator />
|
|
179
|
+
<Button variant="secondary" size="lg" aria-label="Copy">
|
|
180
|
+
<Icon name="playFill" className="size-16 text-foreground-neutral-subtle" />
|
|
181
|
+
</Button>
|
|
182
|
+
<ButtonGroupSeparator />
|
|
183
|
+
<Button variant="secondary" size="lg" aria-label="Paste">
|
|
184
|
+
<Icon name="speedFill" className="size-16 text-foreground-neutral-subtle" />
|
|
185
|
+
</Button>
|
|
186
|
+
</ButtonGroup>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
{/* Orientation */}
|
|
191
|
+
<div className="flex flex-col gap-8">
|
|
192
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
193
|
+
Orientation
|
|
194
|
+
</Code>
|
|
195
|
+
<div className="flex gap-24 items-start">
|
|
196
|
+
<ButtonGroup aria-label="Horizontal">
|
|
197
|
+
<Button variant="secondary" size="sm" aria-label="Zoom in">
|
|
198
|
+
<Icon name="addLine" className="size-16 text-foreground-neutral-subtle" />
|
|
199
|
+
</Button>
|
|
200
|
+
<ButtonGroupSeparator />
|
|
201
|
+
<Button variant="secondary" size="sm" aria-label="Zoom out">
|
|
202
|
+
<Icon name="subtractLine" className="size-16 text-foreground-neutral-subtle" />
|
|
203
|
+
</Button>
|
|
204
|
+
</ButtonGroup>
|
|
205
|
+
<ButtonGroup orientation="vertical" className="h-fit w-fit" aria-label="Vertical">
|
|
206
|
+
<Button variant="secondary" size="sm" aria-label="Increase">
|
|
207
|
+
<Icon name="addLine" className="size-16 text-foreground-neutral-subtle" />
|
|
208
|
+
</Button>
|
|
209
|
+
<ButtonGroupSeparator orientation="horizontal" />
|
|
210
|
+
<Button variant="secondary" size="sm" aria-label="Decrease">
|
|
211
|
+
<Icon name="subtractLine" className="size-16 text-foreground-neutral-subtle" />
|
|
212
|
+
</Button>
|
|
213
|
+
</ButtonGroup>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
{/* Split Button */}
|
|
218
|
+
<div className="flex flex-col gap-8">
|
|
219
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
220
|
+
Split Button
|
|
221
|
+
</Code>
|
|
222
|
+
<ButtonGroup aria-label="Save actions">
|
|
223
|
+
<Button variant="secondary" size="sm" className=" !text-foreground-neutral-subtle">
|
|
224
|
+
Save
|
|
225
|
+
</Button>
|
|
226
|
+
<ButtonGroupSeparator />
|
|
227
|
+
<Button variant="secondary" size="sm" aria-label="More options">
|
|
228
|
+
<Icon name="arrowDownSLine" className="size-16 text-foreground-neutral-subtle" />
|
|
229
|
+
</Button>
|
|
230
|
+
</ButtonGroup>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
{/* With Input */}
|
|
234
|
+
<div className="flex flex-col gap-8">
|
|
235
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
236
|
+
With Input
|
|
237
|
+
</Code>
|
|
238
|
+
<ButtonGroup className="w-320" aria-label="Search">
|
|
239
|
+
<Input placeholder="Search..." aria-label="Search input" />
|
|
240
|
+
<ButtonGroupSeparator />
|
|
241
|
+
<Button variant="secondary" aria-label="Submit search">
|
|
242
|
+
<Icon name="searchLine" className="size-16 text-foreground-neutral-subtle" />
|
|
243
|
+
</Button>
|
|
244
|
+
</ButtonGroup>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
{/* Quantity Input */}
|
|
248
|
+
<div className="flex flex-col gap-8">
|
|
249
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
250
|
+
Quantity Selector
|
|
251
|
+
</Code>
|
|
252
|
+
<ButtonGroup className="w-280" aria-label="Quantity selector">
|
|
253
|
+
<Button variant="secondary" aria-label="Decrease">
|
|
254
|
+
<Icon name="subtractLine" className="size-16 text-foreground-neutral-subtle" />
|
|
255
|
+
</Button>
|
|
256
|
+
<ButtonGroupSeparator />
|
|
257
|
+
<Input placeholder="1" className="text-center" aria-label="Quantity" />
|
|
258
|
+
<ButtonGroupSeparator />
|
|
259
|
+
<Button variant="secondary" aria-label="Increase">
|
|
260
|
+
<Icon name="addLine" className="size-16 text-foreground-neutral-subtle" />
|
|
261
|
+
</Button>
|
|
262
|
+
</ButtonGroup>
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
{/* With Select */}
|
|
266
|
+
<div className="flex flex-col gap-8">
|
|
267
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
268
|
+
With Select
|
|
269
|
+
</Code>
|
|
270
|
+
<SelectExample />
|
|
271
|
+
</div>
|
|
272
|
+
|
|
273
|
+
{/* With Popover */}
|
|
274
|
+
<div className="flex flex-col gap-8">
|
|
275
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
276
|
+
With Popover
|
|
277
|
+
</Code>
|
|
278
|
+
<PopoverExample />
|
|
279
|
+
</div>
|
|
280
|
+
|
|
281
|
+
{/* Nested Groups */}
|
|
282
|
+
<div className="flex flex-col gap-8">
|
|
283
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
284
|
+
Nested Groups
|
|
285
|
+
</Code>
|
|
286
|
+
<div className="inline-flex gap-8">
|
|
287
|
+
<ButtonGroup aria-label="Page selection">
|
|
288
|
+
<Button
|
|
289
|
+
variant="transparent"
|
|
290
|
+
className="w-42 !text-foreground-neutral-subtle"
|
|
291
|
+
aria-label="Page 1"
|
|
292
|
+
>
|
|
293
|
+
1
|
|
294
|
+
</Button>
|
|
295
|
+
<ButtonGroupSeparator />
|
|
296
|
+
<Button
|
|
297
|
+
variant="transparent"
|
|
298
|
+
className="w-42 !text-foreground-neutral-subtle"
|
|
299
|
+
aria-label="Page 2"
|
|
300
|
+
>
|
|
301
|
+
2
|
|
302
|
+
</Button>
|
|
303
|
+
<ButtonGroupSeparator />
|
|
304
|
+
<Button
|
|
305
|
+
variant="transparent"
|
|
306
|
+
className="w-42 !text-foreground-neutral-subtle"
|
|
307
|
+
aria-label="Page 3"
|
|
308
|
+
>
|
|
309
|
+
3
|
|
310
|
+
</Button>
|
|
311
|
+
<ButtonGroupSeparator />
|
|
312
|
+
<Button
|
|
313
|
+
variant="transparent"
|
|
314
|
+
className="w-42 !text-foreground-neutral-subtle"
|
|
315
|
+
aria-label="Page 4"
|
|
316
|
+
>
|
|
317
|
+
4
|
|
318
|
+
</Button>
|
|
319
|
+
<ButtonGroupSeparator />
|
|
320
|
+
<Button
|
|
321
|
+
variant="transparent"
|
|
322
|
+
className="w-42 !text-foreground-neutral-subtle"
|
|
323
|
+
aria-label="Page 5"
|
|
324
|
+
>
|
|
325
|
+
5
|
|
326
|
+
</Button>
|
|
327
|
+
</ButtonGroup>
|
|
328
|
+
<ButtonGroup aria-label="Pagination controls">
|
|
329
|
+
<Button variant="transparent" aria-label="Previous page">
|
|
330
|
+
<Icon name="arrowLeftSLine" className="size-16 text-foreground-neutral-subtle" />
|
|
331
|
+
</Button>
|
|
332
|
+
<ButtonGroupSeparator />
|
|
333
|
+
<Button variant="transparent" aria-label="Next page">
|
|
334
|
+
<Icon name="arrowRightSLine" className="size-16 text-foreground-neutral-subtle" />
|
|
335
|
+
</Button>
|
|
336
|
+
</ButtonGroup>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
{/* Disabled State */}
|
|
341
|
+
<div className="flex flex-col gap-8">
|
|
342
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
343
|
+
Disabled State
|
|
344
|
+
</Code>
|
|
345
|
+
<ButtonGroup aria-label="Button group with disabled state">
|
|
346
|
+
<Button variant="secondary" size="sm" className=" !text-foreground-neutral-subtle">
|
|
347
|
+
Enabled
|
|
348
|
+
</Button>
|
|
349
|
+
<ButtonGroupSeparator />
|
|
350
|
+
<Button variant="secondary" size="sm" disabled>
|
|
351
|
+
Disabled
|
|
352
|
+
</Button>
|
|
353
|
+
<ButtonGroupSeparator />
|
|
354
|
+
<Button variant="secondary" size="sm" className=" !text-foreground-neutral-subtle">
|
|
355
|
+
Enabled
|
|
356
|
+
</Button>
|
|
357
|
+
</ButtonGroup>
|
|
358
|
+
</div>
|
|
359
|
+
</div>
|
|
360
|
+
),
|
|
361
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import {Slot} from '@radix-ui/react-slot';
|
|
2
|
+
import {cva, type VariantProps} from 'class-variance-authority';
|
|
3
|
+
import type {ComponentProps} from 'react';
|
|
4
|
+
import {cn} from 'utils/cn';
|
|
5
|
+
|
|
6
|
+
const buttonGroupVariants = cva(
|
|
7
|
+
[
|
|
8
|
+
'flex w-fit items-stretch',
|
|
9
|
+
// Apply shadow to the group container instead of individual children
|
|
10
|
+
'rounded-6 shadow-button-neutral',
|
|
11
|
+
// Focus management
|
|
12
|
+
'[&>*]:focus-visible:z-10 [&>*]:focus-visible:relative',
|
|
13
|
+
// Select trigger sizing
|
|
14
|
+
"[&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit",
|
|
15
|
+
// Input flex
|
|
16
|
+
'[&>input]:flex-1',
|
|
17
|
+
// Nested button groups
|
|
18
|
+
'has-[>[data-slot=button-group]]:gap-8',
|
|
19
|
+
// Remove shadows from all children to prevent inner shadow artifacts
|
|
20
|
+
'[&>*]:shadow-none',
|
|
21
|
+
],
|
|
22
|
+
{
|
|
23
|
+
variants: {
|
|
24
|
+
orientation: {
|
|
25
|
+
horizontal: [
|
|
26
|
+
// Remove left border-radius and left border from all but first child
|
|
27
|
+
'[&>*:not(:first-child)]:rounded-l-none',
|
|
28
|
+
'[&>*:not(:first-child)]:border-l-0',
|
|
29
|
+
// Remove right border-radius from all but last child
|
|
30
|
+
'[&>*:not(:last-child)]:rounded-r-none',
|
|
31
|
+
],
|
|
32
|
+
vertical: [
|
|
33
|
+
'flex-col',
|
|
34
|
+
// Remove top border-radius and top border from all but first child
|
|
35
|
+
'[&>*:not(:first-child)]:rounded-t-none',
|
|
36
|
+
'[&>*:not(:first-child)]:border-t-0',
|
|
37
|
+
// Remove bottom border-radius from all but last child
|
|
38
|
+
'[&>*:not(:last-child)]:rounded-b-none',
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
defaultVariants: {
|
|
43
|
+
orientation: 'horizontal',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
type ButtonGroupProps = ComponentProps<'div'> & VariantProps<typeof buttonGroupVariants>;
|
|
49
|
+
|
|
50
|
+
function ButtonGroup({className, orientation, ...props}: ButtonGroupProps) {
|
|
51
|
+
return (
|
|
52
|
+
// biome-ignore lint/a11y/useSemanticElements: role="group" is semantically correct for button groups
|
|
53
|
+
<div
|
|
54
|
+
role="group"
|
|
55
|
+
data-slot="button-group"
|
|
56
|
+
data-orientation={orientation}
|
|
57
|
+
className={cn(buttonGroupVariants({orientation}), className)}
|
|
58
|
+
{...props}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
type ButtonGroupTextProps = ComponentProps<'div'> & {
|
|
64
|
+
asChild?: boolean;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
function ButtonGroupText({className, asChild = false, ...props}: ButtonGroupTextProps) {
|
|
68
|
+
const Comp = asChild ? Slot : 'div';
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<Comp
|
|
72
|
+
data-slot="button-group-text"
|
|
73
|
+
className={cn(
|
|
74
|
+
'flex items-center gap-8 rounded-6 px-12',
|
|
75
|
+
'bg-background-field-base text-foreground-neutral-subtle',
|
|
76
|
+
'text-sm leading-20 font-medium',
|
|
77
|
+
'shadow-button-neutral',
|
|
78
|
+
"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-16",
|
|
79
|
+
className,
|
|
80
|
+
)}
|
|
81
|
+
{...props}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
type ButtonGroupSeparatorProps = ComponentProps<'div'> & {
|
|
87
|
+
orientation?: 'horizontal' | 'vertical';
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
function ButtonGroupSeparator({
|
|
91
|
+
className,
|
|
92
|
+
orientation = 'vertical',
|
|
93
|
+
...props
|
|
94
|
+
}: ButtonGroupSeparatorProps) {
|
|
95
|
+
return (
|
|
96
|
+
<div
|
|
97
|
+
aria-hidden="true"
|
|
98
|
+
data-slot="button-group-separator"
|
|
99
|
+
data-orientation={orientation}
|
|
100
|
+
className={cn(
|
|
101
|
+
'shrink-0 self-stretch',
|
|
102
|
+
'bg-border-neutral-strong',
|
|
103
|
+
orientation === 'vertical' ? 'h-auto w-px' : 'h-px w-auto',
|
|
104
|
+
className,
|
|
105
|
+
)}
|
|
106
|
+
{...props}
|
|
107
|
+
/>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export {ButtonGroup, ButtonGroupSeparator, ButtonGroupText, buttonGroupVariants};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './button-group';
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import {Slot} from '@radix-ui/react-slot';
|
|
2
2
|
import {Icon} from 'components/icon/icon';
|
|
3
|
+
import {ShinyText} from 'components/shiny-text';
|
|
3
4
|
import {Text} from 'components/typography';
|
|
5
|
+
import {useResolvedTheme} from 'hooks/useResolvedTheme';
|
|
4
6
|
import type {ComponentProps, HTMLAttributes, ReactNode} from 'react';
|
|
7
|
+
import {ShipfoxLoader} from 'shipfox-loader-react';
|
|
5
8
|
import {cn} from 'utils/cn';
|
|
6
9
|
|
|
7
10
|
export type CodeBlockFooterProps = HTMLAttributes<HTMLDivElement> & {
|
|
@@ -23,11 +26,17 @@ export function CodeBlockFooter({
|
|
|
23
26
|
...props
|
|
24
27
|
}: CodeBlockFooterProps) {
|
|
25
28
|
const Comp = asChild ? Slot : 'div';
|
|
29
|
+
const resolvedTheme = useResolvedTheme();
|
|
26
30
|
|
|
27
31
|
const defaultIcon =
|
|
28
32
|
icon ??
|
|
29
33
|
(state === 'running' ? (
|
|
30
|
-
<
|
|
34
|
+
<ShipfoxLoader
|
|
35
|
+
size={20}
|
|
36
|
+
animation="circular"
|
|
37
|
+
color={resolvedTheme === 'dark' ? 'white' : 'orange'}
|
|
38
|
+
background={resolvedTheme === 'dark' ? 'dark' : 'light'}
|
|
39
|
+
/>
|
|
31
40
|
) : (
|
|
32
41
|
<Icon
|
|
33
42
|
name="checkCircleSolid"
|
|
@@ -57,7 +66,15 @@ export function CodeBlockFooter({
|
|
|
57
66
|
<CodeBlockFooterIcon className="text-tag-success-icon">{defaultIcon}</CodeBlockFooterIcon>
|
|
58
67
|
{(message || description) && (
|
|
59
68
|
<CodeBlockFooterContent>
|
|
60
|
-
{message &&
|
|
69
|
+
{message && (
|
|
70
|
+
<CodeBlockFooterMessage>
|
|
71
|
+
{state === 'running' && typeof message === 'string' ? (
|
|
72
|
+
<ShinyText text={message} speed={3} />
|
|
73
|
+
) : (
|
|
74
|
+
message
|
|
75
|
+
)}
|
|
76
|
+
</CodeBlockFooterMessage>
|
|
77
|
+
)}
|
|
61
78
|
{description && <CodeBlockFooterDescription>{description}</CodeBlockFooterDescription>}
|
|
62
79
|
</CodeBlockFooterContent>
|
|
63
80
|
)}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type {Meta, StoryObj} from '@storybook/react';
|
|
2
|
+
import {useState} from 'react';
|
|
3
|
+
import {Icon} from '../icon';
|
|
4
|
+
import {Popover, PopoverContent, PopoverTrigger} from '../popover';
|
|
5
|
+
import {
|
|
6
|
+
Command,
|
|
7
|
+
CommandEmpty,
|
|
8
|
+
CommandGroup,
|
|
9
|
+
CommandInput,
|
|
10
|
+
CommandItem,
|
|
11
|
+
CommandList,
|
|
12
|
+
CommandSeparator,
|
|
13
|
+
CommandShortcut,
|
|
14
|
+
CommandTrigger,
|
|
15
|
+
} from './command';
|
|
16
|
+
|
|
17
|
+
const meta = {
|
|
18
|
+
title: 'Components/Command',
|
|
19
|
+
component: Command,
|
|
20
|
+
tags: ['autodocs'],
|
|
21
|
+
} satisfies Meta<typeof Command>;
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
|
|
25
|
+
type Story = StoryObj<typeof meta>;
|
|
26
|
+
|
|
27
|
+
export const Default: Story = {
|
|
28
|
+
render: () => (
|
|
29
|
+
<Command className="rounded-10 shadow-tooltip max-w-400">
|
|
30
|
+
<CommandInput placeholder="Type a command or search..." />
|
|
31
|
+
<CommandList>
|
|
32
|
+
<CommandEmpty>No results found.</CommandEmpty>
|
|
33
|
+
<CommandGroup heading="Suggestions">
|
|
34
|
+
<CommandItem>
|
|
35
|
+
<Icon name="calendar2Line" className="size-16 mr-8" />
|
|
36
|
+
<span>Calendar</span>
|
|
37
|
+
</CommandItem>
|
|
38
|
+
<CommandItem>
|
|
39
|
+
<Icon name="emotion2Line" className="size-16 mr-8" />
|
|
40
|
+
<span>Search Emoji</span>
|
|
41
|
+
</CommandItem>
|
|
42
|
+
<CommandItem>
|
|
43
|
+
<Icon name="calculatorLine" className="size-16 mr-8" />
|
|
44
|
+
<span>Calculator</span>
|
|
45
|
+
</CommandItem>
|
|
46
|
+
</CommandGroup>
|
|
47
|
+
<CommandSeparator />
|
|
48
|
+
<CommandGroup heading="Settings">
|
|
49
|
+
<CommandItem>
|
|
50
|
+
<Icon name="user3Line" className="size-16 mr-8" />
|
|
51
|
+
<span>Profile</span>
|
|
52
|
+
<CommandShortcut>⌘P</CommandShortcut>
|
|
53
|
+
</CommandItem>
|
|
54
|
+
<CommandItem>
|
|
55
|
+
<Icon name="mailLine" className="size-16 mr-8" />
|
|
56
|
+
<span>Mail</span>
|
|
57
|
+
<CommandShortcut>⌘M</CommandShortcut>
|
|
58
|
+
</CommandItem>
|
|
59
|
+
<CommandItem>
|
|
60
|
+
<Icon name="settings3Line" className="size-16 mr-8" />
|
|
61
|
+
<span>Settings</span>
|
|
62
|
+
<CommandShortcut>⌘S</CommandShortcut>
|
|
63
|
+
</CommandItem>
|
|
64
|
+
</CommandGroup>
|
|
65
|
+
</CommandList>
|
|
66
|
+
</Command>
|
|
67
|
+
),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const Combobox: Story = {
|
|
71
|
+
render: () => {
|
|
72
|
+
const frameworks = [
|
|
73
|
+
{value: 'next.js', label: 'Next.js', icon: 'reactjsLine' as const},
|
|
74
|
+
{value: 'sveltekit', label: 'SvelteKit', icon: 'svelteLine' as const},
|
|
75
|
+
{value: 'nuxt.js', label: 'Nuxt.js', icon: 'vuejsLine' as const},
|
|
76
|
+
{value: 'remix', label: 'Remix', icon: 'reactjsLine' as const},
|
|
77
|
+
{value: 'astro', label: 'Astro', icon: 'rocketLine' as const},
|
|
78
|
+
{value: 'vue', label: 'Vue', icon: 'vuejsLine' as const},
|
|
79
|
+
{value: 'react', label: 'React', icon: 'reactjsLine' as const},
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
function ComboboxDemo() {
|
|
83
|
+
const [open, setOpen] = useState(false);
|
|
84
|
+
const [value, setValue] = useState('');
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div className="flex flex-col gap-16">
|
|
88
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
89
|
+
<PopoverTrigger asChild>
|
|
90
|
+
<CommandTrigger className="w-280" placeholder="Select framework...">
|
|
91
|
+
{value ? frameworks.find((framework) => framework.value === value)?.label : null}
|
|
92
|
+
</CommandTrigger>
|
|
93
|
+
</PopoverTrigger>
|
|
94
|
+
<PopoverContent className="w-280 p-0" align="start">
|
|
95
|
+
<Command>
|
|
96
|
+
<CommandInput placeholder="Search framework..." />
|
|
97
|
+
<CommandList>
|
|
98
|
+
<CommandEmpty>No framework found.</CommandEmpty>
|
|
99
|
+
<CommandGroup>
|
|
100
|
+
{frameworks.map((framework) => (
|
|
101
|
+
<CommandItem
|
|
102
|
+
key={framework.value}
|
|
103
|
+
value={framework.value}
|
|
104
|
+
onSelect={(currentValue) => {
|
|
105
|
+
setValue(currentValue === value ? '' : currentValue);
|
|
106
|
+
setOpen(false);
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
<Icon
|
|
110
|
+
name="check"
|
|
111
|
+
className={`size-16 mr-8 ${value === framework.value ? 'opacity-100' : 'opacity-0'}`}
|
|
112
|
+
/>
|
|
113
|
+
<Icon name={framework.icon} className="size-16 mr-8" />
|
|
114
|
+
{framework.label}
|
|
115
|
+
</CommandItem>
|
|
116
|
+
))}
|
|
117
|
+
</CommandGroup>
|
|
118
|
+
</CommandList>
|
|
119
|
+
</Command>
|
|
120
|
+
</PopoverContent>
|
|
121
|
+
</Popover>
|
|
122
|
+
{value && (
|
|
123
|
+
<p className="text-sm text-foreground-neutral-muted">
|
|
124
|
+
Selected: {frameworks.find((f) => f.value === value)?.label}
|
|
125
|
+
</p>
|
|
126
|
+
)}
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return <ComboboxDemo />;
|
|
132
|
+
},
|
|
133
|
+
};
|