aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/templates/assets/javascripts/components/palette
diff options
context:
space:
mode:
author简律纯 <i@jyunko.cn>2023-10-07 06:48:07 +0800
committer简律纯 <i@jyunko.cn>2023-10-07 06:48:07 +0800
commit991fd7a6d67ee017c57beaaa21fc31c4bee7944d (patch)
treee895202203fcaa50b0052f60ef6fc7d6d2928cf9 /src/templates/assets/javascripts/components/palette
parentd62900046bb6f754a8e6e7e670a66a90134055d9 (diff)
downloadinfini-991fd7a6d67ee017c57beaaa21fc31c4bee7944d.tar.gz
infini-991fd7a6d67ee017c57beaaa21fc31c4bee7944d.zip
feat(version): versions
Diffstat (limited to 'src/templates/assets/javascripts/components/palette')
-rw-r--r--src/templates/assets/javascripts/components/palette/index.ts180
1 files changed, 180 insertions, 0 deletions
diff --git a/src/templates/assets/javascripts/components/palette/index.ts b/src/templates/assets/javascripts/components/palette/index.ts
new file mode 100644
index 00000000..cf578f60
--- /dev/null
+++ b/src/templates/assets/javascripts/components/palette/index.ts
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2016-2023 Martin Donath <martin.donath@squidfunk.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+import {
+ Observable,
+ Subject,
+ asyncScheduler,
+ defer,
+ finalize,
+ fromEvent,
+ map,
+ mergeMap,
+ observeOn,
+ of,
+ shareReplay,
+ startWith,
+ tap
+} from "rxjs"
+
+import { getElements } from "~/browser"
+import { h } from "~/utilities"
+
+import {
+ Component,
+ getComponentElement
+} from "../_"
+
+/* ----------------------------------------------------------------------------
+ * Types
+ * ------------------------------------------------------------------------- */
+
+/**
+ * Palette colors
+ */
+export interface PaletteColor {
+ scheme?: string /* Color scheme */
+ primary?: string /* Primary color */
+ accent?: string /* Accent color */
+}
+
+/**
+ * Palette
+ */
+export interface Palette {
+ index: number /* Palette index */
+ color: PaletteColor /* Palette colors */
+}
+
+/* ----------------------------------------------------------------------------
+ * Functions
+ * ------------------------------------------------------------------------- */
+
+/**
+ * Watch color palette
+ *
+ * @param inputs - Color palette element
+ *
+ * @returns Color palette observable
+ */
+export function watchPalette(
+ inputs: HTMLInputElement[]
+): Observable<Palette> {
+ const current = __md_get<Palette>("__palette") || {
+ index: inputs.findIndex(input => matchMedia(
+ input.getAttribute("data-md-color-media")!
+ ).matches)
+ }
+
+ /* Emit changes in color palette */
+ return of(...inputs)
+ .pipe(
+ mergeMap(input => fromEvent(input, "change")
+ .pipe(
+ map(() => input)
+ )
+ ),
+ startWith(inputs[Math.max(0, current.index)]),
+ map(input => ({
+ index: inputs.indexOf(input),
+ color: {
+ scheme: input.getAttribute("data-md-color-scheme"),
+ primary: input.getAttribute("data-md-color-primary"),
+ accent: input.getAttribute("data-md-color-accent")
+ }
+ } as Palette)),
+ shareReplay(1)
+ )
+}
+
+/**
+ * Mount color palette
+ *
+ * @param el - Color palette element
+ *
+ * @returns Color palette component observable
+ */
+export function mountPalette(
+ el: HTMLElement
+): Observable<Component<Palette>> {
+ const meta = h("meta", { name: "theme-color" })
+ document.head.appendChild(meta)
+
+ // Add color scheme meta tag
+ const scheme = h("meta", { name: "color-scheme" })
+ document.head.appendChild(scheme)
+
+ /* Mount component on subscription */
+ return defer(() => {
+ const push$ = new Subject<Palette>()
+ push$.subscribe(palette => {
+ document.body.setAttribute("data-md-color-switching", "")
+
+ /* Set color palette */
+ for (const [key, value] of Object.entries(palette.color))
+ document.body.setAttribute(`data-md-color-${key}`, value)
+
+ /* Toggle visibility */
+ for (let index = 0; index < inputs.length; index++) {
+ const label = inputs[index].nextElementSibling
+ if (label instanceof HTMLElement)
+ label.hidden = palette.index !== index
+ }
+
+ /* Persist preference in local storage */
+ __md_set("__palette", palette)
+ })
+
+ /* Update theme-color meta tag */
+ push$
+ .pipe(
+ map(() => {
+ const header = getComponentElement("header")
+ const style = window.getComputedStyle(header)
+
+ // Set color scheme
+ scheme.content = style.colorScheme
+
+ /* Return color in hexadecimal format */
+ return style.backgroundColor.match(/\d+/g)!
+ .map(value => (+value).toString(16).padStart(2, "0"))
+ .join("")
+ })
+ )
+ .subscribe(color => meta.content = `#${color}`)
+
+ /* Revert transition durations after color switch */
+ push$.pipe(observeOn(asyncScheduler))
+ .subscribe(() => {
+ document.body.removeAttribute("data-md-color-switching")
+ })
+
+ /* Create and return component */
+ const inputs = getElements<HTMLInputElement>("input", el)
+ return watchPalette(inputs)
+ .pipe(
+ tap(state => push$.next(state)),
+ finalize(() => push$.complete()),
+ map(state => ({ ref: el, ...state }))
+ )
+ })
+}