@thangnqdev/ui 0.2.2 → 0.2.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/{chunk-DUPPPGXY.js → chunk-2ANJFOIU.js} +34 -32
- package/dist/chunk-2ANJFOIU.js.map +1 -0
- package/dist/core/index.cjs +31 -29
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/index.cjs +31 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/src/core/ThemeProvider.tsx +75 -57
- package/ui-contract.json +1 -1
- package/dist/chunk-DUPPPGXY.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Avatar, A
|
|
|
2
2
|
export { Alert, AlertDescription, AlertTitle, CircularProgress, EmptyState, feedback_exports as Feedback, ProgressBar, Skeleton, SkeletonCircle, SkeletonText, Spinner, alertVariants, progressVariants, spinnerVariants } from './chunk-WAFHC4DQ.js';
|
|
3
3
|
export { glass_exports as Glass, GlassBadge, GlassButton, GlassCard } from './chunk-A7P3O5Z5.js';
|
|
4
4
|
export { bento_exports as Bento, BentoCard, BentoGrid } from './chunk-C4SUXMI3.js';
|
|
5
|
-
export { core_exports as Core, ThemeProvider, useTheme } from './chunk-
|
|
5
|
+
export { core_exports as Core, ThemeProvider, useTheme } from './chunk-2ANJFOIU.js';
|
|
6
6
|
export { AspectRatio, Container, Divider, Flex, Grid, GridItem, HStack, layout_exports as Layout, PageHeader, PageLayout, PageSidebar, ScrollArea, Section, Spacer, Stack, VStack, containerVariants, dividerVariants, flexVariants, gridItemVariants, gridVariants, sectionVariants, stackVariants } from './chunk-2OXZK6IP.js';
|
|
7
7
|
export { Blockquote, Code, CodeBlock, Heading, Link, Text, typography_exports as Typography, headingVariants, linkVariants, textVariants } from './chunk-ATYGDTMK.js';
|
|
8
8
|
export { ButtonGroup, buttons_exports as Buttons, IconButton, SplitButton, ToggleButton, ToggleGroup, buttonGroupVariants, iconButtonVariants, toggleVariants } from './chunk-KUQWUTZA.js';
|
package/llms.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @thangnqdev/ui — Full Architecture Component System Contract
|
|
2
2
|
|
|
3
3
|
> Machine-readable specification for AI Coding Agents (Claude Code, Antigravity, Cursor, Qwen, Codex).
|
|
4
|
-
> Package: `@thangnqdev/ui` (v0.2.
|
|
4
|
+
> Package: `@thangnqdev/ui` (v0.2.3)
|
|
5
5
|
> Total Registered SSOT Components: 145
|
|
6
6
|
> Aesthetic Core: Spatial Optics, Editorial Bento, and Enterprise-grade Accessible Primitives.
|
|
7
7
|
> Sub-module Exports: `core`, `layout`, `typography`, `buttons`, `forms`, `overlays`, `navigation`, `data-display`, `feedback`, `glass`, `bento`.
|
package/package.json
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useCallback,
|
|
4
|
+
useContext,
|
|
5
|
+
useEffect,
|
|
6
|
+
useLayoutEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
} from 'react';
|
|
2
11
|
|
|
3
12
|
export type Theme = 'dark' | 'light' | 'system';
|
|
4
13
|
|
|
@@ -17,7 +26,13 @@ interface ThemeContextType {
|
|
|
17
26
|
toggleTheme: () => void;
|
|
18
27
|
}
|
|
19
28
|
|
|
20
|
-
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
|
29
|
+
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
|
30
|
+
|
|
31
|
+
const useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
|
|
32
|
+
|
|
33
|
+
function isTheme(value: string | null): value is Theme {
|
|
34
|
+
return value === 'dark' || value === 'light' || value === 'system';
|
|
35
|
+
}
|
|
21
36
|
|
|
22
37
|
export function ThemeProvider({
|
|
23
38
|
children,
|
|
@@ -28,9 +43,10 @@ export function ThemeProvider({
|
|
|
28
43
|
}: ThemeProviderProps) {
|
|
29
44
|
// Always initialize identically on SSR and initial client pass to avoid hydration mismatches
|
|
30
45
|
const [theme, setThemeState] = useState<Theme>(defaultTheme);
|
|
31
|
-
const [resolvedTheme, setResolvedTheme] = useState<'dark' | 'light'>(
|
|
46
|
+
const [resolvedTheme, setResolvedTheme] = useState<'dark' | 'light'>(
|
|
32
47
|
defaultTheme === 'dark' ? 'dark' : 'light'
|
|
33
|
-
);
|
|
48
|
+
);
|
|
49
|
+
const pendingStoredThemeRef = useRef<{ storageKey: string; theme: Theme } | null>(null);
|
|
34
50
|
|
|
35
51
|
const computeResolved = useCallback(
|
|
36
52
|
(t: Theme): 'dark' | 'light' => {
|
|
@@ -42,69 +58,71 @@ export function ThemeProvider({
|
|
|
42
58
|
[enableSystem]
|
|
43
59
|
);
|
|
44
60
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
const applyResolvedTheme = useCallback(
|
|
62
|
+
(nextResolvedTheme: 'dark' | 'light') => {
|
|
63
|
+
setResolvedTheme(nextResolvedTheme);
|
|
64
|
+
|
|
65
|
+
if (typeof document === 'undefined') return;
|
|
66
|
+
|
|
67
|
+
const root = document.documentElement;
|
|
68
|
+
root.setAttribute(attribute, nextResolvedTheme);
|
|
69
|
+
root.classList.toggle('dark', nextResolvedTheme === 'dark');
|
|
70
|
+
root.classList.toggle('light', nextResolvedTheme === 'light');
|
|
71
|
+
},
|
|
72
|
+
[attribute]
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Read the persisted value before paint. The pending ref prevents the DOM/storage
|
|
76
|
+
// sync below from writing defaultTheme over a different persisted theme first.
|
|
77
|
+
useIsomorphicLayoutEffect(() => {
|
|
78
|
+
if (typeof window === 'undefined') return;
|
|
79
|
+
|
|
80
|
+
let initialTheme = defaultTheme;
|
|
81
|
+
try {
|
|
50
82
|
const stored = window.localStorage?.getItem(storageKey);
|
|
51
|
-
if (stored
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
}
|
|
83
|
+
if (isTheme(stored)) initialTheme = stored;
|
|
84
|
+
} catch {
|
|
85
|
+
// Ignore storage access errors in restricted iframe/incognito environments
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
pendingStoredThemeRef.current = { storageKey, theme: initialTheme };
|
|
89
|
+
setThemeState((currentTheme) => (currentTheme === initialTheme ? currentTheme : initialTheme));
|
|
57
90
|
}, [storageKey]);
|
|
58
|
-
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
root.classList.remove('dark');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (typeof window !== 'undefined' && typeof window.localStorage !== 'undefined') {
|
|
77
|
-
try {
|
|
91
|
+
|
|
92
|
+
// Apply user changes before paint. Storage initialization and user updates now
|
|
93
|
+
// have a single write direction, so a click cannot oscillate light <-> dark.
|
|
94
|
+
useIsomorphicLayoutEffect(() => {
|
|
95
|
+
const pendingStoredTheme = pendingStoredThemeRef.current;
|
|
96
|
+
if (pendingStoredTheme?.storageKey === storageKey) {
|
|
97
|
+
if (pendingStoredTheme.theme !== theme) return;
|
|
98
|
+
pendingStoredThemeRef.current = null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const currentResolved = computeResolved(theme);
|
|
102
|
+
applyResolvedTheme(currentResolved);
|
|
103
|
+
|
|
104
|
+
if (typeof window !== 'undefined' && typeof window.localStorage !== 'undefined') {
|
|
105
|
+
try {
|
|
78
106
|
window.localStorage.setItem(storageKey, theme);
|
|
79
107
|
} catch {
|
|
80
|
-
// Ignore storage access errors
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}, [theme,
|
|
108
|
+
// Ignore storage access errors
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}, [theme, computeResolved, applyResolvedTheme, storageKey]);
|
|
84
112
|
|
|
85
113
|
// System media query listener
|
|
86
114
|
useEffect(() => {
|
|
87
115
|
if (theme !== 'system' || !enableSystem || typeof window === 'undefined' || !window.matchMedia) return;
|
|
88
116
|
|
|
89
|
-
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
90
|
-
const handleChange = () => {
|
|
91
|
-
const newResolved = mediaQuery.matches ? 'dark' : 'light';
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
document.documentElement.setAttribute(attribute, newResolved);
|
|
95
|
-
if (newResolved === 'dark') {
|
|
96
|
-
document.documentElement.classList.add('dark');
|
|
97
|
-
document.documentElement.classList.remove('light');
|
|
98
|
-
} else {
|
|
99
|
-
document.documentElement.classList.add('light');
|
|
100
|
-
document.documentElement.classList.remove('dark');
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
};
|
|
117
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
118
|
+
const handleChange = () => {
|
|
119
|
+
const newResolved = mediaQuery.matches ? 'dark' : 'light';
|
|
120
|
+
applyResolvedTheme(newResolved);
|
|
121
|
+
};
|
|
104
122
|
|
|
105
|
-
mediaQuery.addEventListener('change', handleChange);
|
|
106
|
-
return () => mediaQuery.removeEventListener('change', handleChange);
|
|
107
|
-
}, [theme, enableSystem,
|
|
123
|
+
mediaQuery.addEventListener('change', handleChange);
|
|
124
|
+
return () => mediaQuery.removeEventListener('change', handleChange);
|
|
125
|
+
}, [theme, enableSystem, applyResolvedTheme]);
|
|
108
126
|
|
|
109
127
|
const toggleTheme = () => {
|
|
110
128
|
setThemeState((prev) => {
|
package/ui-contract.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/index.ts","../src/core/ThemeProvider.tsx"],"names":[],"mappings":"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","file":"chunk-DUPPPGXY.js","sourcesContent":["export * from './ThemeProvider';\r\nexport * from '../utils/cn';\r\n","import React, { createContext, useContext, useEffect, useState, useMemo, useCallback } from 'react';\r\n\r\nexport type Theme = 'dark' | 'light' | 'system';\r\n\r\nexport interface ThemeProviderProps {\r\n children: React.ReactNode;\r\n defaultTheme?: Theme;\r\n storageKey?: string;\r\n attribute?: string;\r\n enableSystem?: boolean;\r\n}\r\n\r\ninterface ThemeContextType {\r\n theme: Theme;\r\n resolvedTheme: 'dark' | 'light';\r\n setTheme: (theme: Theme) => void;\r\n toggleTheme: () => void;\r\n}\r\n\r\nconst ThemeContext = createContext<ThemeContextType | undefined>(undefined);\r\n\r\nexport function ThemeProvider({\r\n children,\r\n defaultTheme = 'light',\n storageKey = 'ui-theme',\r\n attribute = 'data-theme',\r\n enableSystem = true,\r\n}: ThemeProviderProps) {\r\n // Always initialize identically on SSR and initial client pass to avoid hydration mismatches\r\n const [theme, setThemeState] = useState<Theme>(defaultTheme);\r\n const [resolvedTheme, setResolvedTheme] = useState<'dark' | 'light'>(\r\n defaultTheme === 'dark' ? 'dark' : 'light'\n );\r\n\r\n const computeResolved = useCallback(\r\n (t: Theme): 'dark' | 'light' => {\r\n if (t === 'system' && enableSystem && typeof window !== 'undefined' && window.matchMedia) {\r\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\r\n }\r\n return t === 'dark' ? 'dark' : 'light';\r\n },\r\n [enableSystem]\r\n );\r\n\r\n // Client-side initialization: read storage once mounted\r\n useEffect(() => {\r\n if (typeof window === 'undefined') return;\r\n\r\n try {\r\n const stored = window.localStorage?.getItem(storageKey);\n if (stored === 'dark' || stored === 'light' || stored === 'system') {\n setThemeState(stored);\n }\n } catch {\r\n // Ignore storage access errors in restricted iframe/incognito environments\r\n }\r\n }, [storageKey]);\n\r\n // Sync theme changes to DOM and storage\r\n useEffect(() => {\r\n if (typeof document === 'undefined') return;\r\n\r\n const root = document.documentElement;\r\n const currentResolved = computeResolved(theme);\r\n setResolvedTheme(currentResolved);\r\n\r\n root.setAttribute(attribute, currentResolved);\r\n if (currentResolved === 'dark') {\r\n root.classList.add('dark');\r\n root.classList.remove('light');\r\n } else {\r\n root.classList.add('light');\r\n root.classList.remove('dark');\r\n }\r\n\r\n if (typeof window !== 'undefined' && typeof window.localStorage !== 'undefined') {\r\n try {\r\n window.localStorage.setItem(storageKey, theme);\r\n } catch {\r\n // Ignore storage access errors\r\n }\r\n }\r\n }, [theme, attribute, computeResolved, storageKey]);\r\n\r\n // System media query listener\r\n useEffect(() => {\r\n if (theme !== 'system' || !enableSystem || typeof window === 'undefined' || !window.matchMedia) return;\r\n\r\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\r\n const handleChange = () => {\r\n const newResolved = mediaQuery.matches ? 'dark' : 'light';\r\n setResolvedTheme(newResolved);\r\n if (typeof document !== 'undefined') {\r\n document.documentElement.setAttribute(attribute, newResolved);\r\n if (newResolved === 'dark') {\r\n document.documentElement.classList.add('dark');\r\n document.documentElement.classList.remove('light');\r\n } else {\r\n document.documentElement.classList.add('light');\r\n document.documentElement.classList.remove('dark');\r\n }\r\n }\r\n };\r\n\r\n mediaQuery.addEventListener('change', handleChange);\r\n return () => mediaQuery.removeEventListener('change', handleChange);\r\n }, [theme, enableSystem, attribute]);\r\n\r\n const toggleTheme = () => {\r\n setThemeState((prev) => {\r\n if (prev === 'dark') return 'light';\r\n return 'dark';\r\n });\r\n };\r\n\r\n const value = useMemo(\r\n () => ({\r\n theme,\r\n resolvedTheme,\r\n setTheme: setThemeState,\r\n toggleTheme,\r\n }),\r\n [theme, resolvedTheme]\r\n );\r\n\r\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\r\n}\r\n\r\nexport function useTheme(): ThemeContextType {\r\n const context = useContext(ThemeContext);\r\n if (!context) {\r\n throw new Error('useTheme must be used within a ThemeProvider');\r\n }\r\n return context;\r\n}\r\n"]}
|