@gunjo/ui 0.0.1-alpha.1 → 0.0.1-alpha.2

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 (224) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +90 -0
  3. package/README.md +52 -91
  4. package/package.json +47 -6
  5. package/src/components/display/Accordion.tsx +185 -0
  6. package/src/components/display/AccordionGroup.tsx +155 -0
  7. package/src/components/display/ActionDataTable.tsx +413 -0
  8. package/src/components/display/ActivityTimelineCard.tsx +483 -0
  9. package/src/components/display/AnalyticsCard.tsx +167 -0
  10. package/src/components/display/AssetCard.tsx +242 -0
  11. package/src/components/display/AssetGrid.tsx +164 -0
  12. package/src/components/display/Avatar.tsx +127 -0
  13. package/src/components/display/AvatarGroup.tsx +131 -0
  14. package/src/components/{atoms → display}/Badge.tsx +3 -3
  15. package/src/components/display/BarChart.tsx +247 -0
  16. package/src/components/{molecules → display}/Card.tsx +1 -1
  17. package/src/components/display/Carousel.tsx +593 -0
  18. package/src/components/display/ChartLegend.tsx +124 -0
  19. package/src/components/display/ChatMessage.tsx +382 -0
  20. package/src/components/display/ChoroplethMap.tsx +613 -0
  21. package/src/components/display/Code.tsx +42 -0
  22. package/src/components/display/CodeBlock.tsx +338 -0
  23. package/src/components/display/ColorSwatch.tsx +71 -0
  24. package/src/components/display/ConcentricProgressCard.tsx +545 -0
  25. package/src/components/display/DataTable.tsx +522 -0
  26. package/src/components/display/DistributionBar.tsx +102 -0
  27. package/src/components/display/DocNote.tsx +36 -0
  28. package/src/components/display/DonutChart.tsx +257 -0
  29. package/src/components/display/EmptyState.tsx +44 -0
  30. package/src/components/display/FileTree.tsx +180 -0
  31. package/src/components/display/GaugeChart.tsx +219 -0
  32. package/src/components/display/HeatmapChart.tsx +266 -0
  33. package/src/components/display/Icon.tsx +66 -0
  34. package/src/components/display/ImagePreview.tsx +140 -0
  35. package/src/components/{atoms → display}/Img.tsx +46 -12
  36. package/src/components/display/LabeledDonutCard.tsx +475 -0
  37. package/src/components/display/LineChart.tsx +464 -0
  38. package/src/components/{molecules → display}/List.tsx +20 -13
  39. package/src/components/display/MarkdownRenderer.tsx +157 -0
  40. package/src/components/display/MetadataList.tsx +81 -0
  41. package/src/components/display/MiniDistributionBarCard.tsx +314 -0
  42. package/src/components/display/PieChart.tsx +234 -0
  43. package/src/components/display/QuadrantMatrix.tsx +330 -0
  44. package/src/components/display/RadarChart.tsx +335 -0
  45. package/src/components/display/RadialBarChart.tsx +264 -0
  46. package/src/components/display/RetentionCohortCard.tsx +350 -0
  47. package/src/components/display/RibbonChart.tsx +618 -0
  48. package/src/components/display/SearchableAccordion.tsx +270 -0
  49. package/src/components/display/SegmentTimelineCard.tsx +452 -0
  50. package/src/components/display/SegmentedGaugeCard.tsx +607 -0
  51. package/src/components/display/Spacer.tsx +51 -0
  52. package/src/components/display/SparklineChart.tsx +394 -0
  53. package/src/components/display/StackedBarChart.tsx +393 -0
  54. package/src/components/display/Statistic.tsx +70 -0
  55. package/src/components/{molecules → display}/Table.tsx +22 -7
  56. package/src/components/display/Tag.tsx +80 -0
  57. package/src/components/display/TagEditor.tsx +141 -0
  58. package/src/components/display/Timeline.tsx +121 -0
  59. package/src/components/{atoms → display}/ToolPill.tsx +42 -18
  60. package/src/components/display/TreeView.tsx +226 -0
  61. package/src/components/display/chart-tooltip.tsx +423 -0
  62. package/src/components/display/chart-utils.ts +71 -0
  63. package/src/components/display/circular-chart-utils.ts +147 -0
  64. package/src/components/display/generated/default-variant-keys.ts +90 -0
  65. package/src/components/display/generated/variant-keys.ts +169 -0
  66. package/src/components/{atoms → feedback}/Alert.tsx +12 -5
  67. package/src/components/feedback/Banner.tsx +90 -0
  68. package/src/components/{molecules → feedback}/NotificationCenter.tsx +64 -31
  69. package/src/components/feedback/ProgressWidget.tsx +44 -0
  70. package/src/components/{atoms → feedback}/Spinner.tsx +2 -2
  71. package/src/components/{molecules → feedback}/StatusBar.tsx +4 -4
  72. package/src/components/feedback/StatusScreen.tsx +148 -0
  73. package/src/components/{molecules → feedback}/Stepper.tsx +10 -5
  74. package/src/components/feedback/Toast.tsx +108 -0
  75. package/src/components/feedback/ToastProvider.tsx +78 -0
  76. package/src/components/feedback/generated/default-variant-keys.ts +16 -0
  77. package/src/components/feedback/generated/variant-keys.ts +21 -0
  78. package/src/components/generated/component-manifest.ts +1568 -454
  79. package/src/components/generated/component-style-hints.ts +1958 -718
  80. package/src/components/{atoms → inputs}/ButtonVariants.ts +13 -3
  81. package/src/components/inputs/Calendar.tsx +212 -0
  82. package/src/components/inputs/ChatComposer.tsx +75 -0
  83. package/src/components/inputs/ChatInput.tsx +528 -0
  84. package/src/components/{atoms → inputs}/Checkbox.tsx +2 -2
  85. package/src/components/inputs/Combobox.tsx +175 -0
  86. package/src/components/inputs/CopyButton.tsx +187 -0
  87. package/src/components/inputs/DatePicker.tsx +519 -0
  88. package/src/components/inputs/DateRangePicker.tsx +878 -0
  89. package/src/components/inputs/EditableField.tsx +182 -0
  90. package/src/components/{organisms → inputs}/FileUploader.tsx +24 -9
  91. package/src/components/inputs/FilterButton.tsx +163 -0
  92. package/src/components/{molecules → inputs}/Form.tsx +20 -3
  93. package/src/components/{atoms → inputs}/Input.tsx +2 -0
  94. package/src/components/inputs/InputOTP.tsx +75 -0
  95. package/src/components/inputs/Mention.tsx +279 -0
  96. package/src/components/inputs/NumberInput.tsx +109 -0
  97. package/src/components/inputs/PasswordGroup.tsx +138 -0
  98. package/src/components/inputs/PasswordInput.tsx +74 -0
  99. package/src/components/inputs/PasswordRequirementList.tsx +96 -0
  100. package/src/components/inputs/PasswordStrengthMeter.tsx +93 -0
  101. package/src/components/inputs/PhoneInput.tsx +99 -0
  102. package/src/components/inputs/PostalCodeInput.tsx +98 -0
  103. package/src/components/inputs/RangeSlider.tsx +129 -0
  104. package/src/components/inputs/SearchInput.tsx +76 -0
  105. package/src/components/inputs/Select.tsx +39 -0
  106. package/src/components/{atoms → inputs}/Slider.tsx +18 -5
  107. package/src/components/{molecules → inputs}/SortButton.tsx +5 -2
  108. package/src/components/{atoms → inputs}/Switch.tsx +15 -4
  109. package/src/components/inputs/TagInput.tsx +114 -0
  110. package/src/components/{atoms → inputs}/Textarea.tsx +1 -0
  111. package/src/components/inputs/TimePicker.tsx +150 -0
  112. package/src/components/inputs/Toggle.tsx +48 -0
  113. package/src/components/{atoms → inputs}/ToggleGroup.tsx +2 -2
  114. package/src/components/inputs/TooltipButton.tsx +148 -0
  115. package/src/components/inputs/VoiceInputButton.tsx +317 -0
  116. package/src/components/inputs/calendar-holidays.ts +56 -0
  117. package/src/components/inputs/generated/default-variant-keys.ts +32 -0
  118. package/src/components/{atoms → inputs}/generated/variant-keys.ts +19 -27
  119. package/src/components/layout/AspectRatio.tsx +12 -0
  120. package/src/components/layout/AssetInspectorPanel.tsx +416 -0
  121. package/src/components/layout/Cluster.tsx +56 -0
  122. package/src/components/layout/CollapsiblePanelToggle.tsx +94 -0
  123. package/src/components/layout/Container.tsx +43 -0
  124. package/src/components/layout/DeviceFrame.tsx +227 -0
  125. package/src/components/layout/Grid.tsx +65 -0
  126. package/src/components/layout/HStack.tsx +73 -0
  127. package/src/components/{organisms → layout}/InspectorPanel.tsx +6 -5
  128. package/src/components/layout/MarqueeFrame.tsx +158 -0
  129. package/src/components/layout/Resizable.tsx +94 -0
  130. package/src/components/layout/ScrollArea.tsx +71 -0
  131. package/src/components/{organisms → layout}/SpatialCanvas.tsx +12 -7
  132. package/src/components/layout/VStack.tsx +69 -0
  133. package/src/components/layout/generated/default-variant-keys.ts +16 -0
  134. package/src/components/layout/generated/variant-keys.ts +21 -0
  135. package/src/components/{molecules → navigation}/Breadcrumb.tsx +5 -4
  136. package/src/components/navigation/Command.tsx +266 -0
  137. package/src/components/navigation/CommandPalette.tsx +83 -0
  138. package/src/components/navigation/DocumentPager.tsx +171 -0
  139. package/src/components/navigation/Footer.tsx +88 -0
  140. package/src/components/navigation/Header.tsx +80 -0
  141. package/src/components/{molecules → navigation}/Menubar.tsx +45 -12
  142. package/src/components/navigation/NavigationMenu.tsx +128 -0
  143. package/src/components/navigation/PageAside.tsx +84 -0
  144. package/src/components/{molecules → navigation}/Pagination.tsx +60 -7
  145. package/src/components/{organisms → navigation}/RightRail.tsx +1 -1
  146. package/src/components/navigation/Sidebar.tsx +223 -0
  147. package/src/components/navigation/SidebarItem.tsx +160 -0
  148. package/src/components/{molecules → navigation}/Tabs.tsx +2 -2
  149. package/src/components/navigation/TextLink.tsx +71 -0
  150. package/src/components/navigation/generated/default-variant-keys.ts +12 -0
  151. package/src/components/navigation/generated/variant-keys.ts +13 -0
  152. package/src/components/overlay/AIChatInput.tsx +5 -0
  153. package/src/components/overlay/AIChatMessage.tsx +6 -0
  154. package/src/components/overlay/AlertDialog.tsx +145 -0
  155. package/src/components/overlay/ChatPanel.tsx +180 -0
  156. package/src/components/{molecules → overlay}/ContextMenu.tsx +65 -29
  157. package/src/components/{molecules → overlay}/Dialog.tsx +21 -13
  158. package/src/components/overlay/Drawer.tsx +131 -0
  159. package/src/components/{molecules → overlay}/DropdownMenu.tsx +52 -17
  160. package/src/components/overlay/FloatingPanel.tsx +90 -0
  161. package/src/components/overlay/HoverCard.tsx +36 -0
  162. package/src/components/overlay/MediaLightbox.tsx +403 -0
  163. package/src/components/overlay/MediaPickerDialog.tsx +198 -0
  164. package/src/components/overlay/Modal.tsx +103 -0
  165. package/src/components/overlay/OnboardingFlow.tsx +172 -0
  166. package/src/components/overlay/Popover.tsx +36 -0
  167. package/src/components/overlay/ShareModal.tsx +324 -0
  168. package/src/components/{molecules → overlay}/Sheet.tsx +76 -19
  169. package/src/components/overlay/Tooltip.tsx +130 -0
  170. package/src/components/overlay/generated/default-variant-keys.ts +14 -0
  171. package/src/components/overlay/generated/variant-keys.ts +17 -0
  172. package/src/components/patterns/BlogTemplate.tsx +46 -0
  173. package/src/components/{templates → patterns}/DashboardTemplate.tsx +2 -2
  174. package/src/components/patterns/DocsTemplate.tsx +41 -0
  175. package/src/components/{templates → patterns}/MediaLibraryTemplate.tsx +1 -1
  176. package/src/components/patterns/OnboardingTemplate.tsx +32 -0
  177. package/src/components/patterns/PricingTemplate.tsx +106 -0
  178. package/src/globals.css +173 -22
  179. package/src/index.ts +177 -76
  180. package/tailwind-theme-extend.cjs +48 -3
  181. package/design/atoms-metadata.json +0 -82
  182. package/design/molecules-metadata.json +0 -130
  183. package/design/organisms-metadata.json +0 -38
  184. package/design/templates-metadata.json +0 -38
  185. package/src/components/atoms/Avatar.tsx +0 -57
  186. package/src/components/atoms/Select.tsx +0 -28
  187. package/src/components/atoms/generated/default-variant-keys.ts +0 -36
  188. package/src/components/molecules/AIChatInput.tsx +0 -140
  189. package/src/components/molecules/AIChatMessage.tsx +0 -109
  190. package/src/components/molecules/Accordion.tsx +0 -99
  191. package/src/components/molecules/Calendar.tsx +0 -60
  192. package/src/components/molecules/Carousel.tsx +0 -261
  193. package/src/components/molecules/Command.tsx +0 -152
  194. package/src/components/molecules/FilterButton.tsx +0 -133
  195. package/src/components/molecules/HoverCard.tsx +0 -29
  196. package/src/components/molecules/Modal.tsx +0 -66
  197. package/src/components/molecules/Popover.tsx +0 -31
  198. package/src/components/molecules/ProgressWidget.tsx +0 -40
  199. package/src/components/molecules/Resizable.tsx +0 -47
  200. package/src/components/molecules/ScrollArea.tsx +0 -48
  201. package/src/components/molecules/SidebarItem.tsx +0 -134
  202. package/src/components/molecules/Toast.tsx +0 -57
  203. package/src/components/molecules/Tooltip.tsx +0 -30
  204. package/src/components/molecules/generated/default-variant-keys.ts +0 -22
  205. package/src/components/molecules/generated/variant-keys.ts +0 -33
  206. package/src/components/organisms/CommandPalette.tsx +0 -58
  207. package/src/components/organisms/FloatingPanel.tsx +0 -46
  208. package/src/components/organisms/ShareModal.tsx +0 -182
  209. package/src/components/organisms/ToastProvider.tsx +0 -49
  210. /package/src/components/{atoms → display}/Kbd.tsx +0 -0
  211. /package/src/components/{atoms → display}/Separator.tsx +0 -0
  212. /package/src/components/{atoms → display}/Skeleton.tsx +0 -0
  213. /package/src/components/{atoms → feedback}/Progress.tsx +0 -0
  214. /package/src/components/{atoms → inputs}/Button.tsx +0 -0
  215. /package/src/components/{atoms → inputs}/Label.tsx +0 -0
  216. /package/src/components/{atoms → inputs}/RadioGroup.tsx +0 -0
  217. /package/src/components/{organisms → navigation}/AppRail.tsx +0 -0
  218. /package/src/components/{templates → patterns}/AuthTemplate.tsx +0 -0
  219. /package/src/components/{templates → patterns}/BannalyzeTemplate.tsx +0 -0
  220. /package/src/components/{templates → patterns}/ChatTemplate.tsx +0 -0
  221. /package/src/components/{templates → patterns}/EditorTemplate.tsx +0 -0
  222. /package/src/components/{templates → patterns}/KanbanTemplate.tsx +0 -0
  223. /package/src/components/{templates → patterns}/LandingTemplate.tsx +0 -0
  224. /package/src/components/{templates → patterns}/SettingsTemplate.tsx +0 -0
@@ -0,0 +1,171 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { IconChevronLeft as ChevronLeft, IconChevronRight as ChevronRight } from "@tabler/icons-react";
5
+ import { cn } from "../../lib/utils"
6
+
7
+ export interface DocumentPagerItem {
8
+ href: string
9
+ directionLabel: React.ReactNode
10
+ title: React.ReactNode
11
+ subtitle?: React.ReactNode
12
+ description?: React.ReactNode
13
+ categoryLabel?: React.ReactNode
14
+ thumbnailSrc?: string
15
+ thumbnailAlt?: string
16
+ thumbnailFallback?: React.ReactNode
17
+ ariaLabel?: string
18
+ }
19
+
20
+ export interface DocumentPagerLinkProps {
21
+ href: string
22
+ className?: string
23
+ "aria-label"?: string
24
+ children: React.ReactNode
25
+ }
26
+
27
+ export interface DocumentPagerProps extends React.HTMLAttributes<HTMLElement> {
28
+ previous?: DocumentPagerItem | null
29
+ next?: DocumentPagerItem | null
30
+ linkComponent?: React.ElementType<DocumentPagerLinkProps>
31
+ }
32
+
33
+ function DocumentPagerThumb({
34
+ src,
35
+ alt,
36
+ fallback,
37
+ }: {
38
+ src?: string
39
+ alt?: string
40
+ fallback?: React.ReactNode
41
+ }) {
42
+ const [errored, setErrored] = React.useState(false)
43
+
44
+ if (!src && !errored) return null
45
+
46
+ return (
47
+ <div className="relative h-20 w-24 shrink-0 overflow-hidden rounded-md border border-border/70 bg-muted/40 p-1.5 sm:w-28">
48
+ {src && !errored ? (
49
+ /* eslint-disable-next-line @next/next/no-img-element */
50
+ <img
51
+ src={src}
52
+ alt={alt ?? ""}
53
+ onError={() => setErrored(true)}
54
+ loading="lazy"
55
+ decoding="async"
56
+ className="h-full w-full rounded-[4px] object-cover object-top shadow-sm"
57
+ />
58
+ ) : (
59
+ <div className="grid h-full w-full place-items-center rounded-[4px] bg-muted text-center text-[10px] leading-tight text-muted-foreground">
60
+ {fallback ?? "Preview"}
61
+ </div>
62
+ )}
63
+ </div>
64
+ )
65
+ }
66
+
67
+ function DocumentPagerCard({
68
+ item,
69
+ direction,
70
+ LinkComponent,
71
+ }: {
72
+ item: DocumentPagerItem
73
+ direction: "previous" | "next"
74
+ LinkComponent: React.ElementType<DocumentPagerLinkProps>
75
+ }) {
76
+ const ariaLabel =
77
+ item.ariaLabel ??
78
+ `${typeof item.directionLabel === "string" ? item.directionLabel : direction}: ${
79
+ typeof item.title === "string" ? item.title : ""
80
+ }`
81
+
82
+ return (
83
+ <LinkComponent
84
+ href={item.href}
85
+ className="group block h-full rounded-md border border-border/70 bg-background p-3 transition-colors hover:border-primary-border hover:bg-muted/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
86
+ aria-label={ariaLabel}
87
+ >
88
+ <div
89
+ className={cn(
90
+ "mb-2.5 flex items-center",
91
+ direction === "next" ? "justify-end" : "justify-start"
92
+ )}
93
+ >
94
+ <span className="inline-flex items-center gap-1.5 text-sm font-semibold text-muted-foreground transition-colors group-hover:text-primary">
95
+ {direction === "previous" ? <ChevronLeft className="h-4 w-4" /> : null}
96
+ {item.directionLabel}
97
+ {direction === "next" ? <ChevronRight className="h-4 w-4" /> : null}
98
+ </span>
99
+ </div>
100
+
101
+ <div className="flex min-w-0 gap-3">
102
+ <DocumentPagerThumb
103
+ src={item.thumbnailSrc}
104
+ alt={item.thumbnailAlt}
105
+ fallback={item.thumbnailFallback}
106
+ />
107
+ <div className="min-w-0 flex-1 space-y-2">
108
+ <div className="flex min-w-0 flex-col items-start gap-1.5">
109
+ <span className="min-w-0 max-w-full space-y-0.5 text-sm font-semibold tracking-tight transition-colors group-hover:text-primary">
110
+ <span className="block break-words leading-snug">{item.title}</span>
111
+ {item.subtitle ? (
112
+ <span className="block break-words text-[10px] font-normal leading-snug text-muted-foreground">
113
+ {item.subtitle}
114
+ </span>
115
+ ) : null}
116
+ </span>
117
+ {item.categoryLabel ? (
118
+ <span className="inline-flex min-h-5 max-w-full shrink-0 items-center rounded-full border border-border/60 px-2.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
119
+ {item.categoryLabel}
120
+ </span>
121
+ ) : null}
122
+ </div>
123
+ {item.description ? (
124
+ <p className="line-clamp-3 text-xs leading-relaxed text-muted-foreground">
125
+ {item.description}
126
+ </p>
127
+ ) : null}
128
+ </div>
129
+ </div>
130
+ </LinkComponent>
131
+ )
132
+ }
133
+
134
+ const DocumentPager = React.forwardRef<HTMLElement, DocumentPagerProps>(
135
+ ({ previous, next, linkComponent, className, ...props }, ref) => {
136
+ const LinkComponent = linkComponent ?? "a"
137
+
138
+ if (!previous && !next) return null
139
+
140
+ return (
141
+ <nav
142
+ ref={ref}
143
+ className={cn(
144
+ "grid w-full gap-3 border-t border-border/60 px-0 pb-0 pt-5 xl:grid-cols-2",
145
+ className
146
+ )}
147
+ {...props}
148
+ >
149
+ {previous ? (
150
+ <DocumentPagerCard
151
+ item={previous}
152
+ direction="previous"
153
+ LinkComponent={LinkComponent}
154
+ />
155
+ ) : (
156
+ <div className="hidden md:block" aria-hidden="true" />
157
+ )}
158
+ {next ? (
159
+ <DocumentPagerCard
160
+ item={next}
161
+ direction="next"
162
+ LinkComponent={LinkComponent}
163
+ />
164
+ ) : null}
165
+ </nav>
166
+ )
167
+ }
168
+ )
169
+ DocumentPager.displayName = "DocumentPager"
170
+
171
+ export { DocumentPager }
@@ -0,0 +1,88 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "../../lib/utils"
4
+
5
+ const Footer = React.forwardRef<
6
+ HTMLElement,
7
+ React.HTMLAttributes<HTMLElement>
8
+ >(({ className, ...props }, ref) => (
9
+ <footer
10
+ ref={ref}
11
+ className={cn(
12
+ "flex w-full flex-col gap-6 border-t border-border bg-background px-6 py-8",
13
+ className
14
+ )}
15
+ {...props}
16
+ />
17
+ ))
18
+ Footer.displayName = "Footer"
19
+
20
+ const FooterColumns = React.forwardRef<
21
+ HTMLDivElement,
22
+ React.HTMLAttributes<HTMLDivElement>
23
+ >(({ className, ...props }, ref) => (
24
+ <div
25
+ ref={ref}
26
+ className={cn("grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4", className)}
27
+ {...props}
28
+ />
29
+ ))
30
+ FooterColumns.displayName = "FooterColumns"
31
+
32
+ interface FooterSectionProps
33
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
34
+ title?: React.ReactNode
35
+ }
36
+
37
+ const FooterSection = React.forwardRef<HTMLDivElement, FooterSectionProps>(
38
+ ({ className, title, children, ...props }, ref) => (
39
+ <div ref={ref} className={cn("flex flex-col gap-2", className)} {...props}>
40
+ {title ? (
41
+ <p className="text-sm font-semibold text-foreground">{title}</p>
42
+ ) : null}
43
+ {children}
44
+ </div>
45
+ )
46
+ )
47
+ FooterSection.displayName = "FooterSection"
48
+
49
+ const FooterLink = React.forwardRef<
50
+ HTMLAnchorElement,
51
+ React.AnchorHTMLAttributes<HTMLAnchorElement>
52
+ >(({ className, ...props }, ref) => (
53
+ <a
54
+ ref={ref}
55
+ className={cn(
56
+ "text-sm text-muted-foreground transition-colors hover:text-foreground",
57
+ className
58
+ )}
59
+ {...props}
60
+ />
61
+ ))
62
+ FooterLink.displayName = "FooterLink"
63
+
64
+ const FooterBrand = React.forwardRef<
65
+ HTMLDivElement,
66
+ React.HTMLAttributes<HTMLDivElement>
67
+ >(({ className, ...props }, ref) => (
68
+ <div
69
+ ref={ref}
70
+ className={cn("flex flex-col gap-2", className)}
71
+ {...props}
72
+ />
73
+ ))
74
+ FooterBrand.displayName = "FooterBrand"
75
+
76
+ const FooterCopyright = React.forwardRef<
77
+ HTMLParagraphElement,
78
+ React.HTMLAttributes<HTMLParagraphElement>
79
+ >(({ className, ...props }, ref) => (
80
+ <p
81
+ ref={ref}
82
+ className={cn("border-t border-border pt-4 text-xs text-muted-foreground", className)}
83
+ {...props}
84
+ />
85
+ ))
86
+ FooterCopyright.displayName = "FooterCopyright"
87
+
88
+ export { Footer, FooterColumns, FooterSection, FooterLink, FooterBrand, FooterCopyright }
@@ -0,0 +1,80 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+
5
+ import { cn } from "../../lib/utils"
6
+
7
+ const Header = React.forwardRef<
8
+ HTMLElement,
9
+ React.HTMLAttributes<HTMLElement>
10
+ >(({ className, ...props }, ref) => (
11
+ <header
12
+ ref={ref}
13
+ className={cn(
14
+ "flex w-full flex-wrap items-center justify-between gap-x-4 gap-y-3 border-b border-border bg-background px-4 py-3 sm:px-6",
15
+ className
16
+ )}
17
+ {...props}
18
+ />
19
+ ))
20
+ Header.displayName = "Header"
21
+
22
+ const HeaderBrand = React.forwardRef<
23
+ HTMLDivElement,
24
+ React.HTMLAttributes<HTMLDivElement>
25
+ >(({ className, ...props }, ref) => (
26
+ <div
27
+ ref={ref}
28
+ className={cn("flex min-w-0 shrink-0 items-center gap-2", className)}
29
+ {...props}
30
+ />
31
+ ))
32
+ HeaderBrand.displayName = "HeaderBrand"
33
+
34
+ const HeaderNav = React.forwardRef<
35
+ HTMLElement,
36
+ React.HTMLAttributes<HTMLElement>
37
+ >(({ className, ...props }, ref) => (
38
+ <nav
39
+ ref={ref}
40
+ className={cn(
41
+ "order-last flex w-full min-w-0 items-center gap-4 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden sm:order-none sm:w-auto",
42
+ className
43
+ )}
44
+ {...props}
45
+ />
46
+ ))
47
+ HeaderNav.displayName = "HeaderNav"
48
+
49
+ interface HeaderNavLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
50
+ active?: boolean
51
+ }
52
+
53
+ const HeaderNavLink = React.forwardRef<HTMLAnchorElement, HeaderNavLinkProps>(
54
+ ({ className, active, ...props }, ref) => (
55
+ <a
56
+ ref={ref}
57
+ className={cn(
58
+ "whitespace-nowrap text-sm transition-colors hover:text-foreground",
59
+ active ? "font-medium text-foreground" : "text-muted-foreground",
60
+ className
61
+ )}
62
+ {...props}
63
+ />
64
+ )
65
+ )
66
+ HeaderNavLink.displayName = "HeaderNavLink"
67
+
68
+ const HeaderActions = React.forwardRef<
69
+ HTMLDivElement,
70
+ React.HTMLAttributes<HTMLDivElement>
71
+ >(({ className, ...props }, ref) => (
72
+ <div
73
+ ref={ref}
74
+ className={cn("ml-auto flex shrink-0 items-center gap-2", className)}
75
+ {...props}
76
+ />
77
+ ))
78
+ HeaderActions.displayName = "HeaderActions"
79
+
80
+ export { Header, HeaderBrand, HeaderNav, HeaderNavLink, HeaderActions }
@@ -2,9 +2,10 @@
2
2
 
3
3
  import * as React from "react"
4
4
  import * as MenubarPrimitive from "@radix-ui/react-menubar"
5
- import { Check, ChevronRight, Circle } from "lucide-react"
5
+ import { IconCheck as Check, IconChevronRight as ChevronRight, IconCircle as Circle } from "@tabler/icons-react";
6
6
 
7
7
  import { cn } from "../../lib/utils"
8
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../overlay/Tooltip"
8
9
 
9
10
  const Menubar = React.forwardRef<
10
11
  React.ElementRef<typeof MenubarPrimitive.Root>,
@@ -98,18 +99,50 @@ const MenubarItem = React.forwardRef<
98
99
  React.ElementRef<typeof MenubarPrimitive.Item>,
99
100
  React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Item> & {
100
101
  inset?: boolean
102
+ disabledReason?: React.ReactNode
103
+ disabledReasonLabel?: string
104
+ disabledReasonPortalContainer?: HTMLElement | null
101
105
  }
102
- >(({ className, inset, ...props }, ref) => (
103
- <MenubarPrimitive.Item
104
- ref={ref}
105
- className={cn(
106
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
107
- inset && "pl-8",
108
- className
109
- )}
110
- {...props}
111
- />
112
- ))
106
+ >(({ className, inset, disabledReason, disabledReasonLabel, disabledReasonPortalContainer, disabled, onSelect, ...props }, ref) => {
107
+ const item = (
108
+ <MenubarPrimitive.Item
109
+ ref={ref}
110
+ disabled={disabled}
111
+ onSelect={(event) => {
112
+ if (disabled) {
113
+ event.preventDefault()
114
+ return
115
+ }
116
+ onSelect?.(event)
117
+ }}
118
+ className={cn(
119
+ "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
120
+ inset && "pl-8",
121
+ className
122
+ )}
123
+ {...props}
124
+ />
125
+ )
126
+
127
+ if (!disabled || !disabledReason) return item
128
+
129
+ return (
130
+ <Tooltip>
131
+ <TooltipTrigger asChild>
132
+ <span
133
+ className="block w-full"
134
+ tabIndex={0}
135
+ aria-label={disabledReasonLabel ?? (typeof disabledReason === "string" ? disabledReason : undefined)}
136
+ >
137
+ {item}
138
+ </span>
139
+ </TooltipTrigger>
140
+ <TooltipContent portalContainer={disabledReasonPortalContainer} className="max-w-64 text-left">
141
+ {disabledReason}
142
+ </TooltipContent>
143
+ </Tooltip>
144
+ )
145
+ })
113
146
  MenubarItem.displayName = MenubarPrimitive.Item.displayName
114
147
 
115
148
  const MenubarCheckboxItem = React.forwardRef<
@@ -0,0 +1,128 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
5
+ import { IconChevronDown as ChevronDown } from "@tabler/icons-react";
6
+ import { cva } from "class-variance-authority"
7
+
8
+ import { cn } from "../../lib/utils"
9
+
10
+ const NavigationMenu = React.forwardRef<
11
+ React.ElementRef<typeof NavigationMenuPrimitive.Root>,
12
+ React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
13
+ >(({ className, children, ...props }, ref) => (
14
+ <NavigationMenuPrimitive.Root
15
+ ref={ref}
16
+ className={cn(
17
+ "relative z-10 flex max-w-max flex-1 items-center justify-center",
18
+ className
19
+ )}
20
+ {...props}
21
+ >
22
+ {children}
23
+ <NavigationMenuViewport />
24
+ </NavigationMenuPrimitive.Root>
25
+ ))
26
+ NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
27
+
28
+ const NavigationMenuList = React.forwardRef<
29
+ React.ElementRef<typeof NavigationMenuPrimitive.List>,
30
+ React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
31
+ >(({ className, ...props }, ref) => (
32
+ <NavigationMenuPrimitive.List
33
+ ref={ref}
34
+ className={cn(
35
+ "group flex flex-1 list-none items-center justify-center space-x-1",
36
+ className
37
+ )}
38
+ {...props}
39
+ />
40
+ ))
41
+ NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
42
+
43
+ const NavigationMenuItem = NavigationMenuPrimitive.Item
44
+
45
+ export const navigationMenuTriggerStyle = cva(
46
+ "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-muted hover:text-foreground focus:bg-muted focus:text-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-muted/50 data-[state=open]:bg-muted/50"
47
+ )
48
+
49
+ const NavigationMenuTrigger = React.forwardRef<
50
+ React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
51
+ React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
52
+ >(({ className, children, ...props }, ref) => (
53
+ <NavigationMenuPrimitive.Trigger
54
+ ref={ref}
55
+ className={cn(navigationMenuTriggerStyle(), "group", className)}
56
+ {...props}
57
+ >
58
+ {children}{" "}
59
+ <ChevronDown
60
+ className="relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180"
61
+ aria-hidden
62
+ />
63
+ </NavigationMenuPrimitive.Trigger>
64
+ ))
65
+ NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
66
+
67
+ const NavigationMenuContent = React.forwardRef<
68
+ React.ElementRef<typeof NavigationMenuPrimitive.Content>,
69
+ React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
70
+ >(({ className, ...props }, ref) => (
71
+ <NavigationMenuPrimitive.Content
72
+ ref={ref}
73
+ className={cn(
74
+ "left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto",
75
+ className
76
+ )}
77
+ {...props}
78
+ />
79
+ ))
80
+ NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
81
+
82
+ const NavigationMenuLink = NavigationMenuPrimitive.Link
83
+
84
+ const NavigationMenuViewport = React.forwardRef<
85
+ React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
86
+ React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
87
+ >(({ className, ...props }, ref) => (
88
+ <div className={cn("absolute left-1/2 top-full flex -translate-x-1/2 justify-center")}>
89
+ <NavigationMenuPrimitive.Viewport
90
+ className={cn(
91
+ "origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-[min(var(--radix-navigation-menu-viewport-width),calc(100vw-2rem))] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90",
92
+ className
93
+ )}
94
+ ref={ref}
95
+ {...props}
96
+ />
97
+ </div>
98
+ ))
99
+ NavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName
100
+
101
+ const NavigationMenuIndicator = React.forwardRef<
102
+ React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
103
+ React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
104
+ >(({ className, ...props }, ref) => (
105
+ <NavigationMenuPrimitive.Indicator
106
+ ref={ref}
107
+ className={cn(
108
+ "top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
109
+ className
110
+ )}
111
+ {...props}
112
+ >
113
+ <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
114
+ </NavigationMenuPrimitive.Indicator>
115
+ ))
116
+ NavigationMenuIndicator.displayName =
117
+ NavigationMenuPrimitive.Indicator.displayName
118
+
119
+ export {
120
+ NavigationMenu,
121
+ NavigationMenuList,
122
+ NavigationMenuItem,
123
+ NavigationMenuContent,
124
+ NavigationMenuTrigger,
125
+ NavigationMenuLink,
126
+ NavigationMenuIndicator,
127
+ NavigationMenuViewport,
128
+ }
@@ -0,0 +1,84 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "../../lib/utils"
5
+ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "../display/Accordion"
6
+ import { RightRail } from "./RightRail"
7
+
8
+ export interface PageAsideProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
9
+ title: React.ReactNode
10
+ renderAside: () => React.ReactNode
11
+ children: React.ReactNode
12
+ asideLabel?: string
13
+ contentLabel?: string
14
+ mobileDescription?: React.ReactNode
15
+ defaultOpen?: boolean
16
+ width?: string
17
+ contentClassName?: string
18
+ mobileClassName?: string
19
+ railClassName?: string
20
+ railContentClassName?: string
21
+ openLabel?: string
22
+ closeLabel?: string
23
+ }
24
+
25
+ const PageAside = React.forwardRef<HTMLDivElement, PageAsideProps>(
26
+ (
27
+ {
28
+ className,
29
+ title,
30
+ renderAside,
31
+ children,
32
+ asideLabel,
33
+ contentLabel,
34
+ mobileDescription,
35
+ defaultOpen = true,
36
+ width = "w-72",
37
+ contentClassName,
38
+ mobileClassName,
39
+ railClassName,
40
+ railContentClassName,
41
+ openLabel = "Open",
42
+ closeLabel = "Close",
43
+ ...props
44
+ },
45
+ ref
46
+ ) => {
47
+ const label = asideLabel ?? (typeof title === "string" ? title : undefined)
48
+
49
+ return (
50
+ <div ref={ref} className={cn("flex min-h-0 w-full flex-col lg:flex-row", className)} {...props}>
51
+ <div className={cn("min-w-0 flex-1", contentClassName)} aria-label={contentLabel}>
52
+ <aside className={cn("mb-4 rounded-md border bg-background p-3 lg:hidden", mobileClassName)} aria-label={label}>
53
+ <Accordion type="single" collapsible defaultValue={defaultOpen ? "page-aside" : undefined} className="w-full border-0">
54
+ <AccordionItem value="page-aside" className="border-0">
55
+ <AccordionTrigger className="px-0 py-0 text-left hover:no-underline" openLabel={openLabel} closeLabel={closeLabel}>
56
+ <span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
57
+ {title}
58
+ </span>
59
+ </AccordionTrigger>
60
+ <AccordionContent className="px-0 pb-0 pt-3">
61
+ <div className="space-y-3">
62
+ {mobileDescription ? (
63
+ <p className="text-xs text-muted-foreground">{mobileDescription}</p>
64
+ ) : null}
65
+ {renderAside()}
66
+ </div>
67
+ </AccordionContent>
68
+ </AccordionItem>
69
+ </Accordion>
70
+ </aside>
71
+ {children}
72
+ </div>
73
+ <RightRail width={width} className={cn("hidden bg-background lg:flex", railClassName)} aria-label={label}>
74
+ <div className={cn("space-y-5 p-4", railContentClassName)}>
75
+ {renderAside()}
76
+ </div>
77
+ </RightRail>
78
+ </div>
79
+ )
80
+ }
81
+ )
82
+ PageAside.displayName = "PageAside"
83
+
84
+ export { PageAside }