@optilogic/core 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +107 -0
  3. package/dist/index.cjs +6003 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +2310 -0
  6. package/dist/index.d.ts +2310 -0
  7. package/dist/index.js +5828 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/styles.css +96 -0
  10. package/dist/tailwind-preset.cjs +106 -0
  11. package/dist/tailwind-preset.cjs.map +1 -0
  12. package/dist/tailwind-preset.d.cts +23 -0
  13. package/dist/tailwind-preset.d.ts +23 -0
  14. package/dist/tailwind-preset.js +101 -0
  15. package/dist/tailwind-preset.js.map +1 -0
  16. package/package.json +154 -0
  17. package/src/components/accordion.tsx +187 -0
  18. package/src/components/alert-dialog.tsx +143 -0
  19. package/src/components/autocomplete.tsx +271 -0
  20. package/src/components/badge.tsx +62 -0
  21. package/src/components/button.tsx +85 -0
  22. package/src/components/calendar.tsx +235 -0
  23. package/src/components/card.tsx +94 -0
  24. package/src/components/checkbox.tsx +77 -0
  25. package/src/components/chip.tsx +77 -0
  26. package/src/components/confirmation-modal.tsx +195 -0
  27. package/src/components/context-menu.tsx +406 -0
  28. package/src/components/copy-button.tsx +84 -0
  29. package/src/components/data-grid/DataGrid.tsx +1027 -0
  30. package/src/components/data-grid/components/CellEditor.tsx +346 -0
  31. package/src/components/data-grid/components/FilterPopover.tsx +459 -0
  32. package/src/components/data-grid/components/HeaderCell.tsx +207 -0
  33. package/src/components/data-grid/components/index.ts +14 -0
  34. package/src/components/data-grid/hooks/index.ts +28 -0
  35. package/src/components/data-grid/hooks/useColumnResize.ts +378 -0
  36. package/src/components/data-grid/hooks/useDataGridState.ts +346 -0
  37. package/src/components/data-grid/hooks/useKeyboardNavigation.ts +361 -0
  38. package/src/components/data-grid/index.ts +71 -0
  39. package/src/components/data-grid/types.ts +478 -0
  40. package/src/components/data-grid/utils/dataProcessing.ts +277 -0
  41. package/src/components/data-grid/utils/index.ts +12 -0
  42. package/src/components/date-picker.tsx +366 -0
  43. package/src/components/dropdown-menu.tsx +230 -0
  44. package/src/components/icon-button.tsx +157 -0
  45. package/src/components/input.tsx +40 -0
  46. package/src/components/label.tsx +37 -0
  47. package/src/components/loading-spinner.tsx +113 -0
  48. package/src/components/modal.tsx +207 -0
  49. package/src/components/popover.tsx +62 -0
  50. package/src/components/progress.tsx +41 -0
  51. package/src/components/resizable-panel.tsx +434 -0
  52. package/src/components/resize-handle.tsx +187 -0
  53. package/src/components/select.tsx +160 -0
  54. package/src/components/separator.tsx +50 -0
  55. package/src/components/skeleton.tsx +37 -0
  56. package/src/components/switch.tsx +59 -0
  57. package/src/components/table.tsx +136 -0
  58. package/src/components/tabs.tsx +102 -0
  59. package/src/components/textarea.tsx +36 -0
  60. package/src/components/theme-picker.tsx +245 -0
  61. package/src/components/toaster.tsx +84 -0
  62. package/src/components/tooltip.tsx +199 -0
  63. package/src/index.ts +318 -0
  64. package/src/styles.css +96 -0
  65. package/src/tailwind-preset.ts +129 -0
  66. package/src/theme/index.ts +41 -0
  67. package/src/theme/presets.ts +502 -0
  68. package/src/theme/types.ts +164 -0
  69. package/src/theme/utils.ts +309 -0
  70. package/src/utils/cn.ts +14 -0
@@ -0,0 +1,245 @@
1
+ /**
2
+ * ThemePicker Component
3
+ *
4
+ * A polished theme selection component built with library primitives.
5
+ * Features:
6
+ * - Visual theme swatches showing color palette preview
7
+ * - Popover-based selection dropdown
8
+ * - Current theme indicator
9
+ * - Keyboard accessible
10
+ * - Smooth theme transitions
11
+ */
12
+
13
+ import * as React from "react";
14
+ import { Palette, Check } from "lucide-react";
15
+ import { cn } from "../utils/cn";
16
+ import { Popover, PopoverTrigger, PopoverContent } from "./popover";
17
+ import { IconButton } from "./icon-button";
18
+ import { Button } from "./button";
19
+ import { Tooltip } from "./tooltip";
20
+ import { ALL_THEMES, applyTheme, type Theme } from "../theme";
21
+
22
+ export interface ThemePickerProps {
23
+ /** Currently selected theme (controlled) */
24
+ value?: Theme;
25
+ /** Callback when theme changes */
26
+ onValueChange?: (theme: Theme) => void;
27
+ /** Available themes to choose from */
28
+ themes?: Theme[];
29
+ /** Trigger style variant */
30
+ triggerVariant?: "icon" | "button";
31
+ /** Size of the trigger */
32
+ triggerSize?: "sm" | "default" | "lg";
33
+ /** Whether to apply the theme to the DOM when selected */
34
+ applyOnSelect?: boolean;
35
+ /** Additional class name for the trigger */
36
+ className?: string;
37
+ /** Alignment of the popover */
38
+ align?: "start" | "center" | "end";
39
+ /** Side of the popover */
40
+ side?: "top" | "right" | "bottom" | "left";
41
+ }
42
+
43
+ /**
44
+ * Theme swatch component showing a preview of theme colors
45
+ */
46
+ interface ThemeSwatchProps {
47
+ theme: Theme;
48
+ isSelected: boolean;
49
+ onClick: () => void;
50
+ }
51
+
52
+ function ThemeSwatch({ theme, isSelected, onClick }: ThemeSwatchProps) {
53
+ return (
54
+ <Tooltip
55
+ content={
56
+ <div className="space-y-1">
57
+ <p className="font-medium">{theme.name}</p>
58
+ {theme.description && (
59
+ <p className="text-xs text-muted-foreground">{theme.description}</p>
60
+ )}
61
+ </div>
62
+ }
63
+ position="bottom"
64
+ delayDuration={300}
65
+ >
66
+ <button
67
+ type="button"
68
+ onClick={onClick}
69
+ className={cn(
70
+ "relative flex flex-col items-center gap-1.5 p-2 rounded-lg transition-all",
71
+ "hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
72
+ isSelected && "bg-accent ring-2 ring-primary"
73
+ )}
74
+ >
75
+ <div
76
+ className="relative w-12 h-8 rounded-md overflow-hidden border border-border shadow-sm"
77
+ style={{ backgroundColor: theme.background }}
78
+ >
79
+ <div
80
+ className="absolute bottom-0 left-0 right-0 h-2"
81
+ style={{ backgroundColor: theme.primary }}
82
+ />
83
+ <div
84
+ className="absolute top-1 right-1 w-2 h-2 rounded-full"
85
+ style={{ backgroundColor: theme.accent }}
86
+ />
87
+ <div
88
+ className="absolute top-1 left-1 w-4 h-1 rounded-full"
89
+ style={{ backgroundColor: theme.foreground }}
90
+ />
91
+ </div>
92
+
93
+ <span className="text-xs font-medium text-foreground truncate max-w-[60px]">
94
+ {theme.name.split(" ")[0]}
95
+ </span>
96
+
97
+ {isSelected && (
98
+ <div className="absolute -top-1 -right-1 w-4 h-4 bg-primary rounded-full flex items-center justify-center">
99
+ <Check className="w-2.5 h-2.5 text-primary-foreground" />
100
+ </div>
101
+ )}
102
+ </button>
103
+ </Tooltip>
104
+ );
105
+ }
106
+
107
+ /**
108
+ * ThemePicker Component
109
+ *
110
+ * A theme selection dropdown that displays available themes as visual swatches.
111
+ *
112
+ * @example Basic usage
113
+ * ```tsx
114
+ * <ThemePicker onValueChange={(theme) => console.log(theme)} />
115
+ * ```
116
+ *
117
+ * @example Controlled with custom themes
118
+ * ```tsx
119
+ * const [theme, setTheme] = useState(OPTILOGIC_LEGACY_THEME);
120
+ * <ThemePicker
121
+ * value={theme}
122
+ * onValueChange={setTheme}
123
+ * themes={[OPTILOGIC_LEGACY_THEME, DARK_ELEGANT_THEME]}
124
+ * />
125
+ * ```
126
+ */
127
+ export function ThemePicker({
128
+ value,
129
+ onValueChange,
130
+ themes = ALL_THEMES,
131
+ triggerVariant = "icon",
132
+ triggerSize = "default",
133
+ applyOnSelect = true,
134
+ className,
135
+ align = "end",
136
+ side = "bottom",
137
+ }: ThemePickerProps) {
138
+ const [open, setOpen] = React.useState(false);
139
+ const [selectedTheme, setSelectedTheme] = React.useState<Theme | undefined>(
140
+ value || themes[0]
141
+ );
142
+
143
+ React.useEffect(() => {
144
+ if (value) {
145
+ setSelectedTheme(value);
146
+ }
147
+ }, [value]);
148
+
149
+ const handleThemeSelect = (theme: Theme) => {
150
+ setSelectedTheme(theme);
151
+
152
+ if (applyOnSelect) {
153
+ applyTheme(theme);
154
+ }
155
+
156
+ onValueChange?.(theme);
157
+ setOpen(false);
158
+ };
159
+
160
+ const handleKeyDown = (e: React.KeyboardEvent) => {
161
+ if (!open) return;
162
+
163
+ const currentIndex = themes.findIndex((t) => t.id === selectedTheme?.id);
164
+
165
+ switch (e.key) {
166
+ case "ArrowRight":
167
+ case "ArrowDown":
168
+ e.preventDefault();
169
+ const nextIndex = (currentIndex + 1) % themes.length;
170
+ handleThemeSelect(themes[nextIndex]);
171
+ break;
172
+ case "ArrowLeft":
173
+ case "ArrowUp":
174
+ e.preventDefault();
175
+ const prevIndex = currentIndex <= 0 ? themes.length - 1 : currentIndex - 1;
176
+ handleThemeSelect(themes[prevIndex]);
177
+ break;
178
+ }
179
+ };
180
+
181
+ return (
182
+ <Popover open={open} onOpenChange={setOpen}>
183
+ <PopoverTrigger asChild>
184
+ {triggerVariant === "icon" ? (
185
+ <IconButton
186
+ variant="ghost"
187
+ size={triggerSize}
188
+ aria-label="Select theme"
189
+ className={className}
190
+ >
191
+ <Palette className="w-4 h-4" />
192
+ </IconButton>
193
+ ) : (
194
+ <Button
195
+ variant="outline"
196
+ size={triggerSize === "lg" ? "default" : triggerSize}
197
+ className={cn("gap-2", className)}
198
+ >
199
+ <Palette className="w-4 h-4" />
200
+ <span>{selectedTheme?.name || "Theme"}</span>
201
+ </Button>
202
+ )}
203
+ </PopoverTrigger>
204
+
205
+ <PopoverContent
206
+ align={align}
207
+ side={side}
208
+ className="w-auto p-3"
209
+ onKeyDown={handleKeyDown}
210
+ >
211
+ <div className="space-y-3">
212
+ <div className="px-1">
213
+ <h4 className="text-sm font-medium">Select Theme</h4>
214
+ <p className="text-xs text-muted-foreground">
215
+ Choose a color theme for the interface
216
+ </p>
217
+ </div>
218
+
219
+ <div className="grid grid-cols-4 gap-1">
220
+ {themes.map((theme) => (
221
+ <ThemeSwatch
222
+ key={theme.id}
223
+ theme={theme}
224
+ isSelected={selectedTheme?.id === theme.id}
225
+ onClick={() => handleThemeSelect(theme)}
226
+ />
227
+ ))}
228
+ </div>
229
+
230
+ {selectedTheme && (
231
+ <div className="pt-2 border-t border-border">
232
+ <div className="flex items-center gap-2 px-1">
233
+ <div
234
+ className="w-4 h-4 rounded-full border border-border"
235
+ style={{ backgroundColor: selectedTheme.primary }}
236
+ />
237
+ <span className="text-sm font-medium">{selectedTheme.name}</span>
238
+ </div>
239
+ </div>
240
+ )}
241
+ </div>
242
+ </PopoverContent>
243
+ </Popover>
244
+ );
245
+ }
@@ -0,0 +1,84 @@
1
+ import { Toaster as Sonner } from "sonner";
2
+
3
+ export interface ToasterProps {
4
+ /**
5
+ * Position of the toaster
6
+ * @default "top-right"
7
+ */
8
+ position?:
9
+ | "top-left"
10
+ | "top-center"
11
+ | "top-right"
12
+ | "bottom-left"
13
+ | "bottom-center"
14
+ | "bottom-right";
15
+
16
+ /**
17
+ * Whether to expand toasts on hover
18
+ * @default false
19
+ */
20
+ expand?: boolean;
21
+
22
+ /**
23
+ * Whether to use rich colors
24
+ * @default true
25
+ */
26
+ richColors?: boolean;
27
+
28
+ /**
29
+ * Whether to show close button
30
+ * @default true
31
+ */
32
+ closeButton?: boolean;
33
+
34
+ /**
35
+ * Theme mode
36
+ * @default "system"
37
+ */
38
+ theme?: "light" | "dark" | "system";
39
+ }
40
+
41
+ /**
42
+ * Toaster component using Sonner
43
+ *
44
+ * Provides toast notifications throughout the app.
45
+ * Add this component once at the root of your app.
46
+ *
47
+ * Usage:
48
+ * import { toast } from "sonner";
49
+ * toast.success("Success!");
50
+ * toast.error("Error!");
51
+ * toast.info("Info!");
52
+ * toast.warning("Warning!");
53
+ *
54
+ * @example
55
+ * // In your app root
56
+ * <Toaster />
57
+ *
58
+ * // Then anywhere in your app
59
+ * toast.success("Saved successfully!");
60
+ */
61
+ export function Toaster({
62
+ position = "top-right",
63
+ expand = false,
64
+ richColors = true,
65
+ closeButton = true,
66
+ theme = "system",
67
+ }: ToasterProps = {}) {
68
+ return (
69
+ <Sonner
70
+ position={position}
71
+ expand={expand}
72
+ richColors={richColors}
73
+ closeButton={closeButton}
74
+ theme={theme}
75
+ toastOptions={{
76
+ style: {
77
+ background: "hsl(var(--card))",
78
+ border: "1px solid hsl(var(--border))",
79
+ color: "hsl(var(--foreground))",
80
+ },
81
+ }}
82
+ />
83
+ );
84
+ }
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Tooltip Component
3
+ *
4
+ * A compact, themeable tooltip built on Radix UI.
5
+ * Uses a portal to render at document body level, ensuring it's never clipped.
6
+ *
7
+ * Features:
8
+ * - Portal-based rendering (never clipped by containers)
9
+ * - Configurable delay duration
10
+ * - Automatic position flipping when near edges
11
+ * - Theme-aware styling
12
+ */
13
+
14
+ import * as React from "react";
15
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
16
+
17
+ import { cn } from "../utils/cn";
18
+
19
+ /**
20
+ * TooltipProvider - Wrap your app with this for shared tooltip delay behavior
21
+ */
22
+ const TooltipProvider = TooltipPrimitive.Provider;
23
+
24
+ /**
25
+ * TooltipRoot - The root component for controlled usage
26
+ */
27
+ const TooltipRoot = TooltipPrimitive.Root;
28
+
29
+ /**
30
+ * TooltipTrigger - The element that triggers the tooltip
31
+ */
32
+ const TooltipTrigger = TooltipPrimitive.Trigger;
33
+
34
+ /**
35
+ * TooltipPortal - Portals the content to document.body
36
+ */
37
+ const TooltipPortal = TooltipPrimitive.Portal;
38
+
39
+ /**
40
+ * TooltipContent - The styled tooltip content
41
+ */
42
+ const TooltipContent = React.forwardRef<
43
+ React.ElementRef<typeof TooltipPrimitive.Content>,
44
+ React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
45
+ >(({ className, sideOffset = 6, ...props }, ref) => (
46
+ <TooltipPrimitive.Portal>
47
+ <TooltipPrimitive.Content
48
+ ref={ref}
49
+ sideOffset={sideOffset}
50
+ className={cn(
51
+ // Base styles
52
+ "z-[99999] overflow-hidden",
53
+ "px-2.5 py-1.5 rounded-md",
54
+ // Colors
55
+ "bg-popover text-popover-foreground",
56
+ "border border-border",
57
+ // Typography
58
+ "text-xs font-medium",
59
+ // Shadow
60
+ "shadow-md",
61
+ // Animation
62
+ "animate-in fade-in-0 zoom-in-95",
63
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
64
+ "data-[side=bottom]:slide-in-from-top-2",
65
+ "data-[side=left]:slide-in-from-right-2",
66
+ "data-[side=right]:slide-in-from-left-2",
67
+ "data-[side=top]:slide-in-from-bottom-2",
68
+ className
69
+ )}
70
+ {...props}
71
+ />
72
+ </TooltipPrimitive.Portal>
73
+ ));
74
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
75
+
76
+ /**
77
+ * TooltipArrow - Optional arrow pointing to the trigger
78
+ */
79
+ const TooltipArrow = React.forwardRef<
80
+ React.ElementRef<typeof TooltipPrimitive.Arrow>,
81
+ React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Arrow>
82
+ >(({ className, ...props }, ref) => (
83
+ <TooltipPrimitive.Arrow
84
+ ref={ref}
85
+ className={cn("fill-popover", className)}
86
+ {...props}
87
+ />
88
+ ));
89
+ TooltipArrow.displayName = TooltipPrimitive.Arrow.displayName;
90
+
91
+ // ============================================================================
92
+ // Simple Tooltip API (backwards compatible with existing usage)
93
+ // ============================================================================
94
+
95
+ export interface TooltipProps {
96
+ /**
97
+ * Tooltip content - can be string or ReactNode for rich content
98
+ */
99
+ content: React.ReactNode;
100
+
101
+ /**
102
+ * Element to wrap with tooltip
103
+ */
104
+ children: React.ReactElement;
105
+
106
+ /**
107
+ * Position of the tooltip relative to the trigger
108
+ * @default "top"
109
+ */
110
+ position?: "top" | "bottom" | "left" | "right";
111
+
112
+ /**
113
+ * Delay before showing tooltip (in milliseconds)
114
+ * @default 200
115
+ */
116
+ delayDuration?: number;
117
+
118
+ /**
119
+ * Offset from the trigger element (in pixels)
120
+ * @default 6
121
+ */
122
+ sideOffset?: number;
123
+
124
+ /**
125
+ * Maximum width of the tooltip
126
+ * @default 250
127
+ */
128
+ maxWidth?: number;
129
+
130
+ /**
131
+ * Whether the tooltip is disabled
132
+ * @default false
133
+ */
134
+ disabled?: boolean;
135
+
136
+ /**
137
+ * Additional class names for the tooltip content
138
+ */
139
+ className?: string;
140
+ }
141
+
142
+ /**
143
+ * Tooltip component
144
+ *
145
+ * Simple wrapper around Radix Tooltip for ease of use.
146
+ * Renders in a portal so it's never clipped by containers.
147
+ *
148
+ * @example
149
+ * <Tooltip content="Hello world">
150
+ * <button>Hover me</button>
151
+ * </Tooltip>
152
+ *
153
+ * @example
154
+ * <Tooltip content="Click to save" position="bottom" delayDuration={0}>
155
+ * <button>Save</button>
156
+ * </Tooltip>
157
+ */
158
+ function Tooltip({
159
+ content,
160
+ children,
161
+ position = "top",
162
+ delayDuration = 200,
163
+ sideOffset = 6,
164
+ maxWidth = 250,
165
+ disabled = false,
166
+ className,
167
+ }: TooltipProps) {
168
+ // Don't render tooltip if no content or disabled
169
+ if (!content || disabled) {
170
+ return children;
171
+ }
172
+
173
+ return (
174
+ <TooltipPrimitive.Provider delayDuration={delayDuration}>
175
+ <TooltipPrimitive.Root>
176
+ <TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
177
+ <TooltipContent
178
+ side={position}
179
+ sideOffset={sideOffset}
180
+ className={className}
181
+ style={{ maxWidth: `${maxWidth}px` }}
182
+ >
183
+ {content}
184
+ </TooltipContent>
185
+ </TooltipPrimitive.Root>
186
+ </TooltipPrimitive.Provider>
187
+ );
188
+ }
189
+
190
+ // Export primitives for advanced usage
191
+ export {
192
+ Tooltip,
193
+ TooltipProvider,
194
+ TooltipRoot,
195
+ TooltipTrigger,
196
+ TooltipPortal,
197
+ TooltipContent,
198
+ TooltipArrow,
199
+ };