@sqlrooms/ui 0.29.0-rc.0 → 0.29.0-rc.2
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/components/accordion.d.ts +2 -2
- package/dist/components/accordion.d.ts.map +1 -1
- package/dist/components/accordion.js +3 -3
- package/dist/components/accordion.js.map +1 -1
- package/dist/components/alert.js +1 -1
- package/dist/components/alert.js.map +1 -1
- package/dist/components/card.js +1 -1
- package/dist/components/card.js.map +1 -1
- package/dist/components/editable-text.d.ts.map +1 -1
- package/dist/components/editable-text.js +34 -8
- package/dist/components/editable-text.js.map +1 -1
- package/dist/components/error-pane.d.ts.map +1 -1
- package/dist/components/error-pane.js +2 -2
- package/dist/components/error-pane.js.map +1 -1
- package/dist/components/hover-card.d.ts +7 -0
- package/dist/components/hover-card.d.ts.map +1 -0
- package/dist/components/hover-card.js +15 -0
- package/dist/components/hover-card.js.map +1 -0
- package/dist/components/progress.d.ts.map +1 -1
- package/dist/components/progress.js.map +1 -1
- package/dist/components/run-button.d.ts +19 -0
- package/dist/components/run-button.d.ts.map +1 -0
- package/dist/components/run-button.js +17 -0
- package/dist/components/run-button.js.map +1 -0
- package/dist/components/sidebar.d.ts.map +1 -1
- package/dist/components/sidebar.js +7 -4
- package/dist/components/sidebar.js.map +1 -1
- package/dist/components/sonner.d.ts +4 -0
- package/dist/components/sonner.d.ts.map +1 -0
- package/dist/components/sonner.js +37 -0
- package/dist/components/sonner.js.map +1 -0
- package/dist/hooks/use-mobile.d.ts.map +1 -1
- package/dist/hooks/use-mobile.js.map +1 -1
- package/dist/hooks/useDebounce.d.ts +33 -0
- package/dist/hooks/useDebounce.d.ts.map +1 -0
- package/dist/hooks/useDebounce.js +45 -0
- package/dist/hooks/useDebounce.js.map +1 -0
- package/dist/hooks/useDebouncedCallback.d.ts +45 -0
- package/dist/hooks/useDebouncedCallback.d.ts.map +1 -0
- package/dist/hooks/useDebouncedCallback.js +70 -0
- package/dist/hooks/useDebouncedCallback.js.map +1 -0
- package/dist/hooks/useDebouncedValue.d.ts +23 -0
- package/dist/hooks/useDebouncedValue.d.ts.map +1 -0
- package/dist/hooks/useDebouncedValue.js +40 -0
- package/dist/hooks/useDebouncedValue.js.map +1 -0
- package/dist/index.d.ts +30 -27
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -27
- package/dist/index.js.map +1 -1
- package/dist/tailwind-preset.d.ts +3 -0
- package/dist/tailwind-preset.d.ts.map +1 -0
- package/dist/tailwind-preset.js +152 -0
- package/dist/tailwind-preset.js.map +1 -0
- package/dist/theme/theme-provider.d.ts +22 -1
- package/dist/theme/theme-provider.d.ts.map +1 -1
- package/dist/theme/theme-provider.js +59 -20
- package/dist/theme/theme-provider.js.map +1 -1
- package/package.json +4 -4
- package/tailwind-preset.css +76 -0
|
@@ -1,10 +1,51 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useContext, useEffect, useLayoutEffect, useState, } from 'react';
|
|
3
|
+
export const DEFAULT_THEME = 'system';
|
|
4
|
+
export const DEFAULT_THEME_STORAGE_KEY = 'sqlrooms-ui-theme';
|
|
3
5
|
const initialState = {
|
|
4
|
-
theme:
|
|
6
|
+
theme: DEFAULT_THEME,
|
|
7
|
+
resolvedTheme: getResolvedTheme(DEFAULT_THEME),
|
|
5
8
|
setTheme: () => null,
|
|
6
9
|
};
|
|
7
10
|
const ThemeProviderContext = createContext(initialState);
|
|
11
|
+
function isTheme(value) {
|
|
12
|
+
return value === 'light' || value === 'dark' || value === 'system';
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Returns the currently stored theme preference without requiring React context.
|
|
16
|
+
* Falls back to the provided default when storage is unavailable or invalid.
|
|
17
|
+
*/
|
|
18
|
+
export function getThemePreference({ defaultTheme = DEFAULT_THEME, storageKey = DEFAULT_THEME_STORAGE_KEY, } = {}) {
|
|
19
|
+
if (typeof window === 'undefined')
|
|
20
|
+
return defaultTheme;
|
|
21
|
+
try {
|
|
22
|
+
const storedTheme = window.localStorage.getItem(storageKey);
|
|
23
|
+
return isTheme(storedTheme) ? storedTheme : defaultTheme;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return defaultTheme;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolves a theme preference to the concrete light/dark theme currently in use.
|
|
31
|
+
*/
|
|
32
|
+
export function getResolvedTheme(theme) {
|
|
33
|
+
if (theme !== 'system')
|
|
34
|
+
return theme;
|
|
35
|
+
if (typeof window === 'undefined')
|
|
36
|
+
return 'light';
|
|
37
|
+
if (typeof window.matchMedia !== 'function')
|
|
38
|
+
return 'light';
|
|
39
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
40
|
+
? 'dark'
|
|
41
|
+
: 'light';
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Returns the concrete light/dark theme currently in use.
|
|
45
|
+
*/
|
|
46
|
+
export function getTheme(options = {}) {
|
|
47
|
+
return getResolvedTheme(getThemePreference(options));
|
|
48
|
+
}
|
|
8
49
|
/**
|
|
9
50
|
* ThemeProvider component that manages and provides theme context to its children.
|
|
10
51
|
* Handles system theme detection and persistence of theme preference.
|
|
@@ -30,17 +71,9 @@ const ThemeProviderContext = createContext(initialState);
|
|
|
30
71
|
* }
|
|
31
72
|
* ```
|
|
32
73
|
*/
|
|
33
|
-
export function ThemeProvider({ children, defaultTheme =
|
|
34
|
-
const [theme, setTheme] = useState(() => {
|
|
35
|
-
|
|
36
|
-
return defaultTheme;
|
|
37
|
-
try {
|
|
38
|
-
return window.localStorage.getItem(storageKey) || defaultTheme;
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
return defaultTheme;
|
|
42
|
-
}
|
|
43
|
-
});
|
|
74
|
+
export function ThemeProvider({ children, defaultTheme = DEFAULT_THEME, storageKey = DEFAULT_THEME_STORAGE_KEY, ...props }) {
|
|
75
|
+
const [theme, setTheme] = useState(() => getThemePreference({ defaultTheme, storageKey }));
|
|
76
|
+
const [resolvedTheme, setResolvedTheme] = useState(() => getResolvedTheme(theme));
|
|
44
77
|
// Apply theme class before paint to avoid a light-theme flash on initial load.
|
|
45
78
|
// (useLayoutEffect on the client, fall back to useEffect in non-DOM environments)
|
|
46
79
|
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
|
@@ -48,19 +81,25 @@ export function ThemeProvider({ children, defaultTheme = 'system', storageKey =
|
|
|
48
81
|
if (typeof window === 'undefined')
|
|
49
82
|
return;
|
|
50
83
|
const root = window.document.documentElement;
|
|
84
|
+
const resolved = getResolvedTheme(theme);
|
|
51
85
|
root.classList.remove('light', 'dark');
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
86
|
+
root.classList.add(resolved);
|
|
87
|
+
setResolvedTheme(resolved);
|
|
88
|
+
if (theme === 'system' && typeof window.matchMedia === 'function') {
|
|
89
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
90
|
+
const listener = () => {
|
|
91
|
+
const next = getResolvedTheme(theme);
|
|
92
|
+
root.classList.remove('light', 'dark');
|
|
93
|
+
root.classList.add(next);
|
|
94
|
+
setResolvedTheme(next);
|
|
95
|
+
};
|
|
96
|
+
mediaQuery.addEventListener('change', listener);
|
|
97
|
+
return () => mediaQuery.removeEventListener('change', listener);
|
|
59
98
|
}
|
|
60
|
-
root.classList.add(theme);
|
|
61
99
|
}, [theme]);
|
|
62
100
|
const value = {
|
|
63
101
|
theme,
|
|
102
|
+
resolvedTheme,
|
|
64
103
|
setTheme: (theme) => {
|
|
65
104
|
if (typeof window !== 'undefined') {
|
|
66
105
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-provider.js","sourceRoot":"","sources":["../../src/theme/theme-provider.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,aAAa,EACb,UAAU,EACV,SAAS,EACT,eAAe,EACf,QAAQ,GACT,MAAM,OAAO,CAAC;AAgCf,MAAM,YAAY,GAAuB;IACvC,KAAK,EAAE,QAAQ;IACf,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI;CACrB,CAAC;AAEF,MAAM,oBAAoB,GAAG,aAAa,CAAqB,YAAY,CAAC,CAAC;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,QAAQ,EACR,YAAY,GAAG,QAAQ,EACvB,UAAU,GAAG,mBAAmB,EAChC,GAAG,KAAK,EACW;IACnB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAQ,GAAG,EAAE;QAC7C,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO,YAAY,CAAC;QACvD,IAAI,CAAC;YACH,OAAQ,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,UAAU,CAAW,IAAI,YAAY,CAAC;QAC5E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,YAAY,CAAC;QACtB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,+EAA+E;IAC/E,kFAAkF;IAClF,MAAM,yBAAyB,GAC7B,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC;IAE9D,yBAAyB,CAAC,GAAG,EAAE;QAC7B,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO;QAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;QAE7C,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAEvC,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;YACvB,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC;iBAClE,OAAO;gBACR,CAAC,CAAC,MAAM;gBACR,CAAC,CAAC,OAAO,CAAC;YAEZ,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAChC,OAAO;QACT,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,MAAM,KAAK,GAAG;QACZ,KAAK;QACL,QAAQ,EAAE,CAAC,KAAY,EAAE,EAAE;YACzB,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;gBAClC,IAAI,CAAC;oBACH,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBACjD,CAAC;gBAAC,MAAM,CAAC;oBACP,mDAAmD;gBACrD,CAAC;YACH,CAAC;YACD,QAAQ,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;KACF,CAAC;IAEF,OAAO,CACL,KAAC,oBAAoB,CAAC,QAAQ,OAAK,KAAK,EAAE,KAAK,EAAE,KAAK,YACnD,QAAQ,GACqB,CACjC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,EAAE;IAC3B,MAAM,OAAO,GAAG,UAAU,CAAC,oBAAoB,CAAC,CAAC;IAEjD,IAAI,OAAO,KAAK,SAAS;QACvB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAElE,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC","sourcesContent":["import {\n createContext,\n useContext,\n useEffect,\n useLayoutEffect,\n useState,\n} from 'react';\n\n/**\n * Available theme options\n * @typedef {'dark' | 'light' | 'system'} Theme\n */\ntype Theme = 'dark' | 'light' | 'system';\n\n/**\n * Props for the ThemeProvider component\n * @interface ThemeProviderProps\n * @property {React.ReactNode} children - Child components that will have access to the theme context\n * @property {Theme} [defaultTheme='system'] - Initial theme to use if none is stored\n * @property {string} [storageKey='sqlrooms-ui-theme'] - Local storage key to persist theme preference\n */\ntype ThemeProviderProps = {\n children: React.ReactNode;\n defaultTheme?: Theme;\n storageKey?: string;\n};\n\n/**\n * Theme context state\n * @interface ThemeProviderState\n * @property {Theme} theme - Current active theme\n * @property {(theme: Theme) => void} setTheme - Function to update the current theme\n */\ntype ThemeProviderState = {\n theme: Theme;\n setTheme: (theme: Theme) => void;\n};\n\nconst initialState: ThemeProviderState = {\n theme: 'system',\n setTheme: () => null,\n};\n\nconst ThemeProviderContext = createContext<ThemeProviderState>(initialState);\n\n/**\n * ThemeProvider component that manages and provides theme context to its children.\n * Handles system theme detection and persistence of theme preference.\n *\n * @example\n * ```tsx\n * // Basic usage with default settings\n * function App() {\n * return (\n * <ThemeProvider>\n * <YourApp />\n * </ThemeProvider>\n * );\n * }\n *\n * // Custom default theme and storage key\n * function App() {\n * return (\n * <ThemeProvider defaultTheme=\"dark\" storageKey=\"my-app-theme\">\n * <YourApp />\n * </ThemeProvider>\n * );\n * }\n * ```\n */\nexport function ThemeProvider({\n children,\n defaultTheme = 'system',\n storageKey = 'sqlrooms-ui-theme',\n ...props\n}: ThemeProviderProps) {\n const [theme, setTheme] = useState<Theme>(() => {\n if (typeof window === 'undefined') return defaultTheme;\n try {\n return (window.localStorage.getItem(storageKey) as Theme) || defaultTheme;\n } catch {\n return defaultTheme;\n }\n });\n\n // Apply theme class before paint to avoid a light-theme flash on initial load.\n // (useLayoutEffect on the client, fall back to useEffect in non-DOM environments)\n const useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n\n useIsomorphicLayoutEffect(() => {\n if (typeof window === 'undefined') return;\n const root = window.document.documentElement;\n\n root.classList.remove('light', 'dark');\n\n if (theme === 'system') {\n const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')\n .matches\n ? 'dark'\n : 'light';\n\n root.classList.add(systemTheme);\n return;\n }\n\n root.classList.add(theme);\n }, [theme]);\n\n const value = {\n theme,\n setTheme: (theme: Theme) => {\n if (typeof window !== 'undefined') {\n try {\n window.localStorage.setItem(storageKey, theme);\n } catch {\n // ignore (e.g. SSR, private mode, blocked storage)\n }\n }\n setTheme(theme);\n },\n };\n\n return (\n <ThemeProviderContext.Provider {...props} value={value}>\n {children}\n </ThemeProviderContext.Provider>\n );\n}\n\n/**\n * Hook to access the current theme and theme setter function.\n * Must be used within a ThemeProvider component.\n *\n * @example\n * ```tsx\n * import { Button } from '@sqlrooms/ui';\n *\n * function ThemeToggle() {\n * const { theme, setTheme } = useTheme();\n * const isDark = theme === 'dark';\n *\n * return (\n * <Button\n * variant=\"outline\"\n * size=\"sm\"\n * onClick={() => setTheme(isDark ? 'light' : 'dark')}\n * >\n * {isDark ? '☀️ Light' : '🌙 Dark'}\n * </Button>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * import { ThemeSwitch } from '@sqlrooms/ui';\n *\n * function AppHeader() {\n * return (\n * <nav className=\"border-b\">\n * <div className=\"flex h-16 items-center px-4\">\n * <div className=\"ml-auto\">\n * <ThemeSwitch />\n * </div>\n * </div>\n * </nav>\n * );\n * }\n * ```\n *\n * @returns {ThemeProviderState} Object containing current theme and setTheme function\n * @throws {Error} If used outside of a ThemeProvider\n */\nexport const useTheme = () => {\n const context = useContext(ThemeProviderContext);\n\n if (context === undefined)\n throw new Error('useTheme must be used within a ThemeProvider');\n\n return context;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"theme-provider.js","sourceRoot":"","sources":["../../src/theme/theme-provider.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n createContext,\n useContext,\n useEffect,\n useLayoutEffect,\n useState,\n} from 'react';\n\n/**\n * Available theme options\n * @typedef {'dark' | 'light' | 'system'} Theme\n */\nexport type Theme = 'dark' | 'light' | 'system';\nexport type ResolvedTheme = Exclude<Theme, 'system'>;\n\nexport const DEFAULT_THEME: Theme = 'system';\nexport const DEFAULT_THEME_STORAGE_KEY = 'sqlrooms-ui-theme';\n\ntype GetThemeOptions = {\n defaultTheme?: Theme;\n storageKey?: string;\n};\n\n/**\n * Props for the ThemeProvider component\n * @interface ThemeProviderProps\n * @property {React.ReactNode} children - Child components that will have access to the theme context\n * @property {Theme} [defaultTheme='system'] - Initial theme to use if none is stored\n * @property {string} [storageKey='sqlrooms-ui-theme'] - Local storage key to persist theme preference\n */\ntype ThemeProviderProps = {\n children: React.ReactNode;\n defaultTheme?: Theme;\n storageKey?: string;\n};\n\n/**\n * Theme context state\n * @interface ThemeProviderState\n * @property {Theme} theme - Current active theme\n * @property {(theme: Theme) => void} setTheme - Function to update the current theme\n */\ntype ThemeProviderState = {\n theme: Theme;\n resolvedTheme: ResolvedTheme;\n setTheme: (theme: Theme) => void;\n};\n\nconst initialState: ThemeProviderState = {\n theme: DEFAULT_THEME,\n resolvedTheme: getResolvedTheme(DEFAULT_THEME),\n setTheme: () => null,\n};\n\nconst ThemeProviderContext = createContext<ThemeProviderState>(initialState);\n\nfunction isTheme(value: string | null): value is Theme {\n return value === 'light' || value === 'dark' || value === 'system';\n}\n\n/**\n * Returns the currently stored theme preference without requiring React context.\n * Falls back to the provided default when storage is unavailable or invalid.\n */\nexport function getThemePreference({\n defaultTheme = DEFAULT_THEME,\n storageKey = DEFAULT_THEME_STORAGE_KEY,\n}: GetThemeOptions = {}): Theme {\n if (typeof window === 'undefined') return defaultTheme;\n\n try {\n const storedTheme = window.localStorage.getItem(storageKey);\n return isTheme(storedTheme) ? storedTheme : defaultTheme;\n } catch {\n return defaultTheme;\n }\n}\n\n/**\n * Resolves a theme preference to the concrete light/dark theme currently in use.\n */\nexport function getResolvedTheme(theme: Theme): ResolvedTheme {\n if (theme !== 'system') return theme;\n if (typeof window === 'undefined') return 'light';\n if (typeof window.matchMedia !== 'function') return 'light';\n\n return window.matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light';\n}\n\n/**\n * Returns the concrete light/dark theme currently in use.\n */\nexport function getTheme(options: GetThemeOptions = {}): ResolvedTheme {\n return getResolvedTheme(getThemePreference(options));\n}\n\n/**\n * ThemeProvider component that manages and provides theme context to its children.\n * Handles system theme detection and persistence of theme preference.\n *\n * @example\n * ```tsx\n * // Basic usage with default settings\n * function App() {\n * return (\n * <ThemeProvider>\n * <YourApp />\n * </ThemeProvider>\n * );\n * }\n *\n * // Custom default theme and storage key\n * function App() {\n * return (\n * <ThemeProvider defaultTheme=\"dark\" storageKey=\"my-app-theme\">\n * <YourApp />\n * </ThemeProvider>\n * );\n * }\n * ```\n */\nexport function ThemeProvider({\n children,\n defaultTheme = DEFAULT_THEME,\n storageKey = DEFAULT_THEME_STORAGE_KEY,\n ...props\n}: ThemeProviderProps) {\n const [theme, setTheme] = useState<Theme>(() =>\n getThemePreference({defaultTheme, storageKey}),\n );\n\n const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(() =>\n getResolvedTheme(theme),\n );\n\n // Apply theme class before paint to avoid a light-theme flash on initial load.\n // (useLayoutEffect on the client, fall back to useEffect in non-DOM environments)\n const useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n\n useIsomorphicLayoutEffect(() => {\n if (typeof window === 'undefined') return;\n const root = window.document.documentElement;\n const resolved = getResolvedTheme(theme);\n\n root.classList.remove('light', 'dark');\n root.classList.add(resolved);\n setResolvedTheme(resolved);\n\n if (theme === 'system' && typeof window.matchMedia === 'function') {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n const listener = () => {\n const next = getResolvedTheme(theme);\n root.classList.remove('light', 'dark');\n root.classList.add(next);\n setResolvedTheme(next);\n };\n mediaQuery.addEventListener('change', listener);\n return () => mediaQuery.removeEventListener('change', listener);\n }\n }, [theme]);\n\n const value = {\n theme,\n resolvedTheme,\n setTheme: (theme: Theme) => {\n if (typeof window !== 'undefined') {\n try {\n window.localStorage.setItem(storageKey, theme);\n } catch {\n // ignore (e.g. SSR, private mode, blocked storage)\n }\n }\n setTheme(theme);\n },\n };\n\n return (\n <ThemeProviderContext.Provider {...props} value={value}>\n {children}\n </ThemeProviderContext.Provider>\n );\n}\n\n/**\n * Hook to access the current theme and theme setter function.\n * Must be used within a ThemeProvider component.\n *\n * @example\n * ```tsx\n * import { Button } from '@sqlrooms/ui';\n *\n * function ThemeToggle() {\n * const { theme, setTheme } = useTheme();\n * const isDark = theme === 'dark';\n *\n * return (\n * <Button\n * variant=\"outline\"\n * size=\"sm\"\n * onClick={() => setTheme(isDark ? 'light' : 'dark')}\n * >\n * {isDark ? '☀️ Light' : '🌙 Dark'}\n * </Button>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * import { ThemeSwitch } from '@sqlrooms/ui';\n *\n * function AppHeader() {\n * return (\n * <nav className=\"border-b\">\n * <div className=\"flex h-16 items-center px-4\">\n * <div className=\"ml-auto\">\n * <ThemeSwitch />\n * </div>\n * </div>\n * </nav>\n * );\n * }\n * ```\n *\n * @returns {ThemeProviderState} Object containing current theme and setTheme function\n * @throws {Error} If used outside of a ThemeProvider\n */\nexport const useTheme = () => {\n const context = useContext(ThemeProviderContext);\n\n if (context === undefined)\n throw new Error('useTheme must be used within a ThemeProvider');\n\n return context;\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqlrooms/ui",
|
|
3
|
-
"version": "0.29.0-rc.
|
|
3
|
+
"version": "0.29.0-rc.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"author": "SQLRooms Contributors",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "https://github.com/sqlrooms/sqlrooms.git"
|
|
10
|
+
"url": "git+https://github.com/sqlrooms/sqlrooms.git"
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"dist",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"@radix-ui/react-context-menu": "^2.2.16",
|
|
38
38
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
39
39
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
40
|
+
"@radix-ui/react-hover-card": "^1.1.15",
|
|
40
41
|
"@radix-ui/react-label": "^2.1.7",
|
|
41
42
|
"@radix-ui/react-menubar": "^1.1.16",
|
|
42
43
|
"@radix-ui/react-popover": "^1.1.15",
|
|
@@ -49,7 +50,6 @@
|
|
|
49
50
|
"@radix-ui/react-slot": "^1.2.3",
|
|
50
51
|
"@radix-ui/react-switch": "^1.2.6",
|
|
51
52
|
"@radix-ui/react-tabs": "^1.1.13",
|
|
52
|
-
"@radix-ui/react-toast": "^1.2.15",
|
|
53
53
|
"@radix-ui/react-toggle": "^1.1.10",
|
|
54
54
|
"@radix-ui/react-toggle-group": "^1.1.11",
|
|
55
55
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
@@ -71,5 +71,5 @@
|
|
|
71
71
|
"peerDependencies": {
|
|
72
72
|
"tailwindcss": "^4.1.18"
|
|
73
73
|
},
|
|
74
|
-
"gitHead": "
|
|
74
|
+
"gitHead": "5d511631992c1af8852ea79ced488867aad4a555"
|
|
75
75
|
}
|
package/tailwind-preset.css
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
@custom-variant dark (&:is(.dark *));
|
|
7
7
|
|
|
8
|
+
@layer base {
|
|
9
|
+
button:not(:disabled),
|
|
10
|
+
[role="button"]:not(:disabled) {
|
|
11
|
+
cursor: pointer;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
:root {
|
|
9
16
|
--background: 0 0% 100%;
|
|
10
17
|
--foreground: 240 10% 3.9%;
|
|
@@ -39,6 +46,24 @@
|
|
|
39
46
|
--sidebar-border: 220 13% 91%;
|
|
40
47
|
--sidebar-ring: 217.2 91.2% 59.8%;
|
|
41
48
|
--radius: 0.5rem;
|
|
49
|
+
--editor-keyword: 240 100% 50%;
|
|
50
|
+
--editor-string: 0 57% 33%;
|
|
51
|
+
--editor-number: 156 100% 30%;
|
|
52
|
+
--editor-comment: 120 100% 25%;
|
|
53
|
+
--editor-operator: 0 0% 0%;
|
|
54
|
+
--editor-punctuation: 0 0% 0%;
|
|
55
|
+
--editor-property: 214 100% 25%;
|
|
56
|
+
--editor-function: 38 63% 33%;
|
|
57
|
+
--editor-type: 156 100% 30%;
|
|
58
|
+
--editor-constant: 204 100% 38%;
|
|
59
|
+
--editor-invalid: 0 70% 49%;
|
|
60
|
+
--editor-selection-match: 207 100% 84%;
|
|
61
|
+
--editor-bracket-match: 0 0% 0%;
|
|
62
|
+
--editor-fold-placeholder: 220 14% 91%;
|
|
63
|
+
--editor-search-match: 60 100% 50%;
|
|
64
|
+
--editor-search-match-selected: 60 100% 50%;
|
|
65
|
+
--editor-lint-error: 0 63% 49%;
|
|
66
|
+
--editor-lint-warning: 32 94% 48%;
|
|
42
67
|
}
|
|
43
68
|
|
|
44
69
|
.dark {
|
|
@@ -74,6 +99,24 @@
|
|
|
74
99
|
--sidebar-accent-foreground: 240 4.8% 95.9%;
|
|
75
100
|
--sidebar-border: 240 3.7% 15.9%;
|
|
76
101
|
--sidebar-ring: 217.2 91.2% 59.8%;
|
|
102
|
+
--editor-keyword: 206 100% 60%;
|
|
103
|
+
--editor-string: 25 58% 59%;
|
|
104
|
+
--editor-number: 120 43% 71%;
|
|
105
|
+
--editor-comment: 122 39% 49%;
|
|
106
|
+
--editor-operator: 0 0% 83%;
|
|
107
|
+
--editor-punctuation: 0 0% 83%;
|
|
108
|
+
--editor-property: 207 100% 74%;
|
|
109
|
+
--editor-function: 49 44% 67%;
|
|
110
|
+
--editor-type: 167 47% 55%;
|
|
111
|
+
--editor-constant: 210 100% 66%;
|
|
112
|
+
--editor-invalid: 0 100% 63%;
|
|
113
|
+
--editor-selection-match: 207 35% 35%;
|
|
114
|
+
--editor-bracket-match: 0 0% 100%;
|
|
115
|
+
--editor-fold-placeholder: 0 0% 24%;
|
|
116
|
+
--editor-search-match: 43 16% 33%;
|
|
117
|
+
--editor-search-match-selected: 43 16% 33%;
|
|
118
|
+
--editor-lint-error: 3 88% 71%;
|
|
119
|
+
--editor-lint-warning: 28 100% 72%;
|
|
77
120
|
}
|
|
78
121
|
|
|
79
122
|
@theme inline {
|
|
@@ -113,6 +156,26 @@
|
|
|
113
156
|
--color-sidebar-accent-foreground: hsl(var(--sidebar-accent-foreground));
|
|
114
157
|
--color-sidebar-border: hsl(var(--sidebar-border));
|
|
115
158
|
--color-sidebar-ring: hsl(var(--sidebar-ring));
|
|
159
|
+
--color-editor-keyword: hsl(var(--editor-keyword));
|
|
160
|
+
--color-editor-string: hsl(var(--editor-string));
|
|
161
|
+
--color-editor-number: hsl(var(--editor-number));
|
|
162
|
+
--color-editor-comment: hsl(var(--editor-comment));
|
|
163
|
+
--color-editor-operator: hsl(var(--editor-operator));
|
|
164
|
+
--color-editor-punctuation: hsl(var(--editor-punctuation));
|
|
165
|
+
--color-editor-property: hsl(var(--editor-property));
|
|
166
|
+
--color-editor-function: hsl(var(--editor-function));
|
|
167
|
+
--color-editor-type: hsl(var(--editor-type));
|
|
168
|
+
--color-editor-constant: hsl(var(--editor-constant));
|
|
169
|
+
--color-editor-invalid: hsl(var(--editor-invalid));
|
|
170
|
+
--color-editor-selection-match: hsl(var(--editor-selection-match));
|
|
171
|
+
--color-editor-bracket-match: hsl(var(--editor-bracket-match));
|
|
172
|
+
--color-editor-fold-placeholder: hsl(var(--editor-fold-placeholder));
|
|
173
|
+
--color-editor-search-match: hsl(var(--editor-search-match));
|
|
174
|
+
--color-editor-search-match-selected: hsl(
|
|
175
|
+
var(--editor-search-match-selected)
|
|
176
|
+
);
|
|
177
|
+
--color-editor-lint-error: hsl(var(--editor-lint-error));
|
|
178
|
+
--color-editor-lint-warning: hsl(var(--editor-lint-warning));
|
|
116
179
|
--animate-sqlrooms-progress: sqlrooms-progress 1s infinite linear;
|
|
117
180
|
}
|
|
118
181
|
|
|
@@ -136,6 +199,19 @@
|
|
|
136
199
|
body {
|
|
137
200
|
@apply bg-background text-foreground;
|
|
138
201
|
}
|
|
202
|
+
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@layer utilities {
|
|
206
|
+
.no-spinner {
|
|
207
|
+
appearance: textfield;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.no-spinner::-webkit-outer-spin-button,
|
|
211
|
+
.no-spinner::-webkit-inner-spin-button {
|
|
212
|
+
appearance: none;
|
|
213
|
+
margin: 0;
|
|
214
|
+
}
|
|
139
215
|
}
|
|
140
216
|
|
|
141
217
|
@layer components {
|