@ponti-studios/ui 2.0.1 → 3.0.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 (319) hide show
  1. package/README.md +36 -3
  2. package/dist/auth/index.d.ts +3 -0
  3. package/dist/auth/index.js +3 -0
  4. package/dist/components/data-display/accordion.d.ts +10 -0
  5. package/dist/components/data-display/accordion.js +53 -0
  6. package/{src/components/data-display/index.ts → dist/components/data-display/index.d.ts} +1 -10
  7. package/dist/components/data-display/index.js +8 -0
  8. package/dist/components/data-display/metric-card.d.ts +9 -0
  9. package/dist/components/data-display/metric-card.js +25 -0
  10. package/dist/components/data-display/sort-controls.d.ts +13 -0
  11. package/dist/components/data-display/sort-controls.js +57 -0
  12. package/dist/components/data-display/sort-row.d.ts +10 -0
  13. package/dist/components/data-display/sort-row.js +52 -0
  14. package/dist/components/data-display/table.d.ts +10 -0
  15. package/dist/components/data-display/table.js +57 -0
  16. package/dist/components/feedback/alert.d.ts +9 -0
  17. package/dist/components/feedback/alert.js +38 -0
  18. package/dist/components/feedback/empty-state.d.ts +18 -0
  19. package/dist/components/feedback/empty-state.js +36 -0
  20. package/{src/components/feedback/index.ts → dist/components/feedback/index.d.ts} +2 -0
  21. package/dist/components/feedback/index.js +7 -0
  22. package/dist/components/feedback/progress.d.ts +6 -0
  23. package/dist/components/feedback/progress.js +25 -0
  24. package/dist/components/feedback/skeleton.d.ts +2 -0
  25. package/dist/components/feedback/skeleton.js +11 -0
  26. package/dist/components/feedback/spinner.d.ts +14 -0
  27. package/dist/components/feedback/spinner.js +27 -0
  28. package/dist/components/feedback/update-guard.d.ts +13 -0
  29. package/dist/components/feedback/update-guard.js +38 -0
  30. package/dist/components/filters/active-filters-bar.d.ts +14 -0
  31. package/dist/components/filters/active-filters-bar.js +44 -0
  32. package/dist/components/filters/filter-chip.d.ts +11 -0
  33. package/dist/components/filters/filter-chip.js +33 -0
  34. package/dist/components/filters/filter-select.d.ts +17 -0
  35. package/dist/components/filters/filter-select.js +46 -0
  36. package/dist/components/filters/index.js +4 -0
  37. package/dist/components/forms/checkbox.d.ts +4 -0
  38. package/dist/components/forms/checkbox.js +24 -0
  39. package/dist/components/forms/date-month-select.d.ts +11 -0
  40. package/dist/components/forms/date-month-select.js +47 -0
  41. package/dist/components/forms/date-picker.d.ts +60 -0
  42. package/dist/components/forms/date-picker.js +193 -0
  43. package/dist/components/forms/date-utils.d.ts +14 -0
  44. package/dist/components/forms/date-utils.js +18 -0
  45. package/dist/components/forms/drop-zone.d.ts +42 -0
  46. package/dist/components/forms/drop-zone.js +184 -0
  47. package/{src/components/forms/index.ts → dist/components/forms/index.d.ts} +3 -9
  48. package/dist/components/forms/index.js +17 -0
  49. package/dist/components/forms/input.d.ts +3 -0
  50. package/dist/components/forms/input.js +15 -0
  51. package/dist/components/forms/label.d.ts +3 -0
  52. package/dist/components/forms/label.js +14 -0
  53. package/dist/components/forms/passkey-management.d.ts +14 -0
  54. package/dist/components/forms/passkey-management.js +132 -0
  55. package/dist/components/forms/radio-group.d.ts +6 -0
  56. package/dist/components/forms/radio-group.js +28 -0
  57. package/dist/components/forms/select.d.ts +18 -0
  58. package/dist/components/forms/select.js +103 -0
  59. package/dist/components/forms/slider.d.ts +9 -0
  60. package/dist/components/forms/slider.js +25 -0
  61. package/dist/components/forms/stepper.d.ts +15 -0
  62. package/dist/components/forms/stepper.js +41 -0
  63. package/dist/components/forms/switch.d.ts +6 -0
  64. package/dist/components/forms/switch.js +21 -0
  65. package/dist/components/forms/text-field.d.ts +15 -0
  66. package/dist/components/forms/text-field.js +46 -0
  67. package/dist/components/forms/text-field.types.d.ts +8 -0
  68. package/dist/components/forms/textarea.d.ts +3 -0
  69. package/dist/components/forms/textarea.js +14 -0
  70. package/dist/components/layout/index.js +5 -0
  71. package/dist/components/layout/particle-background.d.ts +19 -0
  72. package/dist/components/layout/particle-background.js +171 -0
  73. package/dist/components/layout/scroll-area.d.ts +9 -0
  74. package/dist/components/layout/scroll-area.js +35 -0
  75. package/dist/components/layout/section-intro.d.ts +12 -0
  76. package/dist/components/layout/section-intro.js +28 -0
  77. package/dist/components/layout/surface-panel.d.ts +7 -0
  78. package/dist/components/layout/surface-panel.js +11 -0
  79. package/dist/components/navigation/app-navigation.d.ts +67 -0
  80. package/dist/components/navigation/app-navigation.js +106 -0
  81. package/{src/components/navigation/index.ts → dist/components/navigation/index.d.ts} +1 -7
  82. package/dist/components/navigation/index.js +4 -0
  83. package/dist/components/navigation/pagination-controls.d.ts +7 -0
  84. package/dist/components/navigation/pagination-controls.js +50 -0
  85. package/dist/components/navigation/tabs.d.ts +7 -0
  86. package/dist/components/navigation/tabs.js +35 -0
  87. package/dist/components/overlays/bottom-sheet.d.ts +20 -0
  88. package/dist/components/overlays/bottom-sheet.js +101 -0
  89. package/dist/components/overlays/command.d.ts +29 -0
  90. package/dist/components/overlays/command.js +123 -0
  91. package/dist/components/overlays/dialog.d.ts +30 -0
  92. package/dist/components/overlays/dialog.js +105 -0
  93. package/dist/components/overlays/dropdown-menu.d.ts +29 -0
  94. package/dist/components/overlays/dropdown-menu.js +128 -0
  95. package/dist/components/overlays/index.d.ts +7 -0
  96. package/dist/components/overlays/index.js +8 -0
  97. package/dist/components/overlays/popover.d.ts +14 -0
  98. package/dist/components/overlays/popover.js +56 -0
  99. package/dist/components/overlays/sheet.d.ts +27 -0
  100. package/dist/components/overlays/sheet.js +81 -0
  101. package/dist/components/overlays/tooltip.d.ts +10 -0
  102. package/dist/components/overlays/tooltip.js +23 -0
  103. package/dist/components/primitives/avatar.d.ts +14 -0
  104. package/dist/components/primitives/avatar.js +72 -0
  105. package/dist/components/primitives/badge.d.ts +7 -0
  106. package/dist/components/primitives/badge.js +27 -0
  107. package/dist/components/primitives/button.d.ts +13 -0
  108. package/dist/components/primitives/button.js +113 -0
  109. package/{src/components/primitives/button.types.ts → dist/components/primitives/button.types.d.ts} +4 -6
  110. package/dist/components/primitives/card.d.ts +9 -0
  111. package/dist/components/primitives/card.js +56 -0
  112. package/{src/components/primitives/index.ts → dist/components/primitives/index.d.ts} +1 -9
  113. package/dist/components/primitives/index.js +7 -0
  114. package/dist/components/primitives/segmented-control.d.ts +20 -0
  115. package/dist/components/primitives/segmented-control.js +27 -0
  116. package/dist/components/primitives/separator.d.ts +4 -0
  117. package/dist/components/primitives/separator.js +16 -0
  118. package/dist/constants/chart-colors.d.ts +33 -0
  119. package/dist/constants/chart-colors.js +31 -0
  120. package/dist/hooks/index.js +9 -0
  121. package/{src/hooks/sort.types.ts → dist/hooks/sort.types.d.ts} +2 -4
  122. package/dist/hooks/use-api-client.d.ts +30 -0
  123. package/dist/hooks/use-api-client.js +106 -0
  124. package/dist/hooks/use-debounced-value.d.ts +1 -0
  125. package/dist/hooks/use-debounced-value.js +12 -0
  126. package/dist/hooks/use-derived-filter-state.d.ts +20 -0
  127. package/dist/hooks/use-derived-filter-state.js +39 -0
  128. package/dist/hooks/use-filter-state.d.ts +12 -0
  129. package/dist/hooks/use-filter-state.js +37 -0
  130. package/dist/hooks/use-media-query.d.ts +6 -0
  131. package/dist/hooks/use-media-query.js +27 -0
  132. package/dist/hooks/use-mobile.d.ts +1 -0
  133. package/dist/hooks/use-mobile.js +14 -0
  134. package/dist/hooks/use-sort.d.ts +11 -0
  135. package/dist/hooks/use-sort.js +14 -0
  136. package/{src/index.ts → dist/index.d.ts} +4 -32
  137. package/dist/index.js +15 -0
  138. package/dist/integrations/service-worker.d.ts +2 -0
  139. package/dist/integrations/service-worker.js +2 -0
  140. package/dist/lib/auth.d.ts +2 -0
  141. package/dist/lib/auth.js +7 -0
  142. package/dist/lib/clipboard.d.ts +1 -0
  143. package/dist/lib/clipboard.js +10 -0
  144. package/dist/lib/index.js +4 -0
  145. package/{src/lib/slot.tsx → dist/lib/slot.d.ts} +2 -15
  146. package/dist/lib/slot.js +14 -0
  147. package/dist/lib/utils.d.ts +2 -0
  148. package/dist/lib/utils.js +8 -0
  149. package/dist/native/components.d.ts +41 -0
  150. package/dist/native/components.js +135 -0
  151. package/dist/native/index.js +3 -0
  152. package/dist/native/theme.d.ts +19 -0
  153. package/dist/native/theme.js +58 -0
  154. package/dist/styles/tokens/css-vars.d.ts +7 -0
  155. package/dist/styles/tokens/css-vars.js +5 -0
  156. package/dist/styles/tokens/generated/dark.colors.generated.d.ts +40 -0
  157. package/dist/styles/tokens/generated/dark.colors.generated.js +39 -0
  158. package/dist/styles/tokens/generated/foundations.generated.d.ts +323 -0
  159. package/dist/styles/tokens/generated/foundations.generated.js +453 -0
  160. package/dist/styles/tokens/generated/light.colors.generated.d.ts +40 -0
  161. package/dist/styles/tokens/generated/light.colors.generated.js +39 -0
  162. package/dist/styles/tokens/index.d.ts +317 -0
  163. package/dist/styles/tokens/index.js +14 -0
  164. package/dist/styles.css +1 -1
  165. package/dist/translations/en.d.ts +34 -0
  166. package/dist/translations/index.d.ts +39 -0
  167. package/dist/translations/ui-provider.d.ts +9 -0
  168. package/dist/translations/ui-provider.js +16 -0
  169. package/dist/types/upload.d.ts +36 -0
  170. package/package.json +50 -33
  171. package/src/components/data-display/accordion.stories.tsx +0 -57
  172. package/src/components/data-display/accordion.tsx +0 -86
  173. package/src/components/data-display/metric-card.stories.tsx +0 -48
  174. package/src/components/data-display/metric-card.tsx +0 -20
  175. package/src/components/data-display/sort-controls.stories.tsx +0 -45
  176. package/src/components/data-display/sort-controls.tsx +0 -96
  177. package/src/components/data-display/sort-row.tsx +0 -88
  178. package/src/components/data-display/table.stories.tsx +0 -75
  179. package/src/components/data-display/table.tsx +0 -80
  180. package/src/components/feedback/alert.stories.tsx +0 -51
  181. package/src/components/feedback/alert.tsx +0 -64
  182. package/src/components/feedback/empty-state.tsx +0 -76
  183. package/src/components/feedback/progress.stories.tsx +0 -54
  184. package/src/components/feedback/progress.tsx +0 -41
  185. package/src/components/feedback/skeleton.stories.tsx +0 -36
  186. package/src/components/feedback/skeleton.tsx +0 -7
  187. package/src/components/feedback/spinner.stories.tsx +0 -52
  188. package/src/components/feedback/spinner.tsx +0 -47
  189. package/src/components/feedback/update-guard.stories.tsx +0 -28
  190. package/src/components/feedback/update-guard.tsx +0 -57
  191. package/src/components/filters/active-filters-bar.stories.tsx +0 -114
  192. package/src/components/filters/active-filters-bar.tsx +0 -61
  193. package/src/components/filters/filter-chip.tsx +0 -57
  194. package/src/components/filters/filter-select.stories.tsx +0 -71
  195. package/src/components/filters/filter-select.tsx +0 -68
  196. package/src/components/forms/checkbox.stories.tsx +0 -35
  197. package/src/components/forms/checkbox.tsx +0 -38
  198. package/src/components/forms/date-month-select.stories.tsx +0 -26
  199. package/src/components/forms/date-month-select.tsx +0 -79
  200. package/src/components/forms/date-picker.stories.tsx +0 -188
  201. package/src/components/forms/date-picker.tsx +0 -303
  202. package/src/components/forms/date-utils.ts +0 -37
  203. package/src/components/forms/drop-zone.stories.tsx +0 -140
  204. package/src/components/forms/drop-zone.tsx +0 -298
  205. package/src/components/forms/input.stories.tsx +0 -47
  206. package/src/components/forms/input.tsx +0 -24
  207. package/src/components/forms/label.stories.tsx +0 -16
  208. package/src/components/forms/label.tsx +0 -25
  209. package/src/components/forms/passkey-management.stories.tsx +0 -85
  210. package/src/components/forms/passkey-management.tsx +0 -168
  211. package/src/components/forms/radio-group.stories.tsx +0 -46
  212. package/src/components/forms/radio-group.tsx +0 -44
  213. package/src/components/forms/select.stories.tsx +0 -207
  214. package/src/components/forms/select.tsx +0 -182
  215. package/src/components/forms/slider.stories.tsx +0 -70
  216. package/src/components/forms/slider.tsx +0 -50
  217. package/src/components/forms/stepper.stories.tsx +0 -111
  218. package/src/components/forms/stepper.tsx +0 -66
  219. package/src/components/forms/switch.stories.tsx +0 -144
  220. package/src/components/forms/switch.tsx +0 -39
  221. package/src/components/forms/text-field.stories.tsx +0 -87
  222. package/src/components/forms/text-field.tsx +0 -75
  223. package/src/components/forms/text-field.types.ts +0 -9
  224. package/src/components/forms/textarea.stories.tsx +0 -150
  225. package/src/components/forms/textarea.tsx +0 -22
  226. package/src/components/layout/particle-background.stories.tsx +0 -38
  227. package/src/components/layout/particle-background.tsx +0 -378
  228. package/src/components/layout/scroll-area.stories.tsx +0 -91
  229. package/src/components/layout/scroll-area.tsx +0 -79
  230. package/src/components/layout/section-intro.stories.tsx +0 -29
  231. package/src/components/layout/section-intro.tsx +0 -31
  232. package/src/components/layout/surface-panel.stories.tsx +0 -32
  233. package/src/components/layout/surface-panel.tsx +0 -23
  234. package/src/components/navigation/app-navigation.stories.tsx +0 -217
  235. package/src/components/navigation/app-navigation.tsx +0 -234
  236. package/src/components/navigation/pagination-controls.stories.tsx +0 -41
  237. package/src/components/navigation/pagination-controls.tsx +0 -46
  238. package/src/components/navigation/tabs.stories.tsx +0 -48
  239. package/src/components/navigation/tabs.tsx +0 -52
  240. package/src/components/overlays/bottom-sheet.stories.tsx +0 -52
  241. package/src/components/overlays/bottom-sheet.tsx +0 -179
  242. package/src/components/overlays/command.stories.tsx +0 -65
  243. package/src/components/overlays/command.tsx +0 -227
  244. package/src/components/overlays/dialog.stories.tsx +0 -148
  245. package/src/components/overlays/dialog.tsx +0 -203
  246. package/src/components/overlays/dropdown-menu.stories.tsx +0 -74
  247. package/src/components/overlays/dropdown-menu.tsx +0 -236
  248. package/src/components/overlays/index.ts +0 -56
  249. package/src/components/overlays/popover.stories.tsx +0 -51
  250. package/src/components/overlays/popover.tsx +0 -78
  251. package/src/components/overlays/sheet.stories.tsx +0 -68
  252. package/src/components/overlays/sheet.tsx +0 -155
  253. package/src/components/overlays/tooltip.stories.tsx +0 -29
  254. package/src/components/overlays/tooltip.tsx +0 -41
  255. package/src/components/primitives/avatar-placeholder.svg +0 -13
  256. package/src/components/primitives/avatar.stories.tsx +0 -72
  257. package/src/components/primitives/avatar.tsx +0 -136
  258. package/src/components/primitives/badge.stories.tsx +0 -43
  259. package/src/components/primitives/badge.tsx +0 -43
  260. package/src/components/primitives/button.stories.tsx +0 -113
  261. package/src/components/primitives/button.tsx +0 -131
  262. package/src/components/primitives/card.stories.tsx +0 -59
  263. package/src/components/primitives/card.tsx +0 -66
  264. package/src/components/primitives/segmented-control.stories.tsx +0 -54
  265. package/src/components/primitives/segmented-control.tsx +0 -73
  266. package/src/components/primitives/separator.stories.tsx +0 -34
  267. package/src/components/primitives/separator.tsx +0 -27
  268. package/src/constants/chart-colors.ts +0 -39
  269. package/src/env.d.ts +0 -9
  270. package/src/hooks/use-api-client.ts +0 -122
  271. package/src/hooks/use-debounced-value.ts +0 -12
  272. package/src/hooks/use-derived-filter-state.ts +0 -70
  273. package/src/hooks/use-filter-state.ts +0 -66
  274. package/src/hooks/use-media-query.ts +0 -69
  275. package/src/hooks/use-mobile.tsx +0 -19
  276. package/src/hooks/use-sort.ts +0 -20
  277. package/src/lib/auth.ts +0 -5
  278. package/src/lib/clipboard.ts +0 -8
  279. package/src/lib/utils.ts +0 -6
  280. package/src/native/components.tsx +0 -198
  281. package/src/native/theme.tsx +0 -69
  282. package/src/storybook/controls.ts +0 -117
  283. package/src/storybook/options.ts +0 -52
  284. package/src/styles/build.ts +0 -1
  285. package/src/styles/index.css +0 -423
  286. package/src/styles/nokia.css +0 -36
  287. package/src/styles/package.css +0 -3
  288. package/src/styles/tokens/chart-colors.stories.tsx +0 -189
  289. package/src/styles/tokens/color-systems.stories.tsx +0 -190
  290. package/src/styles/tokens/css-vars.ts +0 -15
  291. package/src/styles/tokens/generated/dark-class.css +0 -39
  292. package/src/styles/tokens/generated/dark.colors.generated.ts +0 -39
  293. package/src/styles/tokens/generated/dark.css +0 -39
  294. package/src/styles/tokens/generated/foundations.css +0 -149
  295. package/src/styles/tokens/generated/foundations.generated.ts +0 -454
  296. package/src/styles/tokens/generated/light.colors.generated.ts +0 -39
  297. package/src/styles/tokens/generated/light.css +0 -39
  298. package/src/styles/tokens/index.ts +0 -70
  299. package/src/styles/tokens/layout.stories.tsx +0 -50
  300. package/src/styles/tokens/scripts/check-foundations.mjs +0 -126
  301. package/src/styles/tokens/scripts/check-source.mjs +0 -53
  302. package/src/styles/tokens/source/dark.tokens.json +0 -138
  303. package/src/styles/tokens/source/foundations.tokens.json +0 -344
  304. package/src/styles/tokens/source/light.tokens.json +0 -138
  305. package/src/styles/tokens/style-dictionary.dark-class.json +0 -16
  306. package/src/styles/tokens/style-dictionary.dark-storybook.json +0 -16
  307. package/src/styles/tokens/style-dictionary.dark.json +0 -21
  308. package/src/styles/tokens/style-dictionary.foundations.json +0 -21
  309. package/src/styles/tokens/style-dictionary.light-storybook.json +0 -16
  310. package/src/styles/tokens/style-dictionary.light.json +0 -21
  311. package/src/styles/tokens/typography.stories.tsx +0 -157
  312. package/src/translations/en.ts +0 -34
  313. package/src/translations/index.ts +0 -32
  314. package/src/types/upload.ts +0 -42
  315. /package/{src/components/filters/index.ts → dist/components/filters/index.d.ts} +0 -0
  316. /package/{src/components/layout/index.ts → dist/components/layout/index.d.ts} +0 -0
  317. /package/{src/hooks/index.ts → dist/hooks/index.d.ts} +0 -0
  318. /package/{src/lib/index.ts → dist/lib/index.d.ts} +0 -0
  319. /package/{src/native/index.ts → dist/native/index.d.ts} +0 -0
@@ -1,179 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
-
5
- import { cn } from "../../lib/utils";
6
-
7
- // Non-modal persistent bottom panel with drag-to-collapse/expand.
8
- // Positioning and sizing are app-owned via className (the kit only sets
9
- // structure + the translateY transform; Tailwind translate utilities compose
10
- // via the independent CSS `translate` property).
11
- //
12
- // Distinct from Sheet: Sheet is a modal dialog; BottomSheet is always
13
- // mounted chrome (maps, players, detail panels).
14
-
15
- const FLICK_VELOCITY = 0.4;
16
- const DRAG_THRESHOLD = 0.25;
17
-
18
- export type BottomSheetProps = {
19
- children: React.ReactNode;
20
- /** Controlled expanded state. */
21
- expanded?: boolean;
22
- /** Uncontrolled initial state. @default false */
23
- defaultExpanded?: boolean;
24
- onExpandedChange?: (expanded: boolean) => void;
25
- /** Visible stub height when collapsed. @default 48 */
26
- peek?: number;
27
- /** Accessible name for the panel region. @default "Details panel" */
28
- label?: string;
29
- /** Positioning/sizing (app-owned). */
30
- className?: string;
31
- /** Applied to the scrollable content wrapper alongside its defaults. */
32
- scrollClassName?: string;
33
- /** Applied to the drag handle button alongside its defaults. */
34
- handleClassName?: string;
35
- };
36
-
37
- export function BottomSheet({
38
- children,
39
- expanded: controlledExpanded,
40
- defaultExpanded = false,
41
- onExpandedChange,
42
- peek = 48,
43
- label = "Details panel",
44
- className,
45
- scrollClassName,
46
- handleClassName,
47
- }: BottomSheetProps) {
48
- const sheetRef = React.useRef<HTMLElement>(null);
49
- const [translateY, setTranslateY] = React.useState(0);
50
- const [dragOffset, setDragOffset] = React.useState(0);
51
- const [internalExpanded, setInternalExpanded] = React.useState(defaultExpanded);
52
- const isDragging = React.useRef(false);
53
- const didDrag = React.useRef(false);
54
- const dragRef = React.useRef<{ startY: number; startTime: number } | null>(null);
55
- const reduceMotion = React.useRef(
56
- typeof window !== "undefined" &&
57
- window.matchMedia("(prefers-reduced-motion: reduce)").matches,
58
- );
59
-
60
- const expanded = controlledExpanded ?? internalExpanded;
61
-
62
- const getHeight = React.useCallback(() => sheetRef.current?.offsetHeight ?? 400, []);
63
- const closedY = React.useCallback(() => Math.max(0, getHeight() - peek), [getHeight, peek]);
64
-
65
- const setExpanded = React.useCallback(
66
- (next: boolean) => {
67
- onExpandedChange?.(next);
68
- if (controlledExpanded === undefined) setInternalExpanded(next);
69
- },
70
- [controlledExpanded, onExpandedChange],
71
- );
72
-
73
- const snapTo = React.useCallback((targetY: number) => {
74
- setTranslateY(targetY);
75
- setDragOffset(0);
76
- }, []);
77
-
78
- // Sync visuals with expanded state (and settle closed on mount).
79
- React.useEffect(() => {
80
- snapTo(expanded ? 0 : closedY());
81
- }, [expanded, closedY, snapTo]);
82
-
83
- const open = React.useCallback(() => setExpanded(true), [setExpanded]);
84
- const close = React.useCallback(() => setExpanded(false), [setExpanded]);
85
-
86
- const onPointerDown = React.useCallback((e: React.PointerEvent) => {
87
- isDragging.current = true;
88
- didDrag.current = false;
89
- dragRef.current = { startY: e.clientY, startTime: Date.now() };
90
- (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
91
- }, []);
92
-
93
- const onPointerMove = React.useCallback(
94
- (e: React.PointerEvent) => {
95
- if (!isDragging.current || !dragRef.current) return;
96
- const delta = e.clientY - dragRef.current.startY;
97
- if (Math.abs(delta) > 4) didDrag.current = true;
98
- setDragOffset(Math.max(-translateY, delta));
99
- },
100
- [translateY],
101
- );
102
-
103
- const endDrag = React.useCallback(
104
- (e: React.PointerEvent) => {
105
- if (!isDragging.current || !dragRef.current) return;
106
- isDragging.current = false;
107
-
108
- const delta = e.clientY - dragRef.current.startY;
109
- const elapsed = Date.now() - dragRef.current.startTime;
110
- const velocity = elapsed > 0 ? delta / elapsed : 0;
111
- dragRef.current = null;
112
- setDragOffset(0);
113
-
114
- if (didDrag.current) {
115
- const height = getHeight();
116
- const newY = Math.max(0, translateY + delta);
117
- const fraction = height > 0 ? newY / height : 0;
118
-
119
- if (velocity > FLICK_VELOCITY) close();
120
- else if (velocity < -FLICK_VELOCITY) open();
121
- else if (fraction > DRAG_THRESHOLD) close();
122
- else open();
123
- }
124
- },
125
- [translateY, getHeight, open, close],
126
- );
127
-
128
- const onHandleClick = React.useCallback(() => {
129
- if (didDrag.current) return;
130
- if (expanded) close();
131
- else open();
132
- }, [expanded, open, close]);
133
-
134
- const currentY = Math.max(0, translateY + dragOffset);
135
-
136
- return (
137
- <section
138
- ref={sheetRef}
139
- data-slot="bottom-sheet"
140
- aria-label={label}
141
- className={cn("flex touch-none flex-col overflow-hidden", className)}
142
- style={{
143
- transform: `translateY(${currentY}px)`,
144
- transition: isDragging.current
145
- ? "none"
146
- : reduceMotion.current
147
- ? "none"
148
- : "transform 0.35s cubic-bezier(0.32, 0.72, 0, 1)",
149
- }}
150
- >
151
- <button
152
- type="button"
153
- data-slot="bottom-sheet-handle"
154
- aria-expanded={expanded}
155
- aria-label={expanded ? "Collapse panel" : "Expand panel"}
156
- onPointerDown={onPointerDown}
157
- onPointerMove={onPointerMove}
158
- onPointerUp={endDrag}
159
- onPointerCancel={endDrag}
160
- onClick={onHandleClick}
161
- onKeyDown={(e) => {
162
- if (e.key === "Escape") close();
163
- }}
164
- className={cn(
165
- "flex w-full shrink-0 cursor-grab touch-none justify-center py-3.5 active:cursor-grabbing",
166
- handleClassName,
167
- )}
168
- >
169
- <span aria-hidden="true" className="bg-border h-1 w-9 rounded-full" />
170
- </button>
171
- <div
172
- data-slot="bottom-sheet-scroll"
173
- className={cn("flex-1 overflow-y-auto", scrollClassName)}
174
- >
175
- {children}
176
- </div>
177
- </section>
178
- );
179
- }
@@ -1,65 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import { Calculator, Calendar, CreditCard, Settings, Smile, User } from "lucide-react";
3
-
4
- import {
5
- Command,
6
- CommandEmpty,
7
- CommandGroup,
8
- CommandInput,
9
- CommandItem,
10
- CommandList,
11
- CommandSeparator,
12
- CommandShortcut,
13
- } from "./command";
14
-
15
- const meta = {
16
- title: "Overlays/Command",
17
- component: Command,
18
- tags: ["autodocs"],
19
- } satisfies Meta<typeof Command>;
20
-
21
- export default meta;
22
- type Story = StoryObj<typeof meta>;
23
-
24
- export const Default: Story = {
25
- render: () => (
26
- <Command className="w-[450px] rounded-md border">
27
- <CommandInput placeholder="Type a command or search..." />
28
- <CommandList>
29
- <CommandEmpty>No results found.</CommandEmpty>
30
- <CommandGroup heading="Suggestions">
31
- <CommandItem>
32
- <Calendar className="mr-2 size-4" />
33
- <span>Calendar</span>
34
- </CommandItem>
35
- <CommandItem>
36
- <Smile className="mr-2 size-4" />
37
- <span>Search Emoji</span>
38
- </CommandItem>
39
- <CommandItem>
40
- <Calculator className="mr-2 size-4" />
41
- <span>Calculator</span>
42
- </CommandItem>
43
- </CommandGroup>
44
- <CommandSeparator />
45
- <CommandGroup heading="Settings">
46
- <CommandItem>
47
- <User className="mr-2 size-4" />
48
- <span>Profile</span>
49
- <CommandShortcut>⌘P</CommandShortcut>
50
- </CommandItem>
51
- <CommandItem>
52
- <CreditCard className="mr-2 size-4" />
53
- <span>Billing</span>
54
- <CommandShortcut>⌘B</CommandShortcut>
55
- </CommandItem>
56
- <CommandItem>
57
- <Settings className="mr-2 size-4" />
58
- <span>Settings</span>
59
- <CommandShortcut>⌘S</CommandShortcut>
60
- </CommandItem>
61
- </CommandGroup>
62
- </CommandList>
63
- </Command>
64
- ),
65
- };
@@ -1,227 +0,0 @@
1
- import { Combobox as CommandPrimitive } from "@base-ui/react/combobox";
2
- import { SearchIcon } from "lucide-react";
3
- import * as React from "react";
4
-
5
- import { cn } from "../../lib/utils";
6
- import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./dialog";
7
-
8
- function Command({
9
- className,
10
- children,
11
- ...props
12
- }: Omit<React.ComponentProps<typeof CommandPrimitive.Root<string>>, "className"> & {
13
- className?: string;
14
- }) {
15
- return (
16
- <div
17
- data-slot="command"
18
- className={cn(
19
- "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
20
- className,
21
- )}
22
- >
23
- <CommandPrimitive.Root {...props}>{children}</CommandPrimitive.Root>
24
- </div>
25
- );
26
- }
27
-
28
- function CommandDialog({
29
- title = "Command Palette",
30
- description = "Search for a command to run...",
31
- children,
32
- showCloseButton = true,
33
- ...props
34
- }: Omit<React.ComponentProps<typeof Dialog>, "children"> & {
35
- title?: string;
36
- description?: string;
37
- showCloseButton?: boolean;
38
- children?: React.ReactNode;
39
- }) {
40
- return (
41
- <Dialog {...props}>
42
- <DialogHeader className="sr-only">
43
- <DialogTitle>{title}</DialogTitle>
44
- <DialogDescription>{description}</DialogDescription>
45
- </DialogHeader>
46
- <DialogContent className="overflow-hidden p-0" showCloseButton={showCloseButton}>
47
- <Command>{children}</Command>
48
- </DialogContent>
49
- </Dialog>
50
- );
51
- }
52
-
53
- type CommandInputProps = React.ComponentProps<typeof CommandPrimitive.Input> & {
54
- onValueChange?: (value: string) => void;
55
- };
56
-
57
- function CommandInput({
58
- className,
59
- onValueChange,
60
- onChange,
61
- "aria-label": ariaLabel = "Search commands",
62
- ...props
63
- }: CommandInputProps) {
64
- return (
65
- <div data-slot="command-input-wrapper" className="flex h-9 items-center gap-2 border-b px-3">
66
- <SearchIcon className="size-4 shrink-0 opacity-50" />
67
- <CommandPrimitive.Input
68
- data-slot="command-input"
69
- aria-label={ariaLabel}
70
- className={cn(
71
- "placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden",
72
- "disabled:cursor-not-allowed disabled:opacity-50",
73
- className,
74
- )}
75
- onChange={(event) => {
76
- onChange?.(event);
77
- onValueChange?.(event.currentTarget.value);
78
- }}
79
- {...props}
80
- />
81
- </div>
82
- );
83
- }
84
-
85
- function CommandList({
86
- children,
87
- className,
88
- ...props
89
- }: React.ComponentProps<typeof CommandPrimitive.List>) {
90
- if (typeof children === "function") {
91
- return (
92
- <CommandPrimitive.List
93
- data-slot="command-list"
94
- aria-label="Command results"
95
- tabIndex={0}
96
- className={cn("max-h-75 scroll-py-1 overflow-x-hidden overflow-y-auto", className)}
97
- {...props}
98
- >
99
- {children}
100
- </CommandPrimitive.List>
101
- );
102
- }
103
-
104
- const listChildren: React.ReactNode[] = [];
105
- const emptyChildren: React.ReactNode[] = [];
106
-
107
- React.Children.forEach(children, (child) => {
108
- if (React.isValidElement(child) && child.type === CommandEmpty) emptyChildren.push(child);
109
- else listChildren.push(child);
110
- });
111
-
112
- return (
113
- <div data-slot="command-list-container" className="relative">
114
- <CommandPrimitive.List
115
- data-slot="command-list"
116
- aria-label="Command results"
117
- tabIndex={0}
118
- className={cn("max-h-75 scroll-py-1 overflow-x-hidden overflow-y-auto", className)}
119
- {...props}
120
- >
121
- {listChildren}
122
- </CommandPrimitive.List>
123
- {emptyChildren}
124
- </div>
125
- );
126
- }
127
-
128
- function CommandEmpty({
129
- className,
130
- ...props
131
- }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
132
- return (
133
- <CommandPrimitive.Empty
134
- data-slot="command-empty"
135
- className={cn("py-6 text-center text-sm", className)}
136
- {...props}
137
- />
138
- );
139
- }
140
-
141
- function CommandGroup({
142
- className,
143
- heading,
144
- children,
145
- ...props
146
- }: React.ComponentProps<typeof CommandPrimitive.Group> & { heading?: React.ReactNode }) {
147
- return (
148
- <CommandPrimitive.Group
149
- data-slot="command-group"
150
- className={cn("text-foreground overflow-hidden p-1", className)}
151
- {...props}
152
- >
153
- {heading ? (
154
- <CommandPrimitive.GroupLabel className="text-muted-foreground px-2 py-1.5 text-xs font-medium">
155
- {heading}
156
- </CommandPrimitive.GroupLabel>
157
- ) : null}
158
- {children}
159
- </CommandPrimitive.Group>
160
- );
161
- }
162
-
163
- type CommandItemProps = React.ComponentProps<typeof CommandPrimitive.Item> & {
164
- onSelect?: () => void;
165
- };
166
-
167
- function CommandItem({ className, onSelect, onClick, ...props }: CommandItemProps) {
168
- return (
169
- <CommandPrimitive.Item
170
- data-slot="command-item"
171
- className={cn(
172
- "data-highlighted:bg-muted data-highlighted:text-muted-foreground relative flex cursor-pointer items-center gap-3 rounded-md px-3 py-2.5 text-sm outline-hidden select-none",
173
- "data-disabled:pointer-events-none data-disabled:opacity-50",
174
- className,
175
- )}
176
- onClick={(event) => {
177
- onClick?.(event);
178
- onSelect?.();
179
- }}
180
- {...props}
181
- />
182
- );
183
- }
184
-
185
- function CommandSeparator({ className, ...props }: React.ComponentProps<"div">) {
186
- return (
187
- <div
188
- data-slot="command-separator"
189
- aria-hidden="true"
190
- className={cn("bg-border -mx-1 h-px", className)}
191
- {...props}
192
- />
193
- );
194
- }
195
-
196
- function CommandShortcut({ className, ...props }: React.ComponentProps<"span">) {
197
- return (
198
- <span
199
- data-slot="command-shortcut"
200
- className={cn("text-muted-foreground ml-auto text-xs tracking-widest", className)}
201
- {...props}
202
- />
203
- );
204
- }
205
-
206
- function CommandListLoading({ className, ...props }: React.ComponentProps<"div">) {
207
- return (
208
- <div
209
- data-slot="command-list-loading"
210
- className={cn("py-6 text-center text-sm", className)}
211
- {...props}
212
- />
213
- );
214
- }
215
-
216
- export {
217
- Command,
218
- CommandDialog,
219
- CommandEmpty,
220
- CommandGroup,
221
- CommandInput,
222
- CommandItem,
223
- CommandList,
224
- CommandListLoading,
225
- CommandSeparator,
226
- CommandShortcut,
227
- };
@@ -1,148 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import { useEffect, useState } from "react";
3
-
4
- import { booleanControl, hiddenControl } from "../../storybook/controls";
5
- import { Button } from "../primitives/button";
6
- import {
7
- Dialog,
8
- DialogClose,
9
- DialogContent,
10
- DialogDescription,
11
- DialogFooter,
12
- DialogHeader,
13
- DialogTitle,
14
- DialogTrigger,
15
- } from "./dialog";
16
-
17
- const meta = {
18
- title: "Overlays/Dialog",
19
- component: Dialog,
20
- tags: ["autodocs"],
21
- argTypes: {
22
- open: booleanControl("Controls whether the dialog is open", true),
23
- modal: booleanControl("Whether interaction outside the dialog is disabled when open", true),
24
- defaultOpen: hiddenControl,
25
- onOpenChange: hiddenControl,
26
- },
27
- } satisfies Meta<typeof Dialog>;
28
-
29
- export default meta;
30
- type Story = StoryObj<typeof meta>;
31
-
32
- function DialogPreview({
33
- open,
34
- modal,
35
- children,
36
- }: {
37
- open: boolean;
38
- modal: boolean;
39
- children: React.ReactNode;
40
- }) {
41
- const [isOpen, setIsOpen] = useState(open);
42
-
43
- useEffect(() => {
44
- setIsOpen(open);
45
- }, [open]);
46
-
47
- return (
48
- <Dialog open={isOpen} onOpenChange={setIsOpen} modal={modal}>
49
- {children}
50
- </Dialog>
51
- );
52
- }
53
-
54
- export const Default: Story = {
55
- args: {
56
- open: true,
57
- modal: true,
58
- },
59
- render: (args) => (
60
- <DialogPreview open={Boolean(args.open ?? true)} modal={Boolean(args.modal ?? true)}>
61
- <DialogTrigger asChild>
62
- <Button variant="outline">Open Dialog</Button>
63
- </DialogTrigger>
64
- <DialogContent>
65
- <DialogHeader>
66
- <DialogTitle>Edit Profile</DialogTitle>
67
- <DialogDescription>
68
- Make changes to your profile here. Click save when you're done.
69
- </DialogDescription>
70
- </DialogHeader>
71
- <div className="grid gap-4 py-4">
72
- <div className="grid grid-cols-4 items-center gap-4">
73
- <label htmlFor="name" className="text-right text-sm font-medium">
74
- Name
75
- </label>
76
- <input
77
- id="name"
78
- defaultValue="Pedro Duarte"
79
- className="col-span-3 h-9 rounded-md border px-3 text-sm"
80
- />
81
- </div>
82
- </div>
83
- <DialogFooter>
84
- <Button type="submit">Save changes</Button>
85
- </DialogFooter>
86
- </DialogContent>
87
- </DialogPreview>
88
- ),
89
- };
90
-
91
- export const WithCloseButton: Story = {
92
- args: {
93
- open: true,
94
- modal: true,
95
- },
96
- render: (args) => (
97
- <DialogPreview open={Boolean(args.open ?? true)} modal={Boolean(args.modal ?? true)}>
98
- <DialogTrigger asChild>
99
- <Button>Open</Button>
100
- </DialogTrigger>
101
- <DialogContent showCloseButton>
102
- <DialogHeader>
103
- <DialogTitle>Confirmation</DialogTitle>
104
- <DialogDescription>Are you sure you want to proceed?</DialogDescription>
105
- </DialogHeader>
106
- <DialogFooter>
107
- <DialogClose asChild>
108
- <Button>Confirm</Button>
109
- </DialogClose>
110
- </DialogFooter>
111
- </DialogContent>
112
- </DialogPreview>
113
- ),
114
- };
115
-
116
- /**
117
- * `alert` swaps in Base UI's AlertDialog primitive: `role="alertdialog"`,
118
- * forced modal, and no dismiss via outside click or Escape. Use it for
119
- * confirmations that need explicit acknowledgment (destructive actions,
120
- * unsaved-changes warnings). The close (X) button is off by default in this
121
- * mode — dismissal should go through an explicit action.
122
- */
123
- export const Alert: Story = {
124
- render: () => (
125
- <Dialog alert>
126
- <DialogTrigger asChild>
127
- <Button variant="outline">Delete account</Button>
128
- </DialogTrigger>
129
- <DialogContent>
130
- <DialogHeader>
131
- <DialogTitle>Are you absolutely sure?</DialogTitle>
132
- <DialogDescription>
133
- This action cannot be undone. This will permanently delete your account and remove your
134
- data from our servers.
135
- </DialogDescription>
136
- </DialogHeader>
137
- <DialogFooter>
138
- <DialogClose asChild>
139
- <Button variant="outline">Cancel</Button>
140
- </DialogClose>
141
- <DialogClose asChild>
142
- <Button>Continue</Button>
143
- </DialogClose>
144
- </DialogFooter>
145
- </DialogContent>
146
- </Dialog>
147
- ),
148
- };