@r2digisolutions/components 0.14.0 → 0.14.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/README.md
CHANGED
|
@@ -97,24 +97,27 @@ Hay dos ejes independientes en `<html>`:
|
|
|
97
97
|
- **slate** — gris frío (hue 265, el look actual).
|
|
98
98
|
- **neutral** — croma 0, sin castaño azul. Misma familia que Tailwind `neutral` (`#171717`, `#fafafa`).
|
|
99
99
|
|
|
100
|
-
Para evitar FOUC, fíjalo en `app.html`
|
|
100
|
+
Para evitar FOUC, fíjalo en `app.html` con **cookie** (no `localStorage`; en SSR no existe `setItem`):
|
|
101
101
|
|
|
102
102
|
```html
|
|
103
103
|
<html lang="es" data-theme="neutral">
|
|
104
104
|
<head>
|
|
105
105
|
<script>
|
|
106
106
|
(function () {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
(!('theme' in localStorage) &&
|
|
113
|
-
!localStorage.getItem('r2-theme') &&
|
|
114
|
-
window.matchMedia('(prefers-color-scheme: dark)').matches)
|
|
115
|
-
) {
|
|
116
|
-
document.documentElement.classList.add('dark');
|
|
107
|
+
function getCookie(name) {
|
|
108
|
+
var m = document.cookie.match(
|
|
109
|
+
new RegExp('(?:^|; )' + name.replace(/([.$?*|{}()[\]\\/+^])/g, '\\$1') + '=([^;]*)')
|
|
110
|
+
);
|
|
111
|
+
return m ? decodeURIComponent(m[1]) : null;
|
|
117
112
|
}
|
|
113
|
+
var palette = getCookie('r2-theme-palette') || 'neutral';
|
|
114
|
+
document.documentElement.setAttribute('data-theme', palette);
|
|
115
|
+
var theme = getCookie('r2-theme');
|
|
116
|
+
var dark =
|
|
117
|
+
theme === 'dark' ||
|
|
118
|
+
((!theme || theme === 'system') &&
|
|
119
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
120
|
+
document.documentElement.classList.toggle('dark', dark);
|
|
118
121
|
})();
|
|
119
122
|
</script>
|
|
120
123
|
</head>
|
|
@@ -136,23 +136,24 @@
|
|
|
136
136
|
]}
|
|
137
137
|
>
|
|
138
138
|
{#snippet contextualHeaderDefault()}
|
|
139
|
-
{
|
|
139
|
+
{@const ctx = resolvedContextual}
|
|
140
|
+
{#if ctx}
|
|
140
141
|
<div class="min-w-0 space-y-1">
|
|
141
|
-
{#if
|
|
142
|
+
{#if ctx?.parentHref && ctx?.parentLabel}
|
|
142
143
|
<a
|
|
143
|
-
href={
|
|
144
|
+
href={ctx?.parentHref}
|
|
144
145
|
class="text-muted hover:text-primary block truncate text-[11px]"
|
|
145
146
|
>
|
|
146
|
-
{
|
|
147
|
+
{ctx?.parentLabel}
|
|
147
148
|
</a>
|
|
148
149
|
{/if}
|
|
149
|
-
<p class="text-sm font-semibold text-primary truncate">{
|
|
150
|
+
<p class="text-sm font-semibold text-primary truncate">{ctx?.brand}</p>
|
|
150
151
|
<div class="gap-1.5 flex flex-wrap items-center">
|
|
151
|
-
{#if
|
|
152
|
-
<Badge variant="secondary" size="sm">{
|
|
152
|
+
{#if ctx?.status}
|
|
153
|
+
<Badge variant="secondary" size="sm">{ctx?.status}</Badge>
|
|
153
154
|
{/if}
|
|
154
|
-
{#if
|
|
155
|
-
<span class="text-muted truncate text-[11px]">{
|
|
155
|
+
{#if ctx?.description}
|
|
156
|
+
<span class="text-muted truncate text-[11px]">{ctx?.description}</span>
|
|
156
157
|
{/if}
|
|
157
158
|
</div>
|
|
158
159
|
</div>
|
|
@@ -216,11 +217,11 @@
|
|
|
216
217
|
]}
|
|
217
218
|
>
|
|
218
219
|
<Sidebar
|
|
219
|
-
brand={resolvedContextual
|
|
220
|
-
groups={resolvedContextual
|
|
221
|
-
value={resolvedContextual
|
|
220
|
+
brand={resolvedContextual?.brand ?? brand}
|
|
221
|
+
groups={resolvedContextual?.groups ?? []}
|
|
222
|
+
value={resolvedContextual?.value ?? ''}
|
|
222
223
|
collapsible={false}
|
|
223
|
-
header={resolvedContextual
|
|
224
|
+
header={resolvedContextual?.header ?? contextualHeaderDefault}
|
|
224
225
|
onchange={onContextualNav}
|
|
225
226
|
/>
|
|
226
227
|
</div>
|
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
* Mode: `class="dark"` on `<html>`
|
|
5
5
|
* Palette: `data-theme="slate" | "neutral"` on `<html>`
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* - Detects system preference via prefers-color-scheme
|
|
9
|
-
* - Reactive via Svelte 5 runes ($state)
|
|
7
|
+
* Persists to cookies (readable by the FOUC script in `app.html` and by SSR).
|
|
10
8
|
*/
|
|
11
9
|
export type Theme = 'light' | 'dark' | 'system';
|
|
12
10
|
export type ThemePalette = 'slate' | 'neutral';
|
|
@@ -23,7 +21,7 @@ declare class ThemeStore {
|
|
|
23
21
|
* `slate` = cool (hue 265). `neutral` = chroma 0, no blue cast.
|
|
24
22
|
*
|
|
25
23
|
* Pass `{ persist: false }` for a page-level override that does not
|
|
26
|
-
*
|
|
24
|
+
* write the cookie.
|
|
27
25
|
*/
|
|
28
26
|
setPalette(palette: ThemePalette, opts?: {
|
|
29
27
|
persist?: boolean;
|
|
@@ -4,32 +4,67 @@
|
|
|
4
4
|
* Mode: `class="dark"` on `<html>`
|
|
5
5
|
* Palette: `data-theme="slate" | "neutral"` on `<html>`
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* - Detects system preference via prefers-color-scheme
|
|
9
|
-
* - Reactive via Svelte 5 runes ($state)
|
|
7
|
+
* Persists to cookies (readable by the FOUC script in `app.html` and by SSR).
|
|
10
8
|
*/
|
|
11
|
-
const
|
|
12
|
-
const
|
|
9
|
+
const THEME_COOKIE = 'r2-theme';
|
|
10
|
+
const PALETTE_COOKIE = 'r2-theme-palette';
|
|
11
|
+
const LEGACY_THEME_KEY = 'r2-theme';
|
|
12
|
+
const LEGACY_PALETTE_KEY = 'r2-theme-palette';
|
|
13
13
|
const DEFAULT_PALETTE = 'slate';
|
|
14
|
+
const COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
|
|
14
15
|
function getSystemTheme() {
|
|
15
16
|
if (typeof window === 'undefined')
|
|
16
17
|
return 'light';
|
|
17
18
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
18
19
|
}
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
return 'system';
|
|
22
|
-
return localStorage.getItem(STORAGE_KEY) ?? 'system';
|
|
20
|
+
function isTheme(value) {
|
|
21
|
+
return value === 'light' || value === 'dark' || value === 'system';
|
|
23
22
|
}
|
|
24
23
|
function isPalette(value) {
|
|
25
24
|
return value === 'slate' || value === 'neutral';
|
|
26
25
|
}
|
|
26
|
+
function readCookie(name) {
|
|
27
|
+
if (typeof document === 'undefined')
|
|
28
|
+
return null;
|
|
29
|
+
const escaped = name.replace(/([.$?*|{}()[\]\\/+^])/g, '\\$1');
|
|
30
|
+
const match = document.cookie.match(new RegExp(`(?:^|; )${escaped}=([^;]*)`));
|
|
31
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
32
|
+
}
|
|
33
|
+
function writeCookie(name, value) {
|
|
34
|
+
if (typeof document === 'undefined')
|
|
35
|
+
return;
|
|
36
|
+
document.cookie = `${name}=${encodeURIComponent(value)};path=/;max-age=${COOKIE_MAX_AGE};SameSite=Lax`;
|
|
37
|
+
}
|
|
38
|
+
function readLegacyStorage(key) {
|
|
39
|
+
if (typeof localStorage?.getItem !== 'function')
|
|
40
|
+
return null;
|
|
41
|
+
try {
|
|
42
|
+
return localStorage.getItem(key);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function getStoredTheme() {
|
|
49
|
+
const fromCookie = readCookie(THEME_COOKIE);
|
|
50
|
+
if (isTheme(fromCookie))
|
|
51
|
+
return fromCookie;
|
|
52
|
+
const legacy = readLegacyStorage(LEGACY_THEME_KEY);
|
|
53
|
+
if (isTheme(legacy)) {
|
|
54
|
+
writeCookie(THEME_COOKIE, legacy);
|
|
55
|
+
return legacy;
|
|
56
|
+
}
|
|
57
|
+
return 'system';
|
|
58
|
+
}
|
|
27
59
|
function getStoredPalette() {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
60
|
+
const fromCookie = readCookie(PALETTE_COOKIE);
|
|
61
|
+
if (isPalette(fromCookie))
|
|
62
|
+
return fromCookie;
|
|
63
|
+
const legacy = readLegacyStorage(LEGACY_PALETTE_KEY);
|
|
64
|
+
if (isPalette(legacy)) {
|
|
65
|
+
writeCookie(PALETTE_COOKIE, legacy);
|
|
66
|
+
return legacy;
|
|
67
|
+
}
|
|
33
68
|
if (typeof document !== 'undefined') {
|
|
34
69
|
const attr = document.documentElement.getAttribute('data-theme');
|
|
35
70
|
if (isPalette(attr))
|
|
@@ -53,17 +88,16 @@ class ThemeStore {
|
|
|
53
88
|
#palette = $state(DEFAULT_PALETTE);
|
|
54
89
|
#resolved = $derived(this.#theme === 'system' ? getSystemTheme() : this.#theme);
|
|
55
90
|
constructor() {
|
|
56
|
-
if (typeof window
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
91
|
+
if (typeof window === 'undefined')
|
|
92
|
+
return;
|
|
93
|
+
this.#theme = getStoredTheme();
|
|
94
|
+
this.#palette = getStoredPalette();
|
|
95
|
+
applyMode(this.#theme);
|
|
96
|
+
applyPalette(this.#palette);
|
|
97
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
98
|
+
if (this.#theme === 'system')
|
|
99
|
+
applyMode('system');
|
|
100
|
+
});
|
|
67
101
|
}
|
|
68
102
|
get theme() {
|
|
69
103
|
return this.#theme;
|
|
@@ -79,9 +113,7 @@ class ThemeStore {
|
|
|
79
113
|
}
|
|
80
114
|
set(theme) {
|
|
81
115
|
this.#theme = theme;
|
|
82
|
-
|
|
83
|
-
localStorage.setItem(STORAGE_KEY, theme);
|
|
84
|
-
}
|
|
116
|
+
writeCookie(THEME_COOKIE, theme);
|
|
85
117
|
applyMode(theme);
|
|
86
118
|
}
|
|
87
119
|
/**
|
|
@@ -89,13 +121,12 @@ class ThemeStore {
|
|
|
89
121
|
* `slate` = cool (hue 265). `neutral` = chroma 0, no blue cast.
|
|
90
122
|
*
|
|
91
123
|
* Pass `{ persist: false }` for a page-level override that does not
|
|
92
|
-
*
|
|
124
|
+
* write the cookie.
|
|
93
125
|
*/
|
|
94
126
|
setPalette(palette, opts) {
|
|
95
127
|
this.#palette = palette;
|
|
96
|
-
if (opts?.persist !== false
|
|
97
|
-
|
|
98
|
-
}
|
|
128
|
+
if (opts?.persist !== false)
|
|
129
|
+
writeCookie(PALETTE_COOKIE, palette);
|
|
99
130
|
applyPalette(palette);
|
|
100
131
|
}
|
|
101
132
|
toggle() {
|