starlight-theme-bejamas 0.1.18 → 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 +32 -0
- package/package.json +8 -7
- package/src/config.test.ts +40 -0
- package/src/config.ts +4 -3
- package/src/global.d.ts +4 -2
- package/src/index.ts +0 -7
- package/src/lib/theme-choice.test.ts +251 -0
- package/src/lib/theme-choice.ts +346 -0
- package/src/lib/theme-stylesheet-runtime.test.ts +246 -0
- package/src/lib/theme-stylesheet.test.ts +16 -0
- package/src/lib/theme-stylesheet.ts +123 -0
- package/src/overrides/MobileTableOfContents.astro +6 -6
- package/src/overrides/PageFrame.astro +40 -83
- package/src/overrides/PageTitle.astro +2 -2
- package/src/overrides/TableOfContents/TableOfContentsList.astro +1 -1
- package/src/overrides/ThemeProvider.astro +101 -31
- package/src/overrides/ThemeSelect.astro +32 -47
- package/src/styles/theme.css +42 -36
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
export const CURRENT_THEME_STYLESHEET_SELECTOR =
|
|
2
|
+
"link[data-current-theme-stylesheet]";
|
|
3
|
+
|
|
4
|
+
export function buildCurrentThemeStylesheetHref(
|
|
5
|
+
href: string,
|
|
6
|
+
version: string | number,
|
|
7
|
+
) {
|
|
8
|
+
const url = new URL(href, "https://ui.bejamas.com");
|
|
9
|
+
url.searchParams.set("v", String(version));
|
|
10
|
+
|
|
11
|
+
const isAbsolute = /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(href);
|
|
12
|
+
return isAbsolute
|
|
13
|
+
? url.toString()
|
|
14
|
+
: `${url.pathname}${url.search}${url.hash}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const PENDING_THEME_STYLESHEET_SELECTOR =
|
|
18
|
+
"link[data-pending-current-theme-stylesheet]";
|
|
19
|
+
|
|
20
|
+
let stylesheetSwapToken = 0;
|
|
21
|
+
let stylesheetVersionCounter = 0;
|
|
22
|
+
let cancelPendingThemeStylesheetSwap: (() => void) | null = null;
|
|
23
|
+
|
|
24
|
+
function getAnimationFrame() {
|
|
25
|
+
if (typeof window !== "undefined" && window.requestAnimationFrame) {
|
|
26
|
+
return window.requestAnimationFrame.bind(window);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (callback: FrameRequestCallback) =>
|
|
30
|
+
window.setTimeout(() => callback(performance.now()), 0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function refreshCurrentThemeStylesheet() {
|
|
34
|
+
const currentStylesheet = document.querySelector<HTMLLinkElement>(
|
|
35
|
+
"link[data-current-theme-stylesheet]",
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (!currentStylesheet) {
|
|
39
|
+
return Promise.resolve();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
cancelPendingThemeStylesheetSwap?.();
|
|
43
|
+
|
|
44
|
+
// Strictly-monotonic version so two refreshes in the same millisecond can
|
|
45
|
+
// never collide on a cached response (which would swap in a stale theme).
|
|
46
|
+
const nextHref = buildCurrentThemeStylesheetHref(
|
|
47
|
+
currentStylesheet.href,
|
|
48
|
+
`${Date.now()}-${++stylesheetVersionCounter}`,
|
|
49
|
+
);
|
|
50
|
+
const nextStylesheet = currentStylesheet.cloneNode(true) as HTMLLinkElement;
|
|
51
|
+
const swapToken = ++stylesheetSwapToken;
|
|
52
|
+
const requestAnimationFrame = getAnimationFrame();
|
|
53
|
+
|
|
54
|
+
nextStylesheet.href = nextHref;
|
|
55
|
+
nextStylesheet.removeAttribute("data-current-theme-stylesheet");
|
|
56
|
+
nextStylesheet.setAttribute("data-pending-current-theme-stylesheet", "");
|
|
57
|
+
|
|
58
|
+
document
|
|
59
|
+
.querySelectorAll<HTMLLinkElement>(PENDING_THEME_STYLESHEET_SELECTOR)
|
|
60
|
+
.forEach((link) => {
|
|
61
|
+
if (link !== nextStylesheet) {
|
|
62
|
+
link.remove();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return new Promise<void>((resolve) => {
|
|
67
|
+
let settled = false;
|
|
68
|
+
|
|
69
|
+
const settle = () => {
|
|
70
|
+
if (settled) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
settled = true;
|
|
75
|
+
if (cancelPendingThemeStylesheetSwap === cancel) {
|
|
76
|
+
cancelPendingThemeStylesheetSwap = null;
|
|
77
|
+
}
|
|
78
|
+
resolve();
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const cleanup = () => {
|
|
82
|
+
nextStylesheet.removeEventListener("load", onLoad);
|
|
83
|
+
nextStylesheet.removeEventListener("error", onError);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const cancel = () => {
|
|
87
|
+
cleanup();
|
|
88
|
+
nextStylesheet.remove();
|
|
89
|
+
settle();
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const onError = () => {
|
|
93
|
+
cancel();
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const onLoad = () => {
|
|
97
|
+
cleanup();
|
|
98
|
+
|
|
99
|
+
if (swapToken !== stylesheetSwapToken) {
|
|
100
|
+
nextStylesheet.remove();
|
|
101
|
+
settle();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
currentStylesheet.removeAttribute("data-current-theme-stylesheet");
|
|
106
|
+
nextStylesheet.removeAttribute("data-pending-current-theme-stylesheet");
|
|
107
|
+
nextStylesheet.setAttribute("data-current-theme-stylesheet", "");
|
|
108
|
+
|
|
109
|
+
requestAnimationFrame(() => {
|
|
110
|
+
if (swapToken === stylesheetSwapToken) {
|
|
111
|
+
currentStylesheet.remove();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
settle();
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
cancelPendingThemeStylesheetSwap = cancel;
|
|
119
|
+
nextStylesheet.addEventListener("load", onLoad, { once: true });
|
|
120
|
+
nextStylesheet.addEventListener("error", onError, { once: true });
|
|
121
|
+
currentStylesheet.insertAdjacentElement("afterend", nextStylesheet);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
@@ -87,12 +87,12 @@ const { toc } = Astro.locals.starlightRoute;
|
|
|
87
87
|
align-items: center;
|
|
88
88
|
justify-content: space-between;
|
|
89
89
|
border: 1px solid var(--sl-color-gray-5);
|
|
90
|
-
border-radius:
|
|
90
|
+
border-radius: calc(var(--radius) - 2px);
|
|
91
91
|
padding-block: 0.5rem;
|
|
92
92
|
padding-inline-start: 0.75rem;
|
|
93
93
|
padding-inline-end: 0.5rem;
|
|
94
94
|
line-height: 1;
|
|
95
|
-
background-color: var(--
|
|
95
|
+
background-color: var(--background);
|
|
96
96
|
user-select: none;
|
|
97
97
|
cursor: pointer;
|
|
98
98
|
font-weight: 500;
|
|
@@ -133,11 +133,11 @@ const { toc } = Astro.locals.starlightRoute;
|
|
|
133
133
|
transition: background 200ms ease-out;
|
|
134
134
|
}
|
|
135
135
|
details[open] .toggle {
|
|
136
|
-
color: var(--
|
|
136
|
+
color: var(--foreground);
|
|
137
137
|
border-color: var(--sl-color-accent);
|
|
138
138
|
}
|
|
139
139
|
details .toggle:hover {
|
|
140
|
-
color: var(--
|
|
140
|
+
color: var(--foreground);
|
|
141
141
|
border-color: var(--sl-color-gray-2);
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -152,7 +152,7 @@ const { toc } = Astro.locals.starlightRoute;
|
|
|
152
152
|
white-space: nowrap;
|
|
153
153
|
text-overflow: ellipsis;
|
|
154
154
|
overflow: hidden;
|
|
155
|
-
color: var(--
|
|
155
|
+
color: var(--foreground);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
.dropdown {
|
|
@@ -164,7 +164,7 @@ const { toc } = Astro.locals.starlightRoute;
|
|
|
164
164
|
85vh - var(--sl-nav-height) - var(--sl-mobile-toc-height)
|
|
165
165
|
);
|
|
166
166
|
overflow-y: auto;
|
|
167
|
-
background-color: var(--
|
|
167
|
+
background-color: var(--popover);
|
|
168
168
|
box-shadow: var(--sl-shadow-md);
|
|
169
169
|
overscroll-behavior: contain;
|
|
170
170
|
}
|
|
@@ -28,83 +28,44 @@ const isSplashTemplate = template === "splash";
|
|
|
28
28
|
aria-label={Astro.locals.t("sidebarNav.accessibleLabel")}
|
|
29
29
|
>
|
|
30
30
|
<MobileMenuToggle />
|
|
31
|
-
<
|
|
31
|
+
<script>
|
|
32
|
+
if (!customElements.get("sl-sidebar-pane")) {
|
|
33
|
+
class StarlightSidebarPane extends HTMLElement {
|
|
34
|
+
constructor() {
|
|
35
|
+
super();
|
|
36
|
+
|
|
37
|
+
const desktopMediaQuery = matchMedia("(min-width: 64rem)");
|
|
38
|
+
const updatePopover = () => {
|
|
39
|
+
this.popover = desktopMediaQuery.matches ? null : "auto";
|
|
40
|
+
};
|
|
41
|
+
updatePopover();
|
|
42
|
+
desktopMediaQuery.addEventListener("change", updatePopover);
|
|
43
|
+
|
|
44
|
+
this.addEventListener("toggle", () => {
|
|
45
|
+
const isOpen = this.matches(":popover-open");
|
|
46
|
+
document
|
|
47
|
+
.querySelectorAll<HTMLElement>(".main-frame, .sl-skip-link")
|
|
48
|
+
.forEach((element) => element.toggleAttribute("inert", isOpen));
|
|
49
|
+
|
|
50
|
+
if (isOpen && this.closest(".sidebar-splash")) {
|
|
51
|
+
this.scrollTop = 0;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
customElements.define("sl-sidebar-pane", StarlightSidebarPane);
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
<sl-sidebar-pane popover="" id="starlight__sidebar" class="sidebar-pane">
|
|
32
61
|
<div class="sidebar-content sl-flex">
|
|
33
62
|
<slot name="sidebar" />
|
|
34
63
|
</div>
|
|
35
|
-
</
|
|
64
|
+
</sl-sidebar-pane>
|
|
36
65
|
</nav>
|
|
37
66
|
)
|
|
38
67
|
}
|
|
39
68
|
|
|
40
|
-
{
|
|
41
|
-
isSplashTemplate && (
|
|
42
|
-
<script is:inline>
|
|
43
|
-
(() => {
|
|
44
|
-
const installReset = () => {
|
|
45
|
-
const nav = document.querySelector("nav.sidebar-splash");
|
|
46
|
-
if (!(nav instanceof HTMLElement)) return () => {};
|
|
47
|
-
|
|
48
|
-
const pane = nav.querySelector("#starlight__sidebar");
|
|
49
|
-
const toggle = nav.querySelector("starlight-menu-button");
|
|
50
|
-
|
|
51
|
-
if (!(pane instanceof HTMLElement)) return () => {};
|
|
52
|
-
|
|
53
|
-
const resetToTop = () => {
|
|
54
|
-
pane.scrollTop = 0;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// Avoid opening splash menus at a persisted offset.
|
|
58
|
-
resetToTop();
|
|
59
|
-
|
|
60
|
-
if (!(toggle instanceof HTMLElement)) return () => {};
|
|
61
|
-
|
|
62
|
-
const onToggleChange = () => {
|
|
63
|
-
if (toggle.getAttribute("aria-expanded") === "true") {
|
|
64
|
-
requestAnimationFrame(resetToTop);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const onToggleClick = () => {
|
|
69
|
-
requestAnimationFrame(onToggleChange);
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
toggle.addEventListener("click", onToggleClick);
|
|
73
|
-
|
|
74
|
-
const observer = new MutationObserver(onToggleChange);
|
|
75
|
-
observer.observe(toggle, {
|
|
76
|
-
attributes: true,
|
|
77
|
-
attributeFilter: ["aria-expanded"],
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
return () => {
|
|
81
|
-
toggle.removeEventListener("click", onToggleClick);
|
|
82
|
-
observer.disconnect();
|
|
83
|
-
};
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
let cleanup = installReset();
|
|
87
|
-
|
|
88
|
-
const onAstroPageLoad = () => {
|
|
89
|
-
cleanup();
|
|
90
|
-
cleanup = installReset();
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
document.addEventListener("astro:page-load", onAstroPageLoad);
|
|
94
|
-
|
|
95
|
-
addEventListener(
|
|
96
|
-
"pagehide",
|
|
97
|
-
() => {
|
|
98
|
-
cleanup();
|
|
99
|
-
document.removeEventListener("astro:page-load", onAstroPageLoad);
|
|
100
|
-
},
|
|
101
|
-
{ once: true },
|
|
102
|
-
);
|
|
103
|
-
})();
|
|
104
|
-
</script>
|
|
105
|
-
)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
69
|
<div class:list={["main-frame", isSplashTemplate && "main-frame-splash"]}>
|
|
109
70
|
{
|
|
110
71
|
template === "splash" ? (
|
|
@@ -182,15 +143,17 @@ const isSplashTemplate = template === "splash";
|
|
|
182
143
|
}
|
|
183
144
|
|
|
184
145
|
.sidebar-pane {
|
|
185
|
-
|
|
146
|
+
border: 0;
|
|
147
|
+
margin: 0;
|
|
148
|
+
color: inherit;
|
|
149
|
+
display: none;
|
|
186
150
|
position: fixed;
|
|
187
151
|
z-index: var(--sl-z-index-menu);
|
|
188
152
|
inset-block: var(--sl-nav-height) 0;
|
|
189
153
|
inset-inline-start: 0;
|
|
190
154
|
width: 100%;
|
|
191
155
|
background-color: var(--background);
|
|
192
|
-
height: 100svh;
|
|
193
|
-
overflow: scroll;
|
|
156
|
+
height: calc(100svh - var(--sl-nav-height));
|
|
194
157
|
overflow-y: scroll;
|
|
195
158
|
overflow-x: hidden;
|
|
196
159
|
scrollbar-width: none;
|
|
@@ -198,12 +161,11 @@ const isSplashTemplate = template === "splash";
|
|
|
198
161
|
-webkit-overflow-scrolling: touch;
|
|
199
162
|
overscroll-behavior: contain;
|
|
200
163
|
overscroll-behavior-y: contain;
|
|
201
|
-
|
|
202
|
-
padding-top: calc(var(--spacing) * 18);
|
|
164
|
+
padding: 0;
|
|
203
165
|
}
|
|
204
166
|
|
|
205
|
-
|
|
206
|
-
|
|
167
|
+
.sidebar-pane:popover-open {
|
|
168
|
+
display: block;
|
|
207
169
|
}
|
|
208
170
|
|
|
209
171
|
.sidebar-content {
|
|
@@ -219,11 +181,6 @@ const isSplashTemplate = template === "splash";
|
|
|
219
181
|
content: "";
|
|
220
182
|
padding-bottom: 1px;
|
|
221
183
|
}
|
|
222
|
-
|
|
223
|
-
.sidebar-pane {
|
|
224
|
-
padding-top: 0;
|
|
225
|
-
margin-top: calc(var(--spacing) * 18);
|
|
226
|
-
}
|
|
227
184
|
}
|
|
228
185
|
|
|
229
186
|
.main-frame {
|
|
@@ -315,7 +272,7 @@ const isSplashTemplate = template === "splash";
|
|
|
315
272
|
padding-inline-end: var(--sl-nav-pad-x);
|
|
316
273
|
}
|
|
317
274
|
.sidebar-pane {
|
|
318
|
-
|
|
275
|
+
display: block;
|
|
319
276
|
width: var(--sl-sidebar-width);
|
|
320
277
|
background-color: var(--sl-color-bg-sidebar);
|
|
321
278
|
border-inline-end: 1px solid var(--sl-color-hairline-shade);
|
|
@@ -100,7 +100,7 @@ const isRtl = dir === "rtl";
|
|
|
100
100
|
display: flex;
|
|
101
101
|
justify-content: space-between;
|
|
102
102
|
flex-wrap: wrap;
|
|
103
|
-
margin-top: 0
|
|
103
|
+
margin-top: 0;
|
|
104
104
|
align-items: center;
|
|
105
105
|
}
|
|
106
106
|
.sl-bejamas-docs-title-buttons {
|
|
@@ -129,7 +129,7 @@ const isRtl = dir === "rtl";
|
|
|
129
129
|
font-size: 1.875rem;
|
|
130
130
|
line-height: 1.2;
|
|
131
131
|
font-weight: 500;
|
|
132
|
-
color: var(--
|
|
132
|
+
color: var(--foreground);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
@media (min-width: 50rem) {
|
|
@@ -70,7 +70,7 @@ const { toc, isMobile = false, depth = 0 } = Astro.props;
|
|
|
70
70
|
.isMobile a[aria-current="true"],
|
|
71
71
|
.isMobile a[aria-current="true"]:hover,
|
|
72
72
|
.isMobile a[aria-current="true"]:focus {
|
|
73
|
-
color: var(--
|
|
73
|
+
color: var(--foreground);
|
|
74
74
|
background-color: unset;
|
|
75
75
|
}
|
|
76
76
|
.isMobile a[aria-current="true"]::after {
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { buildThemeChoiceBootstrapInlineScript } from "../lib/theme-choice";
|
|
3
|
+
|
|
4
|
+
const themeChoiceBootstrapScript = buildThemeChoiceBootstrapInlineScript();
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<script is:inline set:html={themeChoiceBootstrapScript} />
|
|
8
|
+
|
|
1
9
|
{/* This is intentionally inlined to avoid FOUC. */}
|
|
2
10
|
<script is:inline>
|
|
3
11
|
window.StarlightThemeProvider = (() => {
|
|
4
|
-
const storageKey = "starlight-theme";
|
|
5
12
|
const themeMetaMedias = [
|
|
6
13
|
"(prefers-color-scheme: light)",
|
|
7
14
|
"(prefers-color-scheme: dark)",
|
|
@@ -14,12 +21,12 @@
|
|
|
14
21
|
theme === "auto" || theme === "dark" || theme === "light"
|
|
15
22
|
? theme
|
|
16
23
|
: "auto";
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
const getPreferredColorScheme = () =>
|
|
25
|
+
window.matchMedia("(prefers-color-scheme: light)").matches
|
|
26
|
+
? "light"
|
|
27
|
+
: "dark";
|
|
21
28
|
|
|
22
|
-
const
|
|
29
|
+
const readThemeColors = () => {
|
|
23
30
|
const html = document.documentElement;
|
|
24
31
|
const prevTheme = html.dataset.theme;
|
|
25
32
|
const prevDark = html.classList.contains("dark");
|
|
@@ -45,7 +52,7 @@
|
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
return { light, dark };
|
|
48
|
-
}
|
|
55
|
+
};
|
|
49
56
|
|
|
50
57
|
function ensureThemeMeta(head, media) {
|
|
51
58
|
const baseSelector = `meta[name="theme-color"][media="${media}"]`;
|
|
@@ -67,6 +74,7 @@
|
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
function syncThemeColor(theme) {
|
|
77
|
+
const themeColors = readThemeColors();
|
|
70
78
|
const color = themeColors[theme];
|
|
71
79
|
const head = document.head;
|
|
72
80
|
if (!color || !head) return;
|
|
@@ -77,23 +85,25 @@
|
|
|
77
85
|
});
|
|
78
86
|
}
|
|
79
87
|
|
|
80
|
-
const themeChoice = getStoredThemeChoice();
|
|
81
88
|
const effectiveTheme =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
: themeChoice;
|
|
87
|
-
document.documentElement.dataset.theme = effectiveTheme;
|
|
88
|
-
document.documentElement.dataset.themeChoice = themeChoice;
|
|
89
|
-
document.documentElement.classList.toggle(
|
|
90
|
-
"dark",
|
|
91
|
-
effectiveTheme === "dark",
|
|
92
|
-
);
|
|
89
|
+
document.documentElement.dataset.theme === "light" ||
|
|
90
|
+
document.documentElement.dataset.theme === "dark"
|
|
91
|
+
? document.documentElement.dataset.theme
|
|
92
|
+
: getPreferredColorScheme();
|
|
93
93
|
syncThemeColor(effectiveTheme);
|
|
94
94
|
return {
|
|
95
|
+
refreshThemeColors(theme) {
|
|
96
|
+
const themeChoice = parseTheme(
|
|
97
|
+
theme ?? document.documentElement.dataset.themeChoice,
|
|
98
|
+
);
|
|
99
|
+
const effectiveTheme =
|
|
100
|
+
themeChoice === "auto" ? getPreferredColorScheme() : themeChoice;
|
|
101
|
+
syncThemeColor(effectiveTheme);
|
|
102
|
+
},
|
|
95
103
|
updatePickers(theme) {
|
|
96
|
-
const themeChoice = parseTheme(
|
|
104
|
+
const themeChoice = parseTheme(
|
|
105
|
+
theme ?? document.documentElement.dataset.themeChoice,
|
|
106
|
+
);
|
|
97
107
|
document
|
|
98
108
|
.querySelectorAll("starlight-theme-select")
|
|
99
109
|
.forEach((picker) => {
|
|
@@ -110,19 +120,79 @@
|
|
|
110
120
|
new CustomEvent("select:set", { detail: { value: themeChoice } }),
|
|
111
121
|
);
|
|
112
122
|
}
|
|
113
|
-
|
|
114
|
-
/** @type {HTMLTemplateElement | null} */
|
|
115
|
-
const tmpl = document.querySelector(`#theme-icons`);
|
|
116
|
-
const newIcon =
|
|
117
|
-
tmpl && tmpl.content.querySelector("." + themeChoice);
|
|
118
|
-
if (newIcon) {
|
|
119
|
-
const oldIcon = picker.querySelector("svg.label-icon");
|
|
120
|
-
if (oldIcon) {
|
|
121
|
-
oldIcon.replaceChildren(...newIcon.cloneNode(true).childNodes);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
123
|
});
|
|
125
124
|
},
|
|
126
125
|
};
|
|
127
126
|
})();
|
|
128
127
|
</script>
|
|
128
|
+
|
|
129
|
+
<script>
|
|
130
|
+
import { syncSemanticIconsInRoot } from "@bejamas/semantic-icons/browser";
|
|
131
|
+
import { refreshCurrentThemeStylesheet } from "../lib/theme-stylesheet";
|
|
132
|
+
|
|
133
|
+
const PRESET_CHANGE_EVENT = "bejamas:preset-change";
|
|
134
|
+
const SETUP_KEY = "__bejamasCurrentThemeStylesheetSync";
|
|
135
|
+
let reloadFrame = 0;
|
|
136
|
+
let pendingIconLibrary = null;
|
|
137
|
+
|
|
138
|
+
function syncDocumentIconLibrary() {
|
|
139
|
+
const renderedIcon = document.querySelector("[data-icon-library]");
|
|
140
|
+
const iconLibrary =
|
|
141
|
+
renderedIcon instanceof SVGElement
|
|
142
|
+
? renderedIcon.dataset.iconLibrary
|
|
143
|
+
: document.documentElement.dataset.iconLibrary;
|
|
144
|
+
|
|
145
|
+
if (iconLibrary) {
|
|
146
|
+
document.documentElement.dataset.iconLibrary = iconLibrary;
|
|
147
|
+
syncSemanticIconsInRoot(document, iconLibrary);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const scheduleCurrentThemeStylesheetRefresh = () => {
|
|
152
|
+
if (reloadFrame) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
reloadFrame = requestAnimationFrame(() => {
|
|
157
|
+
reloadFrame = 0;
|
|
158
|
+
void refreshCurrentThemeStylesheet().then(() => {
|
|
159
|
+
if (pendingIconLibrary) {
|
|
160
|
+
document.documentElement.dataset.iconLibrary = pendingIconLibrary;
|
|
161
|
+
syncSemanticIconsInRoot(document, pendingIconLibrary);
|
|
162
|
+
pendingIconLibrary = null;
|
|
163
|
+
}
|
|
164
|
+
window.StarlightThemeProvider?.refreshThemeColors?.();
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
if (!(SETUP_KEY in window)) {
|
|
170
|
+
window[SETUP_KEY] = true;
|
|
171
|
+
syncDocumentIconLibrary();
|
|
172
|
+
|
|
173
|
+
window.addEventListener("theme-toggle-changed", (event) => {
|
|
174
|
+
const themeChoice =
|
|
175
|
+
event instanceof CustomEvent && typeof event.detail?.themeChoice === "string"
|
|
176
|
+
? event.detail.themeChoice
|
|
177
|
+
: undefined;
|
|
178
|
+
|
|
179
|
+
window.StarlightThemeProvider?.refreshThemeColors?.(themeChoice);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
window.addEventListener(PRESET_CHANGE_EVENT, (event) => {
|
|
183
|
+
const presetChange = event;
|
|
184
|
+
|
|
185
|
+
pendingIconLibrary =
|
|
186
|
+
presetChange instanceof CustomEvent &&
|
|
187
|
+
typeof presetChange.detail?.iconLibrary === "string"
|
|
188
|
+
? presetChange.detail.iconLibrary
|
|
189
|
+
: null;
|
|
190
|
+
|
|
191
|
+
scheduleCurrentThemeStylesheetRefresh();
|
|
192
|
+
});
|
|
193
|
+
document.addEventListener("astro:after-swap", () => {
|
|
194
|
+
window.StarlightThemeProvider?.refreshThemeColors?.();
|
|
195
|
+
syncDocumentIconLibrary();
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
</script>
|
|
@@ -36,61 +36,46 @@ import { SunIcon, MoonIcon, LaptopIcon } from "@lucide/astro";
|
|
|
36
36
|
</div>
|
|
37
37
|
</starlight-theme-select>
|
|
38
38
|
|
|
39
|
-
<script is:inline>
|
|
40
|
-
StarlightThemeProvider.updatePickers();
|
|
41
|
-
</script>
|
|
42
|
-
|
|
43
39
|
<script>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const loadTheme = (): Theme =>
|
|
51
|
-
parseTheme(
|
|
52
|
-
typeof localStorage !== "undefined" && localStorage.getItem(storageKey),
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
function storeTheme(theme: Theme): void {
|
|
56
|
-
if (typeof localStorage !== "undefined") {
|
|
57
|
-
localStorage.setItem(
|
|
58
|
-
storageKey,
|
|
59
|
-
theme === "light" || theme === "dark" ? theme : "",
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
40
|
+
import {
|
|
41
|
+
getThemeChoiceFromRoot,
|
|
42
|
+
parseStoredThemeChoice,
|
|
43
|
+
setThemeChoice,
|
|
44
|
+
syncThemeChoiceControls,
|
|
45
|
+
} from "../lib/theme-choice";
|
|
63
46
|
|
|
64
|
-
|
|
65
|
-
|
|
47
|
+
class StarlightThemeSelect extends HTMLElement {
|
|
48
|
+
private selectRoot: HTMLElement | null = null;
|
|
66
49
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
document.documentElement.dataset.theme = resolved;
|
|
71
|
-
document.documentElement.dataset.themeChoice = theme;
|
|
72
|
-
document.documentElement.classList.toggle("dark", resolved === "dark");
|
|
73
|
-
storeTheme(theme);
|
|
74
|
-
}
|
|
50
|
+
private onSelectChange = (event: Event) => {
|
|
51
|
+
const value = (event as CustomEvent<{ value?: unknown }>).detail?.value;
|
|
52
|
+
if (typeof value !== "string") return;
|
|
75
53
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
54
|
+
const nextTheme = parseStoredThemeChoice(value);
|
|
55
|
+
const currentTheme = getThemeChoiceFromRoot(document.documentElement);
|
|
56
|
+
if (nextTheme === currentTheme) return;
|
|
79
57
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
58
|
+
setThemeChoice(nextTheme);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
connectedCallback() {
|
|
62
|
+
this.selectRoot = this.querySelector<HTMLElement>("#starlight-theme-select");
|
|
63
|
+
syncThemeChoiceControls(
|
|
64
|
+
getThemeChoiceFromRoot(document.documentElement),
|
|
65
|
+
this,
|
|
66
|
+
);
|
|
67
|
+
this.selectRoot?.addEventListener("select:change", this.onSelectChange);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
disconnectedCallback() {
|
|
71
|
+
this.selectRoot?.removeEventListener("select:change", this.onSelectChange);
|
|
72
|
+
this.selectRoot = null;
|
|
90
73
|
}
|
|
91
74
|
}
|
|
92
75
|
|
|
93
|
-
customElements.
|
|
76
|
+
if (!customElements.get("starlight-theme-select")) {
|
|
77
|
+
customElements.define("starlight-theme-select", StarlightThemeSelect);
|
|
78
|
+
}
|
|
94
79
|
</script>
|
|
95
80
|
|
|
96
81
|
<style>
|