qstd 0.2.1 → 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/CHANGELOG.md +17 -0
- package/dist/react/index.cjs +84 -0
- package/dist/react/index.d.cts +18 -1
- package/dist/react/index.d.ts +18 -1
- package/dist/react/index.js +84 -1
- package/package.json +19 -18
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.3] - 2025-11-24
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Updated `useTheme` hook to automatically set `data-theme` attribute on `document.documentElement`
|
|
15
|
+
- Renamed internal store property from `theme` to `value` for better clarity
|
|
16
|
+
- Code cleanup in theme utility functions
|
|
17
|
+
|
|
18
|
+
## [0.2.2] - 2025-11-24
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Added `useTheme` hook for managing light/dark theme
|
|
23
|
+
- Syncs with localStorage and across components using events
|
|
24
|
+
- Provides `theme`, `isManual` state and `toggleTheme`, `update` functions
|
|
25
|
+
- Available in `qstd/react`
|
|
26
|
+
|
|
10
27
|
## [0.2.1] - 2025-11-24
|
|
11
28
|
|
|
12
29
|
### Added
|
package/dist/react/index.cjs
CHANGED
|
@@ -3547,6 +3547,89 @@ var CompoundBlock = Object.assign(Block, {
|
|
|
3547
3547
|
});
|
|
3548
3548
|
var block_default = CompoundBlock;
|
|
3549
3549
|
|
|
3550
|
+
// src/react/use-theme/literals.ts
|
|
3551
|
+
var THEME_STORAGE_KEY = "theme";
|
|
3552
|
+
var THEME_STORE_STORAGE_KEY = "themeStore";
|
|
3553
|
+
var THEME_CHANGE_EVENT = "qstd:theme-change";
|
|
3554
|
+
|
|
3555
|
+
// src/react/use-theme/fns.ts
|
|
3556
|
+
var getInitialTheme = () => {
|
|
3557
|
+
const stored = localStorage.getItem(THEME_STORAGE_KEY);
|
|
3558
|
+
return stored === "light" || stored === "dark" ? stored : "light";
|
|
3559
|
+
};
|
|
3560
|
+
var getInitialStore = () => {
|
|
3561
|
+
try {
|
|
3562
|
+
const stored = localStorage.getItem(THEME_STORE_STORAGE_KEY);
|
|
3563
|
+
if (stored) {
|
|
3564
|
+
const parsed = JSON.parse(stored);
|
|
3565
|
+
return {
|
|
3566
|
+
value: parsed.theme ?? getInitialTheme(),
|
|
3567
|
+
isManual: parsed.isManual ?? false
|
|
3568
|
+
};
|
|
3569
|
+
}
|
|
3570
|
+
} catch {
|
|
3571
|
+
}
|
|
3572
|
+
return {
|
|
3573
|
+
value: getInitialTheme(),
|
|
3574
|
+
isManual: false
|
|
3575
|
+
};
|
|
3576
|
+
};
|
|
3577
|
+
var saveStore = (store) => {
|
|
3578
|
+
try {
|
|
3579
|
+
localStorage.setItem(THEME_STORAGE_KEY, store.value);
|
|
3580
|
+
localStorage.setItem(THEME_STORE_STORAGE_KEY, JSON.stringify(store));
|
|
3581
|
+
window.dispatchEvent(
|
|
3582
|
+
new CustomEvent(THEME_CHANGE_EVENT, { detail: store })
|
|
3583
|
+
);
|
|
3584
|
+
} catch {
|
|
3585
|
+
}
|
|
3586
|
+
};
|
|
3587
|
+
|
|
3588
|
+
// src/react/use-theme/index.ts
|
|
3589
|
+
function useTheme() {
|
|
3590
|
+
const [store, setStore] = React11__namespace.default.useState(getInitialStore);
|
|
3591
|
+
React11__namespace.default.useEffect(() => {
|
|
3592
|
+
document.documentElement.setAttribute("data-theme", store.value);
|
|
3593
|
+
}, [store.value]);
|
|
3594
|
+
React11__namespace.default.useEffect(() => {
|
|
3595
|
+
const handleStorageChange = (e) => {
|
|
3596
|
+
if (e.key === THEME_STORAGE_KEY || e.key === THEME_STORE_STORAGE_KEY) {
|
|
3597
|
+
setStore(getInitialStore());
|
|
3598
|
+
}
|
|
3599
|
+
};
|
|
3600
|
+
const handleThemeChange = (e) => {
|
|
3601
|
+
const customEvent = e;
|
|
3602
|
+
if (customEvent.detail) {
|
|
3603
|
+
setStore(customEvent.detail);
|
|
3604
|
+
}
|
|
3605
|
+
};
|
|
3606
|
+
window.addEventListener("storage", handleStorageChange);
|
|
3607
|
+
window.addEventListener(THEME_CHANGE_EVENT, handleThemeChange);
|
|
3608
|
+
return () => {
|
|
3609
|
+
window.removeEventListener("storage", handleStorageChange);
|
|
3610
|
+
window.removeEventListener(THEME_CHANGE_EVENT, handleThemeChange);
|
|
3611
|
+
};
|
|
3612
|
+
}, []);
|
|
3613
|
+
const update = (updates) => {
|
|
3614
|
+
setStore((prev) => {
|
|
3615
|
+
const next = { ...prev, ...updates };
|
|
3616
|
+
saveStore(next);
|
|
3617
|
+
return next;
|
|
3618
|
+
});
|
|
3619
|
+
};
|
|
3620
|
+
const toggleTheme = (theme) => {
|
|
3621
|
+
setStore((prev) => {
|
|
3622
|
+
const next = {
|
|
3623
|
+
value: theme ?? (prev.value === "light" ? "dark" : "light"),
|
|
3624
|
+
isManual: true
|
|
3625
|
+
};
|
|
3626
|
+
saveStore(next);
|
|
3627
|
+
return next;
|
|
3628
|
+
});
|
|
3629
|
+
};
|
|
3630
|
+
return { ...store, update, toggleTheme };
|
|
3631
|
+
}
|
|
3632
|
+
|
|
3550
3633
|
// src/react/index.ts
|
|
3551
3634
|
function useDebounce(value, delay = 500) {
|
|
3552
3635
|
const [debouncedValue, setDebouncedValue] = React11__namespace.default.useState(value);
|
|
@@ -3607,4 +3690,5 @@ function useMatchMedia2(queries, defaultValues = []) {
|
|
|
3607
3690
|
exports.default = block_default;
|
|
3608
3691
|
exports.useDebounce = useDebounce;
|
|
3609
3692
|
exports.useMatchMedia = useMatchMedia2;
|
|
3693
|
+
exports.useTheme = useTheme;
|
|
3610
3694
|
exports.useThrottle = useThrottle;
|
package/dist/react/index.d.cts
CHANGED
|
@@ -20991,6 +20991,23 @@ type BlockComponent = typeof Block & {
|
|
|
20991
20991
|
};
|
|
20992
20992
|
declare const CompoundBlock: BlockComponent;
|
|
20993
20993
|
|
|
20994
|
+
type Theme = "light" | "dark";
|
|
20995
|
+
type ThemeStore = {
|
|
20996
|
+
value: Theme;
|
|
20997
|
+
isManual: boolean;
|
|
20998
|
+
};
|
|
20999
|
+
|
|
21000
|
+
/**
|
|
21001
|
+
* Hook to manage light/dark theme
|
|
21002
|
+
* Syncs with localStorage and across components using events
|
|
21003
|
+
*/
|
|
21004
|
+
declare function useTheme(): {
|
|
21005
|
+
update: (updates: Partial<ThemeStore>) => void;
|
|
21006
|
+
toggleTheme: (theme?: "light" | "dark") => void;
|
|
21007
|
+
value: Theme;
|
|
21008
|
+
isManual: boolean;
|
|
21009
|
+
};
|
|
21010
|
+
|
|
20994
21011
|
/**
|
|
20995
21012
|
* React module - Block component, hooks, and types
|
|
20996
21013
|
*
|
|
@@ -21033,4 +21050,4 @@ declare function useThrottle(value: string, interval?: number): string;
|
|
|
21033
21050
|
*/
|
|
21034
21051
|
declare function useMatchMedia(queries: MediaQuery, defaultValues?: MatchedMedia): MatchedMedia;
|
|
21035
21052
|
|
|
21036
|
-
export { type MatchedMedia, type MediaQuery, type RadioOption, CompoundBlock as default, useDebounce, useMatchMedia, useThrottle };
|
|
21053
|
+
export { type MatchedMedia, type MediaQuery, type RadioOption, type Theme, type ThemeStore, CompoundBlock as default, useDebounce, useMatchMedia, useTheme, useThrottle };
|
package/dist/react/index.d.ts
CHANGED
|
@@ -20991,6 +20991,23 @@ type BlockComponent = typeof Block & {
|
|
|
20991
20991
|
};
|
|
20992
20992
|
declare const CompoundBlock: BlockComponent;
|
|
20993
20993
|
|
|
20994
|
+
type Theme = "light" | "dark";
|
|
20995
|
+
type ThemeStore = {
|
|
20996
|
+
value: Theme;
|
|
20997
|
+
isManual: boolean;
|
|
20998
|
+
};
|
|
20999
|
+
|
|
21000
|
+
/**
|
|
21001
|
+
* Hook to manage light/dark theme
|
|
21002
|
+
* Syncs with localStorage and across components using events
|
|
21003
|
+
*/
|
|
21004
|
+
declare function useTheme(): {
|
|
21005
|
+
update: (updates: Partial<ThemeStore>) => void;
|
|
21006
|
+
toggleTheme: (theme?: "light" | "dark") => void;
|
|
21007
|
+
value: Theme;
|
|
21008
|
+
isManual: boolean;
|
|
21009
|
+
};
|
|
21010
|
+
|
|
20994
21011
|
/**
|
|
20995
21012
|
* React module - Block component, hooks, and types
|
|
20996
21013
|
*
|
|
@@ -21033,4 +21050,4 @@ declare function useThrottle(value: string, interval?: number): string;
|
|
|
21033
21050
|
*/
|
|
21034
21051
|
declare function useMatchMedia(queries: MediaQuery, defaultValues?: MatchedMedia): MatchedMedia;
|
|
21035
21052
|
|
|
21036
|
-
export { type MatchedMedia, type MediaQuery, type RadioOption, CompoundBlock as default, useDebounce, useMatchMedia, useThrottle };
|
|
21053
|
+
export { type MatchedMedia, type MediaQuery, type RadioOption, type Theme, type ThemeStore, CompoundBlock as default, useDebounce, useMatchMedia, useTheme, useThrottle };
|
package/dist/react/index.js
CHANGED
|
@@ -3524,6 +3524,89 @@ var CompoundBlock = Object.assign(Block, {
|
|
|
3524
3524
|
});
|
|
3525
3525
|
var block_default = CompoundBlock;
|
|
3526
3526
|
|
|
3527
|
+
// src/react/use-theme/literals.ts
|
|
3528
|
+
var THEME_STORAGE_KEY = "theme";
|
|
3529
|
+
var THEME_STORE_STORAGE_KEY = "themeStore";
|
|
3530
|
+
var THEME_CHANGE_EVENT = "qstd:theme-change";
|
|
3531
|
+
|
|
3532
|
+
// src/react/use-theme/fns.ts
|
|
3533
|
+
var getInitialTheme = () => {
|
|
3534
|
+
const stored = localStorage.getItem(THEME_STORAGE_KEY);
|
|
3535
|
+
return stored === "light" || stored === "dark" ? stored : "light";
|
|
3536
|
+
};
|
|
3537
|
+
var getInitialStore = () => {
|
|
3538
|
+
try {
|
|
3539
|
+
const stored = localStorage.getItem(THEME_STORE_STORAGE_KEY);
|
|
3540
|
+
if (stored) {
|
|
3541
|
+
const parsed = JSON.parse(stored);
|
|
3542
|
+
return {
|
|
3543
|
+
value: parsed.theme ?? getInitialTheme(),
|
|
3544
|
+
isManual: parsed.isManual ?? false
|
|
3545
|
+
};
|
|
3546
|
+
}
|
|
3547
|
+
} catch {
|
|
3548
|
+
}
|
|
3549
|
+
return {
|
|
3550
|
+
value: getInitialTheme(),
|
|
3551
|
+
isManual: false
|
|
3552
|
+
};
|
|
3553
|
+
};
|
|
3554
|
+
var saveStore = (store) => {
|
|
3555
|
+
try {
|
|
3556
|
+
localStorage.setItem(THEME_STORAGE_KEY, store.value);
|
|
3557
|
+
localStorage.setItem(THEME_STORE_STORAGE_KEY, JSON.stringify(store));
|
|
3558
|
+
window.dispatchEvent(
|
|
3559
|
+
new CustomEvent(THEME_CHANGE_EVENT, { detail: store })
|
|
3560
|
+
);
|
|
3561
|
+
} catch {
|
|
3562
|
+
}
|
|
3563
|
+
};
|
|
3564
|
+
|
|
3565
|
+
// src/react/use-theme/index.ts
|
|
3566
|
+
function useTheme() {
|
|
3567
|
+
const [store, setStore] = React11__default.useState(getInitialStore);
|
|
3568
|
+
React11__default.useEffect(() => {
|
|
3569
|
+
document.documentElement.setAttribute("data-theme", store.value);
|
|
3570
|
+
}, [store.value]);
|
|
3571
|
+
React11__default.useEffect(() => {
|
|
3572
|
+
const handleStorageChange = (e) => {
|
|
3573
|
+
if (e.key === THEME_STORAGE_KEY || e.key === THEME_STORE_STORAGE_KEY) {
|
|
3574
|
+
setStore(getInitialStore());
|
|
3575
|
+
}
|
|
3576
|
+
};
|
|
3577
|
+
const handleThemeChange = (e) => {
|
|
3578
|
+
const customEvent = e;
|
|
3579
|
+
if (customEvent.detail) {
|
|
3580
|
+
setStore(customEvent.detail);
|
|
3581
|
+
}
|
|
3582
|
+
};
|
|
3583
|
+
window.addEventListener("storage", handleStorageChange);
|
|
3584
|
+
window.addEventListener(THEME_CHANGE_EVENT, handleThemeChange);
|
|
3585
|
+
return () => {
|
|
3586
|
+
window.removeEventListener("storage", handleStorageChange);
|
|
3587
|
+
window.removeEventListener(THEME_CHANGE_EVENT, handleThemeChange);
|
|
3588
|
+
};
|
|
3589
|
+
}, []);
|
|
3590
|
+
const update = (updates) => {
|
|
3591
|
+
setStore((prev) => {
|
|
3592
|
+
const next = { ...prev, ...updates };
|
|
3593
|
+
saveStore(next);
|
|
3594
|
+
return next;
|
|
3595
|
+
});
|
|
3596
|
+
};
|
|
3597
|
+
const toggleTheme = (theme) => {
|
|
3598
|
+
setStore((prev) => {
|
|
3599
|
+
const next = {
|
|
3600
|
+
value: theme ?? (prev.value === "light" ? "dark" : "light"),
|
|
3601
|
+
isManual: true
|
|
3602
|
+
};
|
|
3603
|
+
saveStore(next);
|
|
3604
|
+
return next;
|
|
3605
|
+
});
|
|
3606
|
+
};
|
|
3607
|
+
return { ...store, update, toggleTheme };
|
|
3608
|
+
}
|
|
3609
|
+
|
|
3527
3610
|
// src/react/index.ts
|
|
3528
3611
|
function useDebounce(value, delay = 500) {
|
|
3529
3612
|
const [debouncedValue, setDebouncedValue] = React11__default.useState(value);
|
|
@@ -3581,4 +3664,4 @@ function useMatchMedia2(queries, defaultValues = []) {
|
|
|
3581
3664
|
return value;
|
|
3582
3665
|
}
|
|
3583
3666
|
|
|
3584
|
-
export { block_default as default, useDebounce, useMatchMedia2 as useMatchMedia, useThrottle };
|
|
3667
|
+
export { block_default as default, useDebounce, useMatchMedia2 as useMatchMedia, useTheme, useThrottle };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qstd",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Standard Block component and utilities library with Panda CSS",
|
|
5
5
|
"author": "malin1",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,6 +41,22 @@
|
|
|
41
41
|
"sideEffects": [
|
|
42
42
|
"dist/react/index.css"
|
|
43
43
|
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "panda codegen && panda cssgen && tsup && node scripts/inject-css-import.js",
|
|
46
|
+
"dev": "tsup --watch",
|
|
47
|
+
"prepublishOnly": "pnpm build",
|
|
48
|
+
"prepare:local": "pnpm build && cd playground && pnpm prepare",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:watch": "vitest",
|
|
51
|
+
"test:all": "pnpx tsx tests/test-all.ts",
|
|
52
|
+
"test:block": "node tests/test-block.tsx",
|
|
53
|
+
"test:playground": "node tests/playground-e2e.mjs",
|
|
54
|
+
"typecheck": "tsc --noEmit",
|
|
55
|
+
"typecheck:perf": "tsc --noEmit --extendedDiagnostics",
|
|
56
|
+
"typecheck:trace": "tsc --noEmit --generateTrace ./performance/ts-trace",
|
|
57
|
+
"analyze:tsserver": "bash performance/analyze-tsserver.sh",
|
|
58
|
+
"lint": "eslint src --ext ts,tsx"
|
|
59
|
+
},
|
|
44
60
|
"peerDependencies": {
|
|
45
61
|
"react": "^18.0.0 || ^19.0.0",
|
|
46
62
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
@@ -93,20 +109,5 @@
|
|
|
93
109
|
"bugs": {
|
|
94
110
|
"url": "https://github.com/55cancri/qstd/issues"
|
|
95
111
|
},
|
|
96
|
-
"homepage": "https://github.com/55cancri/qstd#readme"
|
|
97
|
-
|
|
98
|
-
"build": "panda codegen && panda cssgen && tsup && node scripts/inject-css-import.js",
|
|
99
|
-
"dev": "tsup --watch",
|
|
100
|
-
"prepare:local": "pnpm build && cd playground && pnpm prepare",
|
|
101
|
-
"test": "vitest run",
|
|
102
|
-
"test:watch": "vitest",
|
|
103
|
-
"test:all": "pnpx tsx tests/test-all.ts",
|
|
104
|
-
"test:block": "node tests/test-block.tsx",
|
|
105
|
-
"test:playground": "node tests/playground-e2e.mjs",
|
|
106
|
-
"typecheck": "tsc --noEmit",
|
|
107
|
-
"typecheck:perf": "tsc --noEmit --extendedDiagnostics",
|
|
108
|
-
"typecheck:trace": "tsc --noEmit --generateTrace ./performance/ts-trace",
|
|
109
|
-
"analyze:tsserver": "bash performance/analyze-tsserver.sh",
|
|
110
|
-
"lint": "eslint src --ext ts,tsx"
|
|
111
|
-
}
|
|
112
|
-
}
|
|
112
|
+
"homepage": "https://github.com/55cancri/qstd#readme"
|
|
113
|
+
}
|