@optilogic/core 1.4.0 → 1.6.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.
package/dist/styles.css CHANGED
@@ -46,6 +46,13 @@
46
46
  --toggle-track-foreground: 0 0% 100%;
47
47
  --input-hover: 234 86% 67%;
48
48
  --hover: 243 82% 20% / 0.18;
49
+ /* Hover/brand tokens — fall back to base tokens when a theme omits them.
50
+ * applyTheme() overrides these per-theme; these are the pre-JS defaults. */
51
+ --primary-hover: var(--primary);
52
+ --accent-hover: var(--accent);
53
+ --accent-active: var(--accent);
54
+ --brand-from: var(--primary);
55
+ --brand-to: var(--chart-2);
49
56
  --divider: 0 0% 82%;
50
57
  --chip: 227 88% 85%;
51
58
  --chip-foreground: 243 82% 20%;
@@ -95,6 +102,11 @@
95
102
  --toggle-track-foreground: 0 0% 98%;
96
103
  --input-hover: 217 91% 60%;
97
104
  --hover: 217 91% 60% / 0.18;
105
+ --primary-hover: var(--primary);
106
+ --accent-hover: var(--accent);
107
+ --accent-active: var(--accent);
108
+ --brand-from: var(--primary);
109
+ --brand-to: var(--chart-2);
98
110
  --divider: 240 4% 26%;
99
111
  --chip: 240 4% 16%;
100
112
  --chip-foreground: 240 5% 83%;
@@ -24,7 +24,8 @@ var optiUiPreset = {
24
24
  // Primary
25
25
  primary: {
26
26
  DEFAULT: "hsl(var(--primary))",
27
- foreground: "hsl(var(--primary-foreground))"
27
+ foreground: "hsl(var(--primary-foreground))",
28
+ hover: "hsl(var(--primary-hover))"
28
29
  },
29
30
  // Secondary
30
31
  secondary: {
@@ -39,7 +40,14 @@ var optiUiPreset = {
39
40
  // Accent
40
41
  accent: {
41
42
  DEFAULT: "hsl(var(--accent))",
42
- foreground: "hsl(var(--accent-foreground))"
43
+ foreground: "hsl(var(--accent-foreground))",
44
+ hover: "hsl(var(--accent-hover))",
45
+ active: "hsl(var(--accent-active))"
46
+ },
47
+ // Brand identity gradient stops
48
+ brand: {
49
+ from: "hsl(var(--brand-from))",
50
+ to: "hsl(var(--brand-to))"
43
51
  },
44
52
  // Universal theme-aware hover tint. The --hover variable holds HSL
45
53
  // channels with alpha (e.g. "243 82% 20% / 0.18"), so themes can
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tailwind-preset.ts"],"names":[],"mappings":"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","file":"tailwind-preset.cjs","sourcesContent":["import type { Config } from \"tailwindcss\";\n\n/**\n * opti-ui Tailwind CSS preset\n *\n * This preset provides the theme configuration required for opti-ui components.\n * Consumers should extend their Tailwind config with this preset.\n *\n * @example\n * // tailwind.config.js\n * import { optiUiPreset } from '@optilogic/core/tailwind-preset';\n *\n * export default {\n * presets: [optiUiPreset],\n * content: [\n * './src/**\\/*.{js,ts,jsx,tsx}',\n * './node_modules/@optilogic/core/dist/**\\/*.{js,mjs}'\n * ]\n * }\n */\nexport const optiUiPreset: Partial<Config> = {\n darkMode: [\"class\"],\n theme: {\n extend: {\n colors: {\n // Background colors\n background: \"hsl(var(--background))\",\n foreground: \"hsl(var(--foreground))\",\n\n // Card\n card: {\n DEFAULT: \"hsl(var(--card))\",\n foreground: \"hsl(var(--card-foreground))\",\n },\n\n // Popover\n popover: {\n DEFAULT: \"hsl(var(--popover))\",\n foreground: \"hsl(var(--popover-foreground))\",\n },\n\n // Primary\n primary: {\n DEFAULT: \"hsl(var(--primary))\",\n foreground: \"hsl(var(--primary-foreground))\",\n },\n\n // Secondary\n secondary: {\n DEFAULT: \"hsl(var(--secondary))\",\n foreground: \"hsl(var(--secondary-foreground))\",\n },\n\n // Muted\n muted: {\n DEFAULT: \"hsl(var(--muted))\",\n foreground: \"hsl(var(--muted-foreground))\",\n },\n\n // Accent\n accent: {\n DEFAULT: \"hsl(var(--accent))\",\n foreground: \"hsl(var(--accent-foreground))\",\n },\n\n // Universal theme-aware hover tint. The --hover variable holds HSL\n // channels with alpha (e.g. \"243 82% 20% / 0.18\"), so themes can\n // override both color and alpha. Defaults to primary @ 18% via inference\n // in applyTheme, with foreground fallback when primary lacks contrast.\n hover: \"hsl(var(--hover))\",\n\n // Destructive\n destructive: {\n DEFAULT: \"hsl(var(--destructive))\",\n foreground: \"hsl(var(--destructive-foreground))\",\n },\n\n // Success\n success: {\n DEFAULT: \"hsl(var(--success))\",\n foreground: \"hsl(var(--success-foreground))\",\n },\n\n // Warning\n warning: {\n DEFAULT: \"hsl(var(--warning))\",\n foreground: \"hsl(var(--warning-foreground))\",\n },\n\n // Border, input, ring\n border: \"hsl(var(--border))\",\n \"popover-border\": \"hsl(var(--popover-border))\",\n input: {\n DEFAULT: \"hsl(var(--input))\",\n hover: \"hsl(var(--input-hover))\",\n },\n ring: \"hsl(var(--ring))\",\n divider: \"hsl(var(--divider))\",\n\n // Toggle/switch track\n \"toggle-track\": {\n DEFAULT: \"hsl(var(--toggle-track))\",\n foreground: \"hsl(var(--toggle-track-foreground))\",\n },\n\n // Chip\n chip: {\n DEFAULT: \"hsl(var(--chip))\",\n foreground: \"hsl(var(--chip-foreground))\",\n },\n\n // Chart colors\n chart: {\n 1: \"hsl(var(--chart-1))\",\n 2: \"hsl(var(--chart-2))\",\n 3: \"hsl(var(--chart-3))\",\n 4: \"hsl(var(--chart-4))\",\n 5: \"hsl(var(--chart-5))\",\n 6: \"hsl(var(--chart-6))\",\n 7: \"hsl(var(--chart-7))\",\n 8: \"hsl(var(--chart-8))\",\n 9: \"hsl(var(--chart-9))\",\n 10: \"hsl(var(--chart-10))\",\n 11: \"hsl(var(--chart-11))\",\n 12: \"hsl(var(--chart-12))\",\n },\n },\n borderRadius: {\n lg: \"var(--radius)\",\n md: \"calc(var(--radius) - 2px)\",\n sm: \"calc(var(--radius) - 4px)\",\n },\n keyframes: {\n \"accordion-down\": {\n from: { height: \"0\" },\n to: { height: \"var(--radix-accordion-content-height)\" },\n },\n \"accordion-up\": {\n from: { height: \"var(--radix-accordion-content-height)\" },\n to: { height: \"0\" },\n },\n },\n animation: {\n \"accordion-down\": \"accordion-down 0.2s ease-out\",\n \"accordion-up\": \"accordion-up 0.2s ease-out\",\n },\n },\n },\n plugins: [],\n};\n\nexport default optiUiPreset;\n"]}
1
+ {"version":3,"sources":["../src/tailwind-preset.ts"],"names":[],"mappings":"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","file":"tailwind-preset.cjs","sourcesContent":["import type { Config } from \"tailwindcss\";\n\n/**\n * opti-ui Tailwind CSS preset\n *\n * This preset provides the theme configuration required for opti-ui components.\n * Consumers should extend their Tailwind config with this preset.\n *\n * @example\n * // tailwind.config.js\n * import { optiUiPreset } from '@optilogic/core/tailwind-preset';\n *\n * export default {\n * presets: [optiUiPreset],\n * content: [\n * './src/**\\/*.{js,ts,jsx,tsx}',\n * './node_modules/@optilogic/core/dist/**\\/*.{js,mjs}'\n * ]\n * }\n */\nexport const optiUiPreset: Partial<Config> = {\n darkMode: [\"class\"],\n theme: {\n extend: {\n colors: {\n // Background colors\n background: \"hsl(var(--background))\",\n foreground: \"hsl(var(--foreground))\",\n\n // Card\n card: {\n DEFAULT: \"hsl(var(--card))\",\n foreground: \"hsl(var(--card-foreground))\",\n },\n\n // Popover\n popover: {\n DEFAULT: \"hsl(var(--popover))\",\n foreground: \"hsl(var(--popover-foreground))\",\n },\n\n // Primary\n primary: {\n DEFAULT: \"hsl(var(--primary))\",\n foreground: \"hsl(var(--primary-foreground))\",\n hover: \"hsl(var(--primary-hover))\",\n },\n\n // Secondary\n secondary: {\n DEFAULT: \"hsl(var(--secondary))\",\n foreground: \"hsl(var(--secondary-foreground))\",\n },\n\n // Muted\n muted: {\n DEFAULT: \"hsl(var(--muted))\",\n foreground: \"hsl(var(--muted-foreground))\",\n },\n\n // Accent\n accent: {\n DEFAULT: \"hsl(var(--accent))\",\n foreground: \"hsl(var(--accent-foreground))\",\n hover: \"hsl(var(--accent-hover))\",\n active: \"hsl(var(--accent-active))\",\n },\n\n // Brand identity gradient stops\n brand: {\n from: \"hsl(var(--brand-from))\",\n to: \"hsl(var(--brand-to))\",\n },\n\n // Universal theme-aware hover tint. The --hover variable holds HSL\n // channels with alpha (e.g. \"243 82% 20% / 0.18\"), so themes can\n // override both color and alpha. Defaults to primary @ 18% via inference\n // in applyTheme, with foreground fallback when primary lacks contrast.\n hover: \"hsl(var(--hover))\",\n\n // Destructive\n destructive: {\n DEFAULT: \"hsl(var(--destructive))\",\n foreground: \"hsl(var(--destructive-foreground))\",\n },\n\n // Success\n success: {\n DEFAULT: \"hsl(var(--success))\",\n foreground: \"hsl(var(--success-foreground))\",\n },\n\n // Warning\n warning: {\n DEFAULT: \"hsl(var(--warning))\",\n foreground: \"hsl(var(--warning-foreground))\",\n },\n\n // Border, input, ring\n border: \"hsl(var(--border))\",\n \"popover-border\": \"hsl(var(--popover-border))\",\n input: {\n DEFAULT: \"hsl(var(--input))\",\n hover: \"hsl(var(--input-hover))\",\n },\n ring: \"hsl(var(--ring))\",\n divider: \"hsl(var(--divider))\",\n\n // Toggle/switch track\n \"toggle-track\": {\n DEFAULT: \"hsl(var(--toggle-track))\",\n foreground: \"hsl(var(--toggle-track-foreground))\",\n },\n\n // Chip\n chip: {\n DEFAULT: \"hsl(var(--chip))\",\n foreground: \"hsl(var(--chip-foreground))\",\n },\n\n // Chart colors\n chart: {\n 1: \"hsl(var(--chart-1))\",\n 2: \"hsl(var(--chart-2))\",\n 3: \"hsl(var(--chart-3))\",\n 4: \"hsl(var(--chart-4))\",\n 5: \"hsl(var(--chart-5))\",\n 6: \"hsl(var(--chart-6))\",\n 7: \"hsl(var(--chart-7))\",\n 8: \"hsl(var(--chart-8))\",\n 9: \"hsl(var(--chart-9))\",\n 10: \"hsl(var(--chart-10))\",\n 11: \"hsl(var(--chart-11))\",\n 12: \"hsl(var(--chart-12))\",\n },\n },\n borderRadius: {\n lg: \"var(--radius)\",\n md: \"calc(var(--radius) - 2px)\",\n sm: \"calc(var(--radius) - 4px)\",\n },\n keyframes: {\n \"accordion-down\": {\n from: { height: \"0\" },\n to: { height: \"var(--radix-accordion-content-height)\" },\n },\n \"accordion-up\": {\n from: { height: \"var(--radix-accordion-content-height)\" },\n to: { height: \"0\" },\n },\n },\n animation: {\n \"accordion-down\": \"accordion-down 0.2s ease-out\",\n \"accordion-up\": \"accordion-up 0.2s ease-out\",\n },\n },\n },\n plugins: [],\n};\n\nexport default optiUiPreset;\n"]}
@@ -20,7 +20,8 @@ var optiUiPreset = {
20
20
  // Primary
21
21
  primary: {
22
22
  DEFAULT: "hsl(var(--primary))",
23
- foreground: "hsl(var(--primary-foreground))"
23
+ foreground: "hsl(var(--primary-foreground))",
24
+ hover: "hsl(var(--primary-hover))"
24
25
  },
25
26
  // Secondary
26
27
  secondary: {
@@ -35,7 +36,14 @@ var optiUiPreset = {
35
36
  // Accent
36
37
  accent: {
37
38
  DEFAULT: "hsl(var(--accent))",
38
- foreground: "hsl(var(--accent-foreground))"
39
+ foreground: "hsl(var(--accent-foreground))",
40
+ hover: "hsl(var(--accent-hover))",
41
+ active: "hsl(var(--accent-active))"
42
+ },
43
+ // Brand identity gradient stops
44
+ brand: {
45
+ from: "hsl(var(--brand-from))",
46
+ to: "hsl(var(--brand-to))"
39
47
  },
40
48
  // Universal theme-aware hover tint. The --hover variable holds HSL
41
49
  // channels with alpha (e.g. "243 82% 20% / 0.18"), so themes can
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tailwind-preset.ts"],"names":[],"mappings":"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","file":"tailwind-preset.js","sourcesContent":["import type { Config } from \"tailwindcss\";\n\n/**\n * opti-ui Tailwind CSS preset\n *\n * This preset provides the theme configuration required for opti-ui components.\n * Consumers should extend their Tailwind config with this preset.\n *\n * @example\n * // tailwind.config.js\n * import { optiUiPreset } from '@optilogic/core/tailwind-preset';\n *\n * export default {\n * presets: [optiUiPreset],\n * content: [\n * './src/**\\/*.{js,ts,jsx,tsx}',\n * './node_modules/@optilogic/core/dist/**\\/*.{js,mjs}'\n * ]\n * }\n */\nexport const optiUiPreset: Partial<Config> = {\n darkMode: [\"class\"],\n theme: {\n extend: {\n colors: {\n // Background colors\n background: \"hsl(var(--background))\",\n foreground: \"hsl(var(--foreground))\",\n\n // Card\n card: {\n DEFAULT: \"hsl(var(--card))\",\n foreground: \"hsl(var(--card-foreground))\",\n },\n\n // Popover\n popover: {\n DEFAULT: \"hsl(var(--popover))\",\n foreground: \"hsl(var(--popover-foreground))\",\n },\n\n // Primary\n primary: {\n DEFAULT: \"hsl(var(--primary))\",\n foreground: \"hsl(var(--primary-foreground))\",\n },\n\n // Secondary\n secondary: {\n DEFAULT: \"hsl(var(--secondary))\",\n foreground: \"hsl(var(--secondary-foreground))\",\n },\n\n // Muted\n muted: {\n DEFAULT: \"hsl(var(--muted))\",\n foreground: \"hsl(var(--muted-foreground))\",\n },\n\n // Accent\n accent: {\n DEFAULT: \"hsl(var(--accent))\",\n foreground: \"hsl(var(--accent-foreground))\",\n },\n\n // Universal theme-aware hover tint. The --hover variable holds HSL\n // channels with alpha (e.g. \"243 82% 20% / 0.18\"), so themes can\n // override both color and alpha. Defaults to primary @ 18% via inference\n // in applyTheme, with foreground fallback when primary lacks contrast.\n hover: \"hsl(var(--hover))\",\n\n // Destructive\n destructive: {\n DEFAULT: \"hsl(var(--destructive))\",\n foreground: \"hsl(var(--destructive-foreground))\",\n },\n\n // Success\n success: {\n DEFAULT: \"hsl(var(--success))\",\n foreground: \"hsl(var(--success-foreground))\",\n },\n\n // Warning\n warning: {\n DEFAULT: \"hsl(var(--warning))\",\n foreground: \"hsl(var(--warning-foreground))\",\n },\n\n // Border, input, ring\n border: \"hsl(var(--border))\",\n \"popover-border\": \"hsl(var(--popover-border))\",\n input: {\n DEFAULT: \"hsl(var(--input))\",\n hover: \"hsl(var(--input-hover))\",\n },\n ring: \"hsl(var(--ring))\",\n divider: \"hsl(var(--divider))\",\n\n // Toggle/switch track\n \"toggle-track\": {\n DEFAULT: \"hsl(var(--toggle-track))\",\n foreground: \"hsl(var(--toggle-track-foreground))\",\n },\n\n // Chip\n chip: {\n DEFAULT: \"hsl(var(--chip))\",\n foreground: \"hsl(var(--chip-foreground))\",\n },\n\n // Chart colors\n chart: {\n 1: \"hsl(var(--chart-1))\",\n 2: \"hsl(var(--chart-2))\",\n 3: \"hsl(var(--chart-3))\",\n 4: \"hsl(var(--chart-4))\",\n 5: \"hsl(var(--chart-5))\",\n 6: \"hsl(var(--chart-6))\",\n 7: \"hsl(var(--chart-7))\",\n 8: \"hsl(var(--chart-8))\",\n 9: \"hsl(var(--chart-9))\",\n 10: \"hsl(var(--chart-10))\",\n 11: \"hsl(var(--chart-11))\",\n 12: \"hsl(var(--chart-12))\",\n },\n },\n borderRadius: {\n lg: \"var(--radius)\",\n md: \"calc(var(--radius) - 2px)\",\n sm: \"calc(var(--radius) - 4px)\",\n },\n keyframes: {\n \"accordion-down\": {\n from: { height: \"0\" },\n to: { height: \"var(--radix-accordion-content-height)\" },\n },\n \"accordion-up\": {\n from: { height: \"var(--radix-accordion-content-height)\" },\n to: { height: \"0\" },\n },\n },\n animation: {\n \"accordion-down\": \"accordion-down 0.2s ease-out\",\n \"accordion-up\": \"accordion-up 0.2s ease-out\",\n },\n },\n },\n plugins: [],\n};\n\nexport default optiUiPreset;\n"]}
1
+ {"version":3,"sources":["../src/tailwind-preset.ts"],"names":[],"mappings":"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","file":"tailwind-preset.js","sourcesContent":["import type { Config } from \"tailwindcss\";\n\n/**\n * opti-ui Tailwind CSS preset\n *\n * This preset provides the theme configuration required for opti-ui components.\n * Consumers should extend their Tailwind config with this preset.\n *\n * @example\n * // tailwind.config.js\n * import { optiUiPreset } from '@optilogic/core/tailwind-preset';\n *\n * export default {\n * presets: [optiUiPreset],\n * content: [\n * './src/**\\/*.{js,ts,jsx,tsx}',\n * './node_modules/@optilogic/core/dist/**\\/*.{js,mjs}'\n * ]\n * }\n */\nexport const optiUiPreset: Partial<Config> = {\n darkMode: [\"class\"],\n theme: {\n extend: {\n colors: {\n // Background colors\n background: \"hsl(var(--background))\",\n foreground: \"hsl(var(--foreground))\",\n\n // Card\n card: {\n DEFAULT: \"hsl(var(--card))\",\n foreground: \"hsl(var(--card-foreground))\",\n },\n\n // Popover\n popover: {\n DEFAULT: \"hsl(var(--popover))\",\n foreground: \"hsl(var(--popover-foreground))\",\n },\n\n // Primary\n primary: {\n DEFAULT: \"hsl(var(--primary))\",\n foreground: \"hsl(var(--primary-foreground))\",\n hover: \"hsl(var(--primary-hover))\",\n },\n\n // Secondary\n secondary: {\n DEFAULT: \"hsl(var(--secondary))\",\n foreground: \"hsl(var(--secondary-foreground))\",\n },\n\n // Muted\n muted: {\n DEFAULT: \"hsl(var(--muted))\",\n foreground: \"hsl(var(--muted-foreground))\",\n },\n\n // Accent\n accent: {\n DEFAULT: \"hsl(var(--accent))\",\n foreground: \"hsl(var(--accent-foreground))\",\n hover: \"hsl(var(--accent-hover))\",\n active: \"hsl(var(--accent-active))\",\n },\n\n // Brand identity gradient stops\n brand: {\n from: \"hsl(var(--brand-from))\",\n to: \"hsl(var(--brand-to))\",\n },\n\n // Universal theme-aware hover tint. The --hover variable holds HSL\n // channels with alpha (e.g. \"243 82% 20% / 0.18\"), so themes can\n // override both color and alpha. Defaults to primary @ 18% via inference\n // in applyTheme, with foreground fallback when primary lacks contrast.\n hover: \"hsl(var(--hover))\",\n\n // Destructive\n destructive: {\n DEFAULT: \"hsl(var(--destructive))\",\n foreground: \"hsl(var(--destructive-foreground))\",\n },\n\n // Success\n success: {\n DEFAULT: \"hsl(var(--success))\",\n foreground: \"hsl(var(--success-foreground))\",\n },\n\n // Warning\n warning: {\n DEFAULT: \"hsl(var(--warning))\",\n foreground: \"hsl(var(--warning-foreground))\",\n },\n\n // Border, input, ring\n border: \"hsl(var(--border))\",\n \"popover-border\": \"hsl(var(--popover-border))\",\n input: {\n DEFAULT: \"hsl(var(--input))\",\n hover: \"hsl(var(--input-hover))\",\n },\n ring: \"hsl(var(--ring))\",\n divider: \"hsl(var(--divider))\",\n\n // Toggle/switch track\n \"toggle-track\": {\n DEFAULT: \"hsl(var(--toggle-track))\",\n foreground: \"hsl(var(--toggle-track-foreground))\",\n },\n\n // Chip\n chip: {\n DEFAULT: \"hsl(var(--chip))\",\n foreground: \"hsl(var(--chip-foreground))\",\n },\n\n // Chart colors\n chart: {\n 1: \"hsl(var(--chart-1))\",\n 2: \"hsl(var(--chart-2))\",\n 3: \"hsl(var(--chart-3))\",\n 4: \"hsl(var(--chart-4))\",\n 5: \"hsl(var(--chart-5))\",\n 6: \"hsl(var(--chart-6))\",\n 7: \"hsl(var(--chart-7))\",\n 8: \"hsl(var(--chart-8))\",\n 9: \"hsl(var(--chart-9))\",\n 10: \"hsl(var(--chart-10))\",\n 11: \"hsl(var(--chart-11))\",\n 12: \"hsl(var(--chart-12))\",\n },\n },\n borderRadius: {\n lg: \"var(--radius)\",\n md: \"calc(var(--radius) - 2px)\",\n sm: \"calc(var(--radius) - 4px)\",\n },\n keyframes: {\n \"accordion-down\": {\n from: { height: \"0\" },\n to: { height: \"var(--radix-accordion-content-height)\" },\n },\n \"accordion-up\": {\n from: { height: \"var(--radix-accordion-content-height)\" },\n to: { height: \"0\" },\n },\n },\n animation: {\n \"accordion-down\": \"accordion-down 0.2s ease-out\",\n \"accordion-up\": \"accordion-up 0.2s ease-out\",\n },\n },\n },\n plugins: [],\n};\n\nexport default optiUiPreset;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optilogic/core",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "Core UI components for Optilogic - A professional React component library",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -126,7 +126,7 @@
126
126
  "build": "tsup && cp src/styles.css dist/styles.css",
127
127
  "dev": "tsup --watch",
128
128
  "typecheck": "tsc --noEmit",
129
- "lint": "eslint src --ext .ts,.tsx",
129
+ "lint": "eslint src",
130
130
  "clean": "rm -rf dist .turbo"
131
131
  }
132
132
  }
@@ -11,6 +11,35 @@ import {
11
11
  AlertDialogTitle,
12
12
  } from "./alert-dialog";
13
13
 
14
+ // On touch devices (no hover), AlertDialog's centered card competes with
15
+ // thumb reach and the action buttons are too small. Under
16
+ // `@media (pointer: coarse) and (hover: none)` we re-anchor the dialog to
17
+ // the bottom edge of the screen, full-width, with safe-area-aware padding
18
+ // and a stacked, taller button row.
19
+ const mobileSheetContentClasses = [
20
+ "[@media(pointer:coarse)and(hover:none)]:!top-auto",
21
+ "[@media(pointer:coarse)and(hover:none)]:!bottom-0",
22
+ "[@media(pointer:coarse)and(hover:none)]:!left-0",
23
+ "[@media(pointer:coarse)and(hover:none)]:!translate-x-0",
24
+ "[@media(pointer:coarse)and(hover:none)]:!translate-y-0",
25
+ "[@media(pointer:coarse)and(hover:none)]:!w-screen",
26
+ "[@media(pointer:coarse)and(hover:none)]:!max-w-none",
27
+ "[@media(pointer:coarse)and(hover:none)]:!rounded-t-xl",
28
+ "[@media(pointer:coarse)and(hover:none)]:!rounded-b-none",
29
+ "[@media(pointer:coarse)and(hover:none)]:!pb-[calc(1.5rem+env(safe-area-inset-bottom))]",
30
+ ].join(" ");
31
+
32
+ const mobileSheetFooterClasses = [
33
+ // Stack buttons full-width with comfortable tap targets on touch devices.
34
+ "[@media(pointer:coarse)and(hover:none)]:!flex-col",
35
+ "[@media(pointer:coarse)and(hover:none)]:!gap-2",
36
+ ].join(" ");
37
+
38
+ const mobileSheetActionClasses = [
39
+ "[@media(pointer:coarse)and(hover:none)]:!w-full",
40
+ "[@media(pointer:coarse)and(hover:none)]:!min-h-11",
41
+ ].join(" ");
42
+
14
43
  export interface ConfirmationModalProps {
15
44
  /** Whether the modal is open */
16
45
  open: boolean;
@@ -71,22 +100,28 @@ export function ConfirmationModal({
71
100
 
72
101
  return (
73
102
  <AlertDialog open={open} onOpenChange={onOpenChange}>
74
- <AlertDialogContent>
103
+ <AlertDialogContent className={mobileSheetContentClasses}>
75
104
  <AlertDialogHeader>
76
105
  <AlertDialogTitle>{title}</AlertDialogTitle>
77
106
  <AlertDialogDescription>{description}</AlertDialogDescription>
78
107
  </AlertDialogHeader>
79
- <AlertDialogFooter>
80
- <AlertDialogCancel onClick={handleCancel}>
108
+ <AlertDialogFooter className={mobileSheetFooterClasses}>
109
+ <AlertDialogCancel
110
+ onClick={handleCancel}
111
+ className={mobileSheetActionClasses}
112
+ >
81
113
  {cancelLabel}
82
114
  </AlertDialogCancel>
83
115
  <AlertDialogAction
84
116
  onClick={handleConfirm}
85
- className={
117
+ className={[
86
118
  destructive
87
119
  ? "bg-destructive text-destructive-foreground hover:bg-destructive/90"
88
- : undefined
89
- }
120
+ : "",
121
+ mobileSheetActionClasses,
122
+ ]
123
+ .filter(Boolean)
124
+ .join(" ")}
90
125
  >
91
126
  {confirmLabel}
92
127
  </AlertDialogAction>
@@ -174,7 +174,10 @@ export function CellEditor<T = Record<string, CellValue>>({
174
174
  );
175
175
 
176
176
  const renderDateEditor = () => {
177
- const dateValue = React.useMemo(() => {
177
+ // Plain computation (not useMemo): this runs inside a conditional render
178
+ // helper, so a hook here would violate the rules of hooks, and the parse is
179
+ // cheap enough that memoization buys nothing.
180
+ const dateValue = ((): Date | undefined => {
178
181
  if (!currentValue) return undefined;
179
182
  if (currentValue instanceof Date) return currentValue;
180
183
  if (typeof currentValue === "string") {
@@ -182,7 +185,7 @@ export function CellEditor<T = Record<string, CellValue>>({
182
185
  return isValid(parsed) ? parsed : undefined;
183
186
  }
184
187
  return undefined;
185
- }, [currentValue]);
188
+ })();
186
189
 
187
190
  const handleDateChange = (date: Date | undefined) => {
188
191
  const stringValue = date ? format(date, "yyyy-MM-dd") : "";
@@ -296,7 +296,10 @@ export function FilterPopover<T = Record<string, CellValue>>({
296
296
  );
297
297
 
298
298
  const renderDateFilter = () => {
299
- const dateValue = React.useMemo(() => {
299
+ // Plain computations (not useMemo): this runs inside a conditional render
300
+ // helper, so hooks here would violate the rules of hooks, and these date
301
+ // parses are cheap enough that memoization buys nothing.
302
+ const dateValue = ((): Date | undefined => {
300
303
  if (!value) return undefined;
301
304
  if (value instanceof Date) return value;
302
305
  if (typeof value === "string") {
@@ -304,9 +307,9 @@ export function FilterPopover<T = Record<string, CellValue>>({
304
307
  return isValid(parsed) ? parsed : undefined;
305
308
  }
306
309
  return undefined;
307
- }, [value]);
310
+ })();
308
311
 
309
- const dateToValue = React.useMemo(() => {
312
+ const dateToValue = ((): Date | undefined => {
310
313
  if (!valueTo) return undefined;
311
314
  if (valueTo instanceof Date) return valueTo;
312
315
  if (typeof valueTo === "string") {
@@ -314,7 +317,7 @@ export function FilterPopover<T = Record<string, CellValue>>({
314
317
  return isValid(parsed) ? parsed : undefined;
315
318
  }
316
319
  return undefined;
317
- }, [valueTo]);
320
+ })();
318
321
 
319
322
  const handleDateChange = (date: Date | undefined) => {
320
323
  setValue(date ? format(date, "yyyy-MM-dd") : "");
@@ -230,10 +230,11 @@ export function applyFilterOperator(
230
230
  return numValue < numFilterValue;
231
231
  case "lte":
232
232
  return numValue <= numFilterValue;
233
- case "between":
233
+ case "between": {
234
234
  const singleFilterValueTo = extractSingleValue(filterValueTo);
235
235
  const numFilterValueTo = Number(singleFilterValueTo);
236
236
  return numValue >= numFilterValue && numValue <= numFilterValueTo;
237
+ }
237
238
  default:
238
239
  return true;
239
240
  }
@@ -53,6 +53,22 @@ export interface ModalProps {
53
53
  * `"w-[80vw] max-w-none"`.
54
54
  */
55
55
  contentClassName?: string;
56
+
57
+ /**
58
+ * Opt into mobile-friendly rendering. When `true`, the modal renders full-
59
+ * screen (no border, no rounded corners, fills the viewport) under
60
+ * `@media (pointer: coarse) and (hover: none)` — i.e. on touch devices
61
+ * without hover. On desktop / hybrid input devices the named `size` is
62
+ * preserved. Default: `false`.
63
+ */
64
+ responsive?: boolean;
65
+
66
+ /**
67
+ * Force mobile (full-screen) rendering regardless of device. Useful for
68
+ * Storybook, manual QA, and the rare case where sheet-style rendering is
69
+ * desired on desktop. Overrides `responsive`. Default: `false`.
70
+ */
71
+ forceMobile?: boolean;
56
72
  }
57
73
 
58
74
  /**
@@ -85,6 +101,18 @@ export interface ModalProps {
85
101
  * >
86
102
  * ...
87
103
  * </Modal>
104
+ *
105
+ * @example
106
+ * // Full-screen on mobile (touch devices), `lg` width on desktop
107
+ * <Modal
108
+ * isOpen={open}
109
+ * onClose={() => setOpen(false)}
110
+ * title="New Database"
111
+ * size="lg"
112
+ * responsive
113
+ * >
114
+ * ...
115
+ * </Modal>
88
116
  */
89
117
  export function Modal({
90
118
  isOpen,
@@ -96,6 +124,8 @@ export function Modal({
96
124
  zIndex = 50,
97
125
  className,
98
126
  contentClassName,
127
+ responsive = false,
128
+ forceMobile = false,
99
129
  }: ModalProps) {
100
130
  React.useEffect(() => {
101
131
  if (!isOpen) return;
@@ -133,9 +163,31 @@ export function Modal({
133
163
  full: "max-w-[95vw]",
134
164
  };
135
165
 
166
+ // Mobile rendering: full-screen, no chrome. Applied via media query when
167
+ // `responsive` is on, or unconditionally when `forceMobile` is on. The `!`
168
+ // important modifier ensures the mobile values win over the base utilities
169
+ // (`p-4`, `rounded-lg`, `border`, `max-w-*`, `max-h-[90vh]`) regardless of
170
+ // class ordering in the generated stylesheet.
171
+ const responsiveOuter =
172
+ "[@media(pointer:coarse)and(hover:none)]:!p-0";
173
+ const responsiveFrame =
174
+ "[@media(pointer:coarse)and(hover:none)]:!w-screen " +
175
+ "[@media(pointer:coarse)and(hover:none)]:!h-[100dvh] " +
176
+ "[@media(pointer:coarse)and(hover:none)]:!max-h-[100dvh] " +
177
+ "[@media(pointer:coarse)and(hover:none)]:!max-w-none " +
178
+ "[@media(pointer:coarse)and(hover:none)]:!rounded-none " +
179
+ "[@media(pointer:coarse)and(hover:none)]:!border-0";
180
+ const forcedOuter = "!p-0";
181
+ const forcedFrame =
182
+ "!w-screen !h-[100dvh] !max-h-[100dvh] !max-w-none !rounded-none !border-0";
183
+
136
184
  return (
137
185
  <div
138
- className="fixed inset-0 flex items-center justify-center p-4"
186
+ className={cn(
187
+ "fixed inset-0 flex items-center justify-center p-4",
188
+ responsive && responsiveOuter,
189
+ forceMobile && forcedOuter
190
+ )}
139
191
  style={{ zIndex }}
140
192
  onClick={onClose}
141
193
  >
@@ -148,6 +200,8 @@ export function Modal({
148
200
  "flex flex-col",
149
201
  "max-h-[90vh]",
150
202
  sizeClasses[size],
203
+ responsive && responsiveFrame,
204
+ forceMobile && forcedFrame,
151
205
  contentClassName
152
206
  )}
153
207
  onClick={(e) => e.stopPropagation()}
@@ -170,7 +170,7 @@ export function ResizablePanel({
170
170
 
171
171
  const clampWidth = React.useCallback(
172
172
  (width: number): number => {
173
- let effectiveMinWidth = minWidthVW;
173
+ const effectiveMinWidth = minWidthVW;
174
174
  let effectiveMaxWidth = maxWidthVW;
175
175
 
176
176
  if (orientation === "right" && leftWidthVW !== undefined && !isOverlay) {
@@ -164,17 +164,19 @@ export function ThemePicker({
164
164
 
165
165
  switch (e.key) {
166
166
  case "ArrowRight":
167
- case "ArrowDown":
167
+ case "ArrowDown": {
168
168
  e.preventDefault();
169
169
  const nextIndex = (currentIndex + 1) % themes.length;
170
170
  handleThemeSelect(themes[nextIndex]);
171
171
  break;
172
+ }
172
173
  case "ArrowLeft":
173
- case "ArrowUp":
174
+ case "ArrowUp": {
174
175
  e.preventDefault();
175
176
  const prevIndex = currentIndex <= 0 ? themes.length - 1 : currentIndex - 1;
176
177
  handleThemeSelect(themes[prevIndex]);
177
178
  break;
179
+ }
178
180
  }
179
181
  };
180
182
 
package/src/index.ts CHANGED
@@ -396,14 +396,17 @@ export {
396
396
  type ThemeHSL,
397
397
  type ColorFieldConfig,
398
398
  // Presets
399
+ OPTILOGIC_LIGHT_THEME,
399
400
  OPTILOGIC_LEGACY_THEME,
400
401
  OPTILOGIC_DARK_THEME,
401
402
  MODERN_LIGHT_THEME,
402
403
  MINIMALIST_LIGHT_THEME,
403
404
  MODERN_DARK_THEME,
404
405
  DARK_ELEGANT_THEME,
406
+ GREEN_THEME,
405
407
  PRESET_THEMES,
406
408
  ALL_THEMES,
409
+ LEGACY_THEME_ID_MAP,
407
410
  getPresetTheme,
408
411
  getDefaultTheme,
409
412
  isPresetTheme,
package/src/styles.css CHANGED
@@ -46,6 +46,13 @@
46
46
  --toggle-track-foreground: 0 0% 100%;
47
47
  --input-hover: 234 86% 67%;
48
48
  --hover: 243 82% 20% / 0.18;
49
+ /* Hover/brand tokens — fall back to base tokens when a theme omits them.
50
+ * applyTheme() overrides these per-theme; these are the pre-JS defaults. */
51
+ --primary-hover: var(--primary);
52
+ --accent-hover: var(--accent);
53
+ --accent-active: var(--accent);
54
+ --brand-from: var(--primary);
55
+ --brand-to: var(--chart-2);
49
56
  --divider: 0 0% 82%;
50
57
  --chip: 227 88% 85%;
51
58
  --chip-foreground: 243 82% 20%;
@@ -95,6 +102,11 @@
95
102
  --toggle-track-foreground: 0 0% 98%;
96
103
  --input-hover: 217 91% 60%;
97
104
  --hover: 217 91% 60% / 0.18;
105
+ --primary-hover: var(--primary);
106
+ --accent-hover: var(--accent);
107
+ --accent-active: var(--accent);
108
+ --brand-from: var(--primary);
109
+ --brand-to: var(--chart-2);
98
110
  --divider: 240 4% 26%;
99
111
  --chip: 240 4% 16%;
100
112
  --chip-foreground: 240 5% 83%;
@@ -43,6 +43,7 @@ export const optiUiPreset: Partial<Config> = {
43
43
  primary: {
44
44
  DEFAULT: "hsl(var(--primary))",
45
45
  foreground: "hsl(var(--primary-foreground))",
46
+ hover: "hsl(var(--primary-hover))",
46
47
  },
47
48
 
48
49
  // Secondary
@@ -61,6 +62,14 @@ export const optiUiPreset: Partial<Config> = {
61
62
  accent: {
62
63
  DEFAULT: "hsl(var(--accent))",
63
64
  foreground: "hsl(var(--accent-foreground))",
65
+ hover: "hsl(var(--accent-hover))",
66
+ active: "hsl(var(--accent-active))",
67
+ },
68
+
69
+ // Brand identity gradient stops
70
+ brand: {
71
+ from: "hsl(var(--brand-from))",
72
+ to: "hsl(var(--brand-to))",
64
73
  },
65
74
 
66
75
  // Universal theme-aware hover tint. The --hover variable holds HSL
@@ -9,14 +9,17 @@ export type { Theme, ThemeHex, ThemeHSL, ColorFieldConfig } from "./types";
9
9
 
10
10
  // Presets
11
11
  export {
12
+ OPTILOGIC_LIGHT_THEME,
12
13
  OPTILOGIC_LEGACY_THEME,
13
14
  OPTILOGIC_DARK_THEME,
14
15
  MODERN_LIGHT_THEME,
15
16
  MINIMALIST_LIGHT_THEME,
16
17
  MODERN_DARK_THEME,
17
18
  DARK_ELEGANT_THEME,
19
+ GREEN_THEME,
18
20
  PRESET_THEMES,
19
21
  ALL_THEMES,
22
+ LEGACY_THEME_ID_MAP,
20
23
  getPresetTheme,
21
24
  getDefaultTheme,
22
25
  isPresetTheme,