@olympusoss/canvas 2.6.19

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 (128) hide show
  1. package/package.json +179 -0
  2. package/src/components/atoms/README.md +11 -0
  3. package/src/components/atoms/aspect-ratio.tsx +32 -0
  4. package/src/components/atoms/avatar.tsx +98 -0
  5. package/src/components/atoms/badge.tsx +44 -0
  6. package/src/components/atoms/brand-mark.tsx +74 -0
  7. package/src/components/atoms/button.tsx +104 -0
  8. package/src/components/atoms/checkbox.tsx +63 -0
  9. package/src/components/atoms/flex-box.tsx +105 -0
  10. package/src/components/atoms/icon.tsx +34 -0
  11. package/src/components/atoms/input.tsx +91 -0
  12. package/src/components/atoms/label.tsx +41 -0
  13. package/src/components/atoms/logo.tsx +89 -0
  14. package/src/components/atoms/progress.tsx +55 -0
  15. package/src/components/atoms/radio-group.tsx +122 -0
  16. package/src/components/atoms/scroll-area.tsx +106 -0
  17. package/src/components/atoms/section.tsx +48 -0
  18. package/src/components/atoms/separator.tsx +45 -0
  19. package/src/components/atoms/skeleton.tsx +17 -0
  20. package/src/components/atoms/slider.tsx +93 -0
  21. package/src/components/atoms/switch.tsx +60 -0
  22. package/src/components/atoms/textarea.tsx +78 -0
  23. package/src/components/atoms/toggle.tsx +80 -0
  24. package/src/components/charts/activity-heatmap.tsx +96 -0
  25. package/src/components/charts/axes.tsx +21 -0
  26. package/src/components/charts/chart-container.tsx +195 -0
  27. package/src/components/charts/chart-legend.tsx +67 -0
  28. package/src/components/charts/chart-tooltip.tsx +161 -0
  29. package/src/components/charts/chart-types.tsx +49 -0
  30. package/src/components/charts/containers.tsx +11 -0
  31. package/src/components/charts/data.tsx +16 -0
  32. package/src/components/charts/details.tsx +25 -0
  33. package/src/components/charts/gauge.tsx +106 -0
  34. package/src/components/charts/grids.tsx +8 -0
  35. package/src/components/charts/index.ts +62 -0
  36. package/src/components/charts/labeled-bar-list.tsx +85 -0
  37. package/src/components/charts/references.tsx +8 -0
  38. package/src/components/charts/service-health-list.tsx +73 -0
  39. package/src/components/charts/sparkline.tsx +52 -0
  40. package/src/components/charts/stacked-bar.tsx +104 -0
  41. package/src/components/charts/text.tsx +10 -0
  42. package/src/components/charts/world-heat-map-inner.tsx +317 -0
  43. package/src/components/charts/world-heat-map.tsx +184 -0
  44. package/src/components/molecules/README.md +12 -0
  45. package/src/components/molecules/action-bar.tsx +73 -0
  46. package/src/components/molecules/activity-item.tsx +74 -0
  47. package/src/components/molecules/alert.tsx +80 -0
  48. package/src/components/molecules/animated-background.tsx +92 -0
  49. package/src/components/molecules/brand-lockup.tsx +48 -0
  50. package/src/components/molecules/breadcrumb.tsx +161 -0
  51. package/src/components/molecules/button-group.tsx +104 -0
  52. package/src/components/molecules/calendar.tsx +216 -0
  53. package/src/components/molecules/card.tsx +101 -0
  54. package/src/components/molecules/code-block.tsx +48 -0
  55. package/src/components/molecules/empty-state.tsx +55 -0
  56. package/src/components/molecules/error-state.tsx +42 -0
  57. package/src/components/molecules/field-display.tsx +35 -0
  58. package/src/components/molecules/input-otp.tsx +74 -0
  59. package/src/components/molecules/loading-state.tsx +36 -0
  60. package/src/components/molecules/notification-item.tsx +67 -0
  61. package/src/components/molecules/notification-list.tsx +45 -0
  62. package/src/components/molecules/number-badge.tsx +53 -0
  63. package/src/components/molecules/page-header.tsx +88 -0
  64. package/src/components/molecules/page-tabs.tsx +94 -0
  65. package/src/components/molecules/pagination.tsx +150 -0
  66. package/src/components/molecules/phone-input.tsx +200 -0
  67. package/src/components/molecules/search-bar.tsx +64 -0
  68. package/src/components/molecules/secret-field.tsx +158 -0
  69. package/src/components/molecules/section-card.tsx +91 -0
  70. package/src/components/molecules/stat-card.tsx +96 -0
  71. package/src/components/molecules/status-badge.tsx +42 -0
  72. package/src/components/molecules/stepper.tsx +96 -0
  73. package/src/components/molecules/table.tsx +157 -0
  74. package/src/components/molecules/toggle-group.tsx +145 -0
  75. package/src/components/molecules/tooltip.tsx +150 -0
  76. package/src/components/molecules/user-avatar-chip.tsx +71 -0
  77. package/src/components/organisms/README.md +14 -0
  78. package/src/components/organisms/accordion.tsx +149 -0
  79. package/src/components/organisms/alert-dialog.tsx +269 -0
  80. package/src/components/organisms/carousel.tsx +244 -0
  81. package/src/components/organisms/collapsible.tsx +69 -0
  82. package/src/components/organisms/command.tsx +143 -0
  83. package/src/components/organisms/context-menu.tsx +333 -0
  84. package/src/components/organisms/dashboard-grid.tsx +360 -0
  85. package/src/components/organisms/data-table.tsx +330 -0
  86. package/src/components/organisms/dialog.tsx +304 -0
  87. package/src/components/organisms/drawer.tsx +100 -0
  88. package/src/components/organisms/dropdown-menu.tsx +434 -0
  89. package/src/components/organisms/editors/code-editor.tsx +144 -0
  90. package/src/components/organisms/editors/index.ts +4 -0
  91. package/src/components/organisms/editors/markdown-editor.tsx +153 -0
  92. package/src/components/organisms/editors/markdown-renderer.ts +27 -0
  93. package/src/components/organisms/editors/prose-canvas-classes.ts +45 -0
  94. package/src/components/organisms/editors/rich-text-editor.tsx +126 -0
  95. package/src/components/organisms/editors/toolbar/md-toolbar.tsx +129 -0
  96. package/src/components/organisms/editors/toolbar/rte-toolbar.tsx +211 -0
  97. package/src/components/organisms/editors/toolbar/toolbar-shell.tsx +45 -0
  98. package/src/components/organisms/editors/use-codemirror-theme.ts +61 -0
  99. package/src/components/organisms/error-boundary.tsx +61 -0
  100. package/src/components/organisms/form.tsx +174 -0
  101. package/src/components/organisms/hover-card.tsx +114 -0
  102. package/src/components/organisms/menubar.tsx +491 -0
  103. package/src/components/organisms/navbar.tsx +101 -0
  104. package/src/components/organisms/navigation-menu.tsx +234 -0
  105. package/src/components/organisms/popover.tsx +144 -0
  106. package/src/components/organisms/resizable.tsx +39 -0
  107. package/src/components/organisms/schema-form.tsx +232 -0
  108. package/src/components/organisms/select.tsx +303 -0
  109. package/src/components/organisms/sheet.tsx +256 -0
  110. package/src/components/organisms/sidebar.tsx +1037 -0
  111. package/src/components/organisms/sonner.tsx +96 -0
  112. package/src/components/organisms/tabs.tsx +132 -0
  113. package/src/components/organisms/theme-provider.tsx +101 -0
  114. package/src/hooks/use-mobile.tsx +19 -0
  115. package/src/index.ts +547 -0
  116. package/src/lib/portal-container.tsx +35 -0
  117. package/src/lib/utils.ts +6 -0
  118. package/src/native.ts +23 -0
  119. package/src/tokens/colors.ts +91 -0
  120. package/src/tokens/index.ts +3 -0
  121. package/src/tokens/spacing.ts +55 -0
  122. package/src/tokens/typography.ts +27 -0
  123. package/styles/canvas.css +55 -0
  124. package/styles/dashboard-grid.css +47 -0
  125. package/styles/leaflet.css +13 -0
  126. package/styles/tokens.css +234 -0
  127. package/tailwind.config.ts +70 -0
  128. package/tsconfig.json +23 -0
@@ -0,0 +1,234 @@
1
+ "use client";
2
+
3
+ import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu";
4
+ import { cva } from "class-variance-authority";
5
+ import { ChevronDown } from "lucide-react";
6
+ import * as React from "react";
7
+
8
+ import { cn } from "../../lib/utils";
9
+
10
+ export interface NavigationMenuProps
11
+ extends React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root> {
12
+ /** Controlled active item value. */
13
+ value?: string;
14
+ /** Initial value for uncontrolled usage. */
15
+ defaultValue?: string;
16
+ /** Fires when the active item changes. */
17
+ onValueChange?: (value: string) => void;
18
+ /**
19
+ * Delay (ms) before opening on hover.
20
+ * @default 200
21
+ */
22
+ delayDuration?: number;
23
+ /**
24
+ * Delay (ms) for skipping subsequent open delays once one menu is open.
25
+ * @default 300
26
+ */
27
+ skipDelayDuration?: number;
28
+ /**
29
+ * Reading direction.
30
+ * @default "ltr"
31
+ */
32
+ dir?: "ltr" | "rtl";
33
+ /**
34
+ * Layout direction.
35
+ * @default "horizontal"
36
+ */
37
+ orientation?: "horizontal" | "vertical";
38
+ /** List + Indicator + Viewport. */
39
+ children?: React.ReactNode;
40
+ className?: string;
41
+ }
42
+
43
+ const NavigationMenu = React.forwardRef<
44
+ React.ElementRef<typeof NavigationMenuPrimitive.Root>,
45
+ NavigationMenuProps
46
+ >(({ className, children, ...props }, ref) => (
47
+ <NavigationMenuPrimitive.Root
48
+ ref={ref}
49
+ className={cn("relative z-10 flex max-w-max flex-1 items-center justify-center", className)}
50
+ {...props}
51
+ >
52
+ {children}
53
+ <NavigationMenuViewport />
54
+ </NavigationMenuPrimitive.Root>
55
+ ));
56
+ NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName;
57
+
58
+ export interface NavigationMenuListProps
59
+ extends React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List> {
60
+ asChild?: boolean;
61
+ /** A list of `<NavigationMenuItem>`s. */
62
+ children?: React.ReactNode;
63
+ className?: string;
64
+ }
65
+
66
+ const NavigationMenuList = React.forwardRef<
67
+ React.ElementRef<typeof NavigationMenuPrimitive.List>,
68
+ NavigationMenuListProps
69
+ >(({ className, ...props }, ref) => (
70
+ <NavigationMenuPrimitive.List
71
+ ref={ref}
72
+ className={cn("group flex flex-1 list-none items-center justify-center space-x-1", className)}
73
+ {...props}
74
+ />
75
+ ));
76
+ NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;
77
+
78
+ export interface NavigationMenuItemProps
79
+ extends React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Item> {
80
+ /** Required for controlled menus — unique id of this item. */
81
+ value?: string;
82
+ /** Trigger + Content (or Link). */
83
+ children?: React.ReactNode;
84
+ className?: string;
85
+ }
86
+
87
+ const NavigationMenuItem = NavigationMenuPrimitive.Item as React.ForwardRefExoticComponent<
88
+ NavigationMenuItemProps & React.RefAttributes<HTMLLIElement>
89
+ >;
90
+
91
+ const navigationMenuTriggerStyle = cva(
92
+ "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-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=open]:text-accent-foreground data-[state=open]:bg-accent/50 data-[state=open]:hover:bg-accent data-[state=open]:focus:bg-accent",
93
+ );
94
+
95
+ export interface NavigationMenuTriggerProps
96
+ extends React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger> {
97
+ asChild?: boolean;
98
+ /** Trigger label. */
99
+ children?: React.ReactNode;
100
+ className?: string;
101
+ }
102
+
103
+ const NavigationMenuTrigger = React.forwardRef<
104
+ React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
105
+ NavigationMenuTriggerProps
106
+ >(({ className, children, ...props }, ref) => (
107
+ <NavigationMenuPrimitive.Trigger
108
+ ref={ref}
109
+ className={cn(navigationMenuTriggerStyle(), "group", className)}
110
+ {...props}
111
+ >
112
+ {children}{" "}
113
+ <ChevronDown
114
+ className="relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180"
115
+ aria-hidden="true"
116
+ />
117
+ </NavigationMenuPrimitive.Trigger>
118
+ ));
119
+ NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName;
120
+
121
+ export interface NavigationMenuContentProps
122
+ extends React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content> {
123
+ /**
124
+ * Force the content to mount even when not active. Useful for
125
+ * preserving form state during transitions.
126
+ * @default false
127
+ */
128
+ forceMount?: true;
129
+ /** Fires when focus enters the content. */
130
+ onFocusOutside?: (event: Event) => void;
131
+ /** Fires on pointer event outside the content. */
132
+ onPointerDownOutside?: (event: CustomEvent<{ originalEvent: PointerEvent }>) => void;
133
+ asChild?: boolean;
134
+ /** Sub-menu content. */
135
+ children?: React.ReactNode;
136
+ className?: string;
137
+ }
138
+
139
+ const NavigationMenuContent = React.forwardRef<
140
+ React.ElementRef<typeof NavigationMenuPrimitive.Content>,
141
+ NavigationMenuContentProps
142
+ >(({ className, ...props }, ref) => (
143
+ <NavigationMenuPrimitive.Content
144
+ ref={ref}
145
+ className={cn(
146
+ "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 ",
147
+ className,
148
+ )}
149
+ {...props}
150
+ />
151
+ ));
152
+ NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName;
153
+
154
+ export interface NavigationMenuLinkProps
155
+ extends React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Link> {
156
+ /**
157
+ * Mark as the current/active link.
158
+ * @default false
159
+ */
160
+ active?: boolean;
161
+ /** Fires when the link is selected (click, Enter, Space). */
162
+ onSelect?: (event: Event) => void;
163
+ asChild?: boolean;
164
+ /** Anchor target. */
165
+ href?: string;
166
+ children?: React.ReactNode;
167
+ className?: string;
168
+ }
169
+
170
+ const NavigationMenuLink = NavigationMenuPrimitive.Link as React.ForwardRefExoticComponent<
171
+ NavigationMenuLinkProps & React.RefAttributes<HTMLAnchorElement>
172
+ >;
173
+
174
+ export interface NavigationMenuViewportProps
175
+ extends React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport> {
176
+ /** Force the viewport to mount even when no content is open. */
177
+ forceMount?: true;
178
+ asChild?: boolean;
179
+ className?: string;
180
+ }
181
+
182
+ const NavigationMenuViewport = React.forwardRef<
183
+ React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
184
+ NavigationMenuViewportProps
185
+ >(({ className, ...props }, ref) => (
186
+ <div className={cn("absolute left-0 top-full flex justify-center")}>
187
+ <NavigationMenuPrimitive.Viewport
188
+ className={cn(
189
+ "origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full 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 md:w-[var(--radix-navigation-menu-viewport-width)]",
190
+ className,
191
+ )}
192
+ ref={ref}
193
+ {...props}
194
+ />
195
+ </div>
196
+ ));
197
+ NavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName;
198
+
199
+ export interface NavigationMenuIndicatorProps
200
+ extends React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator> {
201
+ /** Force the indicator to mount even when no content is open. */
202
+ forceMount?: true;
203
+ asChild?: boolean;
204
+ className?: string;
205
+ }
206
+
207
+ const NavigationMenuIndicator = React.forwardRef<
208
+ React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
209
+ NavigationMenuIndicatorProps
210
+ >(({ className, ...props }, ref) => (
211
+ <NavigationMenuPrimitive.Indicator
212
+ ref={ref}
213
+ className={cn(
214
+ "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",
215
+ className,
216
+ )}
217
+ {...props}
218
+ >
219
+ <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
220
+ </NavigationMenuPrimitive.Indicator>
221
+ ));
222
+ NavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayName;
223
+
224
+ export {
225
+ NavigationMenu,
226
+ NavigationMenuContent,
227
+ NavigationMenuIndicator,
228
+ NavigationMenuItem,
229
+ NavigationMenuLink,
230
+ NavigationMenuList,
231
+ NavigationMenuTrigger,
232
+ NavigationMenuViewport,
233
+ navigationMenuTriggerStyle,
234
+ };
@@ -0,0 +1,144 @@
1
+ "use client";
2
+
3
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
4
+ import * as React from "react";
5
+
6
+ import { cn } from "../../lib/utils";
7
+
8
+ export interface PopoverProps extends React.ComponentProps<typeof PopoverPrimitive.Root> {
9
+ /** Controlled open state. Pair with `onOpenChange`. */
10
+ open?: boolean;
11
+ /**
12
+ * Initial open state for uncontrolled usage.
13
+ * @default false
14
+ */
15
+ defaultOpen?: boolean;
16
+ /** Fires whenever the popover opens or closes. */
17
+ onOpenChange?: (open: boolean) => void;
18
+ /**
19
+ * When true, blocks focus from leaving the popover.
20
+ * @default false
21
+ */
22
+ modal?: boolean;
23
+ /** Trigger + Content. */
24
+ children?: React.ReactNode;
25
+ }
26
+
27
+ const Popover = PopoverPrimitive.Root as React.FC<PopoverProps>;
28
+
29
+ export interface PopoverTriggerProps
30
+ extends React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Trigger> {
31
+ /**
32
+ * Render as a Radix Slot — wrap a custom button while keeping the
33
+ * popover's trigger behaviour.
34
+ * @default false
35
+ */
36
+ asChild?: boolean;
37
+ children?: React.ReactNode;
38
+ className?: string;
39
+ }
40
+
41
+ const PopoverTrigger = PopoverPrimitive.Trigger as React.ForwardRefExoticComponent<
42
+ PopoverTriggerProps & React.RefAttributes<HTMLButtonElement>
43
+ >;
44
+
45
+ export interface PopoverAnchorProps
46
+ extends React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Anchor> {
47
+ /**
48
+ * Render as a Radix Slot. Use to anchor the popover to an element other
49
+ * than the trigger (e.g. a parent input while the trigger is a button).
50
+ * @default false
51
+ */
52
+ asChild?: boolean;
53
+ children?: React.ReactNode;
54
+ }
55
+
56
+ const PopoverAnchor = PopoverPrimitive.Anchor as React.ForwardRefExoticComponent<
57
+ PopoverAnchorProps & React.RefAttributes<HTMLDivElement>
58
+ >;
59
+
60
+ export interface PopoverContentProps
61
+ extends React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> {
62
+ /**
63
+ * Distance in pixels between the anchor and the popover.
64
+ * @default 4
65
+ */
66
+ sideOffset?: number;
67
+ /**
68
+ * Distance from the alignment edge.
69
+ * @default 0
70
+ */
71
+ alignOffset?: number;
72
+ /**
73
+ * Preferred side of the anchor to render on.
74
+ * @default "bottom"
75
+ */
76
+ side?: "top" | "right" | "bottom" | "left";
77
+ /**
78
+ * Alignment along the chosen side.
79
+ * @default "center"
80
+ */
81
+ align?: "start" | "center" | "end";
82
+ /**
83
+ * Avoid colliding with the viewport edges by flipping/shifting.
84
+ * @default true
85
+ */
86
+ avoidCollisions?: boolean;
87
+ /**
88
+ * Padding kept from collision boundaries.
89
+ * @default 0
90
+ */
91
+ collisionPadding?: number | { top?: number; right?: number; bottom?: number; left?: number };
92
+ /**
93
+ * Stick to the trigger or partially follow on scroll.
94
+ * @default "partial"
95
+ */
96
+ sticky?: "partial" | "always";
97
+ /** Hide the content when the trigger is detached or covered. */
98
+ hideWhenDetached?: boolean;
99
+ /** Fires when focus enters the popover after it opens. */
100
+ onOpenAutoFocus?: (event: Event) => void;
101
+ /** Fires when focus leaves the popover after it closes. */
102
+ onCloseAutoFocus?: (event: Event) => void;
103
+ /** Fires when the Escape key is pressed inside the popover. */
104
+ onEscapeKeyDown?: (event: KeyboardEvent) => void;
105
+ /** Fires on pointer event outside the popover. */
106
+ onPointerDownOutside?: (event: CustomEvent<{ originalEvent: PointerEvent }>) => void;
107
+ /** Fires on any interaction outside (focus + pointer). */
108
+ onInteractOutside?: (event: Event) => void;
109
+ /**
110
+ * Force the content to mount even when closed.
111
+ * @default false
112
+ */
113
+ forceMount?: true;
114
+ /**
115
+ * Render as a Radix Slot.
116
+ * @default false
117
+ */
118
+ asChild?: boolean;
119
+ /** Popover body. */
120
+ children?: React.ReactNode;
121
+ /** Tailwind / CSS classes merged via `cn()`. */
122
+ className?: string;
123
+ }
124
+
125
+ const PopoverContent = React.forwardRef<
126
+ React.ElementRef<typeof PopoverPrimitive.Content>,
127
+ PopoverContentProps
128
+ >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
129
+ <PopoverPrimitive.Portal>
130
+ <PopoverPrimitive.Content
131
+ ref={ref}
132
+ align={align}
133
+ sideOffset={sideOffset}
134
+ className={cn(
135
+ "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[var(--radix-popover-content-transform-origin)]",
136
+ className,
137
+ )}
138
+ {...props}
139
+ />
140
+ </PopoverPrimitive.Portal>
141
+ ));
142
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
143
+
144
+ export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };
@@ -0,0 +1,39 @@
1
+ "use client";
2
+
3
+ import { GripVertical } from "lucide-react";
4
+ import { Group, Panel, Separator } from "react-resizable-panels";
5
+
6
+ import { cn } from "../../lib/utils";
7
+
8
+ const ResizablePanelGroup = ({ className, ...props }: React.ComponentProps<typeof Group>) => (
9
+ <Group
10
+ className={cn("flex h-full w-full data-[panel-group-direction=vertical]:flex-col", className)}
11
+ {...props}
12
+ />
13
+ );
14
+
15
+ const ResizablePanel = Panel;
16
+
17
+ const ResizableHandle = ({
18
+ withHandle,
19
+ className,
20
+ ...props
21
+ }: React.ComponentProps<typeof Separator> & {
22
+ withHandle?: boolean;
23
+ }) => (
24
+ <Separator
25
+ className={cn(
26
+ "relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
27
+ className,
28
+ )}
29
+ {...props}
30
+ >
31
+ {withHandle && (
32
+ <div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
33
+ <GripVertical className="h-2.5 w-2.5" />
34
+ </div>
35
+ )}
36
+ </Separator>
37
+ );
38
+
39
+ export { ResizableHandle, ResizablePanel, ResizablePanelGroup };
@@ -0,0 +1,232 @@
1
+ "use client";
2
+
3
+ import Form, { type IChangeEvent, type FormProps as RJSFFormProps } from "@rjsf/core";
4
+ import type {
5
+ FieldTemplateProps,
6
+ ObjectFieldTemplateProps,
7
+ RegistryWidgetsType,
8
+ RJSFSchema,
9
+ SubmitButtonProps,
10
+ TemplatesType,
11
+ UiSchema,
12
+ WidgetProps,
13
+ } from "@rjsf/utils";
14
+ import validator from "@rjsf/validator-ajv8";
15
+ import type * as React from "react";
16
+
17
+ import { cn } from "../../lib/utils";
18
+ import { Input } from "../atoms/input";
19
+ import { Label } from "../atoms/label";
20
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select";
21
+
22
+ /* ────────────────────────────────────────────────────────────────
23
+ Default widgets — Canvas-styled inputs for RJSF
24
+ ──────────────────────────────────────────────────────────────── */
25
+
26
+ const TextWidget: React.FC<WidgetProps> = ({
27
+ id,
28
+ value,
29
+ onChange,
30
+ onBlur,
31
+ onFocus,
32
+ placeholder,
33
+ disabled,
34
+ readonly,
35
+ required,
36
+ label,
37
+ schema,
38
+ }) => {
39
+ const isEmail = schema.format === "email";
40
+ const inputType = isEmail ? "email" : "text";
41
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
42
+ /* c8 ignore next -- clearing branch: user-driven and out of scope for widget unit tests */
43
+ onChange(e.target.value === "" ? undefined : e.target.value);
44
+ };
45
+ /* c8 ignore next -- onBlur is optional from rjsf; undefined branch wires to Input without a handler */
46
+ const handleBlur = onBlur ? () => onBlur(id, value) : undefined;
47
+ /* c8 ignore next -- onFocus is optional from rjsf; undefined branch wires to Input without a handler */
48
+ const handleFocus = onFocus ? () => onFocus(id, value) : undefined;
49
+ return (
50
+ <div className="space-y-1.5">
51
+ {label && (
52
+ <Label htmlFor={id}>
53
+ {label}
54
+ {required && <span className="ml-0.5 text-destructive">*</span>}
55
+ </Label>
56
+ )}
57
+ <Input
58
+ id={id}
59
+ type={inputType}
60
+ value={value ?? ""}
61
+ onChange={handleChange}
62
+ onBlur={handleBlur}
63
+ onFocus={handleFocus}
64
+ placeholder={placeholder}
65
+ disabled={disabled}
66
+ readOnly={readonly}
67
+ />
68
+ </div>
69
+ );
70
+ };
71
+
72
+ const SelectWidget: React.FC<WidgetProps> = ({
73
+ id,
74
+ value,
75
+ onChange,
76
+ disabled,
77
+ readonly,
78
+ required,
79
+ options,
80
+ label,
81
+ placeholder,
82
+ }) => {
83
+ /* c8 ignore next -- enumOptions is always populated for enum schemas; ?? [] is a type-narrowing guard */
84
+ const enumOptions = (options.enumOptions as Array<{ value: unknown; label: string }>) ?? [];
85
+ const handleValueChange = (v: string) => {
86
+ /* c8 ignore next -- rjsf Select clears via undefined, so the "" branch of the coalesce is unreachable */
87
+ onChange(v === "" ? undefined : v);
88
+ };
89
+ /* c8 ignore next -- `placeholder` is optional; "Select…" is the default fallback */
90
+ const placeholderText = placeholder ?? "Select…";
91
+ return (
92
+ <div className="space-y-1.5">
93
+ {label && (
94
+ <Label htmlFor={id}>
95
+ {label}
96
+ {required && <span className="ml-0.5 text-destructive">*</span>}
97
+ </Label>
98
+ )}
99
+ <Select
100
+ value={value ? String(value) : undefined}
101
+ onValueChange={handleValueChange}
102
+ disabled={disabled || readonly}
103
+ >
104
+ <SelectTrigger id={id}>
105
+ <SelectValue placeholder={placeholderText} />
106
+ </SelectTrigger>
107
+ <SelectContent>
108
+ {enumOptions.map((opt) => (
109
+ <SelectItem key={String(opt.value)} value={String(opt.value)}>
110
+ {opt.label}
111
+ </SelectItem>
112
+ ))}
113
+ </SelectContent>
114
+ </Select>
115
+ </div>
116
+ );
117
+ };
118
+
119
+ /* ────────────────────────────────────────────────────────────────
120
+ Default templates
121
+ ──────────────────────────────────────────────────────────────── */
122
+
123
+ const FieldTemplate: React.FC<FieldTemplateProps> = ({
124
+ children,
125
+ description,
126
+ errors,
127
+ help,
128
+ hidden,
129
+ }) => {
130
+ /* c8 ignore next -- defensive: hidden fields are filtered upstream in rjsf before rendering */
131
+ if (hidden) return null;
132
+ return (
133
+ <div className="space-y-1">
134
+ {children}
135
+ {description && <div className="text-xs text-muted-foreground">{description}</div>}
136
+ {errors && <div className="text-xs text-destructive">{errors}</div>}
137
+ {help && <div className="text-xs text-muted-foreground">{help}</div>}
138
+ </div>
139
+ );
140
+ };
141
+
142
+ const ObjectFieldTemplate: React.FC<ObjectFieldTemplateProps> = ({
143
+ title,
144
+ description,
145
+ properties,
146
+ }) => (
147
+ <div className="space-y-4">
148
+ {title && <h3 className="text-base font-semibold">{title}</h3>}
149
+ {description && <p className="text-sm text-muted-foreground">{description}</p>}
150
+ {properties.map((p) => (
151
+ <div key={p.name}>{p.content}</div>
152
+ ))}
153
+ </div>
154
+ );
155
+
156
+ const SubmitButton: React.FC<SubmitButtonProps> = () => null;
157
+
158
+ /* ────────────────────────────────────────────────────────────────
159
+ Exported widget/template packs
160
+ ──────────────────────────────────────────────────────────────── */
161
+
162
+ export const canvasSchemaFormWidgets: RegistryWidgetsType = {
163
+ TextWidget,
164
+ EmailWidget: TextWidget,
165
+ URLWidget: TextWidget,
166
+ SelectWidget,
167
+ };
168
+
169
+ /* c8 ignore start -- null-returning button templates: rjsf calls these for array/clear ops we don't expose in Canvas */
170
+ const nullButton = () => null;
171
+ /* c8 ignore stop */
172
+
173
+ export const canvasSchemaFormTemplates: Partial<TemplatesType> = {
174
+ FieldTemplate,
175
+ ObjectFieldTemplate,
176
+ ButtonTemplates: {
177
+ SubmitButton,
178
+ AddButton: nullButton,
179
+ MoveDownButton: nullButton,
180
+ MoveUpButton: nullButton,
181
+ RemoveButton: nullButton,
182
+ CopyButton: nullButton,
183
+ ClearButton: nullButton,
184
+ },
185
+ };
186
+
187
+ /* ────────────────────────────────────────────────────────────────
188
+ SchemaForm — RJSF wrapper with Canvas defaults
189
+ ──────────────────────────────────────────────────────────────── */
190
+
191
+ export interface SchemaFormProps<T = unknown>
192
+ extends Omit<RJSFFormProps<T>, "validator" | "widgets" | "templates"> {
193
+ schema: RJSFSchema;
194
+ uiSchema?: UiSchema;
195
+ /** Override or extend Canvas's default widget set. */
196
+ widgets?: RegistryWidgetsType;
197
+ /** Override or extend Canvas's default template set. */
198
+ templates?: Partial<TemplatesType>;
199
+ /** Supply a different RJSF validator (defaults to ajv8). */
200
+ validator?: RJSFFormProps<T>["validator"];
201
+ className?: string;
202
+ }
203
+
204
+ export function SchemaForm<T = unknown>({
205
+ schema,
206
+ uiSchema,
207
+ widgets,
208
+ templates,
209
+ validator: customValidator,
210
+ className,
211
+ children,
212
+ ...rest
213
+ }: SchemaFormProps<T>) {
214
+ return (
215
+ <div className={cn("space-y-4", className)}>
216
+ <Form<T>
217
+ schema={schema}
218
+ uiSchema={uiSchema}
219
+ validator={customValidator ?? validator}
220
+ widgets={{ ...canvasSchemaFormWidgets, ...widgets }}
221
+ templates={{ ...canvasSchemaFormTemplates, ...templates }}
222
+ {...rest}
223
+ >
224
+ {children}
225
+ </Form>
226
+ </div>
227
+ );
228
+ }
229
+
230
+ SchemaForm.displayName = "SchemaForm";
231
+
232
+ export type { IChangeEvent, RJSFSchema, UiSchema, WidgetProps };