@svadmin/core 0.8.0 → 0.9.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/core",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Core SDK — hooks, types, context, i18n, permissions, router",
5
5
  "type": "module",
6
6
  "sideEffects": false,
package/src/index.ts CHANGED
@@ -37,8 +37,8 @@ export { audit, setAuditHandler, setAuditLogProvider, getAuditLogProvider } from
37
37
  export type { AuditLogProvider } from './audit';
38
38
  export { setChatProvider, getChatProvider, setChatContext, getChatContext } from './chatProvider.svelte';
39
39
  export type { ChatProvider, ChatMessage, ChatContext, ChatAction } from './chatProvider.svelte';
40
- export { getTheme, setTheme, toggleTheme, getResolvedTheme, getColorTheme, setColorTheme, colorThemes } from './theme.svelte';
41
- export type { ThemeMode, ColorTheme } from './theme.svelte';
40
+ export { getTheme, setTheme, toggleTheme, getResolvedTheme, getColorTheme, setColorTheme, colorThemes, configureTheme, getThemeConfig, clearCssOverrides } from './theme.svelte';
41
+ export type { ThemeMode, ColorTheme, ThemeStrategy, ThemeConfig } from './theme.svelte';
42
42
  export { setUnsavedChanges, getUnsavedChanges, initUnsavedChangesNotifier } from './unsaved-changes.svelte';
43
43
  export { setAdminOptions, getAdminOptions, getTextTransformers } from './options';
44
44
  export type { AdminOptions, TextTransformers, OvertimeConfig } from './options';
@@ -1,11 +1,75 @@
1
1
  // Theme — dark/light/system mode + color theme management (Svelte 5 runes)
2
+ //
3
+ // Supports two class strategies:
4
+ // - 'standard' (default): adds 'dark' class for dark mode (light-first)
5
+ // - 'dark-first': adds 'light' class for light mode (dark-first, e.g. Elygate-style)
2
6
 
3
7
  export type ThemeMode = 'light' | 'dark' | 'system';
4
8
  export type ColorTheme = 'blue' | 'green' | 'rose' | 'orange' | 'violet' | 'zinc';
5
9
 
10
+ /** Controls how the theme class is applied to <html> */
11
+ export type ThemeStrategy = 'standard' | 'dark-first';
12
+
13
+ export interface ThemeConfig {
14
+ /** Class strategy: 'standard' toggles '.dark', 'dark-first' toggles '.light' */
15
+ strategy?: ThemeStrategy;
16
+ /** Custom CSS variables to inject as overrides on <html> */
17
+ cssOverrides?: Record<string, string>;
18
+ /** Whether to disable the built-in color-scheme attribute */
19
+ disableColorScheme?: boolean;
20
+ }
21
+
6
22
  const STORAGE_KEY = 'svadmin-theme';
7
23
  const COLOR_STORAGE_KEY = 'svadmin-color-theme';
8
24
 
25
+ // ── Theme configuration ──────────────────────────────────
26
+ let themeConfig: ThemeConfig = {};
27
+
28
+ /**
29
+ * Configure the theme system. Must be called before setTheme() or
30
+ * automatically via AdminApp's themeConfig prop.
31
+ */
32
+ export function configureTheme(config: ThemeConfig): void {
33
+ themeConfig = { ...config };
34
+ // Re-apply the current theme with new strategy
35
+ applyTheme(mode);
36
+ // Apply CSS overrides
37
+ if (config.cssOverrides) {
38
+ applyCssOverrides(config.cssOverrides);
39
+ }
40
+ }
41
+
42
+ /** Get current theme configuration */
43
+ export function getThemeConfig(): ThemeConfig {
44
+ return { ...themeConfig };
45
+ }
46
+
47
+ function applyCssOverrides(overrides: Record<string, string>): void {
48
+ if (typeof document === 'undefined') return;
49
+ const root = document.documentElement;
50
+ for (const [key, value] of Object.entries(overrides)) {
51
+ const cssVar = key.startsWith('--') ? key : `--${key}`;
52
+ root.style.setProperty(cssVar, value);
53
+ }
54
+ }
55
+
56
+ /** Remove previously applied CSS overrides */
57
+ export function clearCssOverrides(keys?: string[]): void {
58
+ if (typeof document === 'undefined') return;
59
+ const root = document.documentElement;
60
+ if (keys) {
61
+ for (const key of keys) {
62
+ const cssVar = key.startsWith('--') ? key : `--${key}`;
63
+ root.style.removeProperty(cssVar);
64
+ }
65
+ } else if (themeConfig.cssOverrides) {
66
+ for (const key of Object.keys(themeConfig.cssOverrides)) {
67
+ const cssVar = key.startsWith('--') ? key : `--${key}`;
68
+ root.style.removeProperty(cssVar);
69
+ }
70
+ }
71
+ }
72
+
9
73
  // ── Color themes (display metadata) ──────────────────────
10
74
  export const colorThemes: { id: ColorTheme; label: string; color: string }[] = [
11
75
  { id: 'blue', label: 'Blue', color: '#3b82f6' },
@@ -32,8 +96,24 @@ let mode = $state<ThemeMode>(getStoredTheme());
32
96
 
33
97
  function applyTheme(m: ThemeMode): void {
34
98
  const resolved = m === 'system' ? getSystemPreference() : m;
35
- if (typeof document !== 'undefined') {
36
- document.documentElement.classList.toggle('dark', resolved === 'dark');
99
+ if (typeof document === 'undefined') return;
100
+
101
+ const strategy = themeConfig.strategy ?? 'standard';
102
+ const el = document.documentElement;
103
+
104
+ if (strategy === 'dark-first') {
105
+ // Dark-first: default is dark, add 'light' class for light mode
106
+ el.classList.toggle('light', resolved === 'light');
107
+ el.classList.remove('dark'); // ensure no conflict
108
+ } else {
109
+ // Standard: default is light, add 'dark' class for dark mode
110
+ el.classList.toggle('dark', resolved === 'dark');
111
+ el.classList.remove('light'); // ensure no conflict
112
+ }
113
+
114
+ // Set color-scheme attribute unless disabled
115
+ if (!themeConfig.disableColorScheme) {
116
+ el.style.colorScheme = resolved;
37
117
  }
38
118
  }
39
119