@opencosmos/ui 1.3.1
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/.claude/CLAUDE.md +239 -0
- package/README.md +161 -0
- package/dist/cli.mjs +151 -0
- package/dist/dates.d.mts +20 -0
- package/dist/dates.d.ts +20 -0
- package/dist/dates.js +240 -0
- package/dist/dates.js.map +1 -0
- package/dist/dates.mjs +203 -0
- package/dist/dates.mjs.map +1 -0
- package/dist/dnd.d.mts +126 -0
- package/dist/dnd.d.ts +126 -0
- package/dist/dnd.js +274 -0
- package/dist/dnd.js.map +1 -0
- package/dist/dnd.mjs +250 -0
- package/dist/dnd.mjs.map +1 -0
- package/dist/fontThemes-Dh8mtXES.d.mts +868 -0
- package/dist/fontThemes-Dh8mtXES.d.ts +868 -0
- package/dist/forms.d.mts +38 -0
- package/dist/forms.d.ts +38 -0
- package/dist/forms.js +198 -0
- package/dist/forms.js.map +1 -0
- package/dist/forms.mjs +159 -0
- package/dist/forms.mjs.map +1 -0
- package/dist/hooks-1b8WaQf1.d.mts +225 -0
- package/dist/hooks-CKW8vE9H.d.ts +225 -0
- package/dist/hooks.d.mts +3 -0
- package/dist/hooks.d.ts +3 -0
- package/dist/hooks.js +971 -0
- package/dist/hooks.js.map +1 -0
- package/dist/hooks.mjs +943 -0
- package/dist/hooks.mjs.map +1 -0
- package/dist/index-DscTIrZ2.d.mts +29 -0
- package/dist/index-DscTIrZ2.d.ts +29 -0
- package/dist/index.d.mts +3382 -0
- package/dist/index.d.ts +3382 -0
- package/dist/index.js +15146 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +14802 -0
- package/dist/index.mjs.map +1 -0
- package/dist/providers-CXPDMsl7.d.mts +30 -0
- package/dist/providers-Dn_Msjvz.d.ts +30 -0
- package/dist/providers.d.mts +3 -0
- package/dist/providers.d.ts +3 -0
- package/dist/providers.js +1885 -0
- package/dist/providers.js.map +1 -0
- package/dist/providers.mjs +1859 -0
- package/dist/providers.mjs.map +1 -0
- package/dist/tables.d.mts +10 -0
- package/dist/tables.d.ts +10 -0
- package/dist/tables.js +248 -0
- package/dist/tables.js.map +1 -0
- package/dist/tables.mjs +218 -0
- package/dist/tables.mjs.map +1 -0
- package/dist/tokens.d.mts +1065 -0
- package/dist/tokens.d.ts +1065 -0
- package/dist/tokens.js +2637 -0
- package/dist/tokens.js.map +1 -0
- package/dist/tokens.mjs +2555 -0
- package/dist/tokens.mjs.map +1 -0
- package/dist/utils-CIIM7dAC.d.ts +986 -0
- package/dist/utils-Cs04sxth.d.mts +986 -0
- package/dist/utils.d.mts +4 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +874 -0
- package/dist/utils.js.map +1 -0
- package/dist/utils.mjs +806 -0
- package/dist/utils.mjs.map +1 -0
- package/dist/validation-Bj1ye-v_.d.mts +114 -0
- package/dist/validation-Bj1ye-v_.d.ts +114 -0
- package/dist/webgl.d.mts +104 -0
- package/dist/webgl.d.ts +104 -0
- package/dist/webgl.js +226 -0
- package/dist/webgl.js.map +1 -0
- package/dist/webgl.mjs +195 -0
- package/dist/webgl.mjs.map +1 -0
- package/package.json +267 -0
- package/src/cli.ts +206 -0
- package/src/component-registry.ts +183 -0
- package/src/components/actions/Button.test.tsx +61 -0
- package/src/components/actions/Button.tsx +70 -0
- package/src/components/actions/Link.tsx +78 -0
- package/src/components/actions/Magnetic.tsx +68 -0
- package/src/components/actions/Toggle.test.tsx +40 -0
- package/src/components/actions/Toggle.tsx +47 -0
- package/src/components/actions/ToggleGroup.tsx +70 -0
- package/src/components/actions/index.ts +5 -0
- package/src/components/backgrounds/FaultyTerminal.tsx +426 -0
- package/src/components/backgrounds/OrbBackground.tsx +424 -0
- package/src/components/backgrounds/WarpBackground.tsx +358 -0
- package/src/components/backgrounds/index.ts +3 -0
- package/src/components/blocks/Hero.tsx +142 -0
- package/src/components/blocks/social/OpenGraphCard.tsx +243 -0
- package/src/components/cursor/SplashCursor.tsx +1315 -0
- package/src/components/cursor/TargetCursor.tsx +187 -0
- package/src/components/cursor/index.ts +2 -0
- package/src/components/data-display/AspectImage.tsx +73 -0
- package/src/components/data-display/Avatar.test.tsx +35 -0
- package/src/components/data-display/Avatar.tsx +55 -0
- package/src/components/data-display/Badge.test.tsx +43 -0
- package/src/components/data-display/Badge.tsx +84 -0
- package/src/components/data-display/Brand.tsx +123 -0
- package/src/components/data-display/Calendar.tsx +70 -0
- package/src/components/data-display/Card.test.tsx +92 -0
- package/src/components/data-display/Card.tsx +115 -0
- package/src/components/data-display/Code.tsx +210 -0
- package/src/components/data-display/CollapsibleCodeBlock.tsx +238 -0
- package/src/components/data-display/DataTable.tsx +119 -0
- package/src/components/data-display/DescriptionList.tsx +41 -0
- package/src/components/data-display/GitHubIcon.tsx +44 -0
- package/src/components/data-display/Heading.test.tsx +36 -0
- package/src/components/data-display/Heading.tsx +83 -0
- package/src/components/data-display/StatCard.tsx +195 -0
- package/src/components/data-display/Table.tsx +133 -0
- package/src/components/data-display/Text.test.tsx +48 -0
- package/src/components/data-display/Text.tsx +144 -0
- package/src/components/data-display/Timeline.tsx +194 -0
- package/src/components/data-display/TreeView.tsx +226 -0
- package/src/components/data-display/Typewriter.tsx +119 -0
- package/src/components/data-display/VariableWeightText.tsx +130 -0
- package/src/components/data-display/index.ts +19 -0
- package/src/components/feedback/Alert.test.tsx +44 -0
- package/src/components/feedback/Alert.tsx +65 -0
- package/src/components/feedback/EmptyState.tsx +113 -0
- package/src/components/feedback/Progress.test.tsx +60 -0
- package/src/components/feedback/Progress.tsx +30 -0
- package/src/components/feedback/ProgressBar.tsx +158 -0
- package/src/components/feedback/Skeleton.test.tsx +39 -0
- package/src/components/feedback/Skeleton.tsx +45 -0
- package/src/components/feedback/Sonner.tsx +28 -0
- package/src/components/feedback/Spinner.test.tsx +33 -0
- package/src/components/feedback/Spinner.tsx +99 -0
- package/src/components/feedback/Stepper.tsx +307 -0
- package/src/components/feedback/Toast/Toast.tsx +243 -0
- package/src/components/feedback/Toast/index.ts +2 -0
- package/src/components/feedback/index.ts +9 -0
- package/src/components/forms/Checkbox.test.tsx +40 -0
- package/src/components/forms/Checkbox.tsx +31 -0
- package/src/components/forms/ColorPicker.tsx +118 -0
- package/src/components/forms/Combobox.tsx +96 -0
- package/src/components/forms/DragDrop.tsx +440 -0
- package/src/components/forms/FileUpload.tsx +252 -0
- package/src/components/forms/FilterButton.tsx +65 -0
- package/src/components/forms/Form.tsx +197 -0
- package/src/components/forms/Input.test.tsx +46 -0
- package/src/components/forms/Input.tsx +43 -0
- package/src/components/forms/InputOTP.tsx +81 -0
- package/src/components/forms/Label.test.tsx +20 -0
- package/src/components/forms/Label.tsx +25 -0
- package/src/components/forms/RadioGroup.tsx +51 -0
- package/src/components/forms/SearchBar.tsx +215 -0
- package/src/components/forms/Select.test.tsx +118 -0
- package/src/components/forms/Select.tsx +274 -0
- package/src/components/forms/Slider.tsx +29 -0
- package/src/components/forms/Switch.test.tsx +76 -0
- package/src/components/forms/Switch.tsx +30 -0
- package/src/components/forms/TextField.tsx +152 -0
- package/src/components/forms/Textarea.test.tsx +41 -0
- package/src/components/forms/Textarea.tsx +29 -0
- package/src/components/forms/ThemeSwitcher.tsx +290 -0
- package/src/components/forms/ThemeToggle.tsx +151 -0
- package/src/components/forms/index.ts +19 -0
- package/src/components/layout/Accordion.test.tsx +66 -0
- package/src/components/layout/Accordion.tsx +64 -0
- package/src/components/layout/AspectRatio.tsx +7 -0
- package/src/components/layout/Carousel.tsx +277 -0
- package/src/components/layout/Collapsible.test.tsx +40 -0
- package/src/components/layout/Collapsible.tsx +31 -0
- package/src/components/layout/Container.test.tsx +45 -0
- package/src/components/layout/Container.tsx +99 -0
- package/src/components/layout/CustomizerPanel.tsx +400 -0
- package/src/components/layout/DatePicker.tsx +57 -0
- package/src/components/layout/Footer/Footer.tsx +175 -0
- package/src/components/layout/Footer/index.ts +2 -0
- package/src/components/layout/GlassSurface.tsx +82 -0
- package/src/components/layout/Grid.test.tsx +31 -0
- package/src/components/layout/Grid.tsx +130 -0
- package/src/components/layout/Header/Header.tsx +450 -0
- package/src/components/layout/Header/index.ts +2 -0
- package/src/components/layout/PageLayout.tsx +180 -0
- package/src/components/layout/PageTemplate.tsx +158 -0
- package/src/components/layout/Resizable.tsx +48 -0
- package/src/components/layout/ScrollArea.tsx +53 -0
- package/src/components/layout/Separator.test.tsx +28 -0
- package/src/components/layout/Separator.tsx +29 -0
- package/src/components/layout/Sidebar.tsx +171 -0
- package/src/components/layout/Stack.test.tsx +41 -0
- package/src/components/layout/Stack.tsx +89 -0
- package/src/components/layout/glass-surface.css +60 -0
- package/src/components/layout/index.ts +18 -0
- package/src/components/motion/AnimatedBeam.tsx +159 -0
- package/src/components/navigation/Breadcrumb.test.tsx +57 -0
- package/src/components/navigation/Breadcrumb.tsx +119 -0
- package/src/components/navigation/Breadcrumbs.tsx +221 -0
- package/src/components/navigation/Command.tsx +159 -0
- package/src/components/navigation/Menubar.tsx +115 -0
- package/src/components/navigation/NavLink.tsx +55 -0
- package/src/components/navigation/NavigationMenu.tsx +125 -0
- package/src/components/navigation/Pagination.tsx +121 -0
- package/src/components/navigation/SecondaryNav.tsx +100 -0
- package/src/components/navigation/Tabs.test.tsx +47 -0
- package/src/components/navigation/Tabs.tsx +60 -0
- package/src/components/navigation/TertiaryNav.tsx +90 -0
- package/src/components/navigation/index.ts +10 -0
- package/src/components/overlays/AlertDialog.test.tsx +69 -0
- package/src/components/overlays/AlertDialog.tsx +166 -0
- package/src/components/overlays/ContextMenu.tsx +243 -0
- package/src/components/overlays/Dialog.test.tsx +79 -0
- package/src/components/overlays/Dialog.tsx +158 -0
- package/src/components/overlays/Drawer.tsx +128 -0
- package/src/components/overlays/Dropdown.tsx +253 -0
- package/src/components/overlays/DropdownMenu.tsx +242 -0
- package/src/components/overlays/HoverCard.tsx +32 -0
- package/src/components/overlays/Modal.tsx +250 -0
- package/src/components/overlays/NotificationCenter.tsx +364 -0
- package/src/components/overlays/Popover.test.tsx +40 -0
- package/src/components/overlays/Popover.tsx +46 -0
- package/src/components/overlays/Sheet.tsx +163 -0
- package/src/components/overlays/Tooltip.test.tsx +33 -0
- package/src/components/overlays/Tooltip.tsx +32 -0
- package/src/components/overlays/index.ts +12 -0
- package/src/dates.ts +2 -0
- package/src/dnd.ts +1 -0
- package/src/forms.ts +1 -0
- package/src/globals.css +187 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/useForm.ts +247 -0
- package/src/hooks/useMotionPreference.test.ts +102 -0
- package/src/hooks/useMotionPreference.ts +78 -0
- package/src/hooks/useTheme.ts +58 -0
- package/src/hooks.ts +9 -0
- package/src/index.ts +168 -0
- package/src/lib/animations.ts +356 -0
- package/src/lib/breadcrumbs.ts +94 -0
- package/src/lib/colors.ts +493 -0
- package/src/lib/store/customizer.ts +482 -0
- package/src/lib/store/index.ts +3 -0
- package/src/lib/store/theme.ts +55 -0
- package/src/lib/syntax-parser/index.ts +50 -0
- package/src/lib/syntax-parser/patterns.ts +64 -0
- package/src/lib/syntax-parser/tokenizer.ts +117 -0
- package/src/lib/syntax-parser/types.ts +27 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/validation.ts +204 -0
- package/src/lib/webgl/Color.ts +11 -0
- package/src/lib/webgl/Mesh.ts +41 -0
- package/src/lib/webgl/Program.ts +118 -0
- package/src/lib/webgl/Renderer.ts +51 -0
- package/src/lib/webgl/Triangle.ts +27 -0
- package/src/lib/webgl/Vec3.ts +18 -0
- package/src/lib/webgl/index.ts +13 -0
- package/src/nativewind-env.d.ts +1 -0
- package/src/providers/ThemeProvider.tsx +461 -0
- package/src/providers/index.ts +1 -0
- package/src/providers.ts +7 -0
- package/src/tables.ts +1 -0
- package/src/test/setup.ts +39 -0
- package/src/theme.css +158 -0
- package/src/tokens.ts +7 -0
- package/src/utils.ts +12 -0
- package/src/webgl.ts +1 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react'
|
|
2
|
+
import userEvent from '@testing-library/user-event'
|
|
3
|
+
import { describe, it, expect } from 'vitest'
|
|
4
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from './Tabs'
|
|
5
|
+
|
|
6
|
+
describe('Tabs', () => {
|
|
7
|
+
const renderTabs = (defaultValue = 'tab1') =>
|
|
8
|
+
render(
|
|
9
|
+
<Tabs defaultValue={defaultValue}>
|
|
10
|
+
<TabsList>
|
|
11
|
+
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
|
|
12
|
+
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
|
|
13
|
+
<TabsTrigger value="tab3">Tab 3</TabsTrigger>
|
|
14
|
+
</TabsList>
|
|
15
|
+
<TabsContent value="tab1">Content for tab 1</TabsContent>
|
|
16
|
+
<TabsContent value="tab2">Content for tab 2</TabsContent>
|
|
17
|
+
<TabsContent value="tab3">Content for tab 3</TabsContent>
|
|
18
|
+
</Tabs>
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
it('renders tabs with content', () => {
|
|
22
|
+
renderTabs()
|
|
23
|
+
expect(screen.getByRole('tab', { name: /tab 1/i })).toBeInTheDocument()
|
|
24
|
+
expect(screen.getByRole('tab', { name: /tab 2/i })).toBeInTheDocument()
|
|
25
|
+
expect(screen.getByRole('tab', { name: /tab 3/i })).toBeInTheDocument()
|
|
26
|
+
expect(screen.getByText('Content for tab 1')).toBeInTheDocument()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('switches content on tab click', async () => {
|
|
30
|
+
const user = userEvent.setup()
|
|
31
|
+
renderTabs()
|
|
32
|
+
|
|
33
|
+
expect(screen.getByText('Content for tab 1')).toBeInTheDocument()
|
|
34
|
+
expect(screen.queryByText('Content for tab 2')).not.toBeInTheDocument()
|
|
35
|
+
|
|
36
|
+
await user.click(screen.getByRole('tab', { name: /tab 2/i }))
|
|
37
|
+
expect(screen.queryByText('Content for tab 1')).not.toBeInTheDocument()
|
|
38
|
+
expect(screen.getByText('Content for tab 2')).toBeInTheDocument()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('defaults to first tab', () => {
|
|
42
|
+
renderTabs('tab1')
|
|
43
|
+
const tab1 = screen.getByRole('tab', { name: /tab 1/i })
|
|
44
|
+
expect(tab1).toHaveAttribute('data-state', 'active')
|
|
45
|
+
expect(screen.getByText('Content for tab 1')).toBeInTheDocument()
|
|
46
|
+
})
|
|
47
|
+
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
const Tabs = TabsPrimitive.Root
|
|
8
|
+
|
|
9
|
+
const TabsList = (
|
|
10
|
+
{
|
|
11
|
+
ref,
|
|
12
|
+
className,
|
|
13
|
+
...props
|
|
14
|
+
}: React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {
|
|
15
|
+
ref?: React.Ref<React.ElementRef<typeof TabsPrimitive.List>>;
|
|
16
|
+
}
|
|
17
|
+
) => (<TabsPrimitive.List
|
|
18
|
+
ref={ref}
|
|
19
|
+
className={cn(
|
|
20
|
+
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
|
|
21
|
+
className
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>)
|
|
25
|
+
|
|
26
|
+
const TabsTrigger = (
|
|
27
|
+
{
|
|
28
|
+
ref,
|
|
29
|
+
className,
|
|
30
|
+
...props
|
|
31
|
+
}: React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> & {
|
|
32
|
+
ref?: React.Ref<React.ElementRef<typeof TabsPrimitive.Trigger>>;
|
|
33
|
+
}
|
|
34
|
+
) => (<TabsPrimitive.Trigger
|
|
35
|
+
ref={ref}
|
|
36
|
+
className={cn(
|
|
37
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
|
|
38
|
+
className
|
|
39
|
+
)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>)
|
|
42
|
+
|
|
43
|
+
const TabsContent = (
|
|
44
|
+
{
|
|
45
|
+
ref,
|
|
46
|
+
className,
|
|
47
|
+
...props
|
|
48
|
+
}: React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content> & {
|
|
49
|
+
ref?: React.Ref<React.ElementRef<typeof TabsPrimitive.Content>>;
|
|
50
|
+
}
|
|
51
|
+
) => (<TabsPrimitive.Content
|
|
52
|
+
ref={ref}
|
|
53
|
+
className={cn(
|
|
54
|
+
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
55
|
+
className
|
|
56
|
+
)}
|
|
57
|
+
{...props}
|
|
58
|
+
/>)
|
|
59
|
+
|
|
60
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use client';;
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FilterButton } from '../forms/FilterButton';
|
|
4
|
+
|
|
5
|
+
export interface TertiaryNavItem {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface TertiaryNavProps {
|
|
11
|
+
/**
|
|
12
|
+
* Array of navigation items
|
|
13
|
+
*/
|
|
14
|
+
items: TertiaryNavItem[];
|
|
15
|
+
/**
|
|
16
|
+
* Currently active item ID
|
|
17
|
+
*/
|
|
18
|
+
activeId: string;
|
|
19
|
+
/**
|
|
20
|
+
* Callback when an item is selected
|
|
21
|
+
*/
|
|
22
|
+
onItemChange: (id: string) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Additional className for customization
|
|
25
|
+
*/
|
|
26
|
+
className?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Sticky behavior mode
|
|
29
|
+
* - 'stacked': Positions automatically below SecondaryNav (default)
|
|
30
|
+
* - 'standalone': Sticks to top of viewport (0px)
|
|
31
|
+
* @default 'stacked'
|
|
32
|
+
*/
|
|
33
|
+
mode?: 'stacked' | 'standalone';
|
|
34
|
+
/**
|
|
35
|
+
* Top offset for sticky positioning.
|
|
36
|
+
* Overrides 'mode' if provided.
|
|
37
|
+
*/
|
|
38
|
+
top?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Tertiary Navigation Component
|
|
43
|
+
* ... (keep existing comment block) ...
|
|
44
|
+
*/
|
|
45
|
+
export const TertiaryNav = (
|
|
46
|
+
{
|
|
47
|
+
ref,
|
|
48
|
+
items,
|
|
49
|
+
activeId,
|
|
50
|
+
onItemChange,
|
|
51
|
+
mode = 'stacked',
|
|
52
|
+
top,
|
|
53
|
+
className = ''
|
|
54
|
+
}: TertiaryNavProps & {
|
|
55
|
+
ref?: React.Ref<HTMLElement>;
|
|
56
|
+
}
|
|
57
|
+
) => {
|
|
58
|
+
// Determine sticky position based on mode, unless manually overridden
|
|
59
|
+
const stickyClass = top || (mode === 'stacked' ? 'top-32 lg:top-36' : 'top-0');
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<nav
|
|
63
|
+
ref={ref}
|
|
64
|
+
className={`
|
|
65
|
+
sticky ${stickyClass} z-30
|
|
66
|
+
bg-[var(--color-surface)]/60 backdrop-blur-md
|
|
67
|
+
border-b border-[var(--color-border)]
|
|
68
|
+
${className}
|
|
69
|
+
`}
|
|
70
|
+
aria-label="Tertiary navigation"
|
|
71
|
+
>
|
|
72
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
73
|
+
<div className="flex items-center gap-2 overflow-x-auto py-3 scrollbar-hide">
|
|
74
|
+
{items.map((item) => (
|
|
75
|
+
<FilterButton
|
|
76
|
+
key={item.id}
|
|
77
|
+
onClick={() => onItemChange(item.id)}
|
|
78
|
+
active={activeId === item.id}
|
|
79
|
+
shape="rounded"
|
|
80
|
+
aria-current={activeId === item.id ? 'page' : undefined}
|
|
81
|
+
className="whitespace-nowrap"
|
|
82
|
+
>
|
|
83
|
+
{item.label}
|
|
84
|
+
</FilterButton>
|
|
85
|
+
))}
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</nav>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './Breadcrumb';
|
|
2
|
+
export * from './Breadcrumbs';
|
|
3
|
+
export * from './Command';
|
|
4
|
+
export * from './Menubar';
|
|
5
|
+
export * from './NavLink';
|
|
6
|
+
export * from './NavigationMenu';
|
|
7
|
+
export * from './Pagination';
|
|
8
|
+
export * from './SecondaryNav';
|
|
9
|
+
export * from './Tabs';
|
|
10
|
+
export * from './TertiaryNav';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react'
|
|
2
|
+
import userEvent from '@testing-library/user-event'
|
|
3
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
4
|
+
import {
|
|
5
|
+
AlertDialog,
|
|
6
|
+
AlertDialogTrigger,
|
|
7
|
+
AlertDialogContent,
|
|
8
|
+
AlertDialogHeader,
|
|
9
|
+
AlertDialogTitle,
|
|
10
|
+
AlertDialogDescription,
|
|
11
|
+
AlertDialogFooter,
|
|
12
|
+
AlertDialogAction,
|
|
13
|
+
AlertDialogCancel,
|
|
14
|
+
} from './AlertDialog'
|
|
15
|
+
|
|
16
|
+
describe('AlertDialog', () => {
|
|
17
|
+
it('renders trigger', () => {
|
|
18
|
+
render(
|
|
19
|
+
<AlertDialog>
|
|
20
|
+
<AlertDialogTrigger>Delete</AlertDialogTrigger>
|
|
21
|
+
<AlertDialogContent>
|
|
22
|
+
<AlertDialogTitle>Confirm</AlertDialogTitle>
|
|
23
|
+
<AlertDialogDescription>Are you sure?</AlertDialogDescription>
|
|
24
|
+
</AlertDialogContent>
|
|
25
|
+
</AlertDialog>
|
|
26
|
+
)
|
|
27
|
+
expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('opens on trigger click', async () => {
|
|
31
|
+
const user = userEvent.setup()
|
|
32
|
+
render(
|
|
33
|
+
<AlertDialog>
|
|
34
|
+
<AlertDialogTrigger>Delete</AlertDialogTrigger>
|
|
35
|
+
<AlertDialogContent>
|
|
36
|
+
<AlertDialogTitle>Confirm Delete</AlertDialogTitle>
|
|
37
|
+
<AlertDialogDescription>This cannot be undone.</AlertDialogDescription>
|
|
38
|
+
</AlertDialogContent>
|
|
39
|
+
</AlertDialog>
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
expect(screen.queryByText('Confirm Delete')).not.toBeInTheDocument()
|
|
43
|
+
await user.click(screen.getByRole('button', { name: /delete/i }))
|
|
44
|
+
expect(screen.getByText('Confirm Delete')).toBeInTheDocument()
|
|
45
|
+
expect(screen.getByText('This cannot be undone.')).toBeInTheDocument()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('renders action and cancel buttons', async () => {
|
|
49
|
+
const user = userEvent.setup()
|
|
50
|
+
const onAction = vi.fn()
|
|
51
|
+
render(
|
|
52
|
+
<AlertDialog>
|
|
53
|
+
<AlertDialogTrigger>Delete</AlertDialogTrigger>
|
|
54
|
+
<AlertDialogContent>
|
|
55
|
+
<AlertDialogTitle>Confirm</AlertDialogTitle>
|
|
56
|
+
<AlertDialogDescription>Sure?</AlertDialogDescription>
|
|
57
|
+
<AlertDialogFooter>
|
|
58
|
+
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
59
|
+
<AlertDialogAction onClick={onAction}>Confirm</AlertDialogAction>
|
|
60
|
+
</AlertDialogFooter>
|
|
61
|
+
</AlertDialogContent>
|
|
62
|
+
</AlertDialog>
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
await user.click(screen.getByRole('button', { name: /delete/i }))
|
|
66
|
+
expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument()
|
|
67
|
+
expect(screen.getByRole('button', { name: /confirm/i })).toBeInTheDocument()
|
|
68
|
+
})
|
|
69
|
+
})
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
import { buttonVariants } from "../actions/Button"
|
|
7
|
+
|
|
8
|
+
const AlertDialog = AlertDialogPrimitive.Root
|
|
9
|
+
|
|
10
|
+
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
|
|
11
|
+
|
|
12
|
+
const AlertDialogPortal = AlertDialogPrimitive.Portal
|
|
13
|
+
|
|
14
|
+
const AlertDialogOverlay = (
|
|
15
|
+
{
|
|
16
|
+
ref,
|
|
17
|
+
className,
|
|
18
|
+
style,
|
|
19
|
+
...props
|
|
20
|
+
}: React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay> & {
|
|
21
|
+
ref?: React.Ref<React.ElementRef<typeof AlertDialogPrimitive.Overlay>>;
|
|
22
|
+
}
|
|
23
|
+
) => (<AlertDialogPrimitive.Overlay
|
|
24
|
+
className={cn(
|
|
25
|
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
26
|
+
className
|
|
27
|
+
)}
|
|
28
|
+
style={{
|
|
29
|
+
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
|
30
|
+
zIndex: 50,
|
|
31
|
+
...style,
|
|
32
|
+
}}
|
|
33
|
+
{...props}
|
|
34
|
+
ref={ref}
|
|
35
|
+
/>)
|
|
36
|
+
|
|
37
|
+
const AlertDialogContent = (
|
|
38
|
+
{
|
|
39
|
+
ref,
|
|
40
|
+
className,
|
|
41
|
+
style,
|
|
42
|
+
...props
|
|
43
|
+
}: React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content> & {
|
|
44
|
+
ref?: React.Ref<React.ElementRef<typeof AlertDialogPrimitive.Content>>;
|
|
45
|
+
}
|
|
46
|
+
) => (<AlertDialogPortal>
|
|
47
|
+
<AlertDialogOverlay />
|
|
48
|
+
<AlertDialogPrimitive.Content
|
|
49
|
+
ref={ref}
|
|
50
|
+
className={cn(
|
|
51
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
52
|
+
className
|
|
53
|
+
)}
|
|
54
|
+
style={{
|
|
55
|
+
backgroundColor: 'var(--color-background, #ffffff)',
|
|
56
|
+
border: '1px solid var(--color-border, #d4d4d4)',
|
|
57
|
+
borderRadius: '0.75rem',
|
|
58
|
+
boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
|
|
59
|
+
zIndex: 50,
|
|
60
|
+
...style,
|
|
61
|
+
}}
|
|
62
|
+
{...props}
|
|
63
|
+
/>
|
|
64
|
+
</AlertDialogPortal>)
|
|
65
|
+
|
|
66
|
+
const AlertDialogHeader = ({
|
|
67
|
+
className,
|
|
68
|
+
...props
|
|
69
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
70
|
+
<div
|
|
71
|
+
className={cn(
|
|
72
|
+
"flex flex-col space-y-2 text-center sm:text-left",
|
|
73
|
+
className
|
|
74
|
+
)}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
)
|
|
78
|
+
AlertDialogHeader.displayName = "AlertDialogHeader"
|
|
79
|
+
|
|
80
|
+
const AlertDialogFooter = ({
|
|
81
|
+
className,
|
|
82
|
+
...props
|
|
83
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
84
|
+
<div
|
|
85
|
+
className={cn(
|
|
86
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
87
|
+
className
|
|
88
|
+
)}
|
|
89
|
+
{...props}
|
|
90
|
+
/>
|
|
91
|
+
)
|
|
92
|
+
AlertDialogFooter.displayName = "AlertDialogFooter"
|
|
93
|
+
|
|
94
|
+
const AlertDialogTitle = (
|
|
95
|
+
{
|
|
96
|
+
ref,
|
|
97
|
+
className,
|
|
98
|
+
...props
|
|
99
|
+
}: React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title> & {
|
|
100
|
+
ref?: React.Ref<React.ElementRef<typeof AlertDialogPrimitive.Title>>;
|
|
101
|
+
}
|
|
102
|
+
) => (<AlertDialogPrimitive.Title
|
|
103
|
+
ref={ref}
|
|
104
|
+
className={cn("text-lg font-semibold", className)}
|
|
105
|
+
{...props}
|
|
106
|
+
/>)
|
|
107
|
+
|
|
108
|
+
const AlertDialogDescription = (
|
|
109
|
+
{
|
|
110
|
+
ref,
|
|
111
|
+
className,
|
|
112
|
+
...props
|
|
113
|
+
}: React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description> & {
|
|
114
|
+
ref?: React.Ref<React.ElementRef<typeof AlertDialogPrimitive.Description>>;
|
|
115
|
+
}
|
|
116
|
+
) => (<AlertDialogPrimitive.Description
|
|
117
|
+
ref={ref}
|
|
118
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
119
|
+
{...props}
|
|
120
|
+
/>)
|
|
121
|
+
|
|
122
|
+
const AlertDialogAction = (
|
|
123
|
+
{
|
|
124
|
+
ref,
|
|
125
|
+
className,
|
|
126
|
+
...props
|
|
127
|
+
}: React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action> & {
|
|
128
|
+
ref?: React.Ref<React.ElementRef<typeof AlertDialogPrimitive.Action>>;
|
|
129
|
+
}
|
|
130
|
+
) => (<AlertDialogPrimitive.Action
|
|
131
|
+
ref={ref}
|
|
132
|
+
className={cn(buttonVariants(), className)}
|
|
133
|
+
{...props}
|
|
134
|
+
/>)
|
|
135
|
+
|
|
136
|
+
const AlertDialogCancel = (
|
|
137
|
+
{
|
|
138
|
+
ref,
|
|
139
|
+
className,
|
|
140
|
+
...props
|
|
141
|
+
}: React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel> & {
|
|
142
|
+
ref?: React.Ref<React.ElementRef<typeof AlertDialogPrimitive.Cancel>>;
|
|
143
|
+
}
|
|
144
|
+
) => (<AlertDialogPrimitive.Cancel
|
|
145
|
+
ref={ref}
|
|
146
|
+
className={cn(
|
|
147
|
+
buttonVariants({ variant: "outline" }),
|
|
148
|
+
"mt-2 sm:mt-0",
|
|
149
|
+
className
|
|
150
|
+
)}
|
|
151
|
+
{...props}
|
|
152
|
+
/>)
|
|
153
|
+
|
|
154
|
+
export {
|
|
155
|
+
AlertDialog,
|
|
156
|
+
AlertDialogPortal,
|
|
157
|
+
AlertDialogOverlay,
|
|
158
|
+
AlertDialogTrigger,
|
|
159
|
+
AlertDialogContent,
|
|
160
|
+
AlertDialogHeader,
|
|
161
|
+
AlertDialogFooter,
|
|
162
|
+
AlertDialogTitle,
|
|
163
|
+
AlertDialogDescription,
|
|
164
|
+
AlertDialogAction,
|
|
165
|
+
AlertDialogCancel,
|
|
166
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
|
|
4
|
+
import { Check, ChevronRight, Circle } from "lucide-react"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
|
|
8
|
+
const ContextMenu = ContextMenuPrimitive.Root
|
|
9
|
+
|
|
10
|
+
const ContextMenuTrigger = ContextMenuPrimitive.Trigger
|
|
11
|
+
|
|
12
|
+
const ContextMenuGroup = ContextMenuPrimitive.Group
|
|
13
|
+
|
|
14
|
+
const ContextMenuPortal = ContextMenuPrimitive.Portal
|
|
15
|
+
|
|
16
|
+
const ContextMenuSub = ContextMenuPrimitive.Sub
|
|
17
|
+
|
|
18
|
+
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup
|
|
19
|
+
|
|
20
|
+
const ContextMenuSubTrigger = (
|
|
21
|
+
{
|
|
22
|
+
ref,
|
|
23
|
+
className,
|
|
24
|
+
inset,
|
|
25
|
+
children,
|
|
26
|
+
...props
|
|
27
|
+
}: React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
|
|
28
|
+
inset?: boolean
|
|
29
|
+
} & {
|
|
30
|
+
ref?: React.Ref<React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>>;
|
|
31
|
+
}
|
|
32
|
+
) => (<ContextMenuPrimitive.SubTrigger
|
|
33
|
+
ref={ref}
|
|
34
|
+
className={cn(
|
|
35
|
+
"flex cursor-default select-none items-center rounded-xs px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
36
|
+
inset && "pl-8",
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
>
|
|
41
|
+
{children}
|
|
42
|
+
<ChevronRight className="ml-auto h-4 w-4" />
|
|
43
|
+
</ContextMenuPrimitive.SubTrigger>)
|
|
44
|
+
|
|
45
|
+
const ContextMenuSubContent = (
|
|
46
|
+
{
|
|
47
|
+
ref,
|
|
48
|
+
className,
|
|
49
|
+
style,
|
|
50
|
+
...props
|
|
51
|
+
}: React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent> & {
|
|
52
|
+
ref?: React.Ref<React.ElementRef<typeof ContextMenuPrimitive.SubContent>>;
|
|
53
|
+
}
|
|
54
|
+
) => (<ContextMenuPrimitive.SubContent
|
|
55
|
+
ref={ref}
|
|
56
|
+
className={cn(
|
|
57
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=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",
|
|
58
|
+
className
|
|
59
|
+
)}
|
|
60
|
+
style={{
|
|
61
|
+
backgroundColor: 'var(--color-popover, #ffffff)',
|
|
62
|
+
color: 'var(--color-popover-foreground, #0a0a0a)',
|
|
63
|
+
border: '1px solid var(--color-border, #d4d4d4)',
|
|
64
|
+
borderRadius: 'var(--radius, 0.5rem)',
|
|
65
|
+
boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
|
|
66
|
+
zIndex: 50,
|
|
67
|
+
overflow: 'hidden',
|
|
68
|
+
...style,
|
|
69
|
+
}}
|
|
70
|
+
{...props}
|
|
71
|
+
/>)
|
|
72
|
+
|
|
73
|
+
const ContextMenuContent = (
|
|
74
|
+
{
|
|
75
|
+
ref,
|
|
76
|
+
className,
|
|
77
|
+
style,
|
|
78
|
+
...props
|
|
79
|
+
}: React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content> & {
|
|
80
|
+
ref?: React.Ref<React.ElementRef<typeof ContextMenuPrimitive.Content>>;
|
|
81
|
+
}
|
|
82
|
+
) => (<ContextMenuPrimitive.Portal>
|
|
83
|
+
<ContextMenuPrimitive.Content
|
|
84
|
+
ref={ref}
|
|
85
|
+
className={cn(
|
|
86
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=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",
|
|
87
|
+
className
|
|
88
|
+
)}
|
|
89
|
+
style={{
|
|
90
|
+
backgroundColor: 'var(--color-popover, #ffffff)',
|
|
91
|
+
color: 'var(--color-popover-foreground, #0a0a0a)',
|
|
92
|
+
border: '1px solid var(--color-border, #d4d4d4)',
|
|
93
|
+
borderRadius: 'var(--radius, 0.5rem)',
|
|
94
|
+
boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
|
|
95
|
+
zIndex: 50,
|
|
96
|
+
overflow: 'hidden',
|
|
97
|
+
...style,
|
|
98
|
+
}}
|
|
99
|
+
{...props}
|
|
100
|
+
/>
|
|
101
|
+
</ContextMenuPrimitive.Portal>)
|
|
102
|
+
|
|
103
|
+
const ContextMenuItem = (
|
|
104
|
+
{
|
|
105
|
+
ref,
|
|
106
|
+
className,
|
|
107
|
+
inset,
|
|
108
|
+
...props
|
|
109
|
+
}: React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
|
|
110
|
+
inset?: boolean
|
|
111
|
+
} & {
|
|
112
|
+
ref?: React.Ref<React.ElementRef<typeof ContextMenuPrimitive.Item>>;
|
|
113
|
+
}
|
|
114
|
+
) => (<ContextMenuPrimitive.Item
|
|
115
|
+
ref={ref}
|
|
116
|
+
className={cn(
|
|
117
|
+
"relative flex cursor-default select-none items-center rounded-xs px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
118
|
+
inset && "pl-8",
|
|
119
|
+
className
|
|
120
|
+
)}
|
|
121
|
+
{...props}
|
|
122
|
+
/>)
|
|
123
|
+
|
|
124
|
+
const ContextMenuCheckboxItem = (
|
|
125
|
+
{
|
|
126
|
+
ref,
|
|
127
|
+
className,
|
|
128
|
+
children,
|
|
129
|
+
checked,
|
|
130
|
+
...props
|
|
131
|
+
}: React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem> & {
|
|
132
|
+
ref?: React.Ref<React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>>;
|
|
133
|
+
}
|
|
134
|
+
) => (<ContextMenuPrimitive.CheckboxItem
|
|
135
|
+
ref={ref}
|
|
136
|
+
className={cn(
|
|
137
|
+
"relative flex cursor-default select-none items-center rounded-xs py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
138
|
+
className
|
|
139
|
+
)}
|
|
140
|
+
checked={checked}
|
|
141
|
+
{...props}
|
|
142
|
+
>
|
|
143
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
144
|
+
<ContextMenuPrimitive.ItemIndicator>
|
|
145
|
+
<Check className="h-4 w-4" />
|
|
146
|
+
</ContextMenuPrimitive.ItemIndicator>
|
|
147
|
+
</span>
|
|
148
|
+
{children}
|
|
149
|
+
</ContextMenuPrimitive.CheckboxItem>)
|
|
150
|
+
|
|
151
|
+
const ContextMenuRadioItem = (
|
|
152
|
+
{
|
|
153
|
+
ref,
|
|
154
|
+
className,
|
|
155
|
+
children,
|
|
156
|
+
...props
|
|
157
|
+
}: React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem> & {
|
|
158
|
+
ref?: React.Ref<React.ElementRef<typeof ContextMenuPrimitive.RadioItem>>;
|
|
159
|
+
}
|
|
160
|
+
) => (<ContextMenuPrimitive.RadioItem
|
|
161
|
+
ref={ref}
|
|
162
|
+
className={cn(
|
|
163
|
+
"relative flex cursor-default select-none items-center rounded-xs py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
164
|
+
className
|
|
165
|
+
)}
|
|
166
|
+
{...props}
|
|
167
|
+
>
|
|
168
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
169
|
+
<ContextMenuPrimitive.ItemIndicator>
|
|
170
|
+
<Circle className="h-2 w-2 fill-current" />
|
|
171
|
+
</ContextMenuPrimitive.ItemIndicator>
|
|
172
|
+
</span>
|
|
173
|
+
{children}
|
|
174
|
+
</ContextMenuPrimitive.RadioItem>)
|
|
175
|
+
|
|
176
|
+
const ContextMenuLabel = (
|
|
177
|
+
{
|
|
178
|
+
ref,
|
|
179
|
+
className,
|
|
180
|
+
inset,
|
|
181
|
+
...props
|
|
182
|
+
}: React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
|
|
183
|
+
inset?: boolean
|
|
184
|
+
} & {
|
|
185
|
+
ref?: React.Ref<React.ElementRef<typeof ContextMenuPrimitive.Label>>;
|
|
186
|
+
}
|
|
187
|
+
) => (<ContextMenuPrimitive.Label
|
|
188
|
+
ref={ref}
|
|
189
|
+
className={cn(
|
|
190
|
+
"px-2 py-1.5 text-sm font-semibold text-foreground",
|
|
191
|
+
inset && "pl-8",
|
|
192
|
+
className
|
|
193
|
+
)}
|
|
194
|
+
{...props}
|
|
195
|
+
/>)
|
|
196
|
+
|
|
197
|
+
const ContextMenuSeparator = (
|
|
198
|
+
{
|
|
199
|
+
ref,
|
|
200
|
+
className,
|
|
201
|
+
...props
|
|
202
|
+
}: React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator> & {
|
|
203
|
+
ref?: React.Ref<React.ElementRef<typeof ContextMenuPrimitive.Separator>>;
|
|
204
|
+
}
|
|
205
|
+
) => (<ContextMenuPrimitive.Separator
|
|
206
|
+
ref={ref}
|
|
207
|
+
className={cn("-mx-1 my-1 h-px bg-border", className)}
|
|
208
|
+
{...props}
|
|
209
|
+
/>)
|
|
210
|
+
|
|
211
|
+
const ContextMenuShortcut = ({
|
|
212
|
+
className,
|
|
213
|
+
...props
|
|
214
|
+
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
|
215
|
+
return (
|
|
216
|
+
<span
|
|
217
|
+
className={cn(
|
|
218
|
+
"ml-auto text-xs tracking-widest text-muted-foreground",
|
|
219
|
+
className
|
|
220
|
+
)}
|
|
221
|
+
{...props}
|
|
222
|
+
/>
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
ContextMenuShortcut.displayName = "ContextMenuShortcut"
|
|
226
|
+
|
|
227
|
+
export {
|
|
228
|
+
ContextMenu,
|
|
229
|
+
ContextMenuTrigger,
|
|
230
|
+
ContextMenuContent,
|
|
231
|
+
ContextMenuItem,
|
|
232
|
+
ContextMenuCheckboxItem,
|
|
233
|
+
ContextMenuRadioItem,
|
|
234
|
+
ContextMenuLabel,
|
|
235
|
+
ContextMenuSeparator,
|
|
236
|
+
ContextMenuShortcut,
|
|
237
|
+
ContextMenuGroup,
|
|
238
|
+
ContextMenuPortal,
|
|
239
|
+
ContextMenuSub,
|
|
240
|
+
ContextMenuSubContent,
|
|
241
|
+
ContextMenuSubTrigger,
|
|
242
|
+
ContextMenuRadioGroup,
|
|
243
|
+
}
|