@algorithm-shift/design-system 1.2.11 → 1.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/components/Basic/Image/Image.stories.tsx +13 -0
- package/src/components/Basic/Image/Image.tsx +52 -0
- package/src/components/Basic/Shape/Shape.tsx +16 -0
- package/src/components/Basic/Typography/Typography.stories.tsx +18 -0
- package/src/components/Basic/Typography/Typography.tsx +31 -0
- package/src/components/DataDisplay/Table/Table.stories.tsx +35 -0
- package/src/components/DataDisplay/Table/Table.tsx +15 -0
- package/src/components/DataDisplay/index.ts +1 -0
- package/src/components/Global/SelectDropdown.tsx +61 -0
- package/src/components/Global/TinyMceEditor.tsx +81 -0
- package/src/components/Inputs/Checkbox/Checkbox.tsx +18 -0
- package/src/components/Inputs/DatePicker/DatePicker.tsx +81 -0
- package/src/components/Inputs/DateRange/DateRange.tsx +64 -0
- package/src/components/Inputs/Dropdown/Dropdown.tsx +62 -0
- package/src/components/Inputs/EmailInput/EmailInput.tsx +64 -0
- package/src/components/Inputs/FileInput/FileInput.tsx +24 -0
- package/src/components/Inputs/MultiCheckbox/MultiCheckbox.tsx +24 -0
- package/src/components/Inputs/NumberInput/NumberInput.tsx +66 -0
- package/src/components/Inputs/PasswordInput/PasswordInput.tsx +66 -0
- package/src/components/Inputs/PhoneInput/PhoneInput.tsx +58 -0
- package/src/components/Inputs/RadioInput/RadioInput.tsx +24 -0
- package/src/components/Inputs/RichText/RichText.tsx +10 -0
- package/src/components/Inputs/SearchInput/SearchInput.tsx +64 -0
- package/src/components/Inputs/SwitchToggle/SwitchToggle.tsx +22 -0
- package/src/components/Inputs/TextInput/TextInput.tsx +59 -0
- package/src/components/Inputs/Textarea/Textarea.tsx +58 -0
- package/src/components/Inputs/UrlInput/UrlInput.tsx +66 -0
- package/src/components/Layout/Flex.tsx +13 -0
- package/src/components/Layout/Grid.tsx +13 -0
- package/src/components/Navigation/Logo/Logo.stories.tsx +25 -0
- package/src/components/Navigation/Logo/Logo.tsx +33 -0
- package/src/components/Navigation/Notification/Notification.stories.tsx +20 -0
- package/src/components/Navigation/Notification/Notification.tsx +20 -0
- package/src/components/Navigation/Profile/Profile.stories.tsx +20 -0
- package/src/components/Navigation/Profile/Profile.tsx +25 -0
- package/src/components/Navigation/Spacer/Spacer.stories.tsx +18 -0
- package/src/components/Navigation/Spacer/Spacer.tsx +11 -0
- package/src/components/Navigation/Stages/Stages.stories.tsx +24 -0
- package/src/components/Navigation/Stages/Stages.tsx +49 -0
- package/src/components/Navigation/Tabs/Tabs.stories.tsx +38 -0
- package/src/components/Navigation/Tabs/Tabs.tsx +72 -0
- package/src/components/Navigation/index.ts +6 -0
- package/src/components/index.ts +27 -0
- package/src/components/ui/button.stories.tsx +24 -0
- package/src/components/ui/button.tsx +59 -0
- package/src/components/ui/calendar.tsx +211 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/data-table.tsx +138 -0
- package/src/components/ui/dropdown-menu.tsx +258 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +22 -0
- package/src/components/ui/popover.tsx +46 -0
- package/src/components/ui/radio-group.tsx +43 -0
- package/src/components/ui/select.tsx +183 -0
- package/src/components/ui/switch.tsx +29 -0
- package/src/components/ui/table.tsx +121 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/global.css +6 -0
- package/src/index.css +119 -0
- package/src/index.ts +4 -0
- package/src/lib/utils.ts +6 -0
- package/src/stories/Button.stories.ts +54 -0
- package/src/stories/Button.tsx +35 -0
- package/src/stories/Configure.mdx +364 -0
- package/src/stories/Header.stories.ts +34 -0
- package/src/stories/Header.tsx +54 -0
- package/src/stories/Page.stories.ts +33 -0
- package/src/stories/Page.tsx +73 -0
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +1 -0
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +1 -0
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +1 -0
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +1 -0
- package/src/stories/assets/youtube.svg +1 -0
- package/src/stories/button.css +30 -0
- package/src/stories/header.css +32 -0
- package/src/stories/page.css +68 -0
- package/src/types/global.d.ts +85 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
+
|
|
3
|
+
import Spacer from './Spacer';
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
component: Spacer,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
} satisfies Meta<typeof Spacer>;
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof meta>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {
|
|
15
|
+
args: {
|
|
16
|
+
className: 'mt-[20px] mb-[20px] border-[1px] border-[#E0E0E0] border-solid group relative w-full',
|
|
17
|
+
},
|
|
18
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
+
|
|
3
|
+
import Stages from './Stages';
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
component: Stages,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
} satisfies Meta<typeof Stages>;
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof meta>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {
|
|
15
|
+
args: {
|
|
16
|
+
stages: [
|
|
17
|
+
{ id: 1, header: 'Stage 1', isActive: true },
|
|
18
|
+
{ id: 2, header: 'Stage 2', isActive: false },
|
|
19
|
+
{ id: 3, header: 'Stage 3', isActive: false },
|
|
20
|
+
],
|
|
21
|
+
isShowBtn: true,
|
|
22
|
+
buttonText: 'Next',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { StagesProps } from '@/types/global';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
const StagesComponent = ({ stages, isShowBtn, buttonText, className, style }: StagesProps) => {
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div className={className} style={style}>
|
|
10
|
+
<div className="flex items-center justify-between bg-gray-50 p-2 rounded-lg border border-gray-200 w-full">
|
|
11
|
+
<div className="flex items-center">
|
|
12
|
+
<button className="p-2 hover:bg-gray-100 rounded">
|
|
13
|
+
<svg className="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
14
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
15
|
+
</svg>
|
|
16
|
+
</button>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div className="flex items-center flex-1 px-2">
|
|
20
|
+
{stages?.length > 0 && stages?.map((stage: any, index: number) => (
|
|
21
|
+
<React.Fragment key={stage.id}>
|
|
22
|
+
<button
|
|
23
|
+
className={`
|
|
24
|
+
min-w-[120px] px-4 py-2 rounded-full text-sm font-medium transition-colors duration-200 whitespace-nowrap ${stage.isActive ? 'bg-[#034486] text-white shadow-md' : 'bg-white text-gray-700 hover:bg-gray-100 border border-gray-200'}`}
|
|
25
|
+
>
|
|
26
|
+
{stage.header}
|
|
27
|
+
</button>
|
|
28
|
+
|
|
29
|
+
{index < stages.length - 1 && (
|
|
30
|
+
<div className="flex-shrink-0 w-3 h-px bg-gray-300"></div>
|
|
31
|
+
)}
|
|
32
|
+
</React.Fragment>
|
|
33
|
+
))}
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
{isShowBtn && (
|
|
37
|
+
<div className="flex items-center">
|
|
38
|
+
<button className="bg-[#034486] text-white px-6 py-2 rounded-lg text-sm font-medium transition-colors duration-200 shadow-sm">
|
|
39
|
+
{buttonText}
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
42
|
+
)}
|
|
43
|
+
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default StagesComponent;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
+
|
|
3
|
+
import Tabs from './Tabs';
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
component: Tabs,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
} satisfies Meta<typeof Tabs>;
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof meta>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {
|
|
15
|
+
args: {
|
|
16
|
+
tabs: [
|
|
17
|
+
{ header: 'Home', isActive: true },
|
|
18
|
+
{ header: 'Profile' },
|
|
19
|
+
{
|
|
20
|
+
header: 'Settings',
|
|
21
|
+
isDropDown: true,
|
|
22
|
+
children: [
|
|
23
|
+
{ id: 1, header: 'Account' },
|
|
24
|
+
{ id: 2, header: 'Privacy' },
|
|
25
|
+
{ id: 3, header: 'Notifications' },
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{ header: 'Help' },
|
|
29
|
+
],
|
|
30
|
+
className: 'flex col-span-12 col-start-1 items-center h-50 visible bg-[#12715b] p-auto pr-[10px] pt-[10px] pb-[10px] pl-[10px] sm:flex sm:grid-cols-12 sm:col-span-12 sm:col-start-1 sm:items-center sm:h-50 sm:visible sm:bg-[#12715b] sm:p-auto sm:pr-[10px] sm:pt-[10px] sm:pb-[10px] sm:pl-[10px] md:flex md:grid-cols-12 md:col-span-12 md:col-start-1 md:items-center md:h-50 md:visible md:bg-[#12715b] md:p-auto md:pr-[10px] md:pt-[10px] md:pb-[10px] md:pl-[10px] group relative w-full transition-opacity duration:300 pointer-events-none grid-cols-12',
|
|
31
|
+
style: {
|
|
32
|
+
'height': '50px',
|
|
33
|
+
'width': 'auto',
|
|
34
|
+
'backgroundColor': 'rgb(18, 113, 91)',
|
|
35
|
+
'pointerEvents': 'none',
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ChevronDown } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
DropdownMenu,
|
|
7
|
+
DropdownMenuContent,
|
|
8
|
+
DropdownMenuItem,
|
|
9
|
+
DropdownMenuTrigger,
|
|
10
|
+
} from '@/components/ui/dropdown-menu';
|
|
11
|
+
import { TabsProps } from '@/types/global';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
const Tabs = ({ tabs, className, style }: TabsProps) => {
|
|
15
|
+
const rawTabs = Array.isArray(tabs) ? tabs : [];
|
|
16
|
+
const baseClasses =
|
|
17
|
+
'text-[12px] text-[#E9E9E9] p-2 text-center rounded-md transition-colors border-none outline-none focus:outline-none focus:ring-0 focus:ring-offset-0 cursor-pointer select-none ';
|
|
18
|
+
const activeClasses = 'bg-white/10 text-white';
|
|
19
|
+
const hoverClasses = 'hover:bg-white/5';
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className={className} style={style}>
|
|
23
|
+
{rawTabs.map((tab: any, index: number) => {
|
|
24
|
+
const finalClasses = [
|
|
25
|
+
baseClasses,
|
|
26
|
+
tab.isActive ? activeClasses : hoverClasses,
|
|
27
|
+
tab.className || '',
|
|
28
|
+
].join(' ');
|
|
29
|
+
|
|
30
|
+
const hasDropdown = Array.isArray(tab.children) && tab.children.length > 0 && tab.isDropDown;
|
|
31
|
+
|
|
32
|
+
if (hasDropdown) {
|
|
33
|
+
return (
|
|
34
|
+
<DropdownMenu key={index}>
|
|
35
|
+
<DropdownMenuTrigger
|
|
36
|
+
className={`${finalClasses} inline-flex items-center gap-1`}
|
|
37
|
+
>
|
|
38
|
+
{tab.header}
|
|
39
|
+
<ChevronDown className="h-4 w-4 opacity-80" />
|
|
40
|
+
</DropdownMenuTrigger>
|
|
41
|
+
|
|
42
|
+
<DropdownMenuContent
|
|
43
|
+
align="start"
|
|
44
|
+
sideOffset={6}
|
|
45
|
+
className="z-50 min-w-[160px] rounded-md border border-gray-200 bg-white p-1 shadow-lg"
|
|
46
|
+
>
|
|
47
|
+
{tab.children.map((item: any) => (
|
|
48
|
+
<DropdownMenuItem
|
|
49
|
+
key={item.id}
|
|
50
|
+
asChild
|
|
51
|
+
className="cursor-pointer rounded-sm px-3 py-2 text-[12px] text-gray-800 hover:bg-gray-100 focus:bg-gray-100"
|
|
52
|
+
>
|
|
53
|
+
{item.header}
|
|
54
|
+
</DropdownMenuItem>
|
|
55
|
+
))}
|
|
56
|
+
</DropdownMenuContent>
|
|
57
|
+
</DropdownMenu>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div key={index} className={finalClasses} style={{ backgroundColor: 'transparent', border: 'none', ...tab.style }} role="button" tabIndex={0}>
|
|
63
|
+
{tab.header}
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
})}
|
|
67
|
+
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export default Tabs;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as Tabs } from './Tabs/Tabs';
|
|
2
|
+
export { default as Stages } from './Stages/Stages';
|
|
3
|
+
export { default as Spacer } from './Spacer/Spacer';
|
|
4
|
+
export { default as Profile } from './Profile/Profile';
|
|
5
|
+
export { default as Notification } from './Notification/Notification';
|
|
6
|
+
export { default as Logo } from './Logo/Logo';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export * from './ui/button'
|
|
2
|
+
|
|
3
|
+
export { default as FlexLayout } from './Layout/Flex'
|
|
4
|
+
export { default as GridLayout } from './Layout/Grid'
|
|
5
|
+
|
|
6
|
+
export { default as Typography } from './Basic/Typography/Typography'
|
|
7
|
+
export { default as Shape } from './Basic/Shape/Shape'
|
|
8
|
+
export { default as Image } from './Basic/Image/Image'
|
|
9
|
+
export * from './Inputs/TextInput/TextInput'
|
|
10
|
+
export * from './Inputs/NumberInput/NumberInput'
|
|
11
|
+
export * from './Inputs/EmailInput/EmailInput'
|
|
12
|
+
export * from './Inputs/PasswordInput/PasswordInput'
|
|
13
|
+
export * from './Inputs/Textarea/Textarea'
|
|
14
|
+
export * from './Inputs/UrlInput/UrlInput'
|
|
15
|
+
export * from './Inputs/Checkbox/Checkbox'
|
|
16
|
+
export * from './Inputs/RadioInput/RadioInput'
|
|
17
|
+
export * from './Inputs/MultiCheckbox/MultiCheckbox'
|
|
18
|
+
export { default as RichText } from './Inputs/RichText/RichText'
|
|
19
|
+
export * from './Inputs/Dropdown/Dropdown'
|
|
20
|
+
export * from './Inputs/SwitchToggle/SwitchToggle'
|
|
21
|
+
export * from './Inputs/PhoneInput/PhoneInput'
|
|
22
|
+
export * from './Inputs/SearchInput/SearchInput'
|
|
23
|
+
export * from './Inputs/FileInput/FileInput'
|
|
24
|
+
export { default as DatePicker } from './Inputs/DatePicker/DatePicker'
|
|
25
|
+
export { default as DateRange } from './Inputs/DateRange/DateRange'
|
|
26
|
+
export { Table } from './DataDisplay';
|
|
27
|
+
export { Stages, Tabs, Spacer, Profile, Notification, Logo } from './Navigation';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
+
|
|
3
|
+
import { Button } from './button';
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
component: Button,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
} satisfies Meta<typeof Button>;
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof meta>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = { args: { children: "Default" } };
|
|
15
|
+
|
|
16
|
+
export const Outline: Story = { args: { variant: "outline", children: "Outline" } };
|
|
17
|
+
|
|
18
|
+
export const Destructive: Story = { args: { variant: "destructive", children: "Destructive" } };
|
|
19
|
+
|
|
20
|
+
export const Secondary: Story = { args: { variant: "secondary", children: "Secondary" } };
|
|
21
|
+
|
|
22
|
+
export const Ghost: Story = { args: { variant: "ghost", children: "Ghost" } };
|
|
23
|
+
|
|
24
|
+
export const Link: Story = { args: { variant: "link", children: "Link" } };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
14
|
+
destructive:
|
|
15
|
+
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
16
|
+
outline:
|
|
17
|
+
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
18
|
+
secondary:
|
|
19
|
+
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
20
|
+
ghost:
|
|
21
|
+
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
22
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
23
|
+
},
|
|
24
|
+
size: {
|
|
25
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
26
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
27
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
28
|
+
icon: "size-9",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
defaultVariants: {
|
|
32
|
+
variant: "default",
|
|
33
|
+
size: "default",
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
function Button({
|
|
39
|
+
className,
|
|
40
|
+
variant,
|
|
41
|
+
size,
|
|
42
|
+
asChild = false,
|
|
43
|
+
...props
|
|
44
|
+
}: React.ComponentProps<"button"> &
|
|
45
|
+
VariantProps<typeof buttonVariants> & {
|
|
46
|
+
asChild?: boolean
|
|
47
|
+
}) {
|
|
48
|
+
const Comp = asChild ? Slot : "button"
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Comp
|
|
52
|
+
data-slot="button"
|
|
53
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { Button, buttonVariants }
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import {
|
|
3
|
+
ChevronDownIcon,
|
|
4
|
+
ChevronLeftIcon,
|
|
5
|
+
ChevronRightIcon,
|
|
6
|
+
} from "lucide-react"
|
|
7
|
+
import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker"
|
|
8
|
+
|
|
9
|
+
import { cn } from "@/lib/utils"
|
|
10
|
+
import { Button, buttonVariants } from "@/components/ui/button"
|
|
11
|
+
|
|
12
|
+
function Calendar({
|
|
13
|
+
className,
|
|
14
|
+
classNames,
|
|
15
|
+
showOutsideDays = true,
|
|
16
|
+
captionLayout = "label",
|
|
17
|
+
buttonVariant = "ghost",
|
|
18
|
+
formatters,
|
|
19
|
+
components,
|
|
20
|
+
...props
|
|
21
|
+
}: React.ComponentProps<typeof DayPicker> & {
|
|
22
|
+
buttonVariant?: React.ComponentProps<typeof Button>["variant"]
|
|
23
|
+
}) {
|
|
24
|
+
const defaultClassNames = getDefaultClassNames()
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<DayPicker
|
|
28
|
+
showOutsideDays={showOutsideDays}
|
|
29
|
+
className={cn(
|
|
30
|
+
"bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
|
|
31
|
+
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
|
32
|
+
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
|
33
|
+
className
|
|
34
|
+
)}
|
|
35
|
+
captionLayout={captionLayout}
|
|
36
|
+
formatters={{
|
|
37
|
+
formatMonthDropdown: (date) =>
|
|
38
|
+
date.toLocaleString("default", { month: "short" }),
|
|
39
|
+
...formatters,
|
|
40
|
+
}}
|
|
41
|
+
classNames={{
|
|
42
|
+
root: cn("w-fit", defaultClassNames.root),
|
|
43
|
+
months: cn(
|
|
44
|
+
"flex gap-4 flex-col md:flex-row relative",
|
|
45
|
+
defaultClassNames.months
|
|
46
|
+
),
|
|
47
|
+
month: cn("flex flex-col w-full gap-4", defaultClassNames.month),
|
|
48
|
+
nav: cn(
|
|
49
|
+
"flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between",
|
|
50
|
+
defaultClassNames.nav
|
|
51
|
+
),
|
|
52
|
+
button_previous: cn(
|
|
53
|
+
buttonVariants({ variant: buttonVariant }),
|
|
54
|
+
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
|
|
55
|
+
defaultClassNames.button_previous
|
|
56
|
+
),
|
|
57
|
+
button_next: cn(
|
|
58
|
+
buttonVariants({ variant: buttonVariant }),
|
|
59
|
+
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
|
|
60
|
+
defaultClassNames.button_next
|
|
61
|
+
),
|
|
62
|
+
month_caption: cn(
|
|
63
|
+
"flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)",
|
|
64
|
+
defaultClassNames.month_caption
|
|
65
|
+
),
|
|
66
|
+
dropdowns: cn(
|
|
67
|
+
"w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5",
|
|
68
|
+
defaultClassNames.dropdowns
|
|
69
|
+
),
|
|
70
|
+
dropdown_root: cn(
|
|
71
|
+
"relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md",
|
|
72
|
+
defaultClassNames.dropdown_root
|
|
73
|
+
),
|
|
74
|
+
dropdown: cn(
|
|
75
|
+
"absolute bg-popover inset-0 opacity-0",
|
|
76
|
+
defaultClassNames.dropdown
|
|
77
|
+
),
|
|
78
|
+
caption_label: cn(
|
|
79
|
+
"select-none font-medium",
|
|
80
|
+
captionLayout === "label"
|
|
81
|
+
? "text-sm"
|
|
82
|
+
: "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5",
|
|
83
|
+
defaultClassNames.caption_label
|
|
84
|
+
),
|
|
85
|
+
table: "w-full border-collapse",
|
|
86
|
+
weekdays: cn("flex", defaultClassNames.weekdays),
|
|
87
|
+
weekday: cn(
|
|
88
|
+
"text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none",
|
|
89
|
+
defaultClassNames.weekday
|
|
90
|
+
),
|
|
91
|
+
week: cn("flex w-full mt-2", defaultClassNames.week),
|
|
92
|
+
week_number_header: cn(
|
|
93
|
+
"select-none w-(--cell-size)",
|
|
94
|
+
defaultClassNames.week_number_header
|
|
95
|
+
),
|
|
96
|
+
week_number: cn(
|
|
97
|
+
"text-[0.8rem] select-none text-muted-foreground",
|
|
98
|
+
defaultClassNames.week_number
|
|
99
|
+
),
|
|
100
|
+
day: cn(
|
|
101
|
+
"relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
|
|
102
|
+
defaultClassNames.day
|
|
103
|
+
),
|
|
104
|
+
range_start: cn(
|
|
105
|
+
"rounded-l-md bg-accent",
|
|
106
|
+
defaultClassNames.range_start
|
|
107
|
+
),
|
|
108
|
+
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
|
109
|
+
range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
|
|
110
|
+
today: cn(
|
|
111
|
+
"bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none",
|
|
112
|
+
defaultClassNames.today
|
|
113
|
+
),
|
|
114
|
+
outside: cn(
|
|
115
|
+
"text-muted-foreground aria-selected:text-muted-foreground",
|
|
116
|
+
defaultClassNames.outside
|
|
117
|
+
),
|
|
118
|
+
disabled: cn(
|
|
119
|
+
"text-muted-foreground opacity-50",
|
|
120
|
+
defaultClassNames.disabled
|
|
121
|
+
),
|
|
122
|
+
hidden: cn("invisible", defaultClassNames.hidden),
|
|
123
|
+
...classNames,
|
|
124
|
+
}}
|
|
125
|
+
components={{
|
|
126
|
+
Root: ({ className, rootRef, ...props }) => {
|
|
127
|
+
return (
|
|
128
|
+
<div
|
|
129
|
+
data-slot="calendar"
|
|
130
|
+
ref={rootRef}
|
|
131
|
+
className={cn(className)}
|
|
132
|
+
{...props}
|
|
133
|
+
/>
|
|
134
|
+
)
|
|
135
|
+
},
|
|
136
|
+
Chevron: ({ className, orientation, ...props }) => {
|
|
137
|
+
if (orientation === "left") {
|
|
138
|
+
return (
|
|
139
|
+
<ChevronLeftIcon className={cn("size-4", className)} {...props} />
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (orientation === "right") {
|
|
144
|
+
return (
|
|
145
|
+
<ChevronRightIcon
|
|
146
|
+
className={cn("size-4", className)}
|
|
147
|
+
{...props}
|
|
148
|
+
/>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<ChevronDownIcon className={cn("size-4", className)} {...props} />
|
|
154
|
+
)
|
|
155
|
+
},
|
|
156
|
+
DayButton: CalendarDayButton,
|
|
157
|
+
WeekNumber: ({ children, ...props }) => {
|
|
158
|
+
return (
|
|
159
|
+
<td {...props}>
|
|
160
|
+
<div className="flex size-(--cell-size) items-center justify-center text-center">
|
|
161
|
+
{children}
|
|
162
|
+
</div>
|
|
163
|
+
</td>
|
|
164
|
+
)
|
|
165
|
+
},
|
|
166
|
+
...components,
|
|
167
|
+
}}
|
|
168
|
+
{...props}
|
|
169
|
+
/>
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function CalendarDayButton({
|
|
174
|
+
className,
|
|
175
|
+
day,
|
|
176
|
+
modifiers,
|
|
177
|
+
...props
|
|
178
|
+
}: React.ComponentProps<typeof DayButton>) {
|
|
179
|
+
const defaultClassNames = getDefaultClassNames()
|
|
180
|
+
|
|
181
|
+
const ref = React.useRef<HTMLButtonElement>(null)
|
|
182
|
+
React.useEffect(() => {
|
|
183
|
+
if (modifiers.focused) ref.current?.focus()
|
|
184
|
+
}, [modifiers.focused])
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<Button
|
|
188
|
+
ref={ref}
|
|
189
|
+
variant="ghost"
|
|
190
|
+
size="icon"
|
|
191
|
+
data-day={day.date.toLocaleDateString()}
|
|
192
|
+
data-selected-single={
|
|
193
|
+
modifiers.selected &&
|
|
194
|
+
!modifiers.range_start &&
|
|
195
|
+
!modifiers.range_end &&
|
|
196
|
+
!modifiers.range_middle
|
|
197
|
+
}
|
|
198
|
+
data-range-start={modifiers.range_start}
|
|
199
|
+
data-range-end={modifiers.range_end}
|
|
200
|
+
data-range-middle={modifiers.range_middle}
|
|
201
|
+
className={cn(
|
|
202
|
+
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70",
|
|
203
|
+
defaultClassNames.day,
|
|
204
|
+
className
|
|
205
|
+
)}
|
|
206
|
+
{...props}
|
|
207
|
+
/>
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export { Calendar, CalendarDayButton }
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
|
3
|
+
import { CheckIcon } from "lucide-react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
function Checkbox({
|
|
8
|
+
className,
|
|
9
|
+
...props
|
|
10
|
+
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
|
11
|
+
return (
|
|
12
|
+
<CheckboxPrimitive.Root
|
|
13
|
+
data-slot="checkbox"
|
|
14
|
+
className={cn(
|
|
15
|
+
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
16
|
+
className
|
|
17
|
+
)}
|
|
18
|
+
{...props}
|
|
19
|
+
>
|
|
20
|
+
<CheckboxPrimitive.Indicator
|
|
21
|
+
data-slot="checkbox-indicator"
|
|
22
|
+
className="flex items-center justify-center text-current transition-none"
|
|
23
|
+
>
|
|
24
|
+
<CheckIcon className="size-3.5" />
|
|
25
|
+
</CheckboxPrimitive.Indicator>
|
|
26
|
+
</CheckboxPrimitive.Root>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { Checkbox }
|