@svgrid/enterprise 2.0.1 → 2.0.3
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/cdn/svgrid-enterprise.svelte-external.js +1855 -2461
- package/dist/node/studio.js +20 -616
- package/package.json +3 -3
- package/src/SvGridEditPanel.svelte +1 -0
- package/src/studio/themes.ts +55 -172
package/src/studio/themes.ts
CHANGED
|
@@ -1,172 +1,55 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Studio look-and-feel themes:
|
|
3
|
-
* design-system presets (shadcn/ui, Tailwind,
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
export const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
{ id: 'sap', name: 'SAP Fiori', radius: 6, font: '"72", "72full", "Arial", sans-serif',
|
|
57
|
-
light: { bg: '#f5f6f7', fg: '#131e29', muted: '#556b82', border: '#e5e5e5', headerBg: '#ffffff', headerFg: '#131e29', accent: '#0070f2', rowAlt: '#f5f6f7', rowHover: '#eaecee', selectionBg: '#ebf8ff' },
|
|
58
|
-
dark: { bg: '#12171c', fg: '#f5f6f7', muted: '#8396a8', border: '#2e3742', headerBg: '#1d232a', headerFg: '#f5f6f7', accent: '#4db1ff', rowAlt: '#12171c', rowHover: '#222b35', selectionBg: '#1d2d3e' } },
|
|
59
|
-
{ id: 'salesforce', name: 'Salesforce', radius: 4, font: '"Salesforce Sans", "SF Pro", Helvetica, Arial, sans-serif',
|
|
60
|
-
light: { bg: '#ffffff', fg: '#181818', muted: '#757575', border: '#c9c9c9', headerBg: '#f3f3f3', headerFg: '#181818', accent: '#0176d3', rowAlt: '#ffffff', rowHover: '#f3f3f3', selectionBg: '#ecebea' },
|
|
61
|
-
dark: { bg: '#181818', fg: '#ffffff', muted: '#c9c9c9', border: '#3e3e3c', headerBg: '#2e2e2e', headerFg: '#ffffff', accent: '#1b96ff', rowAlt: '#181818', rowHover: '#2e2e2e', selectionBg: '#1e4d8e' } },
|
|
62
|
-
{ id: 'atlassian', name: 'Atlassian', radius: 3, font: '"Charlie Display", "Atlassian Sans", -apple-system, "Segoe UI", sans-serif',
|
|
63
|
-
light: { bg: '#ffffff', fg: '#172b4d', muted: '#44546f', border: '#dcdfe4', headerBg: '#fafbfc', headerFg: '#44546f', accent: '#0c66e4', rowAlt: '#ffffff', rowHover: '#f1f2f4', selectionBg: '#e9f2ff' },
|
|
64
|
-
dark: { bg: '#1d2125', fg: '#b6c2cf', muted: '#8590a2', border: '#38414a', headerBg: '#22272b', headerFg: '#8590a2', accent: '#579dff', rowAlt: '#1d2125', rowHover: '#2c333a', selectionBg: '#09326c' } },
|
|
65
|
-
{ id: 'github', name: 'GitHub', radius: 6, font: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif',
|
|
66
|
-
light: { bg: '#ffffff', fg: '#1f2328', muted: '#59636e', border: '#d1d9e0', headerBg: '#f6f8fa', headerFg: '#59636e', accent: '#0969da', rowAlt: '#ffffff', rowHover: '#f6f8fa', selectionBg: '#ddf4ff' },
|
|
67
|
-
dark: { bg: '#0d1117', fg: '#f0f6fc', muted: '#9198a1', border: '#3d444d', headerBg: '#151b23', headerFg: '#9198a1', accent: '#4493f8', rowAlt: '#0d1117', rowHover: '#151b23', selectionBg: 'rgba(56, 139, 253, 0.10)' } },
|
|
68
|
-
{ id: 'antd', name: 'Ant Design', radius: 6, font: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", sans-serif',
|
|
69
|
-
light: { bg: '#ffffff', fg: 'rgba(0, 0, 0, 0.88)', muted: 'rgba(0, 0, 0, 0.65)', border: '#d9d9d9', headerBg: '#fafafa', headerFg: 'rgba(0, 0, 0, 0.88)', accent: '#1677ff', rowAlt: '#ffffff', rowHover: '#f5f5f5', selectionBg: '#e6f4ff' },
|
|
70
|
-
dark: { bg: '#141414', fg: 'rgba(255, 255, 255, 0.85)', muted: 'rgba(255, 255, 255, 0.65)', border: '#424242', headerBg: '#1f1f1f', headerFg: 'rgba(255, 255, 255, 0.85)', accent: '#177ddc', rowAlt: '#141414', rowHover: '#262626', selectionBg: '#111a2c' } },
|
|
71
|
-
{ id: 'ag-alpine', name: 'Alpine', radius: 3, font: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
72
|
-
light: { bg: '#ffffff', fg: '#181d1f', muted: 'rgba(24, 29, 31, 0.5)', border: '#babfc7', headerBg: '#f8f8f8', headerFg: '#181d1f', accent: '#2196f3', rowAlt: '#fcfcfc', rowHover: 'rgba(33, 150, 243, 0.1)', selectionBg: 'rgba(33, 150, 243, 0.3)' },
|
|
73
|
-
dark: { bg: '#181d1f', fg: '#ffffff', muted: 'rgba(255, 255, 255, 0.5)', border: '#68686e', headerBg: '#222628', headerFg: '#ffffff', accent: '#2196f3', rowAlt: '#222628', rowHover: 'rgba(33, 150, 243, 0.1)', selectionBg: 'rgba(33, 150, 243, 0.3)' } },
|
|
74
|
-
{ id: 'bootstrap', name: 'Bootstrap 5', radius: 6, font: 'system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
|
75
|
-
light: { bg: '#ffffff', fg: '#212529', muted: '#6c757d', border: '#dee2e6', headerBg: '#f8f9fa', headerFg: '#212529', accent: '#0d6efd', rowAlt: '#fcfcfd', rowHover: '#f2f2f2', selectionBg: '#cfe2ff' },
|
|
76
|
-
dark: { bg: '#212529', fg: '#dee2e6', muted: '#adb5bd', border: '#495057', headerBg: '#2b3035', headerFg: '#dee2e6', accent: '#0d6efd', rowAlt: '#25292e', rowHover: '#2c3034', selectionBg: '#031633' } },
|
|
77
|
-
{ id: 'vercel', name: 'Vercel', radius: 6, font: '"Geist", "Geist Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
78
|
-
light: { bg: '#ffffff', fg: '#000000', muted: '#666666', border: '#eaeaea', headerBg: '#fafafa', headerFg: '#666666', accent: '#0070f3', rowAlt: '#fafafa', rowHover: '#f5f5f5', selectionBg: '#e8f0fe' },
|
|
79
|
-
dark: { bg: '#000000', fg: '#ffffff', muted: '#888888', border: '#333333', headerBg: '#111111', headerFg: '#888888', accent: '#3291ff', rowAlt: '#0a0a0a', rowHover: '#1a1a1a', selectionBg: '#0d2847' } },
|
|
80
|
-
{ id: 'linear', name: 'Linear', radius: 6, font: '"Inter Variable", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
81
|
-
light: { bg: '#ffffff', fg: '#282a30', muted: '#6b6f76', border: '#e9e9eb', headerBg: '#f7f8f8', headerFg: '#6b6f76', accent: '#5e6ad2', rowAlt: '#fbfbfc', rowHover: '#f4f4f5', selectionBg: '#e8eafd' },
|
|
82
|
-
dark: { bg: '#08090a', fg: '#f7f8f8', muted: '#8a8f98', border: '#23252a', headerBg: '#0f1011', headerFg: '#8a8f98', accent: '#5e6ad2', rowAlt: '#0f1011', rowHover: '#1a1b1e', selectionBg: '#2a2d4a' } },
|
|
83
|
-
{ id: 'notion', name: 'Notion', radius: 4, font: 'ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif',
|
|
84
|
-
light: { bg: '#ffffff', fg: '#37352f', muted: '#787774', border: '#e9e9e7', headerBg: '#f7f6f3', headerFg: '#787774', accent: '#2383e2', rowAlt: '#fbfbfa', rowHover: '#f1f1ef', selectionBg: '#d9eaf7' },
|
|
85
|
-
dark: { bg: '#191919', fg: '#d4d4d4', muted: '#9b9b9b', border: '#2f2f2f', headerBg: '#202020', headerFg: '#9b9b9b', accent: '#529cca', rowAlt: '#1c1c1c', rowHover: '#252525', selectionBg: '#20415c' } },
|
|
86
|
-
{ id: 'nord', name: 'Nord', radius: 4, font: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
87
|
-
light: { bg: '#eceff4', fg: '#2e3440', muted: '#4c566a', border: '#d8dee9', headerBg: '#e5e9f0', headerFg: '#2e3440', accent: '#5e81ac', rowAlt: '#e5e9f0', rowHover: '#dde3ec', selectionBg: '#d8dee9' },
|
|
88
|
-
dark: { bg: '#2e3440', fg: '#d8dee9', muted: '#4c566a', border: '#434c5e', headerBg: '#3b4252', headerFg: '#e5e9f0', accent: '#88c0d0', rowAlt: '#333b48', rowHover: '#3b4252', selectionBg: '#434c5e' } },
|
|
89
|
-
{ id: 'dracula', name: 'Dracula', radius: 6, font: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
90
|
-
light: { bg: '#fffbeb', fg: '#1f1f1f', muted: '#6c664b', border: '#dedccf', headerBg: '#ece9df', headerFg: '#1f1f1f', accent: '#644ac9', rowAlt: '#fffdf5', rowHover: '#f2efe3', selectionBg: '#cfcfde' },
|
|
91
|
-
dark: { bg: '#282a36', fg: '#f8f8f2', muted: '#6272a4', border: '#44475a', headerBg: '#21222c', headerFg: '#f8f8f2', accent: '#bd93f9', rowAlt: '#2d2f3b', rowHover: '#343746', selectionBg: '#44475a' } },
|
|
92
|
-
{ id: 'catppuccin', name: 'Catppuccin', radius: 8, font: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
93
|
-
light: { bg: '#eff1f5', fg: '#4c4f69', muted: '#6c6f85', border: '#ccd0da', headerBg: '#e6e9ef', headerFg: '#4c4f69', accent: '#8839ef', rowAlt: '#e6e9ef', rowHover: '#dce0e8', selectionBg: '#dce0e8' },
|
|
94
|
-
dark: { bg: '#1e1e2e', fg: '#cdd6f4', muted: '#a6adc8', border: '#313244', headerBg: '#181825', headerFg: '#cdd6f4', accent: '#cba6f7', rowAlt: '#1a1a28', rowHover: '#313244', selectionBg: '#45475a' } },
|
|
95
|
-
]
|
|
96
|
-
|
|
97
|
-
/** The default theme (used when a project has no `preset`). */
|
|
98
|
-
export const defaultStudioTheme: StudioTheme = studioThemes.find((t) => t.id === 'tailwind') ?? studioThemes[0]!
|
|
99
|
-
|
|
100
|
-
/** Look up a theme by id. */
|
|
101
|
-
export const getStudioTheme = (id?: string): StudioTheme | undefined =>
|
|
102
|
-
id ? studioThemes.find((t) => t.id === id) : undefined
|
|
103
|
-
|
|
104
|
-
/** A readable (near-black / white) foreground for text placed on the accent. */
|
|
105
|
-
function onAccent(accent: string): string {
|
|
106
|
-
const m = /^#?([0-9a-f]{6})$/i.exec(accent.trim())
|
|
107
|
-
if (!m) return '#ffffff'
|
|
108
|
-
const n = parseInt(m[1]!, 16)
|
|
109
|
-
const r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255
|
|
110
|
-
// Perceived luminance (sRGB, quick approximation).
|
|
111
|
-
const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255
|
|
112
|
-
return lum > 0.6 ? '#18181b' : '#ffffff'
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** Resolve the effective `--sg-*` tokens for a project theme: the chosen preset
|
|
116
|
-
* (default if none) in the chosen mode, with the accent override applied. */
|
|
117
|
-
export function resolveThemeTokens(theme?: ProjectTheme): Record<string, string> {
|
|
118
|
-
const preset = getStudioTheme(theme?.preset) ?? defaultStudioTheme
|
|
119
|
-
const mode: ThemeMode = theme?.mode === 'dark' ? 'dark' : 'light'
|
|
120
|
-
const p = preset[mode]
|
|
121
|
-
const accent = theme?.accent || p.accent
|
|
122
|
-
return {
|
|
123
|
-
'--sg-bg': p.bg,
|
|
124
|
-
'--sg-fg': p.fg,
|
|
125
|
-
'--sg-muted': p.muted,
|
|
126
|
-
'--sg-border': p.border,
|
|
127
|
-
'--sg-header-bg': p.headerBg,
|
|
128
|
-
'--sg-header-fg': p.headerFg,
|
|
129
|
-
// A subtle inset surface (used by e.g. the master-detail region behind the
|
|
130
|
-
// nested grid). Themed so it isn't a fixed light grey on dark presets.
|
|
131
|
-
'--sg-bg-subtle': p.headerBg,
|
|
132
|
-
'--sg-row-alt-bg': p.rowAlt,
|
|
133
|
-
'--sg-row-hover-bg': p.rowHover,
|
|
134
|
-
'--sg-selection-bg': p.selectionBg,
|
|
135
|
-
// Inputs/checkboxes: pin to the theme's own surface so they never borrow the
|
|
136
|
-
// host page's input tokens (which is how a themed grid ended up with dark
|
|
137
|
-
// checkboxes on a light page when the host mode differed).
|
|
138
|
-
'--sg-input-bg': p.bg,
|
|
139
|
-
'--sg-input-border': p.border,
|
|
140
|
-
'--sg-accent': accent,
|
|
141
|
-
'--sg-on-accent': onAccent(accent),
|
|
142
|
-
'--sg-radius': `${preset.radius}px`,
|
|
143
|
-
'--sg-font': preset.font,
|
|
144
|
-
// The grid's custom scrollbar is a shadow-DOM web component that reads its
|
|
145
|
-
// OWN --sg-scrollbar-* tokens (with fixed light-mode fallbacks). Without
|
|
146
|
-
// these it ignores the theme entirely - a light scrollbar left on a dark
|
|
147
|
-
// grid. CSS custom properties pierce shadow DOM, so defining them here (on
|
|
148
|
-
// the themed container) reaches the scrollbar. Derive them from the palette
|
|
149
|
-
// so the scrollbar tracks the preset + light/dark mode like everything else.
|
|
150
|
-
'--sg-scrollbar-bg': p.headerBg,
|
|
151
|
-
'--sg-scrollbar-border': p.border,
|
|
152
|
-
'--sg-scrollbar-thumb': p.muted,
|
|
153
|
-
'--sg-scrollbar-thumb-hover': p.fg,
|
|
154
|
-
'--sg-scrollbar-thumb-active': p.fg,
|
|
155
|
-
'--sg-scrollbar-arrow': p.muted,
|
|
156
|
-
'--sg-scrollbar-arrow-hover': p.fg,
|
|
157
|
-
'--sg-scrollbar-arrow-hover-bg': p.rowHover,
|
|
158
|
-
'--sg-scrollbar-arrow-active': p.fg,
|
|
159
|
-
'--sg-scrollbar-arrow-active-bg': p.selectionBg,
|
|
160
|
-
'--sg-scrollbar-arrow-disabled': p.border,
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/** True when the project's theme is rendering in dark mode. */
|
|
165
|
-
export const isDarkTheme = (theme?: ProjectTheme): boolean => theme?.mode === 'dark'
|
|
166
|
-
|
|
167
|
-
/** An inline `style` string (`--sg-x: y; ...; color-scheme; font-family`) for previews. */
|
|
168
|
-
export function themeStyleString(theme?: ProjectTheme): string {
|
|
169
|
-
const tokens = resolveThemeTokens(theme)
|
|
170
|
-
const vars = Object.entries(tokens).map(([k, v]) => `${k}: ${v}`).join('; ')
|
|
171
|
-
return `${vars}; color-scheme: ${isDarkTheme(theme) ? 'dark' : 'light'}; font-family: ${tokens['--sg-font']}`
|
|
172
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* The Studio look-and-feel themes: a `ProjectTheme`-aware wrapper around
|
|
3
|
+
* `@svgrid/grid`'s built-in design-system presets (shadcn/ui, Tailwind,
|
|
4
|
+
* Material 3, Excel, Fluent 2, ...) - the single source of truth for these
|
|
5
|
+
* tokens, also used directly (as plain CSS) by app code that isn't going
|
|
6
|
+
* through Studio's project model.
|
|
7
|
+
*
|
|
8
|
+
* Picking a preset (and a light/dark mode) restyles the WHOLE generated app -
|
|
9
|
+
* shell, grids, charts, KPIs, forms - not just the accent.
|
|
10
|
+
*
|
|
11
|
+
* Pure + node-safe (studio subtree): plain data + helpers, so the same tokens
|
|
12
|
+
* drive the browser preview and the emitted `+layout.svelte` / `app.css`.
|
|
13
|
+
*/
|
|
14
|
+
import {
|
|
15
|
+
themePresets,
|
|
16
|
+
defaultThemePreset,
|
|
17
|
+
getThemePreset,
|
|
18
|
+
resolveThemeTokens as resolveTokens,
|
|
19
|
+
type ThemeMode,
|
|
20
|
+
type ThemePalette,
|
|
21
|
+
type ThemePreset,
|
|
22
|
+
} from '@svgrid/grid/themes'
|
|
23
|
+
import type { ProjectTheme } from './project.js'
|
|
24
|
+
|
|
25
|
+
export type { ThemeMode, ThemePalette }
|
|
26
|
+
|
|
27
|
+
/** @deprecated Use `ThemePreset` from `@svgrid/grid/themes` - kept as an alias for the pre-existing public name. */
|
|
28
|
+
export type StudioTheme = ThemePreset
|
|
29
|
+
|
|
30
|
+
/** The built-in Studio themes, in gallery order (default first). */
|
|
31
|
+
export const studioThemes: StudioTheme[] = themePresets
|
|
32
|
+
|
|
33
|
+
/** The default theme (used when a project has no `preset`). */
|
|
34
|
+
export const defaultStudioTheme: StudioTheme = defaultThemePreset
|
|
35
|
+
|
|
36
|
+
/** Look up a theme by id. */
|
|
37
|
+
export const getStudioTheme = (id?: string): StudioTheme | undefined => getThemePreset(id)
|
|
38
|
+
|
|
39
|
+
/** True when the project's theme is rendering in dark mode. */
|
|
40
|
+
export const isDarkTheme = (theme?: ProjectTheme): boolean => theme?.mode === 'dark'
|
|
41
|
+
|
|
42
|
+
/** Resolve the effective `--sg-*` tokens for a project theme: the chosen preset
|
|
43
|
+
* (default if none) in the chosen mode, with the accent override applied. */
|
|
44
|
+
export function resolveThemeTokens(theme?: ProjectTheme): Record<string, string> {
|
|
45
|
+
const preset = getStudioTheme(theme?.preset) ?? defaultStudioTheme
|
|
46
|
+
const mode: ThemeMode = theme?.mode === 'dark' ? 'dark' : 'light'
|
|
47
|
+
return resolveTokens(preset, mode, theme?.accent)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** An inline `style` string (`--sg-x: y; ...; color-scheme; font-family`) for previews. */
|
|
51
|
+
export function themeStyleString(theme?: ProjectTheme): string {
|
|
52
|
+
const tokens = resolveThemeTokens(theme)
|
|
53
|
+
const vars = Object.entries(tokens).map(([k, v]) => `${k}: ${v}`).join('; ')
|
|
54
|
+
return `${vars}; color-scheme: ${isDarkTheme(theme) ? 'dark' : 'light'}; font-family: ${tokens['--sg-font']}`
|
|
55
|
+
}
|