@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,309 @@
1
+ /**
2
+ * Theme Utilities
3
+ *
4
+ * Functions for theme conversion and application.
5
+ * Ported from platform-leapfrog for consistent theming.
6
+ */
7
+
8
+ import type { Theme, ThemeHSL } from "./types";
9
+ import { getDefaultTheme } from "./presets";
10
+
11
+ // Re-export for convenience
12
+ export type { Theme };
13
+
14
+ /**
15
+ * Convert hex color to HSL format for CSS variables
16
+ */
17
+ export function hexToHsl(hex: string): string {
18
+ // Remove # if present
19
+ hex = hex.replace(/^#/, "");
20
+
21
+ // Parse hex values
22
+ const r = parseInt(hex.substring(0, 2), 16) / 255;
23
+ const g = parseInt(hex.substring(2, 4), 16) / 255;
24
+ const b = parseInt(hex.substring(4, 6), 16) / 255;
25
+
26
+ const max = Math.max(r, g, b);
27
+ const min = Math.min(r, g, b);
28
+ let h = 0;
29
+ let s = 0;
30
+ const l = (max + min) / 2;
31
+
32
+ if (max !== min) {
33
+ const d = max - min;
34
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
35
+
36
+ switch (max) {
37
+ case r:
38
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
39
+ break;
40
+ case g:
41
+ h = ((b - r) / d + 2) / 6;
42
+ break;
43
+ case b:
44
+ h = ((r - g) / d + 4) / 6;
45
+ break;
46
+ }
47
+ }
48
+
49
+ // Convert to degrees and percentages
50
+ const hDeg = Math.round(h * 360);
51
+ const sPercent = Math.round(s * 100);
52
+ const lPercent = Math.round(l * 100);
53
+
54
+ return `${hDeg} ${sPercent}% ${lPercent}%`;
55
+ }
56
+
57
+ /**
58
+ * Convert hex theme to HSL theme
59
+ */
60
+ export function themeToHsl(theme: Theme): ThemeHSL {
61
+ return {
62
+ ...theme,
63
+ background: hexToHsl(theme.background),
64
+ foreground: hexToHsl(theme.foreground),
65
+ card: hexToHsl(theme.card),
66
+ cardForeground: hexToHsl(theme.cardForeground),
67
+ popover: hexToHsl(theme.popover),
68
+ popoverForeground: hexToHsl(theme.popoverForeground),
69
+ primary: hexToHsl(theme.primary),
70
+ primaryForeground: hexToHsl(theme.primaryForeground),
71
+ accent: hexToHsl(theme.accent),
72
+ accentForeground: hexToHsl(theme.accentForeground),
73
+ secondary: hexToHsl(theme.secondary),
74
+ secondaryForeground: hexToHsl(theme.secondaryForeground),
75
+ muted: hexToHsl(theme.muted),
76
+ mutedForeground: hexToHsl(theme.mutedForeground),
77
+ destructive: hexToHsl(theme.destructive),
78
+ destructiveForeground: hexToHsl(theme.destructiveForeground),
79
+ success: hexToHsl(theme.success),
80
+ successForeground: hexToHsl(theme.successForeground),
81
+ warning: hexToHsl(theme.warning),
82
+ warningForeground: hexToHsl(theme.warningForeground),
83
+ chip: hexToHsl(theme.chip),
84
+ chipForeground: hexToHsl(theme.chipForeground),
85
+ border: hexToHsl(theme.border),
86
+ input: hexToHsl(theme.input),
87
+ ring: hexToHsl(theme.ring),
88
+ chart1: hexToHsl(theme.chart1),
89
+ chart2: hexToHsl(theme.chart2),
90
+ chart3: hexToHsl(theme.chart3),
91
+ chart4: hexToHsl(theme.chart4),
92
+ chart5: hexToHsl(theme.chart5),
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Apply a theme to the DOM
98
+ *
99
+ * @param theme - The theme to apply
100
+ * @param targetElement - Optional element to apply theme to (defaults to document.documentElement)
101
+ */
102
+ export function applyTheme(theme: Theme, targetElement?: HTMLElement): void {
103
+ const element = targetElement || document.documentElement;
104
+ const hslTheme = themeToHsl(theme);
105
+
106
+ // Set CSS variables as inline styles
107
+ element.style.setProperty("--background", hslTheme.background);
108
+ element.style.setProperty("--foreground", hslTheme.foreground);
109
+ element.style.setProperty("--card", hslTheme.card);
110
+ element.style.setProperty("--card-foreground", hslTheme.cardForeground);
111
+ element.style.setProperty("--popover", hslTheme.popover);
112
+ element.style.setProperty("--popover-foreground", hslTheme.popoverForeground);
113
+ element.style.setProperty("--primary", hslTheme.primary);
114
+ element.style.setProperty("--primary-foreground", hslTheme.primaryForeground);
115
+ element.style.setProperty("--accent", hslTheme.accent);
116
+ element.style.setProperty("--accent-foreground", hslTheme.accentForeground);
117
+ element.style.setProperty("--secondary", hslTheme.secondary);
118
+ element.style.setProperty(
119
+ "--secondary-foreground",
120
+ hslTheme.secondaryForeground
121
+ );
122
+ element.style.setProperty("--muted", hslTheme.muted);
123
+ element.style.setProperty("--muted-foreground", hslTheme.mutedForeground);
124
+ element.style.setProperty("--destructive", hslTheme.destructive);
125
+ element.style.setProperty(
126
+ "--destructive-foreground",
127
+ hslTheme.destructiveForeground
128
+ );
129
+ element.style.setProperty("--success", hslTheme.success);
130
+ element.style.setProperty("--success-foreground", hslTheme.successForeground);
131
+ element.style.setProperty("--warning", hslTheme.warning);
132
+ element.style.setProperty("--warning-foreground", hslTheme.warningForeground);
133
+ element.style.setProperty("--chip", hslTheme.chip);
134
+ element.style.setProperty("--chip-foreground", hslTheme.chipForeground);
135
+ element.style.setProperty("--border", hslTheme.border);
136
+ element.style.setProperty("--input", hslTheme.input);
137
+ element.style.setProperty("--ring", hslTheme.ring);
138
+ element.style.setProperty("--chart-1", hslTheme.chart1);
139
+ element.style.setProperty("--chart-2", hslTheme.chart2);
140
+ element.style.setProperty("--chart-3", hslTheme.chart3);
141
+ element.style.setProperty("--chart-4", hslTheme.chart4);
142
+ element.style.setProperty("--chart-5", hslTheme.chart5);
143
+
144
+ if (theme.radius) {
145
+ element.style.setProperty("--radius", theme.radius);
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Get the currently active theme (uses default)
151
+ */
152
+ export function getCurrentTheme(): Theme {
153
+ return getDefaultTheme();
154
+ }
155
+
156
+ /**
157
+ * Validate theme object structure
158
+ */
159
+ export function validateTheme(theme: unknown): theme is Theme {
160
+ if (!theme || typeof theme !== "object") return false;
161
+
162
+ const t = theme as Record<string, unknown>;
163
+
164
+ // Check required fields
165
+ const requiredFields: string[] = [
166
+ "id",
167
+ "name",
168
+ "background",
169
+ "foreground",
170
+ "card",
171
+ "cardForeground",
172
+ "popover",
173
+ "popoverForeground",
174
+ "primary",
175
+ "primaryForeground",
176
+ "accent",
177
+ "accentForeground",
178
+ "secondary",
179
+ "secondaryForeground",
180
+ "muted",
181
+ "mutedForeground",
182
+ "destructive",
183
+ "destructiveForeground",
184
+ "success",
185
+ "successForeground",
186
+ "warning",
187
+ "warningForeground",
188
+ "chip",
189
+ "chipForeground",
190
+ "border",
191
+ "input",
192
+ "ring",
193
+ "chart1",
194
+ "chart2",
195
+ "chart3",
196
+ "chart4",
197
+ "chart5",
198
+ ];
199
+
200
+ for (const field of requiredFields) {
201
+ if (!(field in t)) {
202
+ console.error(`Theme validation failed: missing field "${field}"`);
203
+ return false;
204
+ }
205
+ }
206
+
207
+ return true;
208
+ }
209
+
210
+ /**
211
+ * Clone a theme (for editing)
212
+ */
213
+ export function cloneTheme(theme: Theme, newName?: string): Theme {
214
+ return {
215
+ ...theme,
216
+ id: `custom-${Date.now()}`,
217
+ name: newName || `${theme.name} - copy`,
218
+ author: "Custom",
219
+ };
220
+ }
221
+
222
+ /**
223
+ * Deep comparison of two themes
224
+ */
225
+ export function areThemesEqual(theme1: Theme, theme2: Theme): boolean {
226
+ if (!theme1 || !theme2) return false;
227
+
228
+ if (theme1.name !== theme2.name) return false;
229
+ if (theme1.description !== theme2.description) return false;
230
+
231
+ const colorFields: (keyof Theme)[] = [
232
+ "background",
233
+ "foreground",
234
+ "card",
235
+ "cardForeground",
236
+ "popover",
237
+ "popoverForeground",
238
+ "primary",
239
+ "primaryForeground",
240
+ "accent",
241
+ "accentForeground",
242
+ "secondary",
243
+ "secondaryForeground",
244
+ "muted",
245
+ "mutedForeground",
246
+ "destructive",
247
+ "destructiveForeground",
248
+ "success",
249
+ "successForeground",
250
+ "warning",
251
+ "warningForeground",
252
+ "chip",
253
+ "chipForeground",
254
+ "border",
255
+ "input",
256
+ "ring",
257
+ "chart1",
258
+ "chart2",
259
+ "chart3",
260
+ "chart4",
261
+ "chart5",
262
+ ];
263
+
264
+ for (const field of colorFields) {
265
+ if (theme1[field] !== theme2[field]) {
266
+ return false;
267
+ }
268
+ }
269
+
270
+ return true;
271
+ }
272
+
273
+ /**
274
+ * Export theme as JSON string
275
+ */
276
+ export function exportTheme(theme: Theme): string {
277
+ return JSON.stringify(theme, null, 2);
278
+ }
279
+
280
+ /**
281
+ * Import theme from JSON string
282
+ */
283
+ export function importTheme(jsonString: string): {
284
+ success: boolean;
285
+ theme?: Theme;
286
+ error?: string;
287
+ } {
288
+ try {
289
+ const theme = JSON.parse(jsonString);
290
+
291
+ if (!validateTheme(theme)) {
292
+ return {
293
+ success: false,
294
+ error:
295
+ "Invalid theme format. Please check that all required fields are present.",
296
+ };
297
+ }
298
+
299
+ return {
300
+ success: true,
301
+ theme,
302
+ };
303
+ } catch (error) {
304
+ return {
305
+ success: false,
306
+ error: error instanceof Error ? error.message : "Failed to parse JSON",
307
+ };
308
+ }
309
+ }
@@ -0,0 +1,14 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ /**
5
+ * Utility function to merge Tailwind CSS classes with proper precedence.
6
+ * Combines clsx for conditional classes with tailwind-merge for deduplication.
7
+ *
8
+ * @example
9
+ * cn("px-4 py-2", "px-6") // => "py-2 px-6"
10
+ * cn("text-red-500", isActive && "text-blue-500") // => conditional
11
+ */
12
+ export function cn(...inputs: ClassValue[]): string {
13
+ return twMerge(clsx(inputs));
14
+ }