tembro 6.0.3 → 6.1.1

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 (253) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/components/actions/action-menu.d.ts +11 -1
  3. package/dist/components/calendar/calendar-range-scheduler.cjs +1 -0
  4. package/dist/components/calendar/calendar-range-scheduler.d.ts +20 -0
  5. package/dist/components/calendar/calendar-range-scheduler.js +1 -0
  6. package/dist/components/calendar/date-time-picker.cjs +1 -0
  7. package/dist/components/calendar/date-time-picker.d.ts +15 -0
  8. package/dist/components/calendar/date-time-picker.js +1 -0
  9. package/dist/components/calendar/index.d.ts +2 -0
  10. package/dist/components/charts/chart-card.cjs +1 -0
  11. package/dist/components/charts/chart-card.d.ts +24 -0
  12. package/dist/components/charts/chart-card.js +1 -0
  13. package/dist/components/charts/index.d.ts +1 -0
  14. package/dist/components/command/advanced-command-menu.cjs +1 -0
  15. package/dist/components/command/advanced-command-menu.d.ts +25 -0
  16. package/dist/components/command/advanced-command-menu.js +1 -0
  17. package/dist/components/command/index.d.ts +1 -0
  18. package/dist/components/data-table/data-grid.cjs +1 -0
  19. package/dist/components/data-table/data-grid.d.ts +15 -0
  20. package/dist/components/data-table/data-grid.js +1 -0
  21. package/dist/components/data-table/index.d.ts +1 -0
  22. package/dist/components/data-table/public.d.ts +1 -0
  23. package/dist/components/display/index.d.ts +0 -2
  24. package/dist/components/feedback/async-boundary.cjs +1 -0
  25. package/dist/components/feedback/async-boundary.d.ts +12 -0
  26. package/dist/components/feedback/async-boundary.js +1 -0
  27. package/dist/components/feedback/index.d.ts +1 -0
  28. package/dist/components/filters/index.d.ts +0 -1
  29. package/dist/components/inputs/index.d.ts +1 -0
  30. package/dist/components/inputs/multi-select.cjs +1 -0
  31. package/dist/components/inputs/multi-select.d.ts +16 -0
  32. package/dist/components/inputs/multi-select.js +1 -0
  33. package/dist/components/inputs/public.d.ts +1 -0
  34. package/dist/components/inputs/time-picker.d.ts +11 -3
  35. package/dist/components/modern/file-manager.cjs +1 -0
  36. package/dist/components/modern/file-manager.d.ts +20 -0
  37. package/dist/components/modern/file-manager.js +1 -0
  38. package/dist/components/modern/index.d.ts +4 -1
  39. package/dist/components/modern/map.cjs +1 -0
  40. package/dist/components/modern/map.d.ts +29 -0
  41. package/dist/components/modern/map.js +1 -0
  42. package/dist/components/modern/media-player.cjs +1 -0
  43. package/dist/components/modern/media-player.d.ts +21 -0
  44. package/dist/components/modern/media-player.js +1 -0
  45. package/dist/components/modern/spreadsheet.cjs +1 -0
  46. package/dist/components/modern/spreadsheet.d.ts +35 -0
  47. package/dist/components/modern/spreadsheet.js +1 -0
  48. package/dist/components/notifications/action-toast.cjs +1 -0
  49. package/dist/components/notifications/action-toast.d.ts +16 -0
  50. package/dist/components/notifications/action-toast.js +1 -0
  51. package/dist/components/notifications/index.d.ts +1 -0
  52. package/dist/components/overlay/drawer.d.ts +3 -1
  53. package/dist/components/patterns/index.d.ts +1 -0
  54. package/dist/components/patterns/permissions-matrix.cjs +1 -0
  55. package/dist/components/patterns/permissions-matrix.d.ts +19 -0
  56. package/dist/components/patterns/permissions-matrix.js +1 -0
  57. package/dist/components/patterns/public.d.ts +1 -0
  58. package/dist/index.d.ts +0 -2
  59. package/dist/src/components/actions/action-menu.cjs +1 -1
  60. package/dist/src/components/actions/action-menu.js +47 -39
  61. package/dist/src/components/actions/copy-button.cjs +1 -1
  62. package/dist/src/components/actions/copy-button.js +4 -1
  63. package/dist/src/components/calendar/calendar-range-scheduler.cjs +1 -0
  64. package/dist/src/components/calendar/calendar-range-scheduler.js +75 -0
  65. package/dist/src/components/calendar/date-picker.cjs +1 -1
  66. package/dist/src/components/calendar/date-picker.js +5 -8
  67. package/dist/src/components/calendar/date-range-picker.cjs +1 -1
  68. package/dist/src/components/calendar/date-range-picker.js +12 -27
  69. package/dist/src/components/calendar/date-time-picker.cjs +1 -0
  70. package/dist/src/components/calendar/date-time-picker.js +36 -0
  71. package/dist/src/components/calendar/index.cjs +1 -1
  72. package/dist/src/components/calendar/index.js +5 -3
  73. package/dist/src/components/charts/chart-card.cjs +1 -0
  74. package/dist/src/components/charts/chart-card.js +55 -0
  75. package/dist/src/components/charts/index.cjs +1 -1
  76. package/dist/src/components/charts/index.js +3 -2
  77. package/dist/src/components/command/advanced-command-menu.cjs +1 -0
  78. package/dist/src/components/command/advanced-command-menu.js +57 -0
  79. package/dist/src/components/command/index.cjs +1 -1
  80. package/dist/src/components/command/index.js +3 -2
  81. package/dist/src/components/data-table/data-grid.cjs +1 -0
  82. package/dist/src/components/data-table/data-grid.js +32 -0
  83. package/dist/src/components/data-table/data-table-saved-filters.cjs +1 -1
  84. package/dist/src/components/data-table/data-table-saved-filters.js +11 -11
  85. package/dist/src/components/data-table/data-table.cjs +1 -1
  86. package/dist/src/components/data-table/data-table.js +175 -174
  87. package/dist/src/components/data-table/index.cjs +1 -1
  88. package/dist/src/components/data-table/index.js +15 -14
  89. package/dist/src/components/data-table/public.cjs +1 -1
  90. package/dist/src/components/data-table/public.js +3 -2
  91. package/dist/src/components/display/index.cjs +1 -1
  92. package/dist/src/components/display/index.js +9 -11
  93. package/dist/src/components/feedback/async-boundary.cjs +1 -0
  94. package/dist/src/components/feedback/async-boundary.js +22 -0
  95. package/dist/src/components/feedback/index.cjs +1 -1
  96. package/dist/src/components/feedback/index.js +2 -1
  97. package/dist/src/components/filters/index.cjs +1 -1
  98. package/dist/src/components/filters/index.js +2 -3
  99. package/dist/src/components/inputs/color-picker.cjs +1 -1
  100. package/dist/src/components/inputs/color-picker.js +63 -46
  101. package/dist/src/components/inputs/index.cjs +1 -1
  102. package/dist/src/components/inputs/index.js +13 -12
  103. package/dist/src/components/inputs/multi-select.cjs +1 -0
  104. package/dist/src/components/inputs/multi-select.js +80 -0
  105. package/dist/src/components/inputs/public.cjs +1 -1
  106. package/dist/src/components/inputs/public.js +13 -12
  107. package/dist/src/components/inputs/slider.cjs +1 -1
  108. package/dist/src/components/inputs/slider.js +3 -3
  109. package/dist/src/components/inputs/tag-input.cjs +1 -1
  110. package/dist/src/components/inputs/tag-input.js +7 -4
  111. package/dist/src/components/inputs/time-picker.cjs +1 -1
  112. package/dist/src/components/inputs/time-picker.js +90 -34
  113. package/dist/src/components/modern/file-manager.cjs +1 -0
  114. package/dist/src/components/modern/file-manager.js +75 -0
  115. package/dist/src/components/modern/index.cjs +1 -1
  116. package/dist/src/components/modern/index.js +7 -4
  117. package/dist/src/components/modern/map.cjs +1 -0
  118. package/dist/src/components/modern/map.js +158 -0
  119. package/dist/src/components/modern/media-player.cjs +1 -0
  120. package/dist/src/components/modern/media-player.js +176 -0
  121. package/dist/src/components/modern/resizable-panel.cjs +1 -1
  122. package/dist/src/components/modern/resizable-panel.js +39 -32
  123. package/dist/src/components/modern/spreadsheet.cjs +1 -0
  124. package/dist/src/components/modern/spreadsheet.js +97 -0
  125. package/dist/src/components/navigation/pagination.cjs +1 -1
  126. package/dist/src/components/navigation/pagination.js +5 -5
  127. package/dist/src/components/notifications/action-toast.cjs +1 -0
  128. package/dist/src/components/notifications/action-toast.js +49 -0
  129. package/dist/src/components/notifications/index.cjs +1 -1
  130. package/dist/src/components/notifications/index.js +3 -2
  131. package/dist/src/components/notifications/toast.cjs +1 -1
  132. package/dist/src/components/notifications/toast.js +68 -63
  133. package/dist/src/components/overlay/alert-dialog.cjs +1 -1
  134. package/dist/src/components/overlay/alert-dialog.js +8 -6
  135. package/dist/src/components/overlay/drawer.cjs +1 -1
  136. package/dist/src/components/overlay/drawer.js +38 -20
  137. package/dist/src/components/patterns/index.cjs +1 -1
  138. package/dist/src/components/patterns/index.js +2 -1
  139. package/dist/src/components/patterns/permissions-matrix.cjs +1 -0
  140. package/dist/src/components/patterns/permissions-matrix.js +43 -0
  141. package/dist/src/components/patterns/public.cjs +1 -1
  142. package/dist/src/components/patterns/public.js +2 -1
  143. package/dist/src/components/ui/checkbox/index.cjs +1 -1
  144. package/dist/src/components/ui/checkbox/index.js +2 -1
  145. package/dist/src/components/ui/dialog/index.cjs +1 -1
  146. package/dist/src/components/ui/dialog/index.js +8 -8
  147. package/dist/src/components/ui/dropdown-menu/index.cjs +1 -1
  148. package/dist/src/components/ui/dropdown-menu/index.js +21 -19
  149. package/dist/src/components/ui/input/group.cjs +1 -1
  150. package/dist/src/components/ui/input/group.js +8 -8
  151. package/dist/src/components/ui/popover/index.cjs +1 -1
  152. package/dist/src/components/ui/popover/index.js +10 -10
  153. package/dist/src/components/ui/tabs/index.cjs +1 -1
  154. package/dist/src/components/ui/tabs/index.js +6 -6
  155. package/dist/src/components/ui/toggle-group/index.cjs +1 -1
  156. package/dist/src/components/ui/toggle-group/index.js +2 -2
  157. package/dist/src/index.cjs +1 -1
  158. package/dist/src/index.js +111 -104
  159. package/dist/src/public-component-surface.cjs +1 -1
  160. package/dist/src/public-component-surface.js +59 -14
  161. package/package.json +1 -2
  162. package/packages/cli/dist/index.cjs +45 -165
  163. package/packages/cli/vendor/src/components/actions/action-menu.tsx +47 -19
  164. package/packages/cli/vendor/src/components/actions/copy-button.tsx +2 -2
  165. package/packages/cli/vendor/src/components/calendar/calendar-range-scheduler.tsx +72 -0
  166. package/packages/cli/vendor/src/components/calendar/date-picker.tsx +2 -5
  167. package/packages/cli/vendor/src/components/calendar/date-range-picker.tsx +5 -12
  168. package/packages/cli/vendor/src/components/calendar/date-time-picker.tsx +53 -0
  169. package/packages/cli/vendor/src/components/calendar/index.ts +5 -3
  170. package/packages/cli/vendor/src/components/charts/chart-card.tsx +81 -0
  171. package/packages/cli/vendor/src/components/charts/index.ts +1 -0
  172. package/packages/cli/vendor/src/components/command/advanced-command-menu.tsx +88 -0
  173. package/packages/cli/vendor/src/components/command/index.ts +2 -1
  174. package/packages/cli/vendor/src/components/data-table/data-grid.tsx +59 -0
  175. package/packages/cli/vendor/src/components/data-table/data-table.tsx +45 -34
  176. package/packages/cli/vendor/src/components/data-table/index.ts +3 -2
  177. package/packages/cli/vendor/src/components/data-table/public.ts +1 -0
  178. package/packages/cli/vendor/src/components/display/index.ts +3 -5
  179. package/packages/cli/vendor/src/components/feedback/async-boundary.tsx +36 -0
  180. package/packages/cli/vendor/src/components/feedback/index.ts +1 -0
  181. package/packages/cli/vendor/src/components/filters/index.ts +0 -1
  182. package/packages/cli/vendor/src/components/inputs/color-picker.tsx +15 -1
  183. package/packages/cli/vendor/src/components/inputs/index.ts +1 -0
  184. package/packages/cli/vendor/src/components/inputs/multi-select.tsx +75 -0
  185. package/packages/cli/vendor/src/components/inputs/public.ts +1 -0
  186. package/packages/cli/vendor/src/components/inputs/slider.tsx +3 -3
  187. package/packages/cli/vendor/src/components/inputs/tag-input.tsx +4 -4
  188. package/packages/cli/vendor/src/components/inputs/time-picker.tsx +91 -38
  189. package/packages/cli/vendor/src/components/modern/file-manager.tsx +79 -0
  190. package/packages/cli/vendor/src/components/modern/index.ts +4 -1
  191. package/packages/cli/vendor/src/components/modern/map.tsx +146 -0
  192. package/packages/cli/vendor/src/components/modern/media-player.tsx +182 -0
  193. package/packages/cli/vendor/src/components/modern/resizable-panel.tsx +15 -5
  194. package/packages/cli/vendor/src/components/modern/spreadsheet.tsx +155 -0
  195. package/packages/cli/vendor/src/components/navigation/pagination.tsx +9 -5
  196. package/packages/cli/vendor/src/components/notifications/action-toast.tsx +78 -0
  197. package/packages/cli/vendor/src/components/notifications/index.ts +2 -1
  198. package/packages/cli/vendor/src/components/notifications/toast.tsx +20 -10
  199. package/packages/cli/vendor/src/components/overlay/alert-dialog.tsx +3 -5
  200. package/packages/cli/vendor/src/components/overlay/drawer.tsx +22 -11
  201. package/packages/cli/vendor/src/components/patterns/index.ts +1 -0
  202. package/packages/cli/vendor/src/components/patterns/permissions-matrix.tsx +71 -0
  203. package/packages/cli/vendor/src/components/patterns/public.ts +1 -0
  204. package/packages/cli/vendor/src/components/ui/checkbox/index.tsx +3 -1
  205. package/packages/cli/vendor/src/components/ui/dialog/index.tsx +4 -4
  206. package/packages/cli/vendor/src/components/ui/dropdown-menu/index.tsx +18 -22
  207. package/packages/cli/vendor/src/components/ui/popover/index.tsx +4 -4
  208. package/packages/cli/vendor/src/components/ui/tabs/index.tsx +8 -3
  209. package/packages/cli/vendor/src/components/ui/toggle-group/index.tsx +6 -6
  210. package/packages/cli/vendor/src/index.ts +0 -2
  211. package/packages/cli/vendor/src/public-component-surface.ts +13 -4
  212. package/packages/cli/vendor/templates/showcase/src/showcase/data/registry.ts +6 -6
  213. package/packages/cli/vendor/templates/showcase/src/showcase/layout/HeroSection.tsx +23 -8
  214. package/packages/cli/vendor/templates/showcase/src/showcase/layout/WorkbenchSidebar.tsx +1 -1
  215. package/packages/cli/vendor/templates/showcase/src/showcase/sections/PatternsSection.tsx +1 -1
  216. package/packages/cli/vendor/templates/styles/globals.css +0 -120
  217. package/registry.json +3197 -2794
  218. package/dist/components/display/info-card.cjs +0 -1
  219. package/dist/components/display/info-card.d.ts +0 -51
  220. package/dist/components/display/info-card.js +0 -1
  221. package/dist/components/display/statistic.cjs +0 -1
  222. package/dist/components/display/statistic.d.ts +0 -25
  223. package/dist/components/display/statistic.js +0 -1
  224. package/dist/components/filters/filter-bar.cjs +0 -1
  225. package/dist/components/filters/filter-bar.d.ts +0 -28
  226. package/dist/components/filters/filter-bar.js +0 -1
  227. package/dist/components/modern/image-cropper.cjs +0 -1
  228. package/dist/components/modern/image-cropper.d.ts +0 -39
  229. package/dist/components/modern/image-cropper.js +0 -1
  230. package/dist/components/ui/number-field/index.cjs +0 -1
  231. package/dist/components/ui/number-field/index.d.ts +0 -18
  232. package/dist/components/ui/number-field/index.js +0 -1
  233. package/dist/components/ui/toolbar/index.cjs +0 -1
  234. package/dist/components/ui/toolbar/index.d.ts +0 -14
  235. package/dist/components/ui/toolbar/index.js +0 -1
  236. package/dist/src/components/display/info-card.cjs +0 -1
  237. package/dist/src/components/display/info-card.js +0 -124
  238. package/dist/src/components/display/statistic.cjs +0 -1
  239. package/dist/src/components/display/statistic.js +0 -104
  240. package/dist/src/components/filters/filter-bar.cjs +0 -1
  241. package/dist/src/components/filters/filter-bar.js +0 -103
  242. package/dist/src/components/modern/image-cropper.cjs +0 -1
  243. package/dist/src/components/modern/image-cropper.js +0 -143
  244. package/dist/src/components/ui/number-field/index.cjs +0 -1
  245. package/dist/src/components/ui/number-field/index.js +0 -73
  246. package/dist/src/components/ui/toolbar/index.cjs +0 -1
  247. package/dist/src/components/ui/toolbar/index.js +0 -50
  248. package/packages/cli/vendor/src/components/display/info-card.tsx +0 -195
  249. package/packages/cli/vendor/src/components/display/statistic.tsx +0 -114
  250. package/packages/cli/vendor/src/components/filters/filter-bar.tsx +0 -163
  251. package/packages/cli/vendor/src/components/modern/image-cropper.tsx +0 -226
  252. package/packages/cli/vendor/src/components/ui/number-field/index.tsx +0 -89
  253. package/packages/cli/vendor/src/components/ui/toolbar/index.tsx +0 -67
@@ -33,7 +33,11 @@ export type ActionMenuItem = {
33
33
 
34
34
  export type ActionMenuProps = {
35
35
  actions: ActionMenuItem[]
36
+ open?: boolean
37
+ defaultOpen?: boolean
38
+ onOpenChange?: (open: boolean) => void
36
39
  label?: React.ReactNode
40
+ triggerLabel?: string
37
41
  trigger?: React.ReactElement
38
42
  align?: "start" | "center" | "end"
39
43
  side?: "top" | "right" | "bottom" | "left"
@@ -50,12 +54,19 @@ export type ActionMenuProps = {
50
54
  menuWidth?: number | string
51
55
  loadingLabel?: React.ReactNode
52
56
  persistIconSpace?: boolean
57
+ itemRoleDescription?: string
58
+ renderItem?: (action: ActionMenuItem, state: { loading: boolean; close: () => void }) => React.ReactNode
59
+ onActionError?: (error: unknown, action: ActionMenuItem) => void
53
60
  }
54
-
61
+
55
62
  function ActionMenu({
56
- actions,
57
- label,
58
- trigger,
63
+ actions,
64
+ open: openProp,
65
+ defaultOpen = false,
66
+ onOpenChange,
67
+ label,
68
+ triggerLabel = "Open actions",
69
+ trigger,
59
70
  align = "end",
60
71
  side = "bottom",
61
72
  sideOffset = 4,
@@ -71,12 +82,21 @@ function ActionMenu({
71
82
  menuWidth,
72
83
  loadingLabel = "Working...",
73
84
  persistIconSpace = true,
85
+ itemRoleDescription,
86
+ renderItem,
87
+ onActionError,
74
88
  }: ActionMenuProps) {
75
89
  const visibleActions = actions.filter((action) => !action.hidden)
76
90
  const [loadingKey, setLoadingKey] = React.useState<string | null>(null)
77
- const [open, setOpen] = React.useState(false)
91
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen)
92
+ const open = openProp ?? internalOpen
78
93
  const lastInvokedActionKeyRef = React.useRef<string | null>(null)
79
94
 
95
+ const setOpen = React.useCallback((nextOpen: boolean) => {
96
+ if (openProp === undefined) setInternalOpen(nextOpen)
97
+ onOpenChange?.(nextOpen)
98
+ }, [onOpenChange, openProp])
99
+
80
100
  const handleSelect = async (action: ActionMenuItem) => {
81
101
  if (action.disabled || action.loading || loadingKey) return
82
102
 
@@ -84,6 +104,9 @@ function ActionMenu({
84
104
  setLoadingKey(action.key)
85
105
  await action.onSelect?.()
86
106
  if (closeOnSelect && !action.keepOpen) setOpen(false)
107
+ } catch (error) {
108
+ onActionError?.(error, action)
109
+ if (!onActionError) throw error
87
110
  } finally {
88
111
  setLoadingKey(null)
89
112
  }
@@ -122,7 +145,7 @@ function ActionMenu({
122
145
  >
123
146
  {!trigger && <MoreHorizontalIcon />}
124
147
  {!trigger && showChevron && <span className="text-[10px] font-medium uppercase tracking-[0.18em]">Menu</span>}
125
- <span className="sr-only">Open actions</span>
148
+ <span className="sr-only">{triggerLabel}</span>
126
149
  </DropdownMenuTrigger>
127
150
  <DropdownMenuContent
128
151
  align={align}
@@ -155,6 +178,7 @@ function ActionMenu({
155
178
  disabled={action.disabled || isLoading}
156
179
  variant={action.destructive ? "destructive" : "default"}
157
180
  closeOnSelect={closeOnSelect && !action.keepOpen}
181
+ aria-roledescription={itemRoleDescription}
158
182
  className={cn(
159
183
  "min-h-11 items-start gap-3 rounded-[calc(var(--radius-md)+1px)] border border-transparent py-2.5 transition-[background-color,color,border-color,box-shadow] data-[highlighted]:shadow-[inset_0_0_0_1px_color-mix(in_oklch,var(--primary),transparent_76%)] data-[disabled]:opacity-45",
160
184
  itemClassName
@@ -173,19 +197,23 @@ function ActionMenu({
173
197
  onMouseDown={stopInteractivePropagation}
174
198
  onDoubleClick={stopInteractivePropagation}
175
199
  >
176
- <span className={cn("mt-0.5 flex size-5 shrink-0 items-center justify-center text-muted-foreground", !persistIconSpace && !isLoading && !action.icon && "hidden")}>
177
- {isLoading ? <Loader2Icon className="size-4 animate-spin" /> : action.icon}
178
- </span>
179
- <span className="min-w-0 flex-1">
180
- <span className="block truncate">{action.label}</span>
181
- {action.description ? (
182
- <DropdownMenuItemDescription className="mt-0.5 truncate">
183
- {isLoading ? loadingLabel : action.description}
184
- </DropdownMenuItemDescription>
185
- ) : null}
186
- </span>
187
- {action.shortcut && (
188
- <DropdownMenuShortcut>{action.shortcut}</DropdownMenuShortcut>
200
+ {renderItem?.(action, { loading: isLoading, close: () => setOpen(false) }) ?? (
201
+ <>
202
+ <span className={cn("mt-0.5 flex size-5 shrink-0 items-center justify-center text-muted-foreground", !persistIconSpace && !isLoading && !action.icon && "hidden")}>
203
+ {isLoading ? <Loader2Icon className="size-4 animate-spin" /> : action.icon}
204
+ </span>
205
+ <span className="min-w-0 flex-1">
206
+ <span className="block truncate">{action.label}</span>
207
+ {action.description ? (
208
+ <DropdownMenuItemDescription className="mt-0.5 truncate">
209
+ {isLoading ? loadingLabel : action.description}
210
+ </DropdownMenuItemDescription>
211
+ ) : null}
212
+ </span>
213
+ {action.shortcut && (
214
+ <DropdownMenuShortcut>{action.shortcut}</DropdownMenuShortcut>
215
+ )}
216
+ </>
189
217
  )}
190
218
  </DropdownMenuItem>
191
219
  </React.Fragment>
@@ -97,8 +97,8 @@ function CopyButton({
97
97
  aria-live="polite"
98
98
  {...props}
99
99
  >
100
- {showIcon && (copied ? <CheckIcon data-icon="inline-start" /> : <CopyIcon data-icon="inline-start" />)}
101
- {children ?? (copied ? copiedLabel : copyLabel)}
100
+ {showIcon && (copied ? <CheckIcon data-icon="inline-start" className="size-3.5" /> : <CopyIcon data-icon="inline-start" className="size-3.5" />)}
101
+ {children ?? (copied ? copiedLabel : copyLabel)}
102
102
  </Button>
103
103
  )
104
104
  }
@@ -0,0 +1,72 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { CalendarDaysIcon } from "lucide-react"
5
+
6
+ import { DateRangePicker, type DateRangePickerValue } from "@/components/calendar/date-range-picker"
7
+ import { Badge } from "@/components/ui/badge"
8
+ import { Button } from "@/components/ui/button"
9
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
10
+ import { cn } from "@/lib/utils"
11
+
12
+ export type CalendarRangeSchedulerEvent = {
13
+ id: string
14
+ title: React.ReactNode
15
+ range: DateRangePickerValue
16
+ tone?: "default" | "info" | "success" | "warning" | "danger"
17
+ meta?: React.ReactNode
18
+ }
19
+
20
+ export type CalendarRangeSchedulerProps = Omit<React.ComponentProps<typeof Card>, "onSelect"> & {
21
+ value?: DateRangePickerValue
22
+ onValueChange?: (range: DateRangePickerValue) => void
23
+ events?: CalendarRangeSchedulerEvent[]
24
+ onEventSelect?: (event: CalendarRangeSchedulerEvent) => void
25
+ title?: React.ReactNode
26
+ action?: React.ReactNode
27
+ }
28
+
29
+ function CalendarRangeScheduler({
30
+ value,
31
+ onValueChange,
32
+ events = [],
33
+ onEventSelect,
34
+ title = "Schedule range",
35
+ action,
36
+ className,
37
+ ...props
38
+ }: CalendarRangeSchedulerProps) {
39
+ return (
40
+ <Card data-slot="calendar-range-scheduler" className={cn("min-w-0", className)} {...props}>
41
+ <CardHeader className="flex-row items-center justify-between gap-3">
42
+ <CardTitle className="flex items-center gap-2"><CalendarDaysIcon className="size-4" />{title}</CardTitle>
43
+ {action}
44
+ </CardHeader>
45
+ <CardContent className="grid gap-4 lg:grid-cols-[320px_minmax(0,1fr)]">
46
+ <DateRangePicker value={value} onValueChange={onValueChange} />
47
+ <div className="grid content-start gap-2">
48
+ {events.length ? events.map((event) => (
49
+ <button
50
+ key={event.id}
51
+ type="button"
52
+ className="grid gap-1 rounded-lg border bg-background p-3 text-left transition hover:border-primary/45 hover:bg-muted/40"
53
+ onClick={() => onEventSelect?.(event)}
54
+ >
55
+ <span className="flex items-center justify-between gap-2">
56
+ <span className="font-medium">{event.title}</span>
57
+ <Badge label={event.tone ?? "scheduled"} status={event.tone === "danger" ? "danger" : event.tone === "warning" ? "warning" : event.tone === "success" ? "success" : "info"} variant="soft" />
58
+ </span>
59
+ <span className="text-xs text-muted-foreground">{event.range.from || "Start"} - {event.range.to || "End"}</span>
60
+ {event.meta ? <span className="text-xs text-muted-foreground">{event.meta}</span> : null}
61
+ </button>
62
+ )) : (
63
+ <div className="rounded-lg border border-dashed p-6 text-sm text-muted-foreground">No scheduled ranges.</div>
64
+ )}
65
+ <Button type="button" variant="outline" onClick={() => onValueChange?.({ from: "", to: "" })}>Clear range</Button>
66
+ </div>
67
+ </CardContent>
68
+ </Card>
69
+ )
70
+ }
71
+
72
+ export { CalendarRangeScheduler }
@@ -86,11 +86,8 @@ function DatePicker({
86
86
  />
87
87
  }
88
88
  >
89
- <CalendarIcon data-icon="inline-start" className={cn(hasValue && "text-primary")} />
90
- <span className={cn("grid min-w-0 flex-1", triggerVariant === "default" && "gap-0.5")}>
91
- {triggerVariant === "default" ? <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted-foreground">
92
- {labels?.selected ?? "Date"}
93
- </span> : null}
89
+ <CalendarIcon data-icon="inline-start" className={cn("size-4", hasValue && "text-primary")} />
90
+ <span className="grid min-w-0 flex-1">
94
91
  <span className={cn("truncate text-sm", hasValue && "font-semibold text-foreground")}>
95
92
  {hasValue ? formatValue(String(value)) : placeholder ?? labels?.placeholder ?? "Select date"}
96
93
  </span>
@@ -176,22 +176,15 @@ function DateRangePicker({
176
176
  />
177
177
  }
178
178
  >
179
- <CalendarIcon data-icon="inline-start" className={cn(hasValue && "text-primary")} />
179
+ <CalendarIcon data-icon="inline-start" className={cn("size-4", hasValue && "text-primary")} />
180
180
  {hasValue ? (
181
- <span className="grid min-w-0 flex-1 grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-2">
182
- <span className="grid min-w-0 gap-0.5">
183
- <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted-foreground">{labels?.start ?? "Start"}</span>
184
- <span className="truncate text-sm font-semibold text-foreground">{from ? formatValue(from) : "..."}</span>
185
- </span>
181
+ <span className="flex min-w-0 flex-1 items-center gap-2">
182
+ <span className="min-w-0 truncate text-sm font-semibold text-foreground">{from ? formatValue(from) : "..."}</span>
186
183
  <span className="text-muted-foreground">-</span>
187
- <span className="grid min-w-0 gap-0.5">
188
- <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted-foreground">{labels?.end ?? "End"}</span>
189
- <span className="truncate text-sm font-semibold text-foreground">{to ? formatValue(to) : "..."}</span>
190
- </span>
184
+ <span className="min-w-0 truncate text-sm font-semibold text-foreground">{to ? formatValue(to) : "..."}</span>
191
185
  </span>
192
186
  ) : (
193
- <span className="grid min-w-0 flex-1 gap-0.5">
194
- <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted-foreground">Range</span>
187
+ <span className="grid min-w-0 flex-1">
195
188
  <span className="truncate text-sm">{label}</span>
196
189
  </span>
197
190
  )}
@@ -0,0 +1,53 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+
5
+ import { DatePicker, type DatePickerProps } from "@/components/calendar/date-picker"
6
+ import { TimePicker } from "@/components/inputs/time-picker"
7
+ import { cn } from "@/lib/utils"
8
+
9
+ export type DateTimeValue = {
10
+ date?: string
11
+ time?: string
12
+ }
13
+
14
+ export type CalendarDateTimePickerProps = Omit<React.ComponentProps<"div">, "value" | "onChange"> & {
15
+ value?: DateTimeValue
16
+ onValueChange?: (value: DateTimeValue) => void
17
+ datePickerProps?: Omit<DatePickerProps, "value" | "onValueChange">
18
+ timeLabel?: React.ReactNode
19
+ disabled?: boolean
20
+ }
21
+
22
+ function CalendarDateTimePicker({
23
+ value,
24
+ onValueChange,
25
+ datePickerProps,
26
+ timeLabel = "Time",
27
+ disabled,
28
+ className,
29
+ ...props
30
+ }: CalendarDateTimePickerProps) {
31
+ const current = value ?? {}
32
+
33
+ return (
34
+ <div data-slot="date-time-picker" className={cn("grid gap-3 sm:grid-cols-2", className)} {...props}>
35
+ <DatePicker
36
+ {...datePickerProps}
37
+ value={current.date}
38
+ disabled={disabled || datePickerProps?.disabled}
39
+ onValueChange={(date) => onValueChange?.({ ...current, date })}
40
+ />
41
+ <label className="grid gap-1.5 text-sm font-medium">
42
+ <span>{timeLabel}</span>
43
+ <TimePicker
44
+ value={current.time ?? ""}
45
+ onChange={(event) => onValueChange?.({ ...current, time: event.currentTarget.value })}
46
+ disabled={disabled}
47
+ />
48
+ </label>
49
+ </div>
50
+ )
51
+ }
52
+
53
+ export { CalendarDateTimePicker }
@@ -1,4 +1,6 @@
1
1
  export * from "./date-utils"
2
- export * from "./calendar"
3
- export * from "./date-picker"
4
- export * from "./date-range-picker"
2
+ export * from "./calendar"
3
+ export * from "./date-picker"
4
+ export * from "./date-range-picker"
5
+ export * from "./date-time-picker"
6
+ export * from "./calendar-range-scheduler"
@@ -0,0 +1,81 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+
5
+ import { BarChart, LineChart, DonutChart, type ChartDatum, type ChartSeries, type ChartState } from "@/components/charts/charts"
6
+ import { Badge } from "@/components/ui/badge"
7
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
8
+ import { Select } from "@/components/ui/select"
9
+ import { cn } from "@/lib/utils"
10
+
11
+ export type ChartCardType = "bar" | "line" | "donut"
12
+ export type ChartCardFilter = {
13
+ label: string
14
+ value: string
15
+ }
16
+
17
+ export type ChartCardProps = Omit<React.ComponentProps<typeof Card>, "type"> & {
18
+ title?: React.ReactNode
19
+ description?: React.ReactNode
20
+ type?: ChartCardType
21
+ data?: ChartDatum[]
22
+ series?: ChartSeries[]
23
+ labels?: React.ReactNode[]
24
+ state?: ChartState
25
+ metric?: React.ReactNode
26
+ badge?: React.ReactNode
27
+ filters?: ChartCardFilter[]
28
+ filterValue?: string
29
+ onFilterChange?: (value: string) => void
30
+ }
31
+
32
+ function ChartCard({
33
+ title,
34
+ description,
35
+ type = "bar",
36
+ data = [],
37
+ series,
38
+ labels,
39
+ state,
40
+ metric,
41
+ badge,
42
+ filters,
43
+ filterValue,
44
+ onFilterChange,
45
+ className,
46
+ ...props
47
+ }: ChartCardProps) {
48
+ return (
49
+ <Card data-slot="chart-card" className={cn("min-w-0", className)} {...props}>
50
+ <CardHeader className="flex-row items-start justify-between gap-3">
51
+ <div className="grid gap-1">
52
+ {title ? <CardTitle>{title}</CardTitle> : null}
53
+ {description ? <CardDescription>{description}</CardDescription> : null}
54
+ {metric ? <div className="text-2xl font-semibold tracking-tight">{metric}</div> : null}
55
+ </div>
56
+ <div className="flex shrink-0 items-center gap-2">
57
+ {badge ?? <Badge label={type} variant="secondary" />}
58
+ {filters?.length ? (
59
+ <Select
60
+ value={filterValue}
61
+ onValueChange={(value) => value && onFilterChange?.(value)}
62
+ options={filters}
63
+ triggerClassName="h-9 w-36"
64
+ />
65
+ ) : null}
66
+ </div>
67
+ </CardHeader>
68
+ <CardContent>
69
+ {type === "line" ? (
70
+ <LineChart series={series} labels={labels} values={data.map((item) => item.value)} state={state} />
71
+ ) : type === "donut" ? (
72
+ <DonutChart data={data} state={state} />
73
+ ) : (
74
+ <BarChart data={data} series={series} state={state} />
75
+ )}
76
+ </CardContent>
77
+ </Card>
78
+ )
79
+ }
80
+
81
+ export { ChartCard }
@@ -1,2 +1,3 @@
1
1
  export * from './charts'
2
2
  export * from './progress-circle'
3
+ export * from './chart-card'
@@ -0,0 +1,88 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { SearchIcon } from "lucide-react"
5
+
6
+ import { Button } from "@/components/ui/button"
7
+ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandShortcut } from "@/components/ui/command"
8
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"
9
+
10
+ export type AdvancedCommandMenuItem = {
11
+ id: string
12
+ label: React.ReactNode
13
+ description?: React.ReactNode
14
+ icon?: React.ReactNode
15
+ shortcut?: React.ReactNode
16
+ keywords?: string[]
17
+ onSelect?: () => void
18
+ }
19
+
20
+ export type AdvancedCommandMenuGroup = {
21
+ id: string
22
+ label: React.ReactNode
23
+ items: AdvancedCommandMenuItem[]
24
+ }
25
+
26
+ export type AdvancedCommandMenuProps = {
27
+ open?: boolean
28
+ onOpenChange?: (open: boolean) => void
29
+ groups: AdvancedCommandMenuGroup[]
30
+ title?: React.ReactNode
31
+ triggerLabel?: React.ReactNode
32
+ placeholder?: string
33
+ }
34
+
35
+ function AdvancedCommandMenu({
36
+ open,
37
+ onOpenChange,
38
+ groups,
39
+ title = "Command menu",
40
+ triggerLabel = "Command",
41
+ placeholder = "Search commands...",
42
+ }: AdvancedCommandMenuProps) {
43
+ const [internalOpen, setInternalOpen] = React.useState(false)
44
+ const resolvedOpen = open ?? internalOpen
45
+ const setOpen = onOpenChange ?? setInternalOpen
46
+
47
+ return (
48
+ <>
49
+ <Button type="button" variant="outline" leftIcon={<SearchIcon className="size-4" />} onClick={() => setOpen(true)}>{triggerLabel}</Button>
50
+ <Dialog open={resolvedOpen} onOpenChange={setOpen}>
51
+ <DialogContent className="overflow-hidden p-0">
52
+ <DialogHeader className="border-b px-4 py-3">
53
+ <DialogTitle>{title}</DialogTitle>
54
+ </DialogHeader>
55
+ <Command>
56
+ <CommandInput placeholder={placeholder} />
57
+ <CommandList>
58
+ <CommandEmpty>No command found.</CommandEmpty>
59
+ {groups.map((group) => (
60
+ <CommandGroup key={group.id} heading={group.label}>
61
+ {group.items.map((item) => (
62
+ <CommandItem
63
+ key={item.id}
64
+ value={[item.id, item.label, item.description, ...(item.keywords ?? [])].filter(Boolean).join(" ")}
65
+ onSelect={() => {
66
+ item.onSelect?.()
67
+ setOpen(false)
68
+ }}
69
+ >
70
+ {item.icon}
71
+ <span className="grid min-w-0 flex-1">
72
+ <span className="truncate">{item.label}</span>
73
+ {item.description ? <span className="truncate text-xs text-muted-foreground">{item.description}</span> : null}
74
+ </span>
75
+ {item.shortcut ? <CommandShortcut>{item.shortcut}</CommandShortcut> : null}
76
+ </CommandItem>
77
+ ))}
78
+ </CommandGroup>
79
+ ))}
80
+ </CommandList>
81
+ </Command>
82
+ </DialogContent>
83
+ </Dialog>
84
+ </>
85
+ )
86
+ }
87
+
88
+ export { AdvancedCommandMenu }
@@ -1 +1,2 @@
1
- export * from "./command-palette"
1
+ export * from "./command-palette"
2
+ export * from "./advanced-command-menu"
@@ -0,0 +1,59 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+
5
+ import { Input } from "@/components/ui/input"
6
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
7
+ import { cn } from "@/lib/utils"
8
+
9
+ export type DataGridColumn<TRow extends Record<string, unknown>> = {
10
+ key: keyof TRow & string
11
+ header: React.ReactNode
12
+ editable?: boolean
13
+ width?: React.CSSProperties["width"]
14
+ render?: (value: TRow[keyof TRow], row: TRow, rowIndex: number) => React.ReactNode
15
+ }
16
+
17
+ export type DataGridProps<TRow extends Record<string, unknown>> = React.ComponentProps<"div"> & {
18
+ columns: DataGridColumn<TRow>[]
19
+ rows: TRow[]
20
+ onRowsChange?: (rows: TRow[]) => void
21
+ }
22
+
23
+ function DataGrid<TRow extends Record<string, unknown>>({ columns, rows, onRowsChange, className, ...props }: DataGridProps<TRow>) {
24
+ const updateCell = (rowIndex: number, key: keyof TRow & string, value: string) => {
25
+ onRowsChange?.(rows.map((row, index) => index === rowIndex ? { ...row, [key]: value } : row))
26
+ }
27
+
28
+ return (
29
+ <div data-slot="data-grid" className={cn("overflow-hidden rounded-lg border", className)} {...props}>
30
+ <Table>
31
+ <TableHeader>
32
+ <TableRow>{columns.map((column) => <TableHead key={column.key} style={{ width: column.width }}>{column.header}</TableHead>)}</TableRow>
33
+ </TableHeader>
34
+ <TableBody>
35
+ {rows.map((row, rowIndex) => (
36
+ <TableRow key={rowIndex}>
37
+ {columns.map((column) => {
38
+ const value = row[column.key]
39
+ return (
40
+ <TableCell key={column.key}>
41
+ {column.editable ? (
42
+ <Input value={String(value ?? "")} onValueChange={(nextValue) => updateCell(rowIndex, column.key, nextValue)} />
43
+ ) : column.render ? (
44
+ column.render(value, row, rowIndex)
45
+ ) : (
46
+ String(value ?? "")
47
+ )}
48
+ </TableCell>
49
+ )
50
+ })}
51
+ </TableRow>
52
+ ))}
53
+ </TableBody>
54
+ </Table>
55
+ </div>
56
+ )
57
+ }
58
+
59
+ export { DataGrid }
@@ -4,8 +4,9 @@ import * as React from "react"
4
4
  import {
5
5
  flexRender,
6
6
  getCoreRowModel,
7
- getPaginationRowModel,
8
- useReactTable,
7
+ getPaginationRowModel,
8
+ getSortedRowModel,
9
+ useReactTable,
9
10
  type Cell,
10
11
  type ColumnDef,
11
12
  type Header,
@@ -288,40 +289,50 @@ function DataTable<TData, TValue = unknown>({
288
289
  ]
289
290
  }, [columns, features?.rowActions, rowActions])
290
291
 
291
- const paginationConfig = pagination === false ? undefined : pagination
292
- const controlledPagination = paginationConfig
293
- ? {
294
- pageIndex: paginationConfig.pageIndex,
295
- pageSize: paginationConfig.pageSize,
296
- }
297
- : undefined
298
- const manualPagination = Boolean(paginationConfig && paginationConfig.manual !== false)
299
- const resolvedRowSelection = rowSelection ?? {}
300
-
301
- // TanStack Table returns imperative helpers that React Compiler flags by design.
302
- // eslint-disable-next-line react-hooks/incompatible-library
303
- const table = useReactTable({
292
+ const paginationConfig = pagination === false ? undefined : pagination
293
+ const [internalSorting, setInternalSorting] = React.useState<SortingState>([])
294
+ const [internalColumnVisibility, setInternalColumnVisibility] = React.useState<VisibilityState>({})
295
+ const [internalRowSelection, setInternalRowSelection] = React.useState<RowSelectionState>({})
296
+ const [internalExpanded, setInternalExpanded] = React.useState<ExpandedState>({})
297
+ const [internalColumnPinning, setInternalColumnPinning] = React.useState<ColumnPinningState>({})
298
+ const resolvedSorting = sorting ?? internalSorting
299
+ const resolvedColumnVisibility = columnVisibility ?? internalColumnVisibility
300
+ const resolvedRowSelection = rowSelection ?? internalRowSelection
301
+ const resolvedExpanded = expanded ?? internalExpanded
302
+ const resolvedColumnPinning = columnPinning ?? internalColumnPinning
303
+ const controlledPagination = paginationConfig
304
+ ? {
305
+ pageIndex: paginationConfig.pageIndex,
306
+ pageSize: paginationConfig.pageSize,
307
+ }
308
+ : undefined
309
+ const manualPagination = Boolean(paginationConfig && paginationConfig.manual !== false)
310
+
311
+ // TanStack Table returns imperative helpers that React Compiler flags by design.
312
+ // eslint-disable-next-line react-hooks/incompatible-library
313
+ const table = useReactTable({
304
314
  data,
305
315
  columns: resolvedColumns,
306
- getRowId,
307
- getCoreRowModel: getCoreRowModel(),
308
- getPaginationRowModel: paginationConfig && !manualPagination ? getPaginationRowModel() : undefined,
309
- manualPagination,
310
- pageCount: paginationConfig?.pageCount,
311
- state: {
312
- sorting,
313
- columnVisibility,
314
- rowSelection: resolvedRowSelection,
315
- pagination: controlledPagination,
316
- expanded,
317
- ...(columnPinning ? { columnPinning } : {}),
318
- },
319
- onSortingChange,
320
- onColumnVisibilityChange,
321
- onRowSelectionChange,
322
- onExpandedChange,
323
- onColumnPinningChange,
324
- enableRowSelection,
316
+ getRowId,
317
+ getCoreRowModel: getCoreRowModel(),
318
+ getSortedRowModel: getSortedRowModel(),
319
+ getPaginationRowModel: paginationConfig && !manualPagination ? getPaginationRowModel() : undefined,
320
+ manualPagination,
321
+ pageCount: paginationConfig?.pageCount,
322
+ state: {
323
+ sorting: resolvedSorting,
324
+ columnVisibility: resolvedColumnVisibility,
325
+ rowSelection: resolvedRowSelection,
326
+ pagination: controlledPagination,
327
+ expanded: resolvedExpanded,
328
+ columnPinning: resolvedColumnPinning,
329
+ },
330
+ onSortingChange: onSortingChange ?? setInternalSorting,
331
+ onColumnVisibilityChange: onColumnVisibilityChange ?? setInternalColumnVisibility,
332
+ onRowSelectionChange: onRowSelectionChange ?? setInternalRowSelection,
333
+ onExpandedChange: onExpandedChange ?? setInternalExpanded,
334
+ onColumnPinningChange: onColumnPinningChange ?? setInternalColumnPinning,
335
+ enableRowSelection,
325
336
  getRowCanExpand,
326
337
  getExpandedRowModel: getExpandedRowModel(),
327
338
  })
@@ -1,5 +1,6 @@
1
- export * from './data-table'
2
- export * from './data-table-pagination'
1
+ export * from './data-table'
2
+ export * from './data-grid'
3
+ export * from './data-table-pagination'
3
4
  export * from './data-table-toolbar'
4
5
  export * from './data-table-column-visibility-menu'
5
6
  export * from './data-table-select-column'