@optilogic/core 1.3.5 → 1.3.7
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/index.cjs +75 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +75 -46
- package/dist/index.js.map +1 -1
- package/dist/styles.css +2 -0
- package/dist/tailwind-preset.cjs +5 -0
- package/dist/tailwind-preset.cjs.map +1 -1
- package/dist/tailwind-preset.js +5 -0
- package/dist/tailwind-preset.js.map +1 -1
- package/package.json +1 -1
- package/src/components/accordion.tsx +4 -4
- package/src/components/autocomplete.tsx +4 -4
- package/src/components/button.tsx +3 -3
- package/src/components/calendar.tsx +2 -2
- package/src/components/chip.tsx +1 -2
- package/src/components/combobox.tsx +3 -3
- package/src/components/context-menu.tsx +2 -2
- package/src/components/data-grid/DataGrid.tsx +2 -2
- package/src/components/data-grid/components/HeaderCell.tsx +2 -2
- package/src/components/dropdown-menu.tsx +4 -4
- package/src/components/file-view/FileView.tsx +1 -1
- package/src/components/icon-button.tsx +9 -9
- package/src/components/multi-select.tsx +5 -5
- package/src/components/select.tsx +1 -1
- package/src/components/table.tsx +1 -1
- package/src/components/theme-picker.tsx +1 -1
- package/src/styles.css +2 -0
- package/src/tailwind-preset.ts +6 -0
- package/src/theme/types.ts +3 -0
- package/src/theme/utils.ts +30 -0
package/dist/styles.css
CHANGED
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
--toggle-track: 0 0% 82%;
|
|
46
46
|
--toggle-track-foreground: 0 0% 100%;
|
|
47
47
|
--input-hover: 234 86% 67%;
|
|
48
|
+
--hover: 243 82% 20% / 0.18;
|
|
48
49
|
--divider: 0 0% 82%;
|
|
49
50
|
--chip: 227 88% 85%;
|
|
50
51
|
--chip-foreground: 243 82% 20%;
|
|
@@ -93,6 +94,7 @@
|
|
|
93
94
|
--toggle-track: 240 4% 26%;
|
|
94
95
|
--toggle-track-foreground: 0 0% 98%;
|
|
95
96
|
--input-hover: 217 91% 60%;
|
|
97
|
+
--hover: 217 91% 60% / 0.18;
|
|
96
98
|
--divider: 240 4% 26%;
|
|
97
99
|
--chip: 240 4% 16%;
|
|
98
100
|
--chip-foreground: 240 5% 83%;
|
package/dist/tailwind-preset.cjs
CHANGED
|
@@ -41,6 +41,11 @@ var optiUiPreset = {
|
|
|
41
41
|
DEFAULT: "hsl(var(--accent))",
|
|
42
42
|
foreground: "hsl(var(--accent-foreground))"
|
|
43
43
|
},
|
|
44
|
+
// Universal theme-aware hover tint. The --hover variable holds HSL
|
|
45
|
+
// channels with alpha (e.g. "243 82% 20% / 0.18"), so themes can
|
|
46
|
+
// override both color and alpha. Defaults to primary @ 18% via inference
|
|
47
|
+
// in applyTheme, with foreground fallback when primary lacks contrast.
|
|
48
|
+
hover: "hsl(var(--hover))",
|
|
44
49
|
// Destructive
|
|
45
50
|
destructive: {
|
|
46
51
|
DEFAULT: "hsl(var(--destructive))",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tailwind-preset.ts"],"names":[],"mappings":";;;;;AAoBO,IAAM,YAAA,GAAgC;AAAA,EAC3C,QAAA,EAAU,CAAC,OAAO,CAAA;AAAA,EAClB,KAAA,EAAO;AAAA,IACL,MAAA,EAAQ;AAAA,MACN,MAAA,EAAQ;AAAA;AAAA,QAEN,UAAA,EAAY,wBAAA;AAAA,QACZ,UAAA,EAAY,wBAAA;AAAA;AAAA,QAGZ,IAAA,EAAM;AAAA,UACJ,OAAA,EAAS,kBAAA;AAAA,UACT,UAAA,EAAY;AAAA,SACd;AAAA;AAAA,QAGA,OAAA,EAAS;AAAA,UACP,OAAA,EAAS,qBAAA;AAAA,UACT,UAAA,EAAY;AAAA,SACd;AAAA;AAAA,QAGA,OAAA,EAAS;AAAA,UACP,OAAA,EAAS,qBAAA;AAAA,UACT,UAAA,EAAY;AAAA,SACd;AAAA;AAAA,QAGA,SAAA,EAAW;AAAA,UACT,OAAA,EAAS,uBAAA;AAAA,UACT,UAAA,EAAY;AAAA,SACd;AAAA;AAAA,QAGA,KAAA,EAAO;AAAA,UACL,OAAA,EAAS,mBAAA;AAAA,UACT,UAAA,EAAY;AAAA,SACd;AAAA;AAAA,QAGA,MAAA,EAAQ;AAAA,UACN,OAAA,EAAS,oBAAA;AAAA,UACT,UAAA,EAAY;AAAA,SACd;AAAA;AAAA,
|
|
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"]}
|
package/dist/tailwind-preset.js
CHANGED
|
@@ -37,6 +37,11 @@ var optiUiPreset = {
|
|
|
37
37
|
DEFAULT: "hsl(var(--accent))",
|
|
38
38
|
foreground: "hsl(var(--accent-foreground))"
|
|
39
39
|
},
|
|
40
|
+
// Universal theme-aware hover tint. The --hover variable holds HSL
|
|
41
|
+
// channels with alpha (e.g. "243 82% 20% / 0.18"), so themes can
|
|
42
|
+
// override both color and alpha. Defaults to primary @ 18% via inference
|
|
43
|
+
// in applyTheme, with foreground fallback when primary lacks contrast.
|
|
44
|
+
hover: "hsl(var(--hover))",
|
|
40
45
|
// Destructive
|
|
41
46
|
destructive: {
|
|
42
47
|
DEFAULT: "hsl(var(--destructive))",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tailwind-preset.ts"],"names":[],"mappings":";AAoBO,IAAM,YAAA,GAAgC;AAAA,EAC3C,QAAA,EAAU,CAAC,OAAO,CAAA;AAAA,EAClB,KAAA,EAAO;AAAA,IACL,MAAA,EAAQ;AAAA,MACN,MAAA,EAAQ;AAAA;AAAA,QAEN,UAAA,EAAY,wBAAA;AAAA,QACZ,UAAA,EAAY,wBAAA;AAAA;AAAA,QAGZ,IAAA,EAAM;AAAA,UACJ,OAAA,EAAS,kBAAA;AAAA,UACT,UAAA,EAAY;AAAA,SACd;AAAA;AAAA,QAGA,OAAA,EAAS;AAAA,UACP,OAAA,EAAS,qBAAA;AAAA,UACT,UAAA,EAAY;AAAA,SACd;AAAA;AAAA,QAGA,OAAA,EAAS;AAAA,UACP,OAAA,EAAS,qBAAA;AAAA,UACT,UAAA,EAAY;AAAA,SACd;AAAA;AAAA,QAGA,SAAA,EAAW;AAAA,UACT,OAAA,EAAS,uBAAA;AAAA,UACT,UAAA,EAAY;AAAA,SACd;AAAA;AAAA,QAGA,KAAA,EAAO;AAAA,UACL,OAAA,EAAS,mBAAA;AAAA,UACT,UAAA,EAAY;AAAA,SACd;AAAA;AAAA,QAGA,MAAA,EAAQ;AAAA,UACN,OAAA,EAAS,oBAAA;AAAA,UACT,UAAA,EAAY;AAAA,SACd;AAAA;AAAA,
|
|
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"]}
|
package/package.json
CHANGED
|
@@ -37,10 +37,10 @@ const accordionTriggerVariants = cva(
|
|
|
37
37
|
variants: {
|
|
38
38
|
variant: {
|
|
39
39
|
default: "hover:text-foreground/80",
|
|
40
|
-
bordered: "px-4 hover:bg-
|
|
41
|
-
card: "px-4 hover:bg-
|
|
42
|
-
filled: "px-4 hover:bg-
|
|
43
|
-
ghost: "px-2 rounded-md hover:bg-
|
|
40
|
+
bordered: "px-4 hover:bg-hover",
|
|
41
|
+
card: "px-4 hover:bg-hover",
|
|
42
|
+
filled: "px-4 hover:bg-hover",
|
|
43
|
+
ghost: "px-2 rounded-md hover:bg-hover",
|
|
44
44
|
},
|
|
45
45
|
},
|
|
46
46
|
defaultVariants: {
|
|
@@ -156,8 +156,8 @@ export function Autocomplete({
|
|
|
156
156
|
onClick={() => handleSelect(option.value)}
|
|
157
157
|
className={cn(
|
|
158
158
|
"relative flex w-full cursor-pointer select-none items-start gap-2 rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
159
|
-
"hover:bg-
|
|
160
|
-
"focus:bg-
|
|
159
|
+
"hover:bg-hover",
|
|
160
|
+
"focus:bg-hover",
|
|
161
161
|
option.disabled && "pointer-events-none opacity-50",
|
|
162
162
|
value === option.value && "bg-accent/50"
|
|
163
163
|
)}
|
|
@@ -203,7 +203,7 @@ export function Autocomplete({
|
|
|
203
203
|
role="button"
|
|
204
204
|
tabIndex={-1}
|
|
205
205
|
onClick={handleClear}
|
|
206
|
-
className="rounded-sm hover:bg-
|
|
206
|
+
className="rounded-sm hover:bg-hover p-0.5"
|
|
207
207
|
>
|
|
208
208
|
<X className="h-3.5 w-3.5 text-muted-foreground" />
|
|
209
209
|
</span>
|
|
@@ -232,7 +232,7 @@ export function Autocomplete({
|
|
|
232
232
|
<button
|
|
233
233
|
type="button"
|
|
234
234
|
onClick={() => setSearch("")}
|
|
235
|
-
className="p-1 hover:bg-
|
|
235
|
+
className="p-1 hover:bg-hover rounded-sm"
|
|
236
236
|
>
|
|
237
237
|
<X className="h-3.5 w-3.5 text-muted-foreground" />
|
|
238
238
|
</button>
|
|
@@ -14,17 +14,17 @@ const buttonVariants = cva(
|
|
|
14
14
|
variants: {
|
|
15
15
|
variant: {
|
|
16
16
|
default:
|
|
17
|
-
"bg-muted text-foreground hover:bg-
|
|
17
|
+
"bg-muted text-foreground hover:bg-hover disabled:opacity-50 disabled:text-muted-foreground",
|
|
18
18
|
primary:
|
|
19
19
|
"bg-accent text-accent-foreground shadow hover:shadow-md disabled:opacity-50",
|
|
20
20
|
destructive:
|
|
21
21
|
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90 hover:shadow-md disabled:opacity-50",
|
|
22
22
|
outline:
|
|
23
|
-
"border border-input bg-background shadow-sm hover:bg-
|
|
23
|
+
"border border-input bg-background shadow-sm hover:bg-hover disabled:opacity-50 disabled:bg-muted/20 disabled:text-muted-foreground",
|
|
24
24
|
secondary:
|
|
25
25
|
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80 disabled:opacity-50",
|
|
26
26
|
ghost:
|
|
27
|
-
"text-foreground hover:bg-
|
|
27
|
+
"text-foreground hover:bg-hover disabled:text-muted-foreground disabled:bg-muted/30 disabled:opacity-70",
|
|
28
28
|
link: "text-primary underline-offset-4 hover:underline disabled:opacity-50 disabled:text-muted-foreground",
|
|
29
29
|
},
|
|
30
30
|
size: {
|
|
@@ -190,7 +190,7 @@ function Calendar({
|
|
|
190
190
|
}}
|
|
191
191
|
>
|
|
192
192
|
<SelectTrigger
|
|
193
|
-
className="h-7 w-[110px] text-sm font-medium border-none shadow-none hover:bg-
|
|
193
|
+
className="h-7 w-[110px] text-sm font-medium border-none shadow-none hover:bg-hover focus:ring-0 px-2"
|
|
194
194
|
aria-label="Select month"
|
|
195
195
|
>
|
|
196
196
|
<SelectValue>{MONTHS[month]}</SelectValue>
|
|
@@ -213,7 +213,7 @@ function Calendar({
|
|
|
213
213
|
}}
|
|
214
214
|
>
|
|
215
215
|
<SelectTrigger
|
|
216
|
-
className="h-7 w-[70px] text-sm font-medium border-none shadow-none hover:bg-
|
|
216
|
+
className="h-7 w-[70px] text-sm font-medium border-none shadow-none hover:bg-hover focus:ring-0 px-2"
|
|
217
217
|
aria-label="Select year"
|
|
218
218
|
>
|
|
219
219
|
<SelectValue>{year}</SelectValue>
|
package/src/components/chip.tsx
CHANGED
|
@@ -225,8 +225,8 @@ export function Combobox({
|
|
|
225
225
|
onClick={() => handleSelect(option.value)}
|
|
226
226
|
className={cn(
|
|
227
227
|
"relative flex w-full cursor-pointer select-none items-start gap-2 rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
228
|
-
"hover:bg-
|
|
229
|
-
"focus:bg-
|
|
228
|
+
"hover:bg-hover",
|
|
229
|
+
"focus:bg-hover",
|
|
230
230
|
option.disabled && "pointer-events-none opacity-50",
|
|
231
231
|
value === option.value && "bg-accent/50"
|
|
232
232
|
)}
|
|
@@ -282,7 +282,7 @@ export function Combobox({
|
|
|
282
282
|
tabIndex={-1}
|
|
283
283
|
onMouseDown={(e) => e.preventDefault()}
|
|
284
284
|
onClick={handleClear}
|
|
285
|
-
className="rounded-sm hover:bg-
|
|
285
|
+
className="rounded-sm hover:bg-hover p-0.5"
|
|
286
286
|
>
|
|
287
287
|
<X className="h-3.5 w-3.5 text-muted-foreground" />
|
|
288
288
|
</span>
|
|
@@ -183,10 +183,10 @@ function MenuItem({ item, onClose, depth = 0 }: MenuItemProps) {
|
|
|
183
183
|
className={cn(
|
|
184
184
|
"w-full flex items-center gap-3 px-3 py-2 text-sm rounded-sm",
|
|
185
185
|
"transition-colors text-left",
|
|
186
|
-
"focus:outline-none focus:bg-
|
|
186
|
+
"focus:outline-none focus:bg-hover",
|
|
187
187
|
item.disabled
|
|
188
188
|
? "opacity-50 cursor-not-allowed text-muted-foreground"
|
|
189
|
-
: "hover:bg-
|
|
189
|
+
: "hover:bg-hover cursor-pointer",
|
|
190
190
|
item.destructive &&
|
|
191
191
|
!item.disabled &&
|
|
192
192
|
"text-destructive hover:text-destructive"
|
|
@@ -671,7 +671,7 @@ export function DataGrid<T = Record<string, CellValue>>({
|
|
|
671
671
|
"absolute top-0 left-0 transition-colors border-b border-border",
|
|
672
672
|
onRowClick && "cursor-pointer",
|
|
673
673
|
isSelected && "bg-accent/20",
|
|
674
|
-
!isSelected && "hover:bg-
|
|
674
|
+
!isSelected && "hover:bg-hover",
|
|
675
675
|
rowClassName && rowClassName(row, virtualRow.index)
|
|
676
676
|
)}
|
|
677
677
|
style={{
|
|
@@ -899,7 +899,7 @@ export function DataGrid<T = Record<string, CellValue>>({
|
|
|
899
899
|
"flex border-b border-border transition-colors",
|
|
900
900
|
onRowClick && "cursor-pointer",
|
|
901
901
|
isSelected && "bg-accent/20",
|
|
902
|
-
!isSelected && "hover:bg-
|
|
902
|
+
!isSelected && "hover:bg-hover",
|
|
903
903
|
rowClassName && rowClassName(row, rowIndex)
|
|
904
904
|
)}
|
|
905
905
|
style={{ width: tableWidth ? `${tableWidth}px` : "100%" }}
|
|
@@ -115,7 +115,7 @@ export function HeaderCell<T = any>({
|
|
|
115
115
|
className={cn(
|
|
116
116
|
"flex items-center gap-1 px-3 py-2 h-full",
|
|
117
117
|
isResizable && "pr-6", // Extra padding to create space for resize handle
|
|
118
|
-
isSortable && "cursor-pointer hover:bg-
|
|
118
|
+
isSortable && "cursor-pointer hover:bg-hover",
|
|
119
119
|
column.align === "center" && "justify-center",
|
|
120
120
|
column.align === "right" && "justify-end"
|
|
121
121
|
)}
|
|
@@ -186,7 +186,7 @@ export function HeaderCell<T = any>({
|
|
|
186
186
|
className={cn(
|
|
187
187
|
"absolute top-0 right-0 w-4 h-full cursor-col-resize z-10",
|
|
188
188
|
"flex items-center justify-center",
|
|
189
|
-
"hover:bg-
|
|
189
|
+
"hover:bg-hover transition-colors",
|
|
190
190
|
"group",
|
|
191
191
|
isResizing && "bg-accent"
|
|
192
192
|
)}
|
|
@@ -29,7 +29,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
|
|
29
29
|
ref={ref}
|
|
30
30
|
className={cn(
|
|
31
31
|
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
32
|
-
"focus:bg-
|
|
32
|
+
"focus:bg-hover",
|
|
33
33
|
"data-[state=open]:bg-accent",
|
|
34
34
|
inset && "pl-8",
|
|
35
35
|
className
|
|
@@ -101,7 +101,7 @@ const DropdownMenuItem = React.forwardRef<
|
|
|
101
101
|
ref={ref}
|
|
102
102
|
className={cn(
|
|
103
103
|
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors",
|
|
104
|
-
"focus:bg-
|
|
104
|
+
"focus:bg-hover",
|
|
105
105
|
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
106
106
|
inset && "pl-8",
|
|
107
107
|
destructive && "text-destructive focus:text-destructive",
|
|
@@ -120,7 +120,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
|
|
120
120
|
ref={ref}
|
|
121
121
|
className={cn(
|
|
122
122
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors",
|
|
123
|
-
"focus:bg-
|
|
123
|
+
"focus:bg-hover",
|
|
124
124
|
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
125
125
|
className
|
|
126
126
|
)}
|
|
@@ -146,7 +146,7 @@ const DropdownMenuRadioItem = React.forwardRef<
|
|
|
146
146
|
ref={ref}
|
|
147
147
|
className={cn(
|
|
148
148
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors",
|
|
149
|
-
"focus:bg-
|
|
149
|
+
"focus:bg-hover",
|
|
150
150
|
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
151
151
|
className
|
|
152
152
|
)}
|
|
@@ -33,7 +33,7 @@ function DefaultErrorState({ error }: { error: FileViewError }) {
|
|
|
33
33
|
onClick={error.onRetry}
|
|
34
34
|
className={cn(
|
|
35
35
|
"rounded-md border border-border bg-background px-3 py-1.5 text-sm text-foreground",
|
|
36
|
-
"transition-colors hover:bg-
|
|
36
|
+
"transition-colors hover:bg-hover",
|
|
37
37
|
)}
|
|
38
38
|
>
|
|
39
39
|
Retry
|
|
@@ -29,41 +29,41 @@ const iconButtonVariants = cva(
|
|
|
29
29
|
variants: {
|
|
30
30
|
variant: {
|
|
31
31
|
/**
|
|
32
|
-
* Default - subtle
|
|
32
|
+
* Default - subtle hover tint
|
|
33
33
|
*/
|
|
34
34
|
default: [
|
|
35
35
|
"bg-transparent text-muted-foreground",
|
|
36
|
-
"hover:bg-
|
|
36
|
+
"hover:bg-hover",
|
|
37
37
|
"border border-transparent",
|
|
38
38
|
],
|
|
39
39
|
/**
|
|
40
|
-
* Ghost - no background,
|
|
40
|
+
* Ghost - no background, subtle hover tint
|
|
41
41
|
*/
|
|
42
42
|
ghost: [
|
|
43
43
|
"bg-transparent text-muted-foreground",
|
|
44
|
-
"hover:bg-
|
|
44
|
+
"hover:bg-hover",
|
|
45
45
|
],
|
|
46
46
|
/**
|
|
47
|
-
* Outline -
|
|
47
|
+
* Outline - bordered, subtle hover tint
|
|
48
48
|
*/
|
|
49
49
|
outline: [
|
|
50
50
|
"bg-transparent text-muted-foreground",
|
|
51
51
|
"border border-border",
|
|
52
|
-
"hover:bg-
|
|
52
|
+
"hover:bg-hover",
|
|
53
53
|
],
|
|
54
54
|
/**
|
|
55
|
-
* Filled - accent background
|
|
55
|
+
* Filled - accent background (branded; darkens on hover)
|
|
56
56
|
*/
|
|
57
57
|
filled: [
|
|
58
58
|
"bg-accent text-accent-foreground",
|
|
59
59
|
"hover:bg-accent/90 hover:shadow-sm",
|
|
60
60
|
],
|
|
61
61
|
/**
|
|
62
|
-
* Muted - muted background with
|
|
62
|
+
* Muted - muted background with subtle hover tint
|
|
63
63
|
*/
|
|
64
64
|
muted: [
|
|
65
65
|
"bg-muted text-muted-foreground",
|
|
66
|
-
"hover:bg-
|
|
66
|
+
"hover:bg-hover",
|
|
67
67
|
],
|
|
68
68
|
},
|
|
69
69
|
size: {
|
|
@@ -203,7 +203,7 @@ export function MultiSelect({
|
|
|
203
203
|
onClick={() => handleToggle(option.value)}
|
|
204
204
|
className={cn(
|
|
205
205
|
"relative flex w-full cursor-pointer select-none items-start gap-2 rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
206
|
-
"hover:bg-
|
|
206
|
+
"hover:bg-hover",
|
|
207
207
|
option.disabled && "pointer-events-none opacity-50"
|
|
208
208
|
)}
|
|
209
209
|
>
|
|
@@ -255,7 +255,7 @@ export function MultiSelect({
|
|
|
255
255
|
role="button"
|
|
256
256
|
tabIndex={-1}
|
|
257
257
|
onClick={(e) => handleRemove(safeValue[i]!, e)}
|
|
258
|
-
className="rounded-sm hover:bg-
|
|
258
|
+
className="rounded-sm hover:bg-hover p-0.5"
|
|
259
259
|
>
|
|
260
260
|
<X className="h-3 w-3" />
|
|
261
261
|
</span>
|
|
@@ -275,7 +275,7 @@ export function MultiSelect({
|
|
|
275
275
|
role="button"
|
|
276
276
|
tabIndex={-1}
|
|
277
277
|
onClick={handleClearAll}
|
|
278
|
-
className="rounded-sm hover:bg-
|
|
278
|
+
className="rounded-sm hover:bg-hover p-0.5"
|
|
279
279
|
>
|
|
280
280
|
<X className="h-3.5 w-3.5 text-muted-foreground" />
|
|
281
281
|
</span>
|
|
@@ -304,7 +304,7 @@ export function MultiSelect({
|
|
|
304
304
|
<button
|
|
305
305
|
type="button"
|
|
306
306
|
onClick={() => setSearch("")}
|
|
307
|
-
className="p-1 hover:bg-
|
|
307
|
+
className="p-1 hover:bg-hover rounded-sm"
|
|
308
308
|
>
|
|
309
309
|
<X className="h-3.5 w-3.5 text-muted-foreground" />
|
|
310
310
|
</button>
|
|
@@ -321,7 +321,7 @@ export function MultiSelect({
|
|
|
321
321
|
onClick={handleSelectAll}
|
|
322
322
|
className={cn(
|
|
323
323
|
"relative flex w-full cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
324
|
-
"hover:bg-
|
|
324
|
+
"hover:bg-hover"
|
|
325
325
|
)}
|
|
326
326
|
>
|
|
327
327
|
<Checkbox
|
|
@@ -119,7 +119,7 @@ const SelectItem = React.forwardRef<
|
|
|
119
119
|
<SelectPrimitive.Item
|
|
120
120
|
ref={ref}
|
|
121
121
|
className={cn(
|
|
122
|
-
"relative flex w-full cursor-default select-none items-start rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-
|
|
122
|
+
"relative flex w-full cursor-default select-none items-start rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-hover data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
123
123
|
className
|
|
124
124
|
)}
|
|
125
125
|
{...props}
|
package/src/components/table.tsx
CHANGED
|
@@ -66,7 +66,7 @@ const TableRow = React.forwardRef<HTMLTableRowElement, TableRowProps>(
|
|
|
66
66
|
<tr
|
|
67
67
|
ref={ref}
|
|
68
68
|
className={cn(
|
|
69
|
-
"border-b transition-colors hover:bg-
|
|
69
|
+
"border-b transition-colors hover:bg-hover data-[state=selected]:bg-muted",
|
|
70
70
|
className
|
|
71
71
|
)}
|
|
72
72
|
{...props}
|
|
@@ -68,7 +68,7 @@ function ThemeSwatch({ theme, isSelected, onClick }: ThemeSwatchProps) {
|
|
|
68
68
|
onClick={onClick}
|
|
69
69
|
className={cn(
|
|
70
70
|
"relative flex flex-col items-center gap-1.5 p-2 rounded-lg transition-all",
|
|
71
|
-
"hover:bg-
|
|
71
|
+
"hover:bg-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
72
72
|
isSelected && "bg-accent ring-2 ring-primary"
|
|
73
73
|
)}
|
|
74
74
|
>
|
package/src/styles.css
CHANGED
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
--toggle-track: 0 0% 82%;
|
|
46
46
|
--toggle-track-foreground: 0 0% 100%;
|
|
47
47
|
--input-hover: 234 86% 67%;
|
|
48
|
+
--hover: 243 82% 20% / 0.18;
|
|
48
49
|
--divider: 0 0% 82%;
|
|
49
50
|
--chip: 227 88% 85%;
|
|
50
51
|
--chip-foreground: 243 82% 20%;
|
|
@@ -93,6 +94,7 @@
|
|
|
93
94
|
--toggle-track: 240 4% 26%;
|
|
94
95
|
--toggle-track-foreground: 0 0% 98%;
|
|
95
96
|
--input-hover: 217 91% 60%;
|
|
97
|
+
--hover: 217 91% 60% / 0.18;
|
|
96
98
|
--divider: 240 4% 26%;
|
|
97
99
|
--chip: 240 4% 16%;
|
|
98
100
|
--chip-foreground: 240 5% 83%;
|
package/src/tailwind-preset.ts
CHANGED
|
@@ -63,6 +63,12 @@ export const optiUiPreset: Partial<Config> = {
|
|
|
63
63
|
foreground: "hsl(var(--accent-foreground))",
|
|
64
64
|
},
|
|
65
65
|
|
|
66
|
+
// Universal theme-aware hover tint. The --hover variable holds HSL
|
|
67
|
+
// channels with alpha (e.g. "243 82% 20% / 0.18"), so themes can
|
|
68
|
+
// override both color and alpha. Defaults to primary @ 18% via inference
|
|
69
|
+
// in applyTheme, with foreground fallback when primary lacks contrast.
|
|
70
|
+
hover: "hsl(var(--hover))",
|
|
71
|
+
|
|
66
72
|
// Destructive
|
|
67
73
|
destructive: {
|
|
68
74
|
DEFAULT: "hsl(var(--destructive))",
|
package/src/theme/types.ts
CHANGED
|
@@ -64,6 +64,7 @@ export interface Theme {
|
|
|
64
64
|
toggleTrack?: string; // --toggle-track (switch track bg when off; fallback: muted)
|
|
65
65
|
toggleTrackForeground?: string; // --toggle-track-foreground (switch thumb when off; fallback: background)
|
|
66
66
|
inputHover?: string; // --input-hover (form control border on hover; fallback: derived)
|
|
67
|
+
hover?: string; // --hover (subtle hover-tint base color; applied at 18% alpha; fallback: inferred from primary/foreground vs background)
|
|
67
68
|
|
|
68
69
|
/** Chart colors */
|
|
69
70
|
chart1: string; // --chart-1
|
|
@@ -131,6 +132,7 @@ export interface ThemeHSL extends Omit<Theme, keyof ThemeColorFields> {
|
|
|
131
132
|
toggleTrack?: string;
|
|
132
133
|
toggleTrackForeground?: string;
|
|
133
134
|
inputHover?: string;
|
|
135
|
+
hover?: string;
|
|
134
136
|
chart1: string;
|
|
135
137
|
chart2: string;
|
|
136
138
|
chart3: string;
|
|
@@ -176,6 +178,7 @@ type ThemeColorFields = {
|
|
|
176
178
|
toggleTrack?: string;
|
|
177
179
|
toggleTrackForeground?: string;
|
|
178
180
|
inputHover?: string;
|
|
181
|
+
hover?: string;
|
|
179
182
|
chart1: string;
|
|
180
183
|
chart2: string;
|
|
181
184
|
chart3: string;
|
package/src/theme/utils.ts
CHANGED
|
@@ -94,6 +94,7 @@ export function themeToHsl(theme: Theme): ThemeHSL {
|
|
|
94
94
|
? hexToHsl(theme.toggleTrackForeground)
|
|
95
95
|
: undefined,
|
|
96
96
|
inputHover: theme.inputHover ? hexToHsl(theme.inputHover) : undefined,
|
|
97
|
+
hover: theme.hover ? hexToHsl(theme.hover) : undefined,
|
|
97
98
|
chart1: hexToHsl(theme.chart1),
|
|
98
99
|
chart2: hexToHsl(theme.chart2),
|
|
99
100
|
chart3: hexToHsl(theme.chart3),
|
|
@@ -123,6 +124,30 @@ function deriveInputHoverHsl(hslTheme: ThemeHSL): string {
|
|
|
123
124
|
return hslTheme.foreground;
|
|
124
125
|
}
|
|
125
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Threshold below which a 12% tint of `primary` becomes too subtle to register
|
|
129
|
+
* as a hover state against the theme's background. Picked empirically: at ~2:1
|
|
130
|
+
* the tint is faintly visible; below that it disappears into the background.
|
|
131
|
+
*/
|
|
132
|
+
const HOVER_PRIMARY_MIN_CONTRAST = 2;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Pick a base color (HSL channels, no alpha) for the --hover tint by inferring
|
|
136
|
+
* what will read as a visible hover state for this specific theme.
|
|
137
|
+
*
|
|
138
|
+
* Strategy: prefer `primary` so hovers feel branded. Fall back to `foreground`
|
|
139
|
+
* when primary is too close in luminance to background — foreground is
|
|
140
|
+
* guaranteed by WCAG to contrast with background (it has to, for text to be
|
|
141
|
+
* readable), so it always produces a visible tint. Alpha is applied
|
|
142
|
+
* downstream in applyTheme so themes can override the channels independently.
|
|
143
|
+
*/
|
|
144
|
+
function deriveHoverChannels(theme: Theme, hslTheme: ThemeHSL): string {
|
|
145
|
+
const primaryBgContrast = getContrastRatio(theme.primary, theme.background);
|
|
146
|
+
return primaryBgContrast >= HOVER_PRIMARY_MIN_CONTRAST
|
|
147
|
+
? hslTheme.primary
|
|
148
|
+
: hslTheme.foreground;
|
|
149
|
+
}
|
|
150
|
+
|
|
126
151
|
/**
|
|
127
152
|
* Apply a theme to the DOM
|
|
128
153
|
*
|
|
@@ -189,6 +214,10 @@ export function applyTheme(theme: Theme, targetElement?: HTMLElement): void {
|
|
|
189
214
|
"--input-hover",
|
|
190
215
|
hslTheme.inputHover ?? deriveInputHoverHsl(hslTheme)
|
|
191
216
|
);
|
|
217
|
+
element.style.setProperty(
|
|
218
|
+
"--hover",
|
|
219
|
+
`${hslTheme.hover ?? deriveHoverChannels(theme, hslTheme)} / 0.18`
|
|
220
|
+
);
|
|
192
221
|
|
|
193
222
|
element.style.setProperty("--chart-1", hslTheme.chart1);
|
|
194
223
|
element.style.setProperty("--chart-2", hslTheme.chart2);
|
|
@@ -332,6 +361,7 @@ export function areThemesEqual(theme1: Theme, theme2: Theme): boolean {
|
|
|
332
361
|
"toggleTrack",
|
|
333
362
|
"toggleTrackForeground",
|
|
334
363
|
"inputHover",
|
|
364
|
+
"hover",
|
|
335
365
|
"chart1",
|
|
336
366
|
"chart2",
|
|
337
367
|
"chart3",
|