@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.
Files changed (148) hide show
  1. package/package.json +3 -2
  2. package/src/components/actions/ActionBar.tsx +38 -0
  3. package/src/components/actions/ActionButton.tsx +59 -0
  4. package/src/components/actions/index.ts +2 -0
  5. package/src/components/actions.ts +1 -0
  6. package/src/components/avatar/Avatar.tsx +62 -0
  7. package/src/components/avatar/AvatarList.tsx +71 -0
  8. package/src/components/avatar/index.ts +2 -0
  9. package/src/components/avatar.ts +1 -0
  10. package/src/components/button/Button.stories.tsx +20 -0
  11. package/src/components/button/Button.tsx +66 -0
  12. package/src/components/button/ConfirmedButton.tsx +66 -0
  13. package/src/components/button/classes.tsx +56 -0
  14. package/src/components/button/index.ts +3 -0
  15. package/src/components/button.ts +1 -0
  16. package/src/components/camera/Camera.stories.tsx +40 -0
  17. package/src/components/camera/Camera.tsx +215 -0
  18. package/src/components/camera/index.ts +1 -0
  19. package/src/components/camera.ts +1 -0
  20. package/src/components/card/Card.stories.tsx +41 -0
  21. package/src/components/card/Card.tsx +68 -0
  22. package/src/components/card/index.ts +1 -0
  23. package/src/components/card.ts +1 -0
  24. package/src/components/checkbox/Checkbox.tsx +46 -0
  25. package/src/components/checkbox/index.ts +1 -0
  26. package/src/components/checkbox.ts +1 -0
  27. package/src/components/chip/Chip.tsx +29 -0
  28. package/src/components/chip/index.ts +1 -0
  29. package/src/components/chip.ts +1 -0
  30. package/src/components/collapsible/Collapsible.tsx +48 -0
  31. package/src/components/collapsible/index.ts +1 -0
  32. package/src/components/collapsible.ts +1 -0
  33. package/src/components/colorPicker/ColorPicker.tsx +82 -0
  34. package/src/components/colorPicker/index.ts +1 -0
  35. package/src/components/colorPicker.ts +1 -0
  36. package/src/components/contextMenu/contextMenu.tsx +43 -0
  37. package/src/components/contextMenu.ts +1 -0
  38. package/src/components/dialog/Dialog.stories.tsx +38 -0
  39. package/src/components/dialog/Dialog.tsx +267 -0
  40. package/src/components/dialog/index.ts +1 -0
  41. package/src/components/dialog.ts +1 -0
  42. package/src/components/divider/Divider.tsx +26 -0
  43. package/src/components/divider/index.ts +1 -0
  44. package/src/components/divider.ts +1 -0
  45. package/src/components/dropdownMenu/DropdownMenu.stories.tsx +47 -0
  46. package/src/components/dropdownMenu/DropdownMenu.tsx +89 -0
  47. package/src/components/dropdownMenu/index.ts +1 -0
  48. package/src/components/dropdownMenu.ts +1 -0
  49. package/src/components/errorBoundary/ErrorBoundary.tsx +23 -0
  50. package/src/components/errorBoundary/index.ts +1 -0
  51. package/src/components/errorBoundary.ts +1 -0
  52. package/src/components/forms/Form.tsx +9 -0
  53. package/src/components/forms/FormikForm.tsx +41 -0
  54. package/src/components/forms/SubmitButton.tsx +15 -0
  55. package/src/components/forms/TextField.tsx +112 -0
  56. package/src/components/forms/index.tsx +4 -0
  57. package/src/components/forms.ts +1 -0
  58. package/src/components/icon/Icon.tsx +28 -0
  59. package/src/components/icon/generated/IconSpritesheet.tsx +442 -0
  60. package/src/components/icon/generated/iconNames.ts +44 -0
  61. package/src/components/icon/index.ts +3 -0
  62. package/src/components/icon.ts +1 -0
  63. package/src/components/imageUploader/ImageUploader.stories.tsx +39 -0
  64. package/src/components/imageUploader/ImageUploader.tsx +203 -0
  65. package/src/components/imageUploader/UploadIcon.tsx +23 -0
  66. package/src/components/imageUploader/index.ts +1 -0
  67. package/src/components/imageUploader.ts +1 -0
  68. package/src/components/infiniteLoadTrigger/InfiniteLoadTrigger.tsx +38 -0
  69. package/src/components/infiniteLoadTrigger.ts +1 -0
  70. package/src/components/input/Input.stories.tsx +17 -0
  71. package/src/components/input/Input.tsx +32 -0
  72. package/src/components/input/index.ts +1 -0
  73. package/src/components/input.ts +1 -0
  74. package/src/components/layouts/PageContent.tsx +51 -0
  75. package/src/components/layouts/PageFixedArea.tsx +17 -0
  76. package/src/components/layouts/PageNav.tsx +23 -0
  77. package/src/components/layouts/PageNowPlaying.tsx +24 -0
  78. package/src/components/layouts/PageRoot.tsx +29 -0
  79. package/src/components/layouts/PageSection.tsx +23 -0
  80. package/src/components/layouts/index.tsx +6 -0
  81. package/src/components/layouts.ts +1 -0
  82. package/src/components/liveUpdateTextField/LiveUpdateTextField.tsx +132 -0
  83. package/src/components/liveUpdateTextField/index.ts +1 -0
  84. package/src/components/liveUpdateTextField.ts +1 -0
  85. package/src/components/navBar/NavBar.tsx +59 -0
  86. package/src/components/navBar/index.ts +1 -0
  87. package/src/components/navBar.ts +1 -0
  88. package/src/components/note/Note.tsx +21 -0
  89. package/src/components/note/index.ts +1 -0
  90. package/src/components/note.ts +1 -0
  91. package/src/components/numberStepper/NumberStepper.stories.tsx +21 -0
  92. package/src/components/numberStepper/NumberStepper.tsx +74 -0
  93. package/src/components/numberStepper/index.ts +1 -0
  94. package/src/components/numberStepper.ts +1 -0
  95. package/src/components/particles/ParticleContext.tsx +11 -0
  96. package/src/components/particles/ParticleLayer.stories.tsx +46 -0
  97. package/src/components/particles/ParticleLayer.tsx +28 -0
  98. package/src/components/particles/index.ts +7 -0
  99. package/src/components/particles/particlesState.ts +502 -0
  100. package/src/components/particles.ts +1 -0
  101. package/src/components/peek/Peek.tsx +74 -0
  102. package/src/components/peek/index.ts +1 -0
  103. package/src/components/peek.ts +1 -0
  104. package/src/components/popover/Popover.tsx +84 -0
  105. package/src/components/popover/index.ts +1 -0
  106. package/src/components/popover.ts +1 -0
  107. package/src/components/relativeTime/RelativeTime.tsx +43 -0
  108. package/src/components/relativeTime/index.ts +1 -0
  109. package/src/components/relativeTime.ts +1 -0
  110. package/src/components/richEditor/EditorContent.tsx +4 -0
  111. package/src/components/richEditor/RichEditor.tsx +38 -0
  112. package/src/components/richEditor/index.ts +1 -0
  113. package/src/components/richEditor.ts +1 -0
  114. package/src/components/select/Select.tsx +247 -0
  115. package/src/components/select/index.ts +1 -0
  116. package/src/components/select.ts +1 -0
  117. package/src/components/skeletons/skeletons.tsx +27 -0
  118. package/src/components/skeletons.ts +1 -0
  119. package/src/components/spinner/Spinner.tsx +59 -0
  120. package/src/components/spinner/index.ts +1 -0
  121. package/src/components/spinner.ts +1 -0
  122. package/src/components/switch/Switch.tsx +23 -0
  123. package/src/components/switch/index.ts +1 -0
  124. package/src/components/switch.ts +1 -0
  125. package/src/components/tabs/tabs.tsx +18 -0
  126. package/src/components/tabs.ts +1 -0
  127. package/src/components/textArea/TextArea.stories.tsx +21 -0
  128. package/src/components/textArea/TextArea.tsx +58 -0
  129. package/src/components/textArea/index.ts +1 -0
  130. package/src/components/textArea.ts +1 -0
  131. package/src/components/toggleGroup/toggleGroup.tsx +11 -0
  132. package/src/components/toggleGroup.ts +1 -0
  133. package/src/components/tooltip/Tooltip.tsx +56 -0
  134. package/src/components/tooltip/index.ts +1 -0
  135. package/src/components/tooltip.ts +1 -0
  136. package/src/components/typography/index.ts +1 -0
  137. package/src/components/typography/typography.tsx +18 -0
  138. package/src/components/typography.ts +1 -0
  139. package/src/hooks/index.ts +7 -0
  140. package/src/hooks/useMergedRef.ts +14 -0
  141. package/src/hooks/useOnUnmount.ts +20 -0
  142. package/src/hooks/useSize.ts +164 -0
  143. package/src/hooks/useStableCallback.ts +11 -0
  144. package/src/hooks/useToggle.tsx +9 -0
  145. package/src/hooks/useVisualViewportOffset.ts +35 -0
  146. package/src/hooks/withClassName.tsx +21 -0
  147. package/src/hooks.ts +1 -0
  148. 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,9 @@
1
+ 'use client';
2
+
3
+ import { Form as FormikForm } from 'formik';
4
+ import { withClassName } from '../../hooks/withClassName.js';
5
+
6
+ export const Form = withClassName(
7
+ FormikForm,
8
+ 'flex flex-col gap-2 items-start',
9
+ );
@@ -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,4 @@
1
+ export * from './Form.js';
2
+ export * from './SubmitButton.js';
3
+ export * from './TextField.js';
4
+ export * from './FormikForm.js';
@@ -0,0 +1 @@
1
+ export * from './forms/index.js';