@a-type/ui 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/datePicker/DatePicker.d.ts +19 -0
- package/dist/cjs/components/datePicker/DatePicker.js +97 -0
- package/dist/cjs/components/datePicker/DatePicker.js.map +1 -0
- package/dist/cjs/components/datePicker/DatePicker.stories.d.ts +16 -0
- package/dist/cjs/components/datePicker/DatePicker.stories.js +28 -0
- package/dist/cjs/components/datePicker/DatePicker.stories.js.map +1 -0
- package/dist/cjs/components/datePicker/index.d.ts +1 -0
- package/dist/cjs/components/datePicker/index.js +3 -0
- package/dist/cjs/components/datePicker/index.js.map +1 -0
- package/dist/esm/components/datePicker/DatePicker.d.ts +19 -0
- package/dist/esm/components/datePicker/DatePicker.js +89 -0
- package/dist/esm/components/datePicker/DatePicker.js.map +1 -0
- package/dist/esm/components/datePicker/DatePicker.stories.d.ts +16 -0
- package/dist/esm/components/datePicker/DatePicker.stories.js +25 -0
- package/dist/esm/components/datePicker/DatePicker.stories.js.map +1 -0
- package/dist/esm/components/datePicker/index.d.ts +1 -0
- package/dist/esm/components/datePicker/index.js +2 -0
- package/dist/esm/components/datePicker/index.js.map +1 -0
- package/package.json +4 -2
- package/src/components/actions/ActionBar.tsx +38 -0
- package/src/components/actions/ActionButton.tsx +59 -0
- package/src/components/actions/index.ts +2 -0
- package/src/components/actions.ts +1 -0
- package/src/components/avatar/Avatar.tsx +62 -0
- package/src/components/avatar/AvatarList.tsx +71 -0
- package/src/components/avatar/index.ts +2 -0
- package/src/components/avatar.ts +1 -0
- package/src/components/button/Button.stories.tsx +20 -0
- package/src/components/button/Button.tsx +66 -0
- package/src/components/button/ConfirmedButton.tsx +66 -0
- package/src/components/button/classes.tsx +56 -0
- package/src/components/button/index.ts +3 -0
- package/src/components/button.ts +1 -0
- package/src/components/camera/Camera.stories.tsx +40 -0
- package/src/components/camera/Camera.tsx +215 -0
- package/src/components/camera/index.ts +1 -0
- package/src/components/camera.ts +1 -0
- package/src/components/card/Card.stories.tsx +41 -0
- package/src/components/card/Card.tsx +68 -0
- package/src/components/card/index.ts +1 -0
- package/src/components/card.ts +1 -0
- package/src/components/checkbox/Checkbox.tsx +46 -0
- package/src/components/checkbox/index.ts +1 -0
- package/src/components/checkbox.ts +1 -0
- package/src/components/chip/Chip.tsx +29 -0
- package/src/components/chip/index.ts +1 -0
- package/src/components/chip.ts +1 -0
- package/src/components/collapsible/Collapsible.tsx +48 -0
- package/src/components/collapsible/index.ts +1 -0
- package/src/components/collapsible.ts +1 -0
- package/src/components/colorPicker/ColorPicker.tsx +82 -0
- package/src/components/colorPicker/index.ts +1 -0
- package/src/components/colorPicker.ts +1 -0
- package/src/components/contextMenu/contextMenu.tsx +43 -0
- package/src/components/contextMenu.ts +1 -0
- package/src/components/datePicker/DatePicker.stories.tsx +33 -0
- package/src/components/datePicker/DatePicker.tsx +258 -0
- package/src/components/datePicker/index.ts +0 -0
- package/src/components/dialog/Dialog.stories.tsx +38 -0
- package/src/components/dialog/Dialog.tsx +267 -0
- package/src/components/dialog/index.ts +1 -0
- package/src/components/dialog.ts +1 -0
- package/src/components/divider/Divider.tsx +26 -0
- package/src/components/divider/index.ts +1 -0
- package/src/components/divider.ts +1 -0
- package/src/components/dropdownMenu/DropdownMenu.stories.tsx +47 -0
- package/src/components/dropdownMenu/DropdownMenu.tsx +89 -0
- package/src/components/dropdownMenu/index.ts +1 -0
- package/src/components/dropdownMenu.ts +1 -0
- package/src/components/errorBoundary/ErrorBoundary.tsx +23 -0
- package/src/components/errorBoundary/index.ts +1 -0
- package/src/components/errorBoundary.ts +1 -0
- package/src/components/forms/Form.tsx +9 -0
- package/src/components/forms/FormikForm.tsx +41 -0
- package/src/components/forms/SubmitButton.tsx +15 -0
- package/src/components/forms/TextField.tsx +112 -0
- package/src/components/forms/index.tsx +4 -0
- package/src/components/forms.ts +1 -0
- package/src/components/icon/Icon.tsx +28 -0
- package/src/components/icon/generated/IconSpritesheet.tsx +442 -0
- package/src/components/icon/generated/iconNames.ts +44 -0
- package/src/components/icon/index.ts +3 -0
- package/src/components/icon.ts +1 -0
- package/src/components/imageUploader/ImageUploader.stories.tsx +39 -0
- package/src/components/imageUploader/ImageUploader.tsx +203 -0
- package/src/components/imageUploader/UploadIcon.tsx +23 -0
- package/src/components/imageUploader/index.ts +1 -0
- package/src/components/imageUploader.ts +1 -0
- package/src/components/infiniteLoadTrigger/InfiniteLoadTrigger.tsx +38 -0
- package/src/components/infiniteLoadTrigger.ts +1 -0
- package/src/components/input/Input.stories.tsx +17 -0
- package/src/components/input/Input.tsx +32 -0
- package/src/components/input/index.ts +1 -0
- package/src/components/input.ts +1 -0
- package/src/components/layouts/PageContent.tsx +51 -0
- package/src/components/layouts/PageFixedArea.tsx +17 -0
- package/src/components/layouts/PageNav.tsx +23 -0
- package/src/components/layouts/PageNowPlaying.tsx +24 -0
- package/src/components/layouts/PageRoot.tsx +29 -0
- package/src/components/layouts/PageSection.tsx +23 -0
- package/src/components/layouts/index.tsx +6 -0
- package/src/components/layouts.ts +1 -0
- package/src/components/liveUpdateTextField/LiveUpdateTextField.tsx +132 -0
- package/src/components/liveUpdateTextField/index.ts +1 -0
- package/src/components/liveUpdateTextField.ts +1 -0
- package/src/components/navBar/NavBar.tsx +59 -0
- package/src/components/navBar/index.ts +1 -0
- package/src/components/navBar.ts +1 -0
- package/src/components/note/Note.tsx +21 -0
- package/src/components/note/index.ts +1 -0
- package/src/components/note.ts +1 -0
- package/src/components/numberStepper/NumberStepper.stories.tsx +21 -0
- package/src/components/numberStepper/NumberStepper.tsx +74 -0
- package/src/components/numberStepper/index.ts +1 -0
- package/src/components/numberStepper.ts +1 -0
- package/src/components/particles/ParticleContext.tsx +11 -0
- package/src/components/particles/ParticleLayer.stories.tsx +46 -0
- package/src/components/particles/ParticleLayer.tsx +28 -0
- package/src/components/particles/index.ts +7 -0
- package/src/components/particles/particlesState.ts +502 -0
- package/src/components/particles.ts +1 -0
- package/src/components/peek/Peek.tsx +74 -0
- package/src/components/peek/index.ts +1 -0
- package/src/components/peek.ts +1 -0
- package/src/components/popover/Popover.tsx +84 -0
- package/src/components/popover/index.ts +1 -0
- package/src/components/popover.ts +1 -0
- package/src/components/relativeTime/RelativeTime.tsx +43 -0
- package/src/components/relativeTime/index.ts +1 -0
- package/src/components/relativeTime.ts +1 -0
- package/src/components/richEditor/EditorContent.tsx +4 -0
- package/src/components/richEditor/RichEditor.tsx +38 -0
- package/src/components/richEditor/index.ts +1 -0
- package/src/components/richEditor.ts +1 -0
- package/src/components/select/Select.tsx +247 -0
- package/src/components/select/index.ts +1 -0
- package/src/components/select.ts +1 -0
- package/src/components/skeletons/skeletons.tsx +27 -0
- package/src/components/skeletons.ts +1 -0
- package/src/components/spinner/Spinner.tsx +59 -0
- package/src/components/spinner/index.ts +1 -0
- package/src/components/spinner.ts +1 -0
- package/src/components/switch/Switch.tsx +23 -0
- package/src/components/switch/index.ts +1 -0
- package/src/components/switch.ts +1 -0
- package/src/components/tabs/tabs.tsx +18 -0
- package/src/components/tabs.ts +1 -0
- package/src/components/textArea/TextArea.stories.tsx +21 -0
- package/src/components/textArea/TextArea.tsx +58 -0
- package/src/components/textArea/index.ts +1 -0
- package/src/components/textArea.ts +1 -0
- package/src/components/toggleGroup/toggleGroup.tsx +11 -0
- package/src/components/toggleGroup.ts +1 -0
- package/src/components/tooltip/Tooltip.tsx +56 -0
- package/src/components/tooltip/index.ts +1 -0
- package/src/components/tooltip.ts +1 -0
- package/src/components/typography/index.ts +1 -0
- package/src/components/typography/typography.tsx +18 -0
- package/src/components/typography.ts +1 -0
- package/src/hooks/index.ts +7 -0
- package/src/hooks/useMergedRef.ts +14 -0
- package/src/hooks/useOnUnmount.ts +20 -0
- package/src/hooks/useSize.ts +164 -0
- package/src/hooks/useStableCallback.ts +11 -0
- package/src/hooks/useToggle.tsx +9 -0
- package/src/hooks/useVisualViewportOffset.ts +35 -0
- package/src/hooks/withClassName.tsx +21 -0
- package/src/hooks.ts +1 -0
- package/src/uno.preset.ts +767 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { withClassName } from '../../hooks/withClassName.js';
|
|
5
|
+
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
|
|
6
|
+
|
|
7
|
+
export const CollapsibleRoot = CollapsiblePrimitive.Root;
|
|
8
|
+
const CollapsibleContentBase = withClassName(
|
|
9
|
+
CollapsiblePrimitive.Content,
|
|
10
|
+
'overflow-hidden animate-forwards',
|
|
11
|
+
'layer-components:[&[data-state=open]]:(animate-radix-collapsible-open-vertical animate-duration-300 animate-ease-springy) layer-components:[&[data-state=closed]]:(animate-radix-collapsible-close-vertical animate-duration-300 animate-ease-springy)',
|
|
12
|
+
'layer-variants:[&[data-horizontal][data-state=open]]:(animate-radix-collapsible-open-horizontal animate-duration-300 animate-ease-springy) layer-variants:[&[data-horizontal][data-state=closed]]:(animate-radix-collapsible-close-horizontal animate-duration-300 animate-ease-springy)',
|
|
13
|
+
'layer-variants:[&[data-both][data-state=open]]:(animate-radix-collapsible-open-both animate-duration-300 animate-ease-springy) layer-variants:[&[data-both][data-state=closed]]:(animate-radix-collapsible-close-both animate-duration-300 animate-ease-springy)',
|
|
14
|
+
);
|
|
15
|
+
// specifically removing className... it's causing problems?
|
|
16
|
+
export const CollapsibleContent = forwardRef<
|
|
17
|
+
HTMLDivElement,
|
|
18
|
+
CollapsiblePrimitive.CollapsibleContentProps & {
|
|
19
|
+
horizontal?: boolean;
|
|
20
|
+
both?: boolean;
|
|
21
|
+
}
|
|
22
|
+
>(function CollapsibleContent({ horizontal, both, ...props }, ref) {
|
|
23
|
+
return (
|
|
24
|
+
<CollapsibleContentBase
|
|
25
|
+
data-horizontal={horizontal}
|
|
26
|
+
data-both={both}
|
|
27
|
+
{...props}
|
|
28
|
+
ref={ref}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
export const CollapsibleTrigger = CollapsiblePrimitive.Trigger;
|
|
33
|
+
|
|
34
|
+
export const CollapsibleSimple = ({
|
|
35
|
+
horizontal,
|
|
36
|
+
both,
|
|
37
|
+
children,
|
|
38
|
+
...props
|
|
39
|
+
}: CollapsiblePrimitive.CollapsibleProps & {
|
|
40
|
+
horizontal?: boolean;
|
|
41
|
+
both?: boolean;
|
|
42
|
+
}) => (
|
|
43
|
+
<CollapsibleRoot {...props}>
|
|
44
|
+
<CollapsibleContent horizontal={horizontal} both={both}>
|
|
45
|
+
{children}
|
|
46
|
+
</CollapsibleContent>
|
|
47
|
+
</CollapsibleRoot>
|
|
48
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Collapsible.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './collapsible/index.js';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Select,
|
|
5
|
+
SelectContent,
|
|
6
|
+
SelectItem,
|
|
7
|
+
SelectTrigger,
|
|
8
|
+
SelectValue,
|
|
9
|
+
} from '../select/Select.js';
|
|
10
|
+
import classNames from 'classnames';
|
|
11
|
+
import { ReactNode } from 'react';
|
|
12
|
+
import { withClassName } from '../../hooks/withClassName.js';
|
|
13
|
+
|
|
14
|
+
export type ThemeName = 'lemon' | 'tomato' | 'leek' | 'blueberry' | 'eggplant';
|
|
15
|
+
|
|
16
|
+
export interface ColorPickerProps {
|
|
17
|
+
value: ThemeName | null;
|
|
18
|
+
onChange: (value: ThemeName) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function ColorPicker({ value, onChange }: ColorPickerProps) {
|
|
22
|
+
const resolvedValue = [
|
|
23
|
+
'lemon',
|
|
24
|
+
'tomato',
|
|
25
|
+
'leek',
|
|
26
|
+
'blueberry',
|
|
27
|
+
'eggplant',
|
|
28
|
+
].includes(value || '')
|
|
29
|
+
? (value as ThemeName)
|
|
30
|
+
: 'lemon';
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Select value={resolvedValue} onValueChange={onChange}>
|
|
34
|
+
<SelectTrigger>
|
|
35
|
+
<SelectValue />
|
|
36
|
+
</SelectTrigger>
|
|
37
|
+
<SelectContent className="z-[calc(var(--z-dialog)+1)]">
|
|
38
|
+
<SelectItem value="lemon">
|
|
39
|
+
<ColorSwatch value="lemon" />
|
|
40
|
+
<ItemLabel>Lemon</ItemLabel>
|
|
41
|
+
</SelectItem>
|
|
42
|
+
<SelectItem value="tomato">
|
|
43
|
+
<ColorSwatch value="tomato" />
|
|
44
|
+
<ItemLabel>Tomato</ItemLabel>
|
|
45
|
+
</SelectItem>
|
|
46
|
+
<SelectItem value="leek">
|
|
47
|
+
<ColorSwatch value="leek" />
|
|
48
|
+
<ItemLabel>Leek</ItemLabel>
|
|
49
|
+
</SelectItem>
|
|
50
|
+
<SelectItem value="blueberry">
|
|
51
|
+
<ColorSwatch value="blueberry" />
|
|
52
|
+
<ItemLabel>Blueberry</ItemLabel>
|
|
53
|
+
</SelectItem>
|
|
54
|
+
<SelectItem value="eggplant">
|
|
55
|
+
<ColorSwatch value="eggplant" />
|
|
56
|
+
<ItemLabel>Eggplant</ItemLabel>
|
|
57
|
+
</SelectItem>
|
|
58
|
+
</SelectContent>
|
|
59
|
+
</Select>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const ItemLabel = withClassName('span', 'display-none');
|
|
64
|
+
|
|
65
|
+
export function ColorSwatch({
|
|
66
|
+
value,
|
|
67
|
+
children,
|
|
68
|
+
}: {
|
|
69
|
+
value: ThemeName;
|
|
70
|
+
children?: ReactNode;
|
|
71
|
+
}) {
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
className={classNames(
|
|
75
|
+
'bg-primary w-16px h-16px rounded-sm',
|
|
76
|
+
`theme-${value ?? 'lemon'}`,
|
|
77
|
+
)}
|
|
78
|
+
>
|
|
79
|
+
{children}
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ColorPicker.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './colorPicker/index.js';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as ContextMenu from '@radix-ui/react-context-menu';
|
|
2
|
+
import { ComponentPropsWithoutRef, forwardRef } from 'react';
|
|
3
|
+
import { withClassName } from '../../hooks/withClassName.js';
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
|
|
6
|
+
export const ContextMenuRoot = ContextMenu.Root;
|
|
7
|
+
|
|
8
|
+
export const ContextMenuContent = forwardRef<
|
|
9
|
+
HTMLDivElement,
|
|
10
|
+
ComponentPropsWithoutRef<typeof ContextMenu.Content>
|
|
11
|
+
>(function Content({ className, onClick, ...props }, ref) {
|
|
12
|
+
return (
|
|
13
|
+
<ContextMenu.Portal>
|
|
14
|
+
<ContextMenu.Content
|
|
15
|
+
className={classNames(
|
|
16
|
+
'layer-components:(min-w-120px bg-white rounded-md border-default overflow-hidden p-2 shadow-md z-menu)',
|
|
17
|
+
'layer-components:transform-origin-[var(--radix-context-menu-transform-origin)]',
|
|
18
|
+
'layer-components:[&[data-state=open]]:animate-popover-in',
|
|
19
|
+
'layer-components:[&[data-state=closed]]:animate-popover-out',
|
|
20
|
+
'layer-components:(max-h-[var(--radix-context-menu-content-available-height)] overflow-y-auto)',
|
|
21
|
+
'important:motion-reduce:animate-none',
|
|
22
|
+
className,
|
|
23
|
+
)}
|
|
24
|
+
onClick={(ev) => {
|
|
25
|
+
ev.stopPropagation();
|
|
26
|
+
onClick?.(ev);
|
|
27
|
+
}}
|
|
28
|
+
ref={ref}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
</ContextMenu.Portal>
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const ContextMenuArrow = withClassName(ContextMenu.Arrow, 'fill-white');
|
|
36
|
+
|
|
37
|
+
export const ContextMenuItem = withClassName(
|
|
38
|
+
ContextMenu.Item,
|
|
39
|
+
'flex items-center py-1 px-2 relative pl-25px select-none outline-none cursor-pointer',
|
|
40
|
+
'hover:bg-gray2 [&[data-highlighted=true]]:bg-gray2 [&[data-disabled=true]]:(opacity-50 cursor-default) disabled:(opacity-50 cursor-default)',
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
export const ContextMenuTrigger = withClassName(ContextMenu.Trigger, '');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './contextMenu/contextMenu.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { DatePicker, DateRangePicker } from './DatePicker.js';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'DatePicker',
|
|
7
|
+
component: DatePicker,
|
|
8
|
+
argTypes: {},
|
|
9
|
+
parameters: {
|
|
10
|
+
controls: { expanded: true },
|
|
11
|
+
},
|
|
12
|
+
} satisfies Meta<typeof DatePicker>;
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
|
|
16
|
+
type Story = StoryObj<typeof DatePicker>;
|
|
17
|
+
|
|
18
|
+
export const Default: Story = {
|
|
19
|
+
render() {
|
|
20
|
+
const [value, setValue] = useState<Date | null>(null);
|
|
21
|
+
return <DatePicker value={value} onChange={setValue} />;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const Range: Story = {
|
|
26
|
+
render() {
|
|
27
|
+
const [value, setValue] = useState<{
|
|
28
|
+
start: Date | null;
|
|
29
|
+
end: Date | null;
|
|
30
|
+
}>({ start: null, end: null });
|
|
31
|
+
return <DateRangePicker value={value} onChange={setValue} />;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Calendar,
|
|
3
|
+
CalendarDay as BaseCalendarDay,
|
|
4
|
+
CalendarDays,
|
|
5
|
+
} from 'calendar-blocks';
|
|
6
|
+
import { Button } from '../button.js';
|
|
7
|
+
import { Icon } from '../icon.js';
|
|
8
|
+
import { ArrowLeftIcon, ArrowRightIcon } from '@radix-ui/react-icons';
|
|
9
|
+
import { useCallback, useState } from 'react';
|
|
10
|
+
import { withClassName } from '../../hooks.js';
|
|
11
|
+
import classNames from 'classnames';
|
|
12
|
+
|
|
13
|
+
export interface DatePickerProps {
|
|
14
|
+
value: Date | null;
|
|
15
|
+
onChange: (date: Date | null) => void;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function DatePicker({
|
|
20
|
+
value,
|
|
21
|
+
onChange,
|
|
22
|
+
className,
|
|
23
|
+
...rest
|
|
24
|
+
}: DatePickerProps) {
|
|
25
|
+
const [{ month, year }, setDisplay] = useState(() => ({
|
|
26
|
+
month: new Date().getMonth(),
|
|
27
|
+
year: new Date().getFullYear(),
|
|
28
|
+
}));
|
|
29
|
+
const monthLabel = new Date(year, month).toLocaleDateString('en-US', {
|
|
30
|
+
month: 'long',
|
|
31
|
+
year: 'numeric',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div
|
|
36
|
+
className={classNames(
|
|
37
|
+
'flex flex-col items-center justify-center w-[calc(var(--day-size,32px)*7)]',
|
|
38
|
+
className,
|
|
39
|
+
)}
|
|
40
|
+
{...rest}
|
|
41
|
+
>
|
|
42
|
+
<MonthRow>
|
|
43
|
+
<MonthButton
|
|
44
|
+
size="icon"
|
|
45
|
+
color="ghost"
|
|
46
|
+
onClick={() =>
|
|
47
|
+
setDisplay((cur) => ({
|
|
48
|
+
month: cur.month - 1,
|
|
49
|
+
year: cur.year,
|
|
50
|
+
}))
|
|
51
|
+
}
|
|
52
|
+
>
|
|
53
|
+
<ArrowLeftIcon />
|
|
54
|
+
</MonthButton>
|
|
55
|
+
<MonthLabel>{monthLabel}</MonthLabel>
|
|
56
|
+
<MonthButton
|
|
57
|
+
size="icon"
|
|
58
|
+
color="ghost"
|
|
59
|
+
onClick={() =>
|
|
60
|
+
setDisplay((cur) => ({
|
|
61
|
+
month: cur.month + 1,
|
|
62
|
+
year: cur.year,
|
|
63
|
+
}))
|
|
64
|
+
}
|
|
65
|
+
>
|
|
66
|
+
<ArrowRightIcon />
|
|
67
|
+
</MonthButton>
|
|
68
|
+
</MonthRow>
|
|
69
|
+
<Calendar
|
|
70
|
+
displayMonth={month}
|
|
71
|
+
displayYear={year}
|
|
72
|
+
value={value}
|
|
73
|
+
onChange={onChange}
|
|
74
|
+
onDisplayChange={setDisplay}
|
|
75
|
+
>
|
|
76
|
+
<CalendarGrid>
|
|
77
|
+
<DayLabels />
|
|
78
|
+
<CalendarDays>
|
|
79
|
+
{(value) => <CalendarDay value={value} key={value.key} />}
|
|
80
|
+
</CalendarDays>
|
|
81
|
+
</CalendarGrid>
|
|
82
|
+
</Calendar>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface DateRangePickerProps {
|
|
88
|
+
value: { start: Date | null; end: Date | null };
|
|
89
|
+
onChange: (value: { start: Date | null; end: Date | null }) => void;
|
|
90
|
+
className?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function DateRangePicker({
|
|
94
|
+
value,
|
|
95
|
+
onChange,
|
|
96
|
+
className,
|
|
97
|
+
}: DateRangePickerProps) {
|
|
98
|
+
const [{ month, year }, setDisplay] = useState(() => ({
|
|
99
|
+
month: new Date().getMonth(),
|
|
100
|
+
year: new Date().getFullYear(),
|
|
101
|
+
}));
|
|
102
|
+
const monthLabel = new Date(year, month).toLocaleDateString('en-US', {
|
|
103
|
+
month: 'long',
|
|
104
|
+
year: 'numeric',
|
|
105
|
+
});
|
|
106
|
+
const nextMonth = new Date(year, month + 1);
|
|
107
|
+
const nextMonthLabel = nextMonth.toLocaleDateString('en-US', {
|
|
108
|
+
month: 'long',
|
|
109
|
+
year: 'numeric',
|
|
110
|
+
});
|
|
111
|
+
const onDisplayChange = useCallback(
|
|
112
|
+
({ month: newMonth, year: newYear }: { month: number; year: number }) => {
|
|
113
|
+
/**
|
|
114
|
+
* Important UX consideration:
|
|
115
|
+
*
|
|
116
|
+
* since we are displaying 2 months at once, we don't
|
|
117
|
+
* always want to change our view if the user's cursor
|
|
118
|
+
* date moves from one month to another. Specifically,
|
|
119
|
+
* if they move from the first visible month to the
|
|
120
|
+
* second visible month, we don't need to change the view,
|
|
121
|
+
* since they are still within the visible range.
|
|
122
|
+
* So, we write logic to ignore that case!
|
|
123
|
+
*/
|
|
124
|
+
if (newMonth === month + 1 && newYear === year) {
|
|
125
|
+
return; // ignore movement from the first to the second frame
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
setDisplay({
|
|
129
|
+
month: newMonth,
|
|
130
|
+
year: newYear,
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
[month, year],
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<Calendar
|
|
138
|
+
displayMonth={month}
|
|
139
|
+
displayYear={year}
|
|
140
|
+
rangeValue={value}
|
|
141
|
+
onRangeChange={(range) => onChange(range)}
|
|
142
|
+
onDisplayChange={onDisplayChange}
|
|
143
|
+
className={classNames('flex justify-center', className)}
|
|
144
|
+
>
|
|
145
|
+
<RangeLayout>
|
|
146
|
+
<MonthButton
|
|
147
|
+
size="icon"
|
|
148
|
+
color="ghost"
|
|
149
|
+
className="[grid-area:prevMonth]"
|
|
150
|
+
onClick={() =>
|
|
151
|
+
setDisplay((cur) => ({
|
|
152
|
+
month: cur.month - 1,
|
|
153
|
+
year: cur.year,
|
|
154
|
+
}))
|
|
155
|
+
}
|
|
156
|
+
>
|
|
157
|
+
<ArrowLeftIcon />
|
|
158
|
+
</MonthButton>
|
|
159
|
+
<MonthLabel className="[grid-area:leftMonth]">{monthLabel}</MonthLabel>
|
|
160
|
+
<MonthLabel className="[grid-area:rightMonth]">
|
|
161
|
+
{nextMonthLabel}
|
|
162
|
+
</MonthLabel>
|
|
163
|
+
<MonthButton
|
|
164
|
+
size="icon"
|
|
165
|
+
color="ghost"
|
|
166
|
+
className="[grid-area:nextMonth]"
|
|
167
|
+
onClick={() =>
|
|
168
|
+
setDisplay((cur) => ({
|
|
169
|
+
month: cur.month + 1,
|
|
170
|
+
year: cur.year,
|
|
171
|
+
}))
|
|
172
|
+
}
|
|
173
|
+
>
|
|
174
|
+
<ArrowRightIcon />
|
|
175
|
+
</MonthButton>
|
|
176
|
+
<CalendarGrid className="[grid-area:leftGrid]">
|
|
177
|
+
<DayLabels />
|
|
178
|
+
<CalendarDays>
|
|
179
|
+
{(value) => <CalendarDay value={value} key={value.key} />}
|
|
180
|
+
</CalendarDays>
|
|
181
|
+
</CalendarGrid>
|
|
182
|
+
<CalendarGrid className="[grid-area:rightGrid]">
|
|
183
|
+
<DayLabels />
|
|
184
|
+
<CalendarDays monthOffset={1}>
|
|
185
|
+
{(value) => <CalendarDay value={value} key={value.key} />}
|
|
186
|
+
</CalendarDays>
|
|
187
|
+
</CalendarGrid>
|
|
188
|
+
</RangeLayout>
|
|
189
|
+
</Calendar>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const MonthRow = withClassName(
|
|
194
|
+
'div',
|
|
195
|
+
'flex flex-row justify-between items-center w-full',
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const MonthLabel = withClassName(
|
|
199
|
+
'span',
|
|
200
|
+
'text-sm font-bold min-w-0 overflow-hidden text-center text-ellipsis',
|
|
201
|
+
'self-center',
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const MonthButton = withClassName(Button, 'self-center');
|
|
205
|
+
|
|
206
|
+
const CalendarGrid = withClassName(
|
|
207
|
+
'div',
|
|
208
|
+
'grid grid-cols-[repeat(7,var(--day-size,32px))] [grid-auto-rows:var(--day-size,32px)]',
|
|
209
|
+
'height-[calc(var(--day-size,32px)*7)] rounded overflow-hidden p-2 bg-gray-1',
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const CalendarDay = withClassName(
|
|
213
|
+
BaseCalendarDay,
|
|
214
|
+
'border border-solid border-transparent bg-white mr--1px mb--1px relative rounded',
|
|
215
|
+
'flex items-center justify-center transition-colors cursor-pointer',
|
|
216
|
+
'[&[data-highlighted]]:(z-1 outline-accent)',
|
|
217
|
+
'[&[data-selected]]:(bg-accent-light z-2)',
|
|
218
|
+
'[&[data-in-range]]:(bg-accent-wash rounded-none z-1)',
|
|
219
|
+
'[&[data-range-start]]:(bg-accent-light rounded-l rounded-r-none z-1)',
|
|
220
|
+
'[&[data-range-end]]:(bg-accent-light rounded-r rounded-l-none z-1)',
|
|
221
|
+
'disabled:(opacity-50 cursor-default)',
|
|
222
|
+
// today dot
|
|
223
|
+
'[&[data-today]]:before:(content-[""] absolute left-[2px] top-[2px] w-[6px] h-[6px] rounded-full bg-primary)',
|
|
224
|
+
'[&[data-top-edge]]:(border-top-gray-2)',
|
|
225
|
+
'[&[data-bottom-edge]]:(border-bottom-gray-2)',
|
|
226
|
+
'[&[data-first-column]]:(border-left-gray-2)',
|
|
227
|
+
'[&[data-last-column]]:(border-right-gray-2)',
|
|
228
|
+
'[&[data-day-first]]:(border-left-gray-2 rounded-tl)',
|
|
229
|
+
'[&[data-day-last]]:(border-right-gray-2 rounded-br)',
|
|
230
|
+
'[&[data-first-column][data-bottom-edge]]:rounded-bl',
|
|
231
|
+
'[&[data-last-column][data-bottom-edge]]:rounded-br',
|
|
232
|
+
'[&[data-first-column][data-top-edge]]:rounded-tl',
|
|
233
|
+
'[&[data-last-column][data-top-edge]]:rounded-tr',
|
|
234
|
+
'[&[data-different-month]]:[visibility:hidden]',
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const DayLabel = withClassName(
|
|
238
|
+
'div',
|
|
239
|
+
'flex items-center justify-center text-sm text-gray-6',
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
const DayLabels = () => (
|
|
243
|
+
<>
|
|
244
|
+
<DayLabel>S</DayLabel>
|
|
245
|
+
<DayLabel>M</DayLabel>
|
|
246
|
+
<DayLabel>T</DayLabel>
|
|
247
|
+
<DayLabel>W</DayLabel>
|
|
248
|
+
<DayLabel>T</DayLabel>
|
|
249
|
+
<DayLabel>F</DayLabel>
|
|
250
|
+
<DayLabel>S</DayLabel>
|
|
251
|
+
</>
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
const RangeLayout = withClassName(
|
|
255
|
+
'div',
|
|
256
|
+
'grid [grid-template-areas:"prevMonth_leftMonth_rightMonth_nextMonth""leftGrid_leftGrid_rightGrid_rightGrid"]',
|
|
257
|
+
'[grid-template-columns:auto_1fr_1fr_auto] [grid-template-rows:auto_1fr] gap-2',
|
|
258
|
+
);
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import {
|
|
3
|
+
Dialog,
|
|
4
|
+
DialogContent,
|
|
5
|
+
DialogTrigger,
|
|
6
|
+
DialogActions,
|
|
7
|
+
DialogClose,
|
|
8
|
+
DialogTitle,
|
|
9
|
+
} from './Dialog.js';
|
|
10
|
+
|
|
11
|
+
const meta = {
|
|
12
|
+
title: 'Dialog',
|
|
13
|
+
component: Dialog,
|
|
14
|
+
argTypes: {},
|
|
15
|
+
parameters: {
|
|
16
|
+
controls: { expanded: true },
|
|
17
|
+
},
|
|
18
|
+
} satisfies Meta<typeof Dialog>;
|
|
19
|
+
|
|
20
|
+
export default meta;
|
|
21
|
+
|
|
22
|
+
type Story = StoryObj<typeof Dialog>;
|
|
23
|
+
|
|
24
|
+
export const Default: Story = {
|
|
25
|
+
args: {
|
|
26
|
+
children: (
|
|
27
|
+
<>
|
|
28
|
+
<DialogTrigger>Open</DialogTrigger>
|
|
29
|
+
<DialogContent>
|
|
30
|
+
<DialogTitle>Hello world</DialogTitle>
|
|
31
|
+
<DialogActions>
|
|
32
|
+
<DialogClose>Close</DialogClose>
|
|
33
|
+
</DialogActions>
|
|
34
|
+
</DialogContent>
|
|
35
|
+
</>
|
|
36
|
+
),
|
|
37
|
+
},
|
|
38
|
+
};
|