@a-type/ui 0.3.2 → 0.3.3
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 +3 -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/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,267 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
4
|
+
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
|
|
5
|
+
import {
|
|
6
|
+
ComponentPropsWithoutRef,
|
|
7
|
+
createContext,
|
|
8
|
+
forwardRef,
|
|
9
|
+
useCallback,
|
|
10
|
+
useContext,
|
|
11
|
+
useRef,
|
|
12
|
+
useState,
|
|
13
|
+
} from 'react';
|
|
14
|
+
import { withClassName } from '../../hooks/withClassName.js';
|
|
15
|
+
import useMergedRef from '../../hooks/useMergedRef.js';
|
|
16
|
+
import { useParticles } from '../particles.js';
|
|
17
|
+
import classNames from 'classnames';
|
|
18
|
+
import { CheckIcon, ChevronDownIcon } from '@radix-ui/react-icons';
|
|
19
|
+
import { selectTriggerClassName } from '../select.js';
|
|
20
|
+
import { useDrag } from '@use-gesture/react';
|
|
21
|
+
|
|
22
|
+
const StyledOverlay = withClassName(
|
|
23
|
+
DialogPrimitive.Overlay,
|
|
24
|
+
'layer-components(bg-overlay fixed inset-0 z-dialog-backdrop animate-fade-in animate-duration-200)',
|
|
25
|
+
'layer-components:[&[data-state=closed]]:animate-fade-out',
|
|
26
|
+
'motion-reduce:animate-none',
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const StyledContent = withClassName(
|
|
30
|
+
DialogPrimitive.Content,
|
|
31
|
+
'layer-components:(translate-0 z-dialog fixed bottom-[calc(var(--viewport-bottom-offset,0px)+var(--gesture-y,0px))] left-0 right-0 h-min-content max-h-[calc(0.85*var(--viewport-height,100vh))])',
|
|
32
|
+
'transform-gpu',
|
|
33
|
+
'animate-ease-out',
|
|
34
|
+
'layer-components:(shadow-xl bg-white rounded-tl-xl rounded-tr-xl p-6 pt-8 border-default border-b-0 overflow-y-auto flex flex-col pb-[calc(3rem+env(safe-area-inset-bottom,0px))])',
|
|
35
|
+
'animate-fade-in-up-big animate-duration-200 [&[data-state=closed]]:animate-fade-out-down-big animate-ease-in motion-reduce:animate-none',
|
|
36
|
+
'layer-components:sm:(left-50% top-50% translate-[-50%] w-90vw max-w-450px max-h-85vh pb-6 rounded-lg border-b-1 pt-6)',
|
|
37
|
+
'sm:(animate-dialog-in [&[data-state=closed]]:animate-dialog-out motion-reduce:animate-none)',
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export const Content = forwardRef<
|
|
41
|
+
HTMLDivElement,
|
|
42
|
+
ComponentPropsWithoutRef<typeof StyledContent> & {
|
|
43
|
+
outerClassName?: string;
|
|
44
|
+
width?: 'lg' | 'md' | 'sm';
|
|
45
|
+
}
|
|
46
|
+
>(function Content(
|
|
47
|
+
{ children, width, outerClassName, className, ...props },
|
|
48
|
+
ref,
|
|
49
|
+
) {
|
|
50
|
+
const particles = useParticles();
|
|
51
|
+
const wasOpenRef = useRef(false);
|
|
52
|
+
const openRef = useCallback(
|
|
53
|
+
(element: HTMLDivElement | null) => {
|
|
54
|
+
if (
|
|
55
|
+
!wasOpenRef.current &&
|
|
56
|
+
element?.getAttribute('data-state') === 'open'
|
|
57
|
+
) {
|
|
58
|
+
wasOpenRef.current = true;
|
|
59
|
+
|
|
60
|
+
const matchesSmall = !window.matchMedia('(min-width:600px)').matches;
|
|
61
|
+
if (!matchesSmall) return;
|
|
62
|
+
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
particles?.addParticles(
|
|
65
|
+
particles.elementExplosion({
|
|
66
|
+
count: 20,
|
|
67
|
+
borders: ['top'],
|
|
68
|
+
color: [
|
|
69
|
+
{
|
|
70
|
+
space: 'rgb',
|
|
71
|
+
values: [0, 0, 0],
|
|
72
|
+
opacity: 0.02,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
space: 'rgb',
|
|
76
|
+
values: [0, 0, 0],
|
|
77
|
+
opacity: 0,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
element,
|
|
81
|
+
startRadius: 15,
|
|
82
|
+
endRadius: 0,
|
|
83
|
+
lifespan: 1000,
|
|
84
|
+
force: 0.5,
|
|
85
|
+
drag: 0.01,
|
|
86
|
+
forceFuzz: 0.5,
|
|
87
|
+
angleFuzz: 0.1,
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
}, 180);
|
|
91
|
+
} else if (element?.getAttribute('data-state') === 'closed') {
|
|
92
|
+
wasOpenRef.current = false;
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
[particles],
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const gestureRef = useRef<HTMLDivElement>(null);
|
|
99
|
+
|
|
100
|
+
const finalRef = useMergedRef(ref, openRef, gestureRef);
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<DialogPrimitive.Portal>
|
|
104
|
+
<StyledOverlay />
|
|
105
|
+
<StyledContent
|
|
106
|
+
data-dialog-content
|
|
107
|
+
ref={finalRef}
|
|
108
|
+
{...props}
|
|
109
|
+
className={classNames(
|
|
110
|
+
{
|
|
111
|
+
'md:max-w-800px': width === 'lg',
|
|
112
|
+
'max-w-600px': width === 'md',
|
|
113
|
+
'max-w-300px': width === 'sm',
|
|
114
|
+
},
|
|
115
|
+
outerClassName || className,
|
|
116
|
+
)}
|
|
117
|
+
>
|
|
118
|
+
<DialogSwipeHandle />
|
|
119
|
+
{children}
|
|
120
|
+
</StyledContent>
|
|
121
|
+
</DialogPrimitive.Portal>
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
export const DialogSwipeHandle = forwardRef<
|
|
126
|
+
HTMLDivElement,
|
|
127
|
+
ComponentPropsWithoutRef<'div'>
|
|
128
|
+
>(function DialogSwipeHandle({ className, ...props }, ref) {
|
|
129
|
+
const close = useContext(DialogCloseContext);
|
|
130
|
+
const innerRef = useRef<HTMLDivElement>(null);
|
|
131
|
+
useDrag(
|
|
132
|
+
({ swipe: [, swipeY], movement: [, dy], velocity: [, vy], last }) => {
|
|
133
|
+
const content = findParentDialogContent(innerRef.current);
|
|
134
|
+
if (!content) return;
|
|
135
|
+
|
|
136
|
+
const contentHeight = content.clientHeight;
|
|
137
|
+
|
|
138
|
+
if (last) console.log(swipeY, dy, contentHeight);
|
|
139
|
+
|
|
140
|
+
if (last && (swipeY === 1 || dy > contentHeight / 2)) {
|
|
141
|
+
console.log('close');
|
|
142
|
+
close();
|
|
143
|
+
}
|
|
144
|
+
const gestureY = last ? 0 : -Math.max(0, dy);
|
|
145
|
+
content.style.setProperty('--gesture-y', `${gestureY}px`);
|
|
146
|
+
content.style.setProperty('transition', last ? 'bottom 0.2s' : '');
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
target: innerRef,
|
|
150
|
+
axis: 'y',
|
|
151
|
+
},
|
|
152
|
+
);
|
|
153
|
+
const finalRef = useMergedRef(ref, innerRef);
|
|
154
|
+
return (
|
|
155
|
+
<div
|
|
156
|
+
ref={finalRef}
|
|
157
|
+
{...props}
|
|
158
|
+
className={classNames(
|
|
159
|
+
'absolute top-0 left-50% transform-gpu -translate-x-1/2 w-20% py-2 rounded-full cursor-grab sm:display-none touch-none',
|
|
160
|
+
className,
|
|
161
|
+
)}
|
|
162
|
+
>
|
|
163
|
+
<div className="w-full h-[4px] bg-gray-4 rounded-full" />
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
function findParentDialogContent(
|
|
169
|
+
element: HTMLElement | null,
|
|
170
|
+
): HTMLElement | null {
|
|
171
|
+
if (!element) return null;
|
|
172
|
+
if (element.getAttribute('data-dialog-content')) return element;
|
|
173
|
+
return findParentDialogContent(element.parentElement);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const DialogCloseContext = createContext<() => void>(() => {});
|
|
177
|
+
|
|
178
|
+
const StyledTitle = withClassName(
|
|
179
|
+
DialogPrimitive.Title,
|
|
180
|
+
'font-title color-black text-3xl mb-4 mt-0',
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const StyledDescription = withClassName(
|
|
184
|
+
DialogPrimitive.Description,
|
|
185
|
+
'mt-3 mb-6 color-gray8 text-md leading-6',
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
// Exports
|
|
189
|
+
export const Dialog = (props: DialogPrimitive.DialogProps) => {
|
|
190
|
+
const [innerOpen, innerOnOpenChange] = useState(props.defaultOpen);
|
|
191
|
+
const open = props.open ?? innerOpen;
|
|
192
|
+
const onOpenChange = useCallback(
|
|
193
|
+
(open: boolean) => {
|
|
194
|
+
innerOnOpenChange(open);
|
|
195
|
+
props.onOpenChange?.(open);
|
|
196
|
+
},
|
|
197
|
+
[props.onOpenChange],
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const close = useCallback(() => {
|
|
201
|
+
onOpenChange(false);
|
|
202
|
+
}, [onOpenChange]);
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<DialogCloseContext.Provider value={close}>
|
|
206
|
+
<DialogPrimitive.Root
|
|
207
|
+
{...props}
|
|
208
|
+
open={open}
|
|
209
|
+
onOpenChange={onOpenChange}
|
|
210
|
+
/>
|
|
211
|
+
</DialogCloseContext.Provider>
|
|
212
|
+
);
|
|
213
|
+
};
|
|
214
|
+
export const DialogTrigger = DialogPrimitive.Trigger;
|
|
215
|
+
export const DialogContent = Content;
|
|
216
|
+
export const DialogTitle = StyledTitle;
|
|
217
|
+
export const DialogDescription = StyledDescription;
|
|
218
|
+
export const DialogClose = DialogPrimitive.Close;
|
|
219
|
+
|
|
220
|
+
export type { DialogProps } from '@radix-ui/react-dialog';
|
|
221
|
+
|
|
222
|
+
export const DialogActions = withClassName(
|
|
223
|
+
'div',
|
|
224
|
+
'flex justify-end sticky w-full gap-3 items-center bg-white py-3 translate-y-6 flex-wrap',
|
|
225
|
+
'bottom--6',
|
|
226
|
+
'sm:(bottom-0)',
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
export const DialogSelectTrigger = forwardRef<
|
|
230
|
+
HTMLButtonElement,
|
|
231
|
+
DialogPrimitive.DialogTriggerProps
|
|
232
|
+
>(function DialogSelectTrigger({ children, className, ...props }, ref) {
|
|
233
|
+
return (
|
|
234
|
+
<DialogPrimitive.Trigger
|
|
235
|
+
className={classNames(selectTriggerClassName, className)}
|
|
236
|
+
{...props}
|
|
237
|
+
>
|
|
238
|
+
<span>{children}</span>
|
|
239
|
+
<ChevronDownIcon />
|
|
240
|
+
</DialogPrimitive.Trigger>
|
|
241
|
+
);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
export const DialogSelectList = withClassName(
|
|
245
|
+
RadioGroupPrimitive.Root,
|
|
246
|
+
'flex flex-col gap-2 overflow-y-auto p-2',
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
export const DialogSelectItemRoot = withClassName(
|
|
250
|
+
RadioGroupPrimitive.Item,
|
|
251
|
+
'flex items-center gap-3 w-full py-3 px-4 text-left border-none rounded-lg font-normal bg-transparent [&:nth-child(2n+1)]:bg-gray-blend cursor-pointer transition-all',
|
|
252
|
+
'[&[data-state=checked]]:(bg-primary-wash text-primary-dark)',
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
export const DialogSelectItem = forwardRef<
|
|
256
|
+
HTMLButtonElement,
|
|
257
|
+
ComponentPropsWithoutRef<typeof DialogSelectItemRoot>
|
|
258
|
+
>(function DialogSelectItem({ children, ...props }, ref) {
|
|
259
|
+
return (
|
|
260
|
+
<DialogSelectItemRoot {...props}>
|
|
261
|
+
<span className="flex-1">{children}</span>
|
|
262
|
+
<RadioGroupPrimitive.Indicator className="flex-0-0-auto">
|
|
263
|
+
<CheckIcon />
|
|
264
|
+
</RadioGroupPrimitive.Indicator>
|
|
265
|
+
</DialogSelectItemRoot>
|
|
266
|
+
);
|
|
267
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Dialog.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dialog/index.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { HTMLAttributes, forwardRef } from 'react';
|
|
2
|
+
import { withClassName } from '../../hooks/withClassName.js';
|
|
3
|
+
import classNames from 'classnames';
|
|
4
|
+
|
|
5
|
+
const DividerBase = withClassName('div', 'w-full h-1px bg-black relative');
|
|
6
|
+
|
|
7
|
+
export interface DividerProps extends HTMLAttributes<HTMLDivElement> {
|
|
8
|
+
compensate?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
9
|
+
padded?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Divider = forwardRef<HTMLDivElement, DividerProps>(
|
|
13
|
+
function Divider({ compensate, padded, className, ...props }, ref) {
|
|
14
|
+
return (
|
|
15
|
+
<DividerBase
|
|
16
|
+
ref={ref}
|
|
17
|
+
style={{
|
|
18
|
+
left: compensate ? `calc(${compensate} * 0.25rem)` : undefined,
|
|
19
|
+
width: compensate ? `calc(100% - ${compensate} * 0.5rem)` : undefined,
|
|
20
|
+
}}
|
|
21
|
+
{...props}
|
|
22
|
+
className={classNames(padded && 'my-4', className)}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
},
|
|
26
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Divider.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './divider/index.js';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import {
|
|
3
|
+
DropdownMenu,
|
|
4
|
+
DropdownMenuContent,
|
|
5
|
+
DropdownMenuItem,
|
|
6
|
+
DropdownMenuItemRightSlot,
|
|
7
|
+
DropdownMenuTrigger,
|
|
8
|
+
} from './DropdownMenu.js';
|
|
9
|
+
import { Icon } from '../icon.js';
|
|
10
|
+
import { DropdownMenuLabel } from '@radix-ui/react-dropdown-menu';
|
|
11
|
+
|
|
12
|
+
const meta = {
|
|
13
|
+
title: 'DropdownMenu',
|
|
14
|
+
component: DropdownMenu,
|
|
15
|
+
argTypes: {},
|
|
16
|
+
parameters: {
|
|
17
|
+
controls: { expanded: true },
|
|
18
|
+
},
|
|
19
|
+
} satisfies Meta<typeof DropdownMenu>;
|
|
20
|
+
|
|
21
|
+
export default meta;
|
|
22
|
+
|
|
23
|
+
type Story = StoryObj<typeof DropdownMenu>;
|
|
24
|
+
|
|
25
|
+
export const Default: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
children: (
|
|
28
|
+
<>
|
|
29
|
+
<DropdownMenuTrigger>Open</DropdownMenuTrigger>
|
|
30
|
+
<DropdownMenuContent>
|
|
31
|
+
<DropdownMenuItem>
|
|
32
|
+
<DropdownMenuLabel>Item 1</DropdownMenuLabel>
|
|
33
|
+
<DropdownMenuItemRightSlot>
|
|
34
|
+
<Icon name="flag" />
|
|
35
|
+
</DropdownMenuItemRightSlot>
|
|
36
|
+
</DropdownMenuItem>
|
|
37
|
+
<DropdownMenuItem>
|
|
38
|
+
<DropdownMenuLabel>Item 2</DropdownMenuLabel>
|
|
39
|
+
<DropdownMenuItemRightSlot>
|
|
40
|
+
<Icon name="add_person" />
|
|
41
|
+
</DropdownMenuItemRightSlot>
|
|
42
|
+
</DropdownMenuItem>
|
|
43
|
+
</DropdownMenuContent>
|
|
44
|
+
</>
|
|
45
|
+
),
|
|
46
|
+
},
|
|
47
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
4
|
+
import { withClassName } from '../../hooks/withClassName.js';
|
|
5
|
+
import classNames from 'classnames';
|
|
6
|
+
|
|
7
|
+
const StyledContent = withClassName(
|
|
8
|
+
DropdownMenuPrimitive.Content,
|
|
9
|
+
'min-w-220px bg-white z-menu shadow-lg rounded-xl border-default',
|
|
10
|
+
'layer-components:transform-origin-[var(--radix-dropdown-menu-transform-origin)]',
|
|
11
|
+
'layer-components:[&[data-state=open]]:animate-popover-in',
|
|
12
|
+
'layer-components:[&[data-state=closed]]:animate-popover-out',
|
|
13
|
+
'layer-components:(max-h-[var(--radix-dropdown-menu-content-available-height)])',
|
|
14
|
+
'important:motion-reduce:animate-none',
|
|
15
|
+
'will-change-transform',
|
|
16
|
+
);
|
|
17
|
+
const itemClassName = classNames(
|
|
18
|
+
'text-md leading-4 color-black rounded-sm flex items-center pr-4 pl-8 py-3 relative text-left select-none cursor-pointer',
|
|
19
|
+
'[&[data-disabled]]:(color-gray9 pointer-events-none)',
|
|
20
|
+
'focus-visible:(bg-gray2 color-gray9)',
|
|
21
|
+
'focus:outline-none',
|
|
22
|
+
);
|
|
23
|
+
const StyledItem = withClassName(DropdownMenuPrimitive.Item, itemClassName);
|
|
24
|
+
const StyledCheckboxItem = withClassName(
|
|
25
|
+
DropdownMenuPrimitive.CheckboxItem,
|
|
26
|
+
itemClassName,
|
|
27
|
+
);
|
|
28
|
+
const StyledRadioItem = withClassName(
|
|
29
|
+
DropdownMenuPrimitive.RadioItem,
|
|
30
|
+
itemClassName,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const StyledLabel = withClassName(
|
|
34
|
+
DropdownMenuPrimitive.Label,
|
|
35
|
+
'pl-25px text-12px leading-6 color-gray7',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const StyledSeparator = withClassName(
|
|
39
|
+
DropdownMenuPrimitive.Separator,
|
|
40
|
+
'h-1px bg-gray5 m-5px',
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const StyledItemIndicator = withClassName(
|
|
44
|
+
DropdownMenuPrimitive.ItemIndicator,
|
|
45
|
+
'absolute left-0 w-25px inline-flex items-center justify-center',
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const StyledArrow = withClassName(
|
|
49
|
+
DropdownMenuPrimitive.Arrow,
|
|
50
|
+
'fill-white stroke-black stroke-1',
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const StyledTrigger = withClassName(
|
|
54
|
+
DropdownMenuPrimitive.Trigger,
|
|
55
|
+
'select-none',
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const StyledPortal = DropdownMenuPrimitive.Portal;
|
|
59
|
+
|
|
60
|
+
// Exports
|
|
61
|
+
export const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
62
|
+
export const DropdownMenuTrigger = StyledTrigger;
|
|
63
|
+
export const DropdownMenuItem = StyledItem;
|
|
64
|
+
export const DropdownMenuCheckboxItem = StyledCheckboxItem;
|
|
65
|
+
export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
66
|
+
export const DropdownMenuRadioItem = StyledRadioItem;
|
|
67
|
+
export const DropdownMenuItemIndicator = StyledItemIndicator;
|
|
68
|
+
export const DropdownMenuLabel = StyledLabel;
|
|
69
|
+
export const DropdownMenuSeparator = StyledSeparator;
|
|
70
|
+
export const DropdownMenuArrow = StyledArrow;
|
|
71
|
+
|
|
72
|
+
export const DropdownMenuContent = ({
|
|
73
|
+
children,
|
|
74
|
+
forceMount,
|
|
75
|
+
...props
|
|
76
|
+
}: DropdownMenuPrimitive.DropdownMenuContentProps & {
|
|
77
|
+
forceMount?: boolean;
|
|
78
|
+
}) => {
|
|
79
|
+
return (
|
|
80
|
+
<StyledPortal forceMount={forceMount}>
|
|
81
|
+
<StyledContent {...props}>
|
|
82
|
+
<div className="overflow-hidden rounded-xl">{children}</div>
|
|
83
|
+
<StyledArrow />
|
|
84
|
+
</StyledContent>
|
|
85
|
+
</StyledPortal>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const DropdownMenuItemRightSlot = withClassName('div', 'ml-auto');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './DropdownMenu.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dropdownMenu/index.js';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import React, { Component, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
export class ErrorBoundary extends Component<
|
|
5
|
+
{ children: ReactNode; fallback?: ReactNode },
|
|
6
|
+
{ error: Error | null }
|
|
7
|
+
> {
|
|
8
|
+
state = {
|
|
9
|
+
error: null,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
static getDerivedStateFromError(error: Error) {
|
|
13
|
+
// Update state so the next render will show the fallback UI.
|
|
14
|
+
return { error };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
render() {
|
|
18
|
+
if (this.state.error) {
|
|
19
|
+
return this.props.fallback || null;
|
|
20
|
+
}
|
|
21
|
+
return this.props.children;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ErrorBoundary.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './errorBoundary/index.js';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Formik, FormikConfig, FormikHelpers, FormikValues } from 'formik';
|
|
2
|
+
import { Form } from './Form.js';
|
|
3
|
+
import { useCallback } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface FormikFormProps<T extends FormikValues = FormikValues>
|
|
6
|
+
extends FormikConfig<T> {
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function FormikForm<Values extends FormikValues>({
|
|
11
|
+
className,
|
|
12
|
+
children,
|
|
13
|
+
onSubmit,
|
|
14
|
+
...props
|
|
15
|
+
}: FormikFormProps<Values>) {
|
|
16
|
+
const wrappedOnSubmit = useCallback(
|
|
17
|
+
async (values: Values, bag: FormikHelpers<Values>) => {
|
|
18
|
+
try {
|
|
19
|
+
bag.setSubmitting(true);
|
|
20
|
+
return await onSubmit(values, bag);
|
|
21
|
+
} finally {
|
|
22
|
+
bag.setSubmitting(false);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
[onSubmit],
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
if (typeof children === 'function') {
|
|
29
|
+
return (
|
|
30
|
+
<Formik<Values> onSubmit={wrappedOnSubmit} {...props}>
|
|
31
|
+
{(formik) => <Form className={className}>{children(formik)}</Form>}
|
|
32
|
+
</Formik>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<Formik<Values> onSubmit={wrappedOnSubmit} {...props}>
|
|
38
|
+
<Form className={className}>{children}</Form>
|
|
39
|
+
</Formik>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useFormikContext } from 'formik';
|
|
2
|
+
import { Button, ButtonProps } from '../button/Button.js';
|
|
3
|
+
|
|
4
|
+
export function SubmitButton(props: ButtonProps) {
|
|
5
|
+
const { isSubmitting, isValid } = useFormikContext();
|
|
6
|
+
return (
|
|
7
|
+
<Button
|
|
8
|
+
loading={isSubmitting}
|
|
9
|
+
disabled={!isValid}
|
|
10
|
+
color="primary"
|
|
11
|
+
type="submit"
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useField } from 'formik';
|
|
4
|
+
import {
|
|
5
|
+
ComponentProps,
|
|
6
|
+
InputHTMLAttributes,
|
|
7
|
+
useEffect,
|
|
8
|
+
useRef,
|
|
9
|
+
Ref,
|
|
10
|
+
forwardRef,
|
|
11
|
+
} from 'react';
|
|
12
|
+
import useMergedRef from '../../hooks/useMergedRef.js';
|
|
13
|
+
import classNames from 'classnames';
|
|
14
|
+
import { Input } from '../input/Input.js';
|
|
15
|
+
import { TextArea, TextAreaProps } from '../textArea/TextArea.js';
|
|
16
|
+
import { withClassName } from '../../hooks.js';
|
|
17
|
+
|
|
18
|
+
export type TextFieldProps = {
|
|
19
|
+
name: string;
|
|
20
|
+
label?: string;
|
|
21
|
+
required?: boolean;
|
|
22
|
+
type?: InputHTMLAttributes<HTMLInputElement>['type'];
|
|
23
|
+
className?: string;
|
|
24
|
+
placeholder?: string;
|
|
25
|
+
autoComplete?: InputHTMLAttributes<HTMLInputElement>['autoComplete'];
|
|
26
|
+
autoFocus?: InputHTMLAttributes<HTMLInputElement>['autoFocus'];
|
|
27
|
+
autoFocusDelay?: number;
|
|
28
|
+
inputRef?: Ref<HTMLInputElement>;
|
|
29
|
+
} & ComponentProps<typeof Input>;
|
|
30
|
+
|
|
31
|
+
const emptyRef = (() => {}) as any;
|
|
32
|
+
|
|
33
|
+
export const TextField = forwardRef<HTMLDivElement, TextFieldProps>(
|
|
34
|
+
function TextField(
|
|
35
|
+
{
|
|
36
|
+
name,
|
|
37
|
+
label,
|
|
38
|
+
className,
|
|
39
|
+
autoFocusDelay,
|
|
40
|
+
autoFocus,
|
|
41
|
+
inputRef,
|
|
42
|
+
onChange,
|
|
43
|
+
onFocus,
|
|
44
|
+
onBlur,
|
|
45
|
+
...rest
|
|
46
|
+
},
|
|
47
|
+
ref,
|
|
48
|
+
) {
|
|
49
|
+
const [props] = useField({
|
|
50
|
+
name,
|
|
51
|
+
onChange,
|
|
52
|
+
onFocus,
|
|
53
|
+
onBlur,
|
|
54
|
+
});
|
|
55
|
+
const innerInputRef = useRef<HTMLInputElement>(null);
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (autoFocusDelay) {
|
|
59
|
+
setTimeout(() => {
|
|
60
|
+
if (innerInputRef.current) innerInputRef.current.focus();
|
|
61
|
+
}, autoFocusDelay);
|
|
62
|
+
}
|
|
63
|
+
}, [autoFocusDelay]);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<FieldRoot className={className} ref={ref}>
|
|
67
|
+
{label && <FieldLabel>{label}</FieldLabel>}
|
|
68
|
+
<Input
|
|
69
|
+
{...props}
|
|
70
|
+
{...rest}
|
|
71
|
+
autoFocus={autoFocus}
|
|
72
|
+
ref={useMergedRef(innerInputRef, inputRef || emptyRef)}
|
|
73
|
+
/>
|
|
74
|
+
</FieldRoot>
|
|
75
|
+
);
|
|
76
|
+
},
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
export type TextAreaFieldProps = {
|
|
80
|
+
name: string;
|
|
81
|
+
label?: string;
|
|
82
|
+
required?: boolean;
|
|
83
|
+
rows?: number;
|
|
84
|
+
disabled?: boolean;
|
|
85
|
+
className?: string;
|
|
86
|
+
inputRef?: Ref<HTMLTextAreaElement>;
|
|
87
|
+
} & TextAreaProps;
|
|
88
|
+
|
|
89
|
+
export function TextAreaField({
|
|
90
|
+
name,
|
|
91
|
+
label,
|
|
92
|
+
className,
|
|
93
|
+
inputRef,
|
|
94
|
+
...rest
|
|
95
|
+
}: TextAreaFieldProps) {
|
|
96
|
+
const [props] = useField(name);
|
|
97
|
+
return (
|
|
98
|
+
<FieldRoot className={className}>
|
|
99
|
+
{label && <FieldLabel>{label}</FieldLabel>}
|
|
100
|
+
<TextArea ref={inputRef} {...props} {...rest} />
|
|
101
|
+
</FieldRoot>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const FieldRoot = withClassName(
|
|
106
|
+
'div',
|
|
107
|
+
'flex flex-col items-stretch gap-1 self-stretch',
|
|
108
|
+
);
|
|
109
|
+
const FieldLabel = withClassName(
|
|
110
|
+
'label',
|
|
111
|
+
'inline-flex flex-col gap-1 text-sm font-bold text-dark-blend mb-1',
|
|
112
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './forms/index.js';
|