@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
package/package.json ADDED
@@ -0,0 +1,179 @@
1
+ {
2
+ "name": "@olympusoss/canvas",
3
+ "version": "2.6.19",
4
+ "type": "module",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "exports": {
8
+ ".": {
9
+ "react-native": "./src/native.ts",
10
+ "types": "./src/index.ts",
11
+ "default": "./src/index.ts"
12
+ },
13
+ "./tokens": {
14
+ "types": "./src/tokens/index.ts",
15
+ "default": "./src/tokens/index.ts"
16
+ },
17
+ "./styles/*": "./styles/*"
18
+ },
19
+ "files": [
20
+ "src",
21
+ "styles",
22
+ "package.json",
23
+ "tsconfig.json",
24
+ "tailwind.config.ts"
25
+ ],
26
+ "sideEffects": [
27
+ "*.css",
28
+ "**/sonner.tsx",
29
+ "**/theme-provider.tsx"
30
+ ],
31
+ "scripts": {
32
+ "typecheck": "tsc --noEmit",
33
+ "lint": "biome check",
34
+ "lint:fix": "biome check --write",
35
+ "format": "biome format --write .",
36
+ "ci": "biome ci && tsc --noEmit && vitest run",
37
+ "test": "vitest run",
38
+ "test:watch": "vitest",
39
+ "test:ui": "vitest --ui",
40
+ "test:coverage": "vitest run --coverage",
41
+ "snapshot-coverage": "node scripts/check-snapshot-coverage.mjs",
42
+ "size": "size-limit",
43
+ "size:why": "size-limit --why",
44
+ "changeset": "changeset",
45
+ "version-packages": "changeset version",
46
+ "release": "changeset publish",
47
+ "prepare": "husky"
48
+ },
49
+ "lint-staged": {
50
+ "*.{ts,tsx,js,jsx,mjs,cjs,json,jsonc,md,css}": "biome check --write --no-errors-on-unmatched"
51
+ },
52
+ "publishConfig": {
53
+ "registry": "https://registry.npmjs.org",
54
+ "access": "public"
55
+ },
56
+ "peerDependencies": {
57
+ "react": ">=18.0.0",
58
+ "react-dom": ">=18.0.0",
59
+ "@rjsf/core": "^6.0.0",
60
+ "@rjsf/utils": "^6.0.0",
61
+ "@rjsf/validator-ajv8": "^6.0.0",
62
+ "libphonenumber-js": "^1.12.0"
63
+ },
64
+ "peerDependenciesMeta": {
65
+ "@rjsf/core": {
66
+ "optional": true
67
+ },
68
+ "@rjsf/utils": {
69
+ "optional": true
70
+ },
71
+ "@rjsf/validator-ajv8": {
72
+ "optional": true
73
+ },
74
+ "libphonenumber-js": {
75
+ "optional": true
76
+ }
77
+ },
78
+ "dependencies": {
79
+ "@codemirror/commands": "^6.7.0",
80
+ "@codemirror/lang-css": "^6.3.0",
81
+ "@codemirror/lang-html": "^6.4.9",
82
+ "@codemirror/lang-javascript": "^6.2.2",
83
+ "@codemirror/lang-json": "^6.0.1",
84
+ "@codemirror/lang-markdown": "^6.3.0",
85
+ "@codemirror/language": "^6.10.3",
86
+ "@codemirror/state": "^6.4.1",
87
+ "@codemirror/view": "^6.34.1",
88
+ "@hookform/resolvers": "^5.2.2",
89
+ "@radix-ui/react-accordion": "^1.2.12",
90
+ "@radix-ui/react-alert-dialog": "^1.1.15",
91
+ "@radix-ui/react-aspect-ratio": "^1.1.8",
92
+ "@radix-ui/react-avatar": "^1.1.11",
93
+ "@radix-ui/react-checkbox": "^1.3.3",
94
+ "@radix-ui/react-collapsible": "^1.1.12",
95
+ "@radix-ui/react-context-menu": "^2.2.16",
96
+ "@radix-ui/react-dialog": "^1.1.15",
97
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
98
+ "@radix-ui/react-hover-card": "^1.1.15",
99
+ "@radix-ui/react-label": "^2.1.8",
100
+ "@radix-ui/react-menubar": "^1.1.16",
101
+ "@radix-ui/react-navigation-menu": "^1.2.14",
102
+ "@radix-ui/react-popover": "^1.1.15",
103
+ "@radix-ui/react-progress": "^1.1.8",
104
+ "@radix-ui/react-radio-group": "^1.3.8",
105
+ "@radix-ui/react-scroll-area": "^1.2.10",
106
+ "@radix-ui/react-select": "^2.2.6",
107
+ "@radix-ui/react-separator": "^1.1.8",
108
+ "@radix-ui/react-slider": "^1.3.6",
109
+ "@radix-ui/react-slot": "^1.2.4",
110
+ "@radix-ui/react-switch": "^1.2.6",
111
+ "@radix-ui/react-tabs": "^1.1.13",
112
+ "@radix-ui/react-toggle": "^1.1.10",
113
+ "@radix-ui/react-toggle-group": "^1.1.11",
114
+ "@radix-ui/react-tooltip": "^1.2.8",
115
+ "@tanstack/react-table": "^8.21.3",
116
+ "@tiptap/core": "^2.10.0",
117
+ "@tiptap/extension-link": "^2.10.0",
118
+ "@tiptap/extension-placeholder": "^2.10.0",
119
+ "@tiptap/react": "^2.10.0",
120
+ "@tiptap/starter-kit": "^2.10.0",
121
+ "class-variance-authority": "^0.7.1",
122
+ "clsx": "^2.1.1",
123
+ "cmdk": "^1.1.1",
124
+ "codemirror": "^6.0.1",
125
+ "d3-geo": "^3.1.1",
126
+ "date-fns": "^4.1.0",
127
+ "dompurify": "^3.2.0",
128
+ "embla-carousel-react": "^8.6.0",
129
+ "input-otp": "^1.4.2",
130
+ "leaflet": "^1.9.4",
131
+ "lucide-react": "^1.8.0",
132
+ "marked": "^15.0.0",
133
+ "next-themes": "^0.4.6",
134
+ "react-day-picker": "^9.14.0",
135
+ "react-grid-layout": "1.5.3",
136
+ "react-hook-form": "^7.72.1",
137
+ "react-leaflet": "^5.0.0",
138
+ "react-resizable-panels": "^4.10.0",
139
+ "react-simple-maps": "^3.0.0",
140
+ "recharts": "2.15.4",
141
+ "sonner": "^2.0.7",
142
+ "tailwind-merge": "^3.0.2",
143
+ "vaul": "^1.1.2",
144
+ "zod": "^4.3.6"
145
+ },
146
+ "devDependencies": {
147
+ "@biomejs/biome": "^2.4.10",
148
+ "@changesets/cli": "^2.31.0",
149
+ "@rjsf/core": "^6.4.1",
150
+ "@rjsf/utils": "^6.4.1",
151
+ "@rjsf/validator-ajv8": "^6.3.1",
152
+ "@size-limit/esbuild": "^12.1.0",
153
+ "@size-limit/file": "^12.1.0",
154
+ "@testing-library/jest-dom": "^6.9.1",
155
+ "@testing-library/react": "^16.3.2",
156
+ "@types/d3-geo": "^3.1.0",
157
+ "@types/dompurify": "^3.0.5",
158
+ "@types/leaflet": "^1.9.0",
159
+ "@types/react": "^19.1.0",
160
+ "@types/react-dom": "^19.1.0",
161
+ "@types/react-grid-layout": "1.3.6",
162
+ "@types/react-simple-maps": "^3.0.6",
163
+ "@vitejs/plugin-react": "^6.0.1",
164
+ "@vitest/coverage-v8": "^4.1.4",
165
+ "@vitest/ui": "^4.1.4",
166
+ "husky": "^9.1.7",
167
+ "jsdom": "^29.0.2",
168
+ "libphonenumber-js": "^1.12.0",
169
+ "lint-staged": "^16.4.0",
170
+ "react": "^19.1.0",
171
+ "react-docgen-typescript": "^2.4.0",
172
+ "react-dom": "^19.1.0",
173
+ "size-limit": "^12.1.0",
174
+ "tailwindcss": "^4.1.4",
175
+ "typescript": "^5.8.3",
176
+ "vitest": "^4.1.4",
177
+ "vitest-axe": "^0.1.0"
178
+ }
179
+ }
@@ -0,0 +1,11 @@
1
+ # Atoms
2
+
3
+ Single primitive wrappers. Zero composition.
4
+
5
+ **Can import**: `tokens/`, `lib/utils`, React. **That's it.**
6
+
7
+ **Cannot import**: anything from `molecules/`, `organisms/`, or `templates/`.
8
+
9
+ If an atom wants to compose another atom, it's probably a molecule — promote it.
10
+
11
+ See [CONTRIBUTING.md](../../../CONTRIBUTING.md) for the full atomic-design rules.
@@ -0,0 +1,32 @@
1
+ "use client";
2
+
3
+ import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio";
4
+ import type * as React from "react";
5
+
6
+ export interface AspectRatioProps
7
+ extends React.ComponentPropsWithoutRef<typeof AspectRatioPrimitive.Root> {
8
+ /**
9
+ * Aspect ratio expressed as a number (e.g. `16/9`, `4/3`, `1`). The
10
+ * child element is sized to fit this ratio while preserving the
11
+ * container's width.
12
+ */
13
+ ratio?: number;
14
+ /**
15
+ * Render as a Radix Slot — useful when wrapping a custom container that
16
+ * already has its own DOM element.
17
+ * @default false
18
+ */
19
+ asChild?: boolean;
20
+ children?: React.ReactNode;
21
+ className?: string;
22
+ }
23
+
24
+ /**
25
+ * Re-export of Radix's `AspectRatio.Root`. Pass `ratio` (number, e.g.
26
+ * `16/9`) to lock a child's aspect ratio.
27
+ */
28
+ const AspectRatio = AspectRatioPrimitive.Root as React.ForwardRefExoticComponent<
29
+ AspectRatioProps & React.RefAttributes<HTMLDivElement>
30
+ >;
31
+
32
+ export { AspectRatio };
@@ -0,0 +1,98 @@
1
+ "use client";
2
+
3
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
4
+ import * as React from "react";
5
+
6
+ import { cn } from "../../lib/utils";
7
+
8
+ export interface AvatarProps extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> {
9
+ /**
10
+ * Render as a Radix Slot — wrap a custom element while keeping the
11
+ * Avatar's image-loading behaviour.
12
+ * @default false
13
+ */
14
+ asChild?: boolean;
15
+ /** Avatar content — typically `<AvatarImage>` + `<AvatarFallback>`. */
16
+ children?: React.ReactNode;
17
+ className?: string;
18
+ }
19
+
20
+ /**
21
+ * Round profile-image container. Composes `Avatar` + `AvatarImage` +
22
+ * `AvatarFallback`. Defaults to `h-10 w-10`.
23
+ */
24
+ const Avatar = React.forwardRef<React.ElementRef<typeof AvatarPrimitive.Root>, AvatarProps>(
25
+ ({ className, ...props }, ref) => (
26
+ <AvatarPrimitive.Root
27
+ ref={ref}
28
+ className={cn(
29
+ "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full bg-white",
30
+ className,
31
+ )}
32
+ {...props}
33
+ />
34
+ ),
35
+ );
36
+ Avatar.displayName = AvatarPrimitive.Root.displayName;
37
+
38
+ export interface AvatarImageProps
39
+ extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image> {
40
+ /** Image URL. Hides itself if loading errors so the fallback can render. */
41
+ src?: string;
42
+ /** Accessible label. Defaults to the empty string for decorative use. */
43
+ alt?: string;
44
+ /** Radix lifecycle hook for tracking the load state. */
45
+ onLoadingStatusChange?: (status: "idle" | "loading" | "loaded" | "error") => void;
46
+ asChild?: boolean;
47
+ className?: string;
48
+ }
49
+
50
+ /**
51
+ * The `<img>` slot inside `<Avatar>`. Hides itself if loading errors so the
52
+ * fallback can render.
53
+ */
54
+ const AvatarImage = React.forwardRef<
55
+ React.ElementRef<typeof AvatarPrimitive.Image>,
56
+ AvatarImageProps
57
+ >(({ className, ...props }, ref) => (
58
+ <AvatarPrimitive.Image
59
+ ref={ref}
60
+ className={cn("aspect-square h-full w-full", className)}
61
+ {...props}
62
+ />
63
+ ));
64
+ AvatarImage.displayName = AvatarPrimitive.Image.displayName;
65
+
66
+ export interface AvatarFallbackProps
67
+ extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback> {
68
+ /**
69
+ * Wait this many ms before showing the fallback (avoids flash on fast
70
+ * image loads).
71
+ */
72
+ delayMs?: number;
73
+ asChild?: boolean;
74
+ /** Initials, icon, or silhouette. */
75
+ children?: React.ReactNode;
76
+ className?: string;
77
+ }
78
+
79
+ /**
80
+ * Rendered when the image is missing or still loading — typically initials,
81
+ * an icon, or a silhouette.
82
+ */
83
+ const AvatarFallback = React.forwardRef<
84
+ React.ElementRef<typeof AvatarPrimitive.Fallback>,
85
+ AvatarFallbackProps
86
+ >(({ className, ...props }, ref) => (
87
+ <AvatarPrimitive.Fallback
88
+ ref={ref}
89
+ className={cn(
90
+ "flex h-full w-full items-center justify-center rounded-full bg-white",
91
+ className,
92
+ )}
93
+ {...props}
94
+ />
95
+ ));
96
+ AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
97
+
98
+ export { Avatar, AvatarFallback, AvatarImage };
@@ -0,0 +1,44 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import type * as React from "react";
3
+
4
+ import { cn } from "../../lib/utils";
5
+
6
+ const badgeVariants = cva(
7
+ "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
12
+ secondary:
13
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
14
+ destructive:
15
+ "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
16
+ outline: "text-foreground",
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ variant: "default",
21
+ },
22
+ },
23
+ );
24
+
25
+ export interface BadgeProps
26
+ extends React.HTMLAttributes<HTMLDivElement>,
27
+ VariantProps<typeof badgeVariants> {
28
+ /**
29
+ * Visual style. `default` is filled brand colour, `secondary` is a muted
30
+ * neutral, `outline` is bordered with no fill, `destructive` flags an
31
+ * error or warning state.
32
+ * @default "default"
33
+ */
34
+ variant?: "default" | "secondary" | "destructive" | "outline";
35
+ /** Badge label or any nested elements. */
36
+ children?: React.ReactNode;
37
+ className?: string;
38
+ }
39
+
40
+ function Badge({ className, variant, ...props }: BadgeProps) {
41
+ return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
42
+ }
43
+
44
+ export { Badge, badgeVariants };
@@ -0,0 +1,74 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ import { cn } from "../../lib/utils";
6
+
7
+ export interface BrandMarkProps extends React.SVGAttributes<SVGSVGElement> {
8
+ /**
9
+ * SVG path data (`d` attribute) for the brand mark. Required — canvas
10
+ * does not ship a default brand shape; consuming apps supply their own.
11
+ */
12
+ path: string;
13
+ /**
14
+ * SVG `viewBox` matching the path's coordinate space.
15
+ * @default "0 0 24 24"
16
+ */
17
+ viewBox?: string;
18
+ /**
19
+ * Optional inner `transform` applied to the path's `<g>` group — useful
20
+ * when the path coordinates need to be translated, rotated, or scaled
21
+ * relative to the viewBox.
22
+ */
23
+ transform?: string;
24
+ /**
25
+ * Gradient start colour. When omitted, the path is filled with
26
+ * `currentColor` and no gradient is generated.
27
+ */
28
+ from?: string;
29
+ /**
30
+ * Gradient end colour. Required when `from` is set.
31
+ */
32
+ to?: string;
33
+ }
34
+
35
+ /**
36
+ * Generic brand-mark primitive. Renders a single SVG `path` either with
37
+ * `currentColor` (the default) or with a two-stop linear gradient when both
38
+ * `from` and `to` are provided. Sizing is controlled via `className`
39
+ * (`h-10 w-auto`); the SVG honours its `viewBox` aspect ratio.
40
+ *
41
+ * Apps consuming canvas typically build a thin wrapper around this component
42
+ * that hard-codes their own brand path / gradient — see the docs site's
43
+ * `OlympusLogo` for a worked example.
44
+ */
45
+ const BrandMark = React.forwardRef<SVGSVGElement, BrandMarkProps>(
46
+ ({ path, viewBox = "0 0 24 24", transform, from, to, className, ...props }, ref) => {
47
+ const gradientId = React.useId();
48
+ const useGradient = from !== undefined && to !== undefined;
49
+ const fill = useGradient ? `url(#${gradientId})` : "currentColor";
50
+ const pathEl = <path d={path} fill={fill} fillRule="evenodd" />;
51
+ return (
52
+ <svg
53
+ ref={ref}
54
+ xmlns="http://www.w3.org/2000/svg"
55
+ viewBox={viewBox}
56
+ className={cn("shrink-0", className)}
57
+ {...props}
58
+ >
59
+ {useGradient && (
60
+ <defs>
61
+ <linearGradient id={gradientId} x1="1" y1="1" x2="0" y2="0">
62
+ <stop offset="0%" stopColor={from} />
63
+ <stop offset="100%" stopColor={to} />
64
+ </linearGradient>
65
+ </defs>
66
+ )}
67
+ {transform ? <g transform={transform}>{pathEl}</g> : pathEl}
68
+ </svg>
69
+ );
70
+ },
71
+ );
72
+ BrandMark.displayName = "BrandMark";
73
+
74
+ export { BrandMark };
@@ -0,0 +1,104 @@
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import * as React from "react";
4
+
5
+ import { cn } from "../../lib/utils";
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
13
+ destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
14
+ outline:
15
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
16
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
17
+ ghost: "hover:bg-accent hover:text-accent-foreground",
18
+ link: "text-primary underline-offset-4 hover:underline",
19
+ },
20
+ size: {
21
+ default: "h-9 px-4 py-2",
22
+ sm: "h-8 rounded-md px-3 text-xs",
23
+ lg: "h-10 rounded-md px-8",
24
+ icon: "h-9 w-9",
25
+ },
26
+ },
27
+ defaultVariants: {
28
+ variant: "default",
29
+ size: "default",
30
+ },
31
+ },
32
+ );
33
+
34
+ export interface ButtonProps
35
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
36
+ VariantProps<typeof buttonVariants> {
37
+ /**
38
+ * Visual emphasis preset. `default` is the primary action, `destructive`
39
+ * is for irreversible actions, `outline` and `secondary` are quieter,
40
+ * `ghost` is borderless, `link` looks like body-text underlined.
41
+ * @default "default"
42
+ */
43
+ variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
44
+ /**
45
+ * Height + horizontal padding preset. `sm` is 32px, `default` is 36px,
46
+ * `lg` is 40px, `icon` is square 36×36 for icon-only buttons (always
47
+ * pair with `aria-label`).
48
+ * @default "default"
49
+ */
50
+ size?: "default" | "sm" | "lg" | "icon";
51
+ /**
52
+ * Render as a Radix Slot — forwards className and behaviour onto the
53
+ * immediate child element instead of rendering a wrapper `<button>`.
54
+ * Common pattern for using a router `<Link>` while keeping the Button
55
+ * styling.
56
+ * @default false
57
+ */
58
+ asChild?: boolean;
59
+ /** Visible label or any nested elements (icons, badges). */
60
+ children?: React.ReactNode;
61
+ /** Tailwind / CSS classes merged onto the root element via `cn()`. */
62
+ className?: string;
63
+ /**
64
+ * When true, dims the button to 50% opacity and blocks pointer/keyboard
65
+ * input.
66
+ * @default false
67
+ */
68
+ disabled?: boolean;
69
+ /**
70
+ * Native HTML button type. `submit` triggers parent form submission;
71
+ * `reset` resets parent form fields; `button` does neither.
72
+ * @default "button"
73
+ */
74
+ type?: "button" | "submit" | "reset";
75
+ /** Click handler. Receives a `MouseEvent<HTMLButtonElement>`. */
76
+ onClick?: React.MouseEventHandler<HTMLButtonElement>;
77
+ /** Form name when this button submits/resets a form (for forms with multiple buttons). */
78
+ name?: string;
79
+ /** Form value sent with the form when this button is the submitter. */
80
+ value?: string | number | readonly string[];
81
+ /**
82
+ * ID of the form this button belongs to, when the button sits outside
83
+ * the `<form>` element.
84
+ */
85
+ form?: string;
86
+ /** Tab order. Default behaviour is one of native `<button>` tab focus. */
87
+ tabIndex?: number;
88
+ /** Accessible label, required for icon-only buttons (`size="icon"`). */
89
+ "aria-label"?: string;
90
+ /** ID of an element that describes the button's purpose. */
91
+ "aria-describedby"?: string;
92
+ }
93
+
94
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
95
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
96
+ const Comp = asChild ? Slot : "button";
97
+ return (
98
+ <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
99
+ );
100
+ },
101
+ );
102
+ Button.displayName = "Button";
103
+
104
+ export { Button, buttonVariants };
@@ -0,0 +1,63 @@
1
+ "use client";
2
+
3
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
4
+ import { Check } from "lucide-react";
5
+ import * as React from "react";
6
+
7
+ import { cn } from "../../lib/utils";
8
+
9
+ export interface CheckboxProps
10
+ extends React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> {
11
+ /**
12
+ * Controlled checked state. Pass `'indeterminate'` to render the dash
13
+ * glyph instead of the check.
14
+ */
15
+ checked?: boolean | "indeterminate";
16
+ /**
17
+ * Initial state for uncontrolled usage.
18
+ * @default false
19
+ */
20
+ defaultChecked?: boolean | "indeterminate";
21
+ /** Fires when the user toggles the checkbox. Use this instead of `onChange`. */
22
+ onCheckedChange?: (checked: boolean | "indeterminate") => void;
23
+ /**
24
+ * Dims to 50% and blocks input.
25
+ * @default false
26
+ */
27
+ disabled?: boolean;
28
+ /**
29
+ * Block native form submit unless the box is checked.
30
+ * @default false
31
+ */
32
+ required?: boolean;
33
+ /** Form field name for native form submission. */
34
+ name?: string;
35
+ /** Form field value when submitted. */
36
+ value?: string;
37
+ asChild?: boolean;
38
+ className?: string;
39
+ }
40
+
41
+ /**
42
+ * Wraps Radix's `Checkbox.Root`. The check glyph is rendered inside
43
+ * automatically.
44
+ */
45
+ const Checkbox = React.forwardRef<React.ElementRef<typeof CheckboxPrimitive.Root>, CheckboxProps>(
46
+ ({ className, ...props }, ref) => (
47
+ <CheckboxPrimitive.Root
48
+ ref={ref}
49
+ className={cn(
50
+ "grid place-content-center peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
51
+ className,
52
+ )}
53
+ {...props}
54
+ >
55
+ <CheckboxPrimitive.Indicator className={cn("grid place-content-center text-current")}>
56
+ <Check className="h-4 w-4" />
57
+ </CheckboxPrimitive.Indicator>
58
+ </CheckboxPrimitive.Root>
59
+ ),
60
+ );
61
+ Checkbox.displayName = CheckboxPrimitive.Root.displayName;
62
+
63
+ export { Checkbox };