@shipfox/react-ui 0.10.0 → 0.11.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.
Files changed (35) hide show
  1. package/.turbo/turbo-build.log +5 -5
  2. package/.turbo/turbo-check.log +2 -2
  3. package/.turbo/turbo-type.log +1 -1
  4. package/CHANGELOG.md +6 -0
  5. package/dist/components/button/button.js +14 -11
  6. package/dist/components/button/button.js.map +1 -1
  7. package/dist/components/dropdown-menu/dropdown-menu.d.ts +58 -0
  8. package/dist/components/dropdown-menu/dropdown-menu.d.ts.map +1 -0
  9. package/dist/components/dropdown-menu/dropdown-menu.js +280 -0
  10. package/dist/components/dropdown-menu/dropdown-menu.js.map +1 -0
  11. package/dist/components/dropdown-menu/dropdown-menu.stories.js +462 -0
  12. package/dist/components/dropdown-menu/dropdown-menu.stories.js.map +1 -0
  13. package/dist/components/dropdown-menu/index.d.ts +3 -0
  14. package/dist/components/dropdown-menu/index.d.ts.map +1 -0
  15. package/dist/components/dropdown-menu/index.js +3 -0
  16. package/dist/components/dropdown-menu/index.js.map +1 -0
  17. package/dist/components/dynamic-item/dynamic-item.d.ts +1 -1
  18. package/dist/components/dynamic-item/dynamic-item.d.ts.map +1 -1
  19. package/dist/components/dynamic-item/dynamic-item.js +4 -4
  20. package/dist/components/dynamic-item/dynamic-item.js.map +1 -1
  21. package/dist/components/dynamic-item/dynamic-item.stories.js +11 -1
  22. package/dist/components/dynamic-item/dynamic-item.stories.js.map +1 -1
  23. package/dist/components/index.d.ts +1 -0
  24. package/dist/components/index.d.ts.map +1 -1
  25. package/dist/components/index.js +1 -0
  26. package/dist/components/index.js.map +1 -1
  27. package/dist/styles.css +1 -1
  28. package/package.json +3 -3
  29. package/src/components/button/button.tsx +12 -12
  30. package/src/components/dropdown-menu/dropdown-menu.stories.tsx +384 -0
  31. package/src/components/dropdown-menu/dropdown-menu.tsx +416 -0
  32. package/src/components/dropdown-menu/index.ts +29 -0
  33. package/src/components/dynamic-item/dynamic-item.stories.tsx +6 -1
  34. package/src/components/dynamic-item/dynamic-item.tsx +9 -3
  35. package/src/components/index.ts +1 -0
@@ -0,0 +1,416 @@
1
+ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
2
+ import {cva, type VariantProps} from 'class-variance-authority';
3
+ import type {ComponentProps} from 'react';
4
+ import {cn} from 'utils/cn';
5
+ import {Icon, type IconName} from '../icon';
6
+
7
+ function DropdownMenu({...props}: ComponentProps<typeof DropdownMenuPrimitive.Root>) {
8
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
9
+ }
10
+
11
+ function DropdownMenuTrigger({
12
+ className,
13
+ ...props
14
+ }: ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
15
+ return (
16
+ <DropdownMenuPrimitive.Trigger
17
+ data-slot="dropdown-menu-trigger"
18
+ className={cn('outline-none', className)}
19
+ {...props}
20
+ />
21
+ );
22
+ }
23
+
24
+ function DropdownMenuPortal({...props}: ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
25
+ return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
26
+ }
27
+
28
+ const dropdownMenuContentVariants = cva(
29
+ [
30
+ 'z-50 min-w-[180px] overflow-hidden rounded-10 p-4',
31
+ 'bg-background-neutral-overlay text-foreground-neutral-subtle',
32
+ 'shadow-tooltip',
33
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out',
34
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
35
+ 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
36
+ 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',
37
+ 'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
38
+ ],
39
+ {
40
+ variants: {
41
+ size: {
42
+ sm: 'min-w-[160px]',
43
+ md: 'min-w-[200px]',
44
+ lg: 'min-w-[240px]',
45
+ },
46
+ },
47
+ defaultVariants: {
48
+ size: 'md',
49
+ },
50
+ },
51
+ );
52
+
53
+ type DropdownMenuContentProps = ComponentProps<typeof DropdownMenuPrimitive.Content> &
54
+ VariantProps<typeof dropdownMenuContentVariants> & {
55
+ container?: HTMLElement | null;
56
+ };
57
+
58
+ function DropdownMenuContent({
59
+ className,
60
+ sideOffset = 4,
61
+ size,
62
+ onCloseAutoFocus,
63
+ container,
64
+ ...props
65
+ }: DropdownMenuContentProps) {
66
+ return (
67
+ <DropdownMenuPrimitive.Portal container={container}>
68
+ <DropdownMenuPrimitive.Content
69
+ data-slot="dropdown-menu-content"
70
+ sideOffset={sideOffset}
71
+ className={cn(dropdownMenuContentVariants({size}), className)}
72
+ onCloseAutoFocus={(e) => {
73
+ e.preventDefault();
74
+ onCloseAutoFocus?.(e);
75
+ }}
76
+ {...props}
77
+ />
78
+ </DropdownMenuPrimitive.Portal>
79
+ );
80
+ }
81
+
82
+ function DropdownMenuGroup({
83
+ className,
84
+ ...props
85
+ }: ComponentProps<typeof DropdownMenuPrimitive.Group>) {
86
+ return (
87
+ <DropdownMenuPrimitive.Group
88
+ data-slot="dropdown-menu-group"
89
+ className={cn('flex flex-col', className)}
90
+ {...props}
91
+ />
92
+ );
93
+ }
94
+
95
+ const dropdownMenuLabelVariants = cva(
96
+ 'px-8 py-4 text-xs leading-20 text-foreground-neutral-subtle select-none',
97
+ {
98
+ variants: {
99
+ inset: {
100
+ true: 'pl-32',
101
+ },
102
+ },
103
+ },
104
+ );
105
+
106
+ type DropdownMenuLabelProps = ComponentProps<typeof DropdownMenuPrimitive.Label> & {
107
+ inset?: boolean;
108
+ };
109
+
110
+ function DropdownMenuLabel({className, inset, ...props}: DropdownMenuLabelProps) {
111
+ return (
112
+ <DropdownMenuPrimitive.Label
113
+ data-slot="dropdown-menu-label"
114
+ className={cn(dropdownMenuLabelVariants({inset}), className)}
115
+ {...props}
116
+ />
117
+ );
118
+ }
119
+
120
+ const dropdownMenuItemVariants = cva(
121
+ [
122
+ 'relative flex cursor-pointer select-none items-center gap-8 rounded-6 px-8 py-6',
123
+ 'text-sm leading-20 text-foreground-neutral-subtle outline-none transition-colors',
124
+ 'focus:bg-background-components-hover',
125
+ 'data-disabled:pointer-events-none data-disabled:text-foreground-neutral-disabled',
126
+ ],
127
+ {
128
+ variants: {
129
+ inset: {
130
+ true: 'pl-32',
131
+ },
132
+ variant: {
133
+ default: '',
134
+ },
135
+ },
136
+ defaultVariants: {
137
+ variant: 'default',
138
+ },
139
+ },
140
+ );
141
+
142
+ type DropdownMenuItemProps = ComponentProps<typeof DropdownMenuPrimitive.Item> &
143
+ VariantProps<typeof dropdownMenuItemVariants> & {
144
+ icon?: IconName;
145
+ shortcut?: string;
146
+ closeOnSelect?: boolean;
147
+ iconStyle?: string;
148
+ };
149
+
150
+ function DropdownMenuItem({
151
+ className,
152
+ inset,
153
+ variant,
154
+ icon,
155
+ shortcut,
156
+ children,
157
+ closeOnSelect = true,
158
+ iconStyle,
159
+ onSelect,
160
+ ...props
161
+ }: DropdownMenuItemProps) {
162
+ return (
163
+ <DropdownMenuPrimitive.Item
164
+ data-slot="dropdown-menu-item"
165
+ className={cn(dropdownMenuItemVariants({inset, variant}), className)}
166
+ onSelect={(e) => {
167
+ if (!closeOnSelect) e.preventDefault();
168
+ onSelect?.(e);
169
+ }}
170
+ {...props}
171
+ >
172
+ {icon && (
173
+ <Icon
174
+ name={icon}
175
+ className={cn('size-16 shrink-0 text-foreground-neutral-subtle', iconStyle)}
176
+ />
177
+ )}
178
+ <span className="flex-1 truncate">{children}</span>
179
+ {shortcut && <DropdownMenuShortcut>{shortcut}</DropdownMenuShortcut>}
180
+ </DropdownMenuPrimitive.Item>
181
+ );
182
+ }
183
+
184
+ type DropdownMenuCheckboxItemProps = ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem> & {
185
+ icon?: IconName;
186
+ shortcut?: string;
187
+ closeOnSelect?: boolean;
188
+ };
189
+
190
+ function DropdownMenuCheckboxItem({
191
+ className,
192
+ children,
193
+ checked,
194
+ icon,
195
+ shortcut,
196
+ closeOnSelect = true,
197
+ onSelect,
198
+ ...props
199
+ }: DropdownMenuCheckboxItemProps) {
200
+ return (
201
+ <DropdownMenuPrimitive.CheckboxItem
202
+ data-slot="dropdown-menu-checkbox-item"
203
+ className={cn(
204
+ [
205
+ 'relative flex cursor-pointer select-none items-center gap-8 rounded-6 py-6 pl-32 pr-8',
206
+ 'text-sm leading-20 text-foreground-neutral-subtle outline-none transition-colors',
207
+ 'focus:bg-background-components-hover',
208
+ 'data-disabled:pointer-events-none data-disabled:text-foreground-neutral-disabled',
209
+ 'data-[state=checked]:text-foreground-neutral-base',
210
+ ],
211
+ className,
212
+ )}
213
+ checked={checked}
214
+ onSelect={(e) => {
215
+ if (!closeOnSelect) e.preventDefault();
216
+ onSelect?.(e);
217
+ }}
218
+ {...props}
219
+ >
220
+ <span className="absolute left-8 flex size-16 items-center justify-center">
221
+ <DropdownMenuPrimitive.ItemIndicator>
222
+ <Icon name="check" className="size-14 text-foreground-neutral-base" />
223
+ </DropdownMenuPrimitive.ItemIndicator>
224
+ </span>
225
+ {icon && <Icon name={icon} className="size-16 shrink-0 text-foreground-neutral-subtle" />}
226
+ <span className="flex-1 truncate">{children}</span>
227
+ {shortcut && <DropdownMenuShortcut>{shortcut}</DropdownMenuShortcut>}
228
+ </DropdownMenuPrimitive.CheckboxItem>
229
+ );
230
+ }
231
+
232
+ function DropdownMenuRadioGroup({
233
+ className,
234
+ ...props
235
+ }: ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
236
+ return (
237
+ <DropdownMenuPrimitive.RadioGroup
238
+ data-slot="dropdown-menu-radio-group"
239
+ className={cn('flex flex-col', className)}
240
+ {...props}
241
+ />
242
+ );
243
+ }
244
+
245
+ type DropdownMenuRadioItemProps = ComponentProps<typeof DropdownMenuPrimitive.RadioItem> & {
246
+ icon?: IconName;
247
+ closeOnSelect?: boolean;
248
+ };
249
+
250
+ function DropdownMenuRadioItem({
251
+ className,
252
+ children,
253
+ icon,
254
+ closeOnSelect = true,
255
+ onSelect,
256
+ ...props
257
+ }: DropdownMenuRadioItemProps) {
258
+ return (
259
+ <DropdownMenuPrimitive.RadioItem
260
+ data-slot="dropdown-menu-radio-item"
261
+ className={cn(
262
+ [
263
+ 'relative flex cursor-pointer select-none items-center gap-8 rounded-6 py-6 pl-32 pr-8',
264
+ 'text-sm leading-20 text-foreground-neutral-subtle outline-none transition-colors',
265
+ 'focus:bg-background-components-hover',
266
+ 'data-disabled:pointer-events-none data-disabled:text-foreground-neutral-disabled',
267
+ 'data-[state=checked]:text-foreground-neutral-base',
268
+ ],
269
+ className,
270
+ )}
271
+ onSelect={(e) => {
272
+ if (!closeOnSelect) e.preventDefault();
273
+ onSelect?.(e);
274
+ }}
275
+ {...props}
276
+ >
277
+ <span className="absolute left-8 flex size-16 items-center justify-center">
278
+ <DropdownMenuPrimitive.ItemIndicator>
279
+ <Icon name="ellipseMiniSolid" className="size-6 text-foreground-neutral-base" />
280
+ </DropdownMenuPrimitive.ItemIndicator>
281
+ </span>
282
+ {icon && <Icon name={icon} className="size-16 shrink-0 text-foreground-neutral-subtle" />}
283
+ <span className="flex-1 truncate">{children}</span>
284
+ </DropdownMenuPrimitive.RadioItem>
285
+ );
286
+ }
287
+
288
+ function DropdownMenuSeparator({
289
+ className,
290
+ ...props
291
+ }: ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
292
+ return (
293
+ <DropdownMenuPrimitive.Separator
294
+ data-slot="dropdown-menu-separator"
295
+ className={cn(
296
+ 'relative -mx-4 my-4 h-px',
297
+ 'bg-border-neutral-menu-top',
298
+ 'after:absolute after:inset-x-0 after:top-px after:h-px',
299
+ 'after:bg-border-neutral-menu-bottom',
300
+ className,
301
+ )}
302
+ {...props}
303
+ />
304
+ );
305
+ }
306
+
307
+ type DropdownMenuShortcutProps = ComponentProps<'span'>;
308
+
309
+ function DropdownMenuShortcut({className, ...props}: DropdownMenuShortcutProps) {
310
+ return (
311
+ <span
312
+ data-slot="dropdown-menu-shortcut"
313
+ className={cn(
314
+ 'ml-auto text-xs leading-20 text-foreground-neutral-muted tabular-nums',
315
+ className,
316
+ )}
317
+ {...props}
318
+ />
319
+ );
320
+ }
321
+
322
+ function DropdownMenuSub({...props}: ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
323
+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
324
+ }
325
+
326
+ type DropdownMenuSubTriggerProps = ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
327
+ inset?: boolean;
328
+ icon?: IconName;
329
+ };
330
+
331
+ function DropdownMenuSubTrigger({
332
+ className,
333
+ inset,
334
+ icon,
335
+ children,
336
+ ...props
337
+ }: DropdownMenuSubTriggerProps) {
338
+ return (
339
+ <DropdownMenuPrimitive.SubTrigger
340
+ data-slot="dropdown-menu-sub-trigger"
341
+ className={cn(
342
+ [
343
+ 'relative flex cursor-pointer select-none items-center gap-8 rounded-6 px-8 py-6',
344
+ 'text-sm leading-20 text-foreground-neutral-subtle outline-none transition-colors',
345
+ 'focus:bg-background-components-hover',
346
+ 'data-[state=open]:bg-background-components-hover',
347
+ 'data-disabled:pointer-events-none data-disabled:text-foreground-neutral-disabled',
348
+ 'data-[state=checked]:text-foreground-neutral-base',
349
+ ],
350
+ inset && 'pl-32',
351
+ className,
352
+ )}
353
+ {...props}
354
+ >
355
+ {icon && <Icon name={icon} className="size-16 shrink-0 text-foreground-neutral-subtle" />}
356
+ <span className="flex-1 truncate">{children}</span>
357
+ <Icon name="chevronRight" className="ml-auto size-14 text-foreground-neutral-muted" />
358
+ </DropdownMenuPrimitive.SubTrigger>
359
+ );
360
+ }
361
+
362
+ function DropdownMenuSubContent({
363
+ className,
364
+ ...props
365
+ }: ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
366
+ return (
367
+ <DropdownMenuPrimitive.SubContent
368
+ data-slot="dropdown-menu-sub-content"
369
+ className={cn(
370
+ [
371
+ 'z-50 min-w-180 overflow-hidden rounded-10 p-4',
372
+ 'bg-background-neutral-overlay text-foreground-neutral-subtle',
373
+ 'shadow-tooltip',
374
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out',
375
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
376
+ 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
377
+ 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',
378
+ 'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
379
+ ],
380
+ className,
381
+ )}
382
+ {...props}
383
+ />
384
+ );
385
+ }
386
+
387
+ export {
388
+ DropdownMenu,
389
+ DropdownMenuTrigger,
390
+ DropdownMenuPortal,
391
+ DropdownMenuContent,
392
+ DropdownMenuGroup,
393
+ DropdownMenuLabel,
394
+ DropdownMenuItem,
395
+ DropdownMenuCheckboxItem,
396
+ DropdownMenuRadioGroup,
397
+ DropdownMenuRadioItem,
398
+ DropdownMenuSeparator,
399
+ DropdownMenuShortcut,
400
+ DropdownMenuSub,
401
+ DropdownMenuSubTrigger,
402
+ DropdownMenuSubContent,
403
+ dropdownMenuContentVariants,
404
+ dropdownMenuItemVariants,
405
+ dropdownMenuLabelVariants,
406
+ };
407
+
408
+ export type {
409
+ DropdownMenuContentProps,
410
+ DropdownMenuLabelProps,
411
+ DropdownMenuItemProps,
412
+ DropdownMenuCheckboxItemProps,
413
+ DropdownMenuRadioItemProps,
414
+ DropdownMenuSubTriggerProps,
415
+ DropdownMenuShortcutProps,
416
+ };
@@ -0,0 +1,29 @@
1
+ export type {
2
+ DropdownMenuCheckboxItemProps,
3
+ DropdownMenuContentProps,
4
+ DropdownMenuItemProps,
5
+ DropdownMenuLabelProps,
6
+ DropdownMenuRadioItemProps,
7
+ DropdownMenuShortcutProps,
8
+ DropdownMenuSubTriggerProps,
9
+ } from './dropdown-menu';
10
+ export {
11
+ DropdownMenu,
12
+ DropdownMenuCheckboxItem,
13
+ DropdownMenuContent,
14
+ DropdownMenuGroup,
15
+ DropdownMenuItem,
16
+ DropdownMenuLabel,
17
+ DropdownMenuPortal,
18
+ DropdownMenuRadioGroup,
19
+ DropdownMenuRadioItem,
20
+ DropdownMenuSeparator,
21
+ DropdownMenuShortcut,
22
+ DropdownMenuSub,
23
+ DropdownMenuSubContent,
24
+ DropdownMenuSubTrigger,
25
+ DropdownMenuTrigger,
26
+ dropdownMenuContentVariants,
27
+ dropdownMenuItemVariants,
28
+ dropdownMenuLabelVariants,
29
+ } from './dropdown-menu';
@@ -144,7 +144,12 @@ export const ConnectGithubAccountItem: Story = {
144
144
  </div>
145
145
  }
146
146
  title="Slack"
147
- description="Personal account (kye-nguyen)"
147
+ description={
148
+ <p className="text-xs leading-20 text-tag-warning-icon inline-flex items-center gap-2">
149
+ Personal account (kyle-nguyen)
150
+ <Icon name="info" size="sm" className="shrink-0 size-16 text-tag-warning-icon" />
151
+ </p>
152
+ }
148
153
  rightElement={
149
154
  <Button variant="primary" size="sm">
150
155
  Change account
@@ -14,7 +14,7 @@ export type DynamicItemProps = Omit<ItemProps, 'variant' | 'children' | 'title'>
14
14
  variant?: 'default' | 'neutral';
15
15
  leftElement?: ReactNode;
16
16
  title?: ReactNode;
17
- description?: string;
17
+ description?: ReactNode;
18
18
  action?: ReactNode;
19
19
  rightElement?: ReactNode;
20
20
  contentClassName?: string;
@@ -49,8 +49,14 @@ export function DynamicItem({
49
49
 
50
50
  {hasContent && (
51
51
  <ItemContent className="text-center sm:text-left">
52
- {title && typeof title === 'string' ? <ItemTitle>{title}</ItemTitle> : title}
53
- {description && <ItemDescription>{description}</ItemDescription>}
52
+ {title ? typeof title === 'string' ? <ItemTitle>{title}</ItemTitle> : title : null}
53
+ {description ? (
54
+ typeof description === 'string' ? (
55
+ <ItemDescription>{description}</ItemDescription>
56
+ ) : (
57
+ description
58
+ )
59
+ ) : null}
54
60
  {action && (
55
61
  <ItemActions className={cn('mt-8 flex flex-wrap items-center gap-16')}>
56
62
  {action}
@@ -5,6 +5,7 @@ export * from './button';
5
5
  export * from './checkbox';
6
6
  export * from './code-block';
7
7
  export * from './dot-grid';
8
+ export * from './dropdown-menu';
8
9
  export * from './dynamic-item';
9
10
  export * from './form';
10
11
  export * from './icon';