UNPKG

angular-material-palette-generator

Version:

This package lets you refine the color palettes to be integrated into Sass theme files generated by the Angular CLI (`ng generate @angular/material:theme-color`).

857 lines (838 loc) 156 kB
import { CdkDrag } from '@angular/cdk/drag-drop'; import * as i0 from '@angular/core'; import { inject, DOCUMENT, signal, computed, effect, Injectable, ElementRef, input, afterRenderEffect, Directive, viewChildren, output, ViewEncapsulation, Component, booleanAttribute, Pipe, model, viewChild, untracked, Injector, RendererFactory2, DestroyRef } from '@angular/core'; import * as i1$3 from '@angular/forms'; import { FormGroup, FormControl, Validators, NgControl, ReactiveFormsModule } from '@angular/forms'; import * as i3 from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip'; import Color from 'colorjs.io'; import * as i1 from '@angular/material/core'; import { MAT_RIPPLE_GLOBAL_OPTIONS, MatRipple } from '@angular/material/core'; import { BreakpointObserver } from '@angular/cdk/layout'; import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; import * as i1$1 from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button'; import * as i2 from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'; import * as i3$3 from '@angular/material/sidenav'; import { MatSidenavModule } from '@angular/material/sidenav'; import { startWith, filter, map, shareReplay } from 'rxjs'; import { TitleCasePipe } from '@angular/common'; import * as i1$2 from '@angular/material/button-toggle'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import * as i3$2 from '@angular/material/dialog'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { contrastRatio } from 'wcag-contrast-utils'; import * as i6 from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field'; import * as i3$1 from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select'; import * as i2$1 from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox'; import * as i4 from '@angular/material/input'; import { MatInputModule } from '@angular/material/input'; import * as i6$1 from '@angular/material/slider'; import { MatSliderModule } from '@angular/material/slider'; import * as i7 from '@angular/cdk/text-field'; import * as i2$2 from '@angular/material/badge'; import { MatBadgeModule } from '@angular/material/badge'; import * as i5 from '@angular/material/chips'; import { MatChipsModule } from '@angular/material/chips'; import * as i9 from '@angular/material/progress-bar'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import * as i10 from '@angular/material/radio'; import { MatRadioModule } from '@angular/material/radio'; import * as i11 from '@angular/material/slide-toggle'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import * as i12 from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs'; import { Overlay } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; const HEX_COLOR_REGEXP = /^#?([0-9abcdef]{3}|[0-9abcdef]{6})$/i; const HEX_COLOR_ERROR_KEY = 'hexColor'; const hexColorValidator = (control) => control.value?.match(HEX_COLOR_REGEXP) ? null : { [HEX_COLOR_ERROR_KEY]: true }; const getDefaultParams = () => ({ p1x: 0, p1y: 0, p2x: 1, p2y: 1 }); const getPaletteGenForm = () => new FormGroup({ color: new FormControl('', [Validators.required, hexColorValidator]), start: new FormControl(0, [Validators.required]), end: new FormControl(100, [Validators.required]), params: new FormControl(getDefaultParams()), reverse: new FormControl(false), neutral: new FormControl(false), }); const parsePaletteGenFormValue = (value) => { try { const formValue = JSON.parse(value); getPaletteGenForm().setValue(formValue); // This will throw an error if the JSON is invalid return formValue; } catch { return null; } }; const paletteGenFormValuesEqual = (a, b) => a.color === b.color && a.start === b.start && a.end === b.end && a.reverse === b.reverse && a.neutral === b.neutral && a.params.p1x === b.params.p1x && a.params.p1y === b.params.p1y && a.params.p2x === b.params.p2x && a.params.p2y === b.params.p2y; /* eslint-disable @typescript-eslint/no-explicit-any */ // ---- // NOTE // ---- // Since the "bezier-easing" package is NOT ESM, we have copied/pasted the source code directly. // - `module.exports` has been replaced by `export default`, and `var` by `const`. // - Some types has been inferred. /** * https://github.com/gre/bezier-easing * BezierEasing - use bezier curve for transition easing function * by Gaëtan Renaudeau 2014 - 2015 – MIT License */ // These values are established by empiricism with tests (tradeoff: performance VS precision) const NEWTON_ITERATIONS = 4; const NEWTON_MIN_SLOPE = 0.001; const SUBDIVISION_PRECISION = 0.0000001; const SUBDIVISION_MAX_ITERATIONS = 10; const kSplineTableSize = 11; const kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); const float32ArraySupported = typeof Float32Array === 'function'; function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } function C(aA1) { return 3.0 * aA1; } // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. function calcBezier(aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; } // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. function getSlope(aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); } function binarySubdivide(aX, aA, aB, mX1, mX2) { let currentX, currentT, i = 0; do { currentT = aA + (aB - aA) / 2.0; currentX = calcBezier(currentT, mX1, mX2) - aX; if (currentX > 0.0) { aB = currentT; } else { aA = currentT; } } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); return currentT; } function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) { for (let i = 0; i < NEWTON_ITERATIONS; ++i) { const currentSlope = getSlope(aGuessT, mX1, mX2); if (currentSlope === 0.0) { return aGuessT; } const currentX = calcBezier(aGuessT, mX1, mX2) - aX; aGuessT -= currentX / currentSlope; } return aGuessT; } function LinearEasing(x) { return x; } function bezier(mX1, mY1, mX2, mY2) { if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) { throw new Error('bezier x values must be in [0, 1] range'); } if (mX1 === mY1 && mX2 === mY2) { return LinearEasing; } // Precompute samples table const sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize); for (let i = 0; i < kSplineTableSize; ++i) { sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2); } function getTForX(aX) { let intervalStart = 0.0; let currentSample = 1; const lastSample = kSplineTableSize - 1; for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) { intervalStart += kSampleStepSize; } --currentSample; // Interpolate to provide an initial guess for t const dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]); const guessForT = intervalStart + dist * kSampleStepSize; const initialSlope = getSlope(guessForT, mX1, mX2); if (initialSlope >= NEWTON_MIN_SLOPE) { return newtonRaphsonIterate(aX, guessForT, mX1, mX2); } else if (initialSlope === 0.0) { return guessForT; } else { return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2); } } return function BezierEasing(x) { // Because JavaScript number are imprecise, we should guarantee the extremes are right. if (x === 0 || x === 1) { return x; } return calcBezier(getTForX(x), mY1, mY2); }; } const cubicBezierFactory = ({ p1x, p1y, p2x, p2y }) => { const cubicBezier = bezier(p1x, p1y, p2x, p2y); return (x) => cubicBezier(x); }; const buildPaletteGenFormValue = (color, params = { p1x: 0, p1y: 0, p2x: 1, p2y: 1 }, neutral = false) => ({ color, start: 0, end: 100, params, reverse: false, neutral, }); const percentageToRgbFactory = ({ color, params, reverse, }) => { const cubicBezier = cubicBezierFactory(params); const lightness = reverse ? (percent) => 1 - cubicBezier(percent / 100) : (percent) => cubicBezier(percent / 100); const _color = new Color(color); return (percent) => { _color.oklch['l'] = lightness(percent); const hex = _color.toString({ format: 'hex' }); if (hex.length === 4) { const [, r, g, b] = hex.split(''); return `#${r}${r}${g}${g}${b}${b}`; } return hex; }; }; const buildPaletteGenData = (formValue) => { if (!formValue) { return { list: [], colorMap: {} }; } const { color, start, end, params, reverse, neutral } = formValue; const percentages = MATERIAL_PALETTE_PERCENTAGES_MAP[neutral ? 'neutral' : 'default']; const percentageToRgb = percentageToRgbFactory({ color, params, reverse }); const list = percentages .map((percentage) => ({ percentage, adjustedPercentage: start + (percentage / 100) * (end - start), })) .map(({ percentage, adjustedPercentage }) => ({ percentage, color: percentageToRgb(adjustedPercentage) })); const colorMap = list.reduce((map, { percentage, color }) => { map[percentage] = color; return map; }, {}); return { list, colorMap }; }; const buildSassMapStringified = (formValue, dataList) => { const settings = ` // ${JSON.stringify(formValue)}\n`; const sassMap = dataList.map(({ percentage, color }) => ` ${percentage}: ${color},\n`).join(''); return settings + sassMap; }; const PALETTE_FORM_CONTROL_SIZE_DEFAULT = 200; const MATERIAL_PALETTE_PERCENTAGES_MAP = { // Works for Material `primary`, `secondary`, `tertiary`, `neutral-variant` and `error` palettes default: [0, 10, 20, 25, 30, 35, 40, 50, 60, 70, 80, 90, 95, 98, 99, 100], // Works for Material `neutral` palette neutral: [0, 4, 6, 10, 12, 17, 20, 22, 24, 25, 30, 35, 40, 50, 60, 70, 80, 87, 90, 92, 94, 95, 96, 98, 99, 100], }; // Match `$azure-palette` from https://github.com/angular/components/blob/main/src/material/core/theming/_palettes.scss const FORM_VALUE_MAP_DEFAULT = { primary: buildPaletteGenFormValue('#005cbb', { p1x: 0, p1y: 0.21, p2x: 1, p2y: 1 }), secondary: buildPaletteGenFormValue('#565e71', { p1x: 0, p1y: 0.2, p2x: 1, p2y: 1 }), tertiary: buildPaletteGenFormValue('#343dff', { p1x: 0, p1y: 0.26, p2x: 1, p2y: 1 }), neutral: buildPaletteGenFormValue('#5e5e62', { p1x: 0, p1y: 0.2, p2x: 1, p2y: 1 }, true), 'neutral-variant': buildPaletteGenFormValue('#5b5e66', { p1x: 0, p1y: 0.2, p2x: 1, p2y: 1 }), error: buildPaletteGenFormValue('#ba1a1a', { p1x: 0, p1y: 0.16, p2x: 0.88, p2y: 1 }), }; const PALETTE_MODES = ['light', 'dark']; const PALETTE_NAMES = ['primary', 'secondary', 'tertiary', 'neutral', 'neutral-variant', 'error']; // TODO: need to refactor this service... // TODO: add a global import... class PaletteGenService { document = inject(DOCUMENT); localStorage = this.document.defaultView?.localStorage; paletteMode = signal('light'); paletteName = signal('primary'); formValueMap = PALETTE_NAMES.reduce((map, paletteName) => { map[paletteName] = signal(FORM_VALUE_MAP_DEFAULT[paletteName]); return map; }, {}); formValue = computed(() => this.formValueMap[this.paletteName()]); dataMap = PALETTE_NAMES.reduce((map, paletteName) => { map[paletteName] = computed(() => buildPaletteGenData(this.formValueMap[paletteName]())); return map; }, {}); formValueSnapshotsMap = PALETTE_NAMES.reduce((map, paletteName) => { map[paletteName] = signal([]); return map; }, {}); reset() { PALETTE_NAMES.forEach((name) => { this.formValueMap[name].set(FORM_VALUE_MAP_DEFAULT[name]); this.formValueSnapshotsMap[name].set([]); }); } controlSize = signal(PALETTE_FORM_CONTROL_SIZE_DEFAULT); refreshCanvas; refreshCanvasOn(trigger) { this.refreshCanvas = trigger; } constructor() { this.restoreFormValueMap(); effect(() => this.storeFormValueMap()); this.restorePaletteState(); effect(() => this.storePaletteState()); this.restoreFormValueSnapshotsMap(); effect(() => this.storeFormValueSnapshotsMap()); } sassMapsToClipboard() { const text = Object.entries(this.dataMap) .map(([paletteName, data]) => { const paletteMap = buildSassMapStringified(this.formValueMap[paletteName](), data().list); return `${paletteName}: (\n${paletteMap}),`; }) .join('\n'); this.document.defaultView?.navigator.clipboard?.writeText(text); } // ----- FormValueMap storage ----- formValueMapStorageKey = 'pg-palette-gen-service.form-value-map'; restoreFormValueMap() { const value = this.localStorage?.getItem(this.formValueMapStorageKey); if (!value) { return; } try { const formValueMap = JSON.parse(value); const form = getPaletteGenForm(); Object.entries(formValueMap).forEach(([paletteName, formValue]) => { form.setValue(formValue); // Validate each `formValue` this.formValueMap[paletteName].set(formValue); }); } catch (err) { this.localStorage?.removeItem(this.formValueMapStorageKey); console.error('PaletteGenService: unable to restore FormValueMap', value, err); } } storeFormValueMap() { const formValueMap = Object.entries(this.formValueMap).reduce((map, [paletteName, formValue]) => { map[paletteName] = formValue(); return map; }, {}); this.localStorage?.setItem(this.formValueMapStorageKey, JSON.stringify(formValueMap)); } // ----- FormValueSnapshotsMap storage ----- formValueMapSnapshotsStorageKey = 'pg-palette-gen-service.form-value-snapshots-map'; restoreFormValueSnapshotsMap() { const value = this.localStorage?.getItem(this.formValueMapSnapshotsStorageKey); if (!value) { return; } try { const formValueSnapshotsMap = JSON.parse(value); const form = getPaletteGenForm(); Object.entries(formValueSnapshotsMap).forEach(([paletteName, snapshots]) => { snapshots.forEach((snapshot) => form.setValue(JSON.parse(snapshot.value))); // Validate each `snapshot` this.formValueSnapshotsMap[paletteName].set(snapshots); }); } catch (err) { this.localStorage?.removeItem(this.formValueMapSnapshotsStorageKey); console.error('PaletteGenService: unable to restore FormValueSnapshotsMap', value, err); } } storeFormValueSnapshotsMap() { const formValueSnapshotsMap = Object.entries(this.formValueSnapshotsMap).reduce((map, [paletteName, snapshots]) => { map[paletteName] = snapshots(); return map; }, {}); this.localStorage?.setItem(this.formValueMapSnapshotsStorageKey, JSON.stringify(formValueSnapshotsMap)); } // ----- PaletteState storage ----- paletteStateStorageKey = 'pg-palette-gen-service.palette-state'; restorePaletteState() { const value = this.localStorage?.getItem(this.paletteStateStorageKey); if (!value) { return; } try { const { mode, name } = JSON.parse(value); if (PALETTE_MODES.includes(mode) && PALETTE_NAMES.includes(name)) { this.paletteMode.set(mode); this.paletteName.set(name); } else { throw new Error('Invalid palette mode and/or name'); } } catch (err) { this.localStorage?.removeItem(this.paletteStateStorageKey); console.error('PaletteGenService: unable to restore PaletteState', value, err); } } storePaletteState() { const paletteState = { mode: this.paletteMode(), name: this.paletteName(), }; this.localStorage?.setItem(this.paletteStateStorageKey, JSON.stringify(paletteState)); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: PaletteGenService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: PaletteGenService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: PaletteGenService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: () => [] }); class CubicBezierControlColorsDirective { host = inject(ElementRef).nativeElement; service = inject(PaletteGenService); cssVarName = input.required({ alias: 'pgCubicBezierControlColors' }); rgbColor = signal(undefined); // Part of the public directive API constructor() { afterRenderEffect(() => { this.service.refreshCanvas?.(); this.cssVarName(); const rgbColor = this.host.computedStyleMap().get('color')?.toString(); this.rgbColor.set(rgbColor); }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: CubicBezierControlColorsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.0", type: CubicBezierControlColorsDirective, isStandalone: true, selector: "[pgCubicBezierControlColors]", inputs: { cssVarName: { classPropertyName: "cssVarName", publicName: "pgCubicBezierControlColors", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "style.color": "\"var(\" + cssVarName() + \")\"" } }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: CubicBezierControlColorsDirective, decorators: [{ type: Directive, args: [{ selector: '[pgCubicBezierControlColors]', host: { '[style.color]': '"var(" + cssVarName() + ")"', }, }] }], ctorParameters: () => [] }); class CubicBezierControlColorsComponent { cssVarConfig = input.required(); cssVarNames = computed(() => Object.values(this.cssVarConfig())); cssVarItems = viewChildren(CubicBezierControlColorsDirective); colorMap = computed(() => { const keys = Object.keys(this.cssVarConfig()); return this.cssVarItems().reduce((map, item, index) => { const rgbColor = item.rgbColor(); if (rgbColor) { map[keys[index]] = rgbColor; } return map; }, {}); }); colorMapChange = output(); constructor() { effect(() => this.colorMapChange.emit(this.colorMap())); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: CubicBezierControlColorsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.0", type: CubicBezierControlColorsComponent, isStandalone: true, selector: "pg-cubic-bezier-control-colors", inputs: { cssVarConfig: { classPropertyName: "cssVarConfig", publicName: "cssVarConfig", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { colorMapChange: "colorMapChange" }, host: { properties: { "attr.role": "\"presentation\"" }, classAttribute: ".pg-cubic-bezier-control-colors" }, viewQueries: [{ propertyName: "cssVarItems", predicate: CubicBezierControlColorsDirective, descendants: true, isSignal: true }], ngImport: i0, template: "@for (cssVarName of cssVarNames(); track $index) {\n <i [pgCubicBezierControlColors]=\"cssVarName\"></i>\n}\n", styles: [".pg-cubic-bezier-control-colors{position:absolute;left:-99999px}\n"], dependencies: [{ kind: "directive", type: CubicBezierControlColorsDirective, selector: "[pgCubicBezierControlColors]", inputs: ["pgCubicBezierControlColors"] }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: CubicBezierControlColorsComponent, decorators: [{ type: Component, args: [{ selector: 'pg-cubic-bezier-control-colors', host: { class: '.pg-cubic-bezier-control-colors', '[attr.role]': '"presentation"', }, imports: [CubicBezierControlColorsDirective], encapsulation: ViewEncapsulation.None, template: "@for (cssVarName of cssVarNames(); track $index) {\n <i [pgCubicBezierControlColors]=\"cssVarName\"></i>\n}\n", styles: [".pg-cubic-bezier-control-colors{position:absolute;left:-99999px}\n"] }] }], ctorParameters: () => [] }); class CubicBezierControlDirectionDirective { direction = output({ alias: 'pgCubicBezierControlDirection' }); emit(event, direction) { event.preventDefault(); this.direction.emit(direction); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: CubicBezierControlDirectionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.0", type: CubicBezierControlDirectionDirective, isStandalone: true, selector: "[pgCubicBezierControlDirection]", outputs: { direction: "pgCubicBezierControlDirection" }, host: { listeners: { "keydown.ArrowUp": "emit($event, \"up\")", "keydown.ArrowRight": "emit($event, \"right\")", "keydown.ArrowDown": "emit($event, \"down\")", "keydown.ArrowLeft": "emit($event, \"left\")" } }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: CubicBezierControlDirectionDirective, decorators: [{ type: Directive, args: [{ selector: '[pgCubicBezierControlDirection]', host: { '(keydown.ArrowUp)': 'emit($event, "up")', '(keydown.ArrowRight)': 'emit($event, "right")', '(keydown.ArrowDown)': 'emit($event, "down")', '(keydown.ArrowLeft)': 'emit($event, "left")', }, }] }] }); class CubicBezierControlRippleDirective { globalRippleDisabled = inject(MAT_RIPPLE_GLOBAL_OPTIONS, { optional: true })?.disabled === true; matRipple = inject(MatRipple, { self: true }); rippleRef; enabled = input(true, { transform: booleanAttribute, alias: 'pgCubicBezierControlRipple' }); constructor() { this.matRipple.disabled = true; // Prevent the first self-triggered ripple this.matRipple.color = 'rgb(from var(--mat-sys-primary) r g b / 0.24)'; this.matRipple.centered = true; this.matRipple.unbounded = true; this.matRipple.radius = 24; } launch() { if (this.globalRippleDisabled || !this.enabled()) { return; } this.matRipple.disabled = false; this.rippleRef = this.matRipple.launch({ persistent: true }); } fadeOut() { if (!this.rippleRef) { return; } this.rippleRef.fadeOut(); this.matRipple.disabled = true; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: CubicBezierControlRippleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.0", type: CubicBezierControlRippleDirective, isStandalone: true, selector: "[pgCubicBezierControlRipple]", inputs: { enabled: { classPropertyName: "enabled", publicName: "pgCubicBezierControlRipple", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "focus": "launch()", "blur": "fadeOut()" } }, hostDirectives: [{ directive: i1.MatRipple }], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: CubicBezierControlRippleDirective, decorators: [{ type: Directive, args: [{ selector: '[pgCubicBezierControlRipple]', hostDirectives: [MatRipple], host: { '(focus)': 'launch()', '(blur)': 'fadeOut()', }, }] }], ctorParameters: () => [] }); const cubicBezierParamsToPoints = ({ params: { p1x, p1y, p2x, p2y }, size, }) => { const func = (x, y) => ({ x: x * size, y: y * size }); return { p1: func(p1x, p1y), p2: func(p2x, p2y), }; }; const pointToCubicBezierParam = ({ point, container, size }) => { const func = (_point, _container) => Math.round((1000 * (_point - _container)) / size) / 1000; return { x: func(point.x, container.x), y: func(point.y, container.y), }; }; // -------------------- class CanvasHandler { ctx; canvasSize; colors; constructor(ctx, canvasSize, colors) { this.ctx = ctx; this.canvasSize = canvasSize; this.colors = colors; } clear() { this.ctx.clearRect(0, 0, this.canvasSize, this.canvasSize); return this; } linear() { this.ctx.beginPath(); this.ctx.moveTo(0, 0); this.ctx.lineWidth = 4; this.ctx.lineTo(this.canvasSize, this.canvasSize); this.ctx.strokeStyle = this.colors.linearColor; this.ctx.stroke(); return this; } curve(interpolate) { this.ctx.beginPath(); this.ctx.moveTo(0, 0); this.ctx.lineWidth = 4; this.ctx.lineJoin = 'round'; for (let step = 0; step <= this.canvasSize; step += 1) { this.ctx.lineTo(step, interpolate(step / this.canvasSize) * this.canvasSize); } this.ctx.strokeStyle = this.colors.curveColor; this.ctx.stroke(); return this; } sticks(params) { this.ctx.beginPath(); this.ctx.lineWidth = 1; this.ctx.lineCap = 'round'; this.ctx.moveTo(0, 0); this.ctx.lineTo(this.canvasSize * params.p1x, this.canvasSize * params.p1y); this.ctx.moveTo(this.canvasSize, this.canvasSize); this.ctx.lineTo(this.canvasSize * params.p2x, this.canvasSize * params.p2y); this.ctx.strokeStyle = this.colors.stickColor; this.ctx.stroke(); return this; } } // -------------------- const movePoint = ([x, y], direction, delta) => { const more = (coord) => Math.round(Math.min(coord + delta, 1) * 100) / 100; const less = (coord) => Math.round(Math.max(0, coord - delta) * 100) / 100; switch (direction) { case 'up': return [x, less(y)]; case 'right': return [more(x), y]; case 'down': return [x, more(y)]; case 'left': return [less(x), y]; } }; const moveCubicBezierParams = ({ p1x, p1y, p2x, p2y }, p, direction, delta = 0.01) => { if (p === 'p1') { const [x, y] = movePoint([p1x, p1y], direction, delta); return { p1x: x, p1y: y, p2x, p2y }; } else { const [x, y] = movePoint([p2x, p2y], direction, delta); return { p1x, p1y, p2x: x, p2y: y }; } }; class CubicBezierParamsPipe { transform({ p1x, p1y, p2x, p2y }, p) { let x; let y; if (p === 'p1') { x = p1x; y = p1y; } else { x = p2x; y = p2y; } return `x: ${x.toFixed(2)} - y: ${y.toFixed(2)}`; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: CubicBezierParamsPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.0.0", ngImport: i0, type: CubicBezierParamsPipe, isStandalone: true, name: "cubicBezierParams" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: CubicBezierParamsPipe, decorators: [{ type: Pipe, args: [{ name: 'cubicBezierParams', }] }] }); class CubicBezierControlComponent { container = inject(ElementRef).nativeElement; params = model({ p1x: 0, p1y: 0, p2x: 1, p2y: 1 }); cubicBezier = computed(() => cubicBezierFactory(this.params())); canvasRef = viewChild.required('canvas'); canvasHandler = computed(() => { const ctx = this.canvasRef().nativeElement.getContext('2d'); return ctx ? new CanvasHandler(ctx, this.canvasSize(), this.colorMap()) : undefined; }); canvasSize = input(PALETTE_FORM_CONTROL_SIZE_DEFAULT); p1 = viewChild.required('p1'); p2 = viewChild.required('p2'); disabled = model(false); skipNextPointsUpdate = false; cdkDrags = viewChildren(CdkDrag); constructor() { afterRenderEffect(() => this.updatePoints()); afterRenderEffect(() => this.updateCanvas()); const ngControl = inject(NgControl, { optional: true, self: true }); if (ngControl) { ngControl.valueAccessor = this; } } updatePoints() { const params = this.params(); const size = this.canvasSize(); if (this.skipNextPointsUpdate) { this.skipNextPointsUpdate = false; return; } const { p1, p2 } = cubicBezierParamsToPoints({ params, size }); const [p1Drag, p2Drag] = untracked(() => this.cdkDrags()); p1Drag.setFreeDragPosition(p1); p2Drag.setFreeDragPosition(p2); } updateParam(p) { const point = this[p]().nativeElement.getBoundingClientRect(); const container = this.container.getBoundingClientRect(); const size = this.canvasSize(); this.skipNextPointsUpdate = true; this.params.update((params) => { const { x, y } = pointToCubicBezierParam({ point, container, size }); const newParams = { ...params }; if (p === 'p1') { newParams.p1x = x; newParams.p1y = y; } else { newParams.p2x = x; newParams.p2y = y; } return newParams; }); this.onChange(this.params()); this.onTouched(); } moveParams(p, direction) { this.params.update((params) => moveCubicBezierParams(params, p, direction)); this.onChange(this.params()); this.onTouched(); } updateCanvas() { const canvasHandler = this.canvasHandler(); if (!canvasHandler) { console.warn('ColorGenBezierComponent: canvas is not supported'); return; } canvasHandler.clear().linear().curve(this.cubicBezier()).sticks(this.params()); } // ----- ControlValueAccessor ----- onChange = () => undefined; onTouched = () => undefined; registerOnChange(onChange) { this.onChange = onChange; } registerOnTouched(onTouched) { this.onTouched = onTouched; } writeValue(params) { this.params.set(params ?? { p1x: 0, p1y: 0, p2x: 1, p2y: 1 }); } setDisabledState(disabled) { this.disabled.set(disabled); } // ----- Material colors in RGB (for the canvas configuration) ----- cssVarConfig = { linearColor: '--mat-sys-surface-container-highest', curveColor: '--mat-sys-primary', stickColor: '--mat-sys-on-surface-variant', }; colorMap = signal({ linearColor: 'grey', curveColor: 'grey', stickColor: 'grey', }); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: CubicBezierControlComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.0.0", type: CubicBezierControlComponent, isStandalone: true, selector: "pg-cubic-bezier-control", inputs: { params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: false, transformFunction: null }, canvasSize: { classPropertyName: "canvasSize", publicName: "canvasSize", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { params: "paramsChange", disabled: "disabledChange" }, host: { properties: { "class.pg-cubic-bezier-control--disabled": "disabled()", "style.--pg-cubic-bezier-control-canvas-size": "canvasSize() + \"px\"" }, classAttribute: "pg-cubic-bezier-control" }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["canvas"], descendants: true, isSignal: true }, { propertyName: "p1", first: true, predicate: ["p1"], descendants: true, isSignal: true }, { propertyName: "p2", first: true, predicate: ["p2"], descendants: true, isSignal: true }, { propertyName: "cdkDrags", predicate: CdkDrag, descendants: true, isSignal: true }], ngImport: i0, template: "<canvas #canvas [width]=\"canvasSize()\" [height]=\"canvasSize()\" class=\"pg-cubic-bezier-control__canvas\"></canvas>\n\n<div\n class=\"pg-cubic-bezier-control__point\"\n cdkDrag\n cdkDragBoundary=\".pg-cubic-bezier-control\"\n [cdkDragDisabled]=\"disabled()\"\n (cdkDragMoved)=\"updateParam('p1')\"\n #p1\n>\n <button\n [matTooltip]=\"params() | cubicBezierParams: 'p1'\"\n matTooltipPosition=\"left\"\n [pgCubicBezierControlRipple]=\"!disabled()\"\n (pgCubicBezierControlDirection)=\"moveParams('p1', $event)\"\n >\n p1\n </button>\n</div>\n\n<div\n class=\"pg-cubic-bezier-control__point\"\n cdkDrag\n cdkDragBoundary=\".pg-cubic-bezier-control\"\n [cdkDragDisabled]=\"disabled()\"\n (cdkDragMoved)=\"updateParam('p2')\"\n #p2\n>\n <button\n [matTooltip]=\"params() | cubicBezierParams: 'p2'\"\n matTooltipPosition=\"right\"\n [pgCubicBezierControlRipple]=\"!disabled()\"\n (pgCubicBezierControlDirection)=\"moveParams('p2', $event)\"\n >\n p2\n </button>\n</div>\n\n<pg-cubic-bezier-control-colors [cssVarConfig]=\"cssVarConfig\" (colorMapChange)=\"colorMap.set($event)\" />\n", styles: [".pg-cubic-bezier-control{position:relative;display:block;width:var(--pg-cubic-bezier-control-canvas-size);height:var(--pg-cubic-bezier-control-canvas-size);transition:opacity ease .12s;text-align:left}.pg-cubic-bezier-control--disabled{opacity:.5}.pg-cubic-bezier-control__canvas{position:absolute;z-index:1;width:100%;height:100%;border-radius:.25rem;background-color:rgb(from var(--mat-sys-surface-variant) r g b/.24)}.pg-cubic-bezier-control__point{position:absolute!important;z-index:2;top:0;left:0;width:0;height:0}.pg-cubic-bezier-control__point button{content:\"\";cursor:pointer;outline:none;position:absolute;top:-.625rem;left:-.625rem;width:1.25rem;height:1.25rem;margin:0;padding:0;border-radius:99px;border:none;background:var(--mat-sys-primary);color:transparent;line-height:inherit;text-align:inherit;font:inherit;-webkit-user-select:none;user-select:none}\n"], dependencies: [{ kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: CubicBezierControlColorsComponent, selector: "pg-cubic-bezier-control-colors", inputs: ["cssVarConfig"], outputs: ["colorMapChange"] }, { kind: "directive", type: CubicBezierControlDirectionDirective, selector: "[pgCubicBezierControlDirection]", outputs: ["pgCubicBezierControlDirection"] }, { kind: "directive", type: CubicBezierControlRippleDirective, selector: "[pgCubicBezierControlRipple]", inputs: ["pgCubicBezierControlRipple"] }, { kind: "pipe", type: CubicBezierParamsPipe, name: "cubicBezierParams" }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: CubicBezierControlComponent, decorators: [{ type: Component, args: [{ selector: 'pg-cubic-bezier-control', host: { class: 'pg-cubic-bezier-control', '[class.pg-cubic-bezier-control--disabled]': 'disabled()', '[style.--pg-cubic-bezier-control-canvas-size]': 'canvasSize() + "px"', }, imports: [ CdkDrag, MatTooltipModule, CubicBezierControlColorsComponent, CubicBezierControlDirectionDirective, CubicBezierControlRippleDirective, CubicBezierParamsPipe, ], encapsulation: ViewEncapsulation.None, template: "<canvas #canvas [width]=\"canvasSize()\" [height]=\"canvasSize()\" class=\"pg-cubic-bezier-control__canvas\"></canvas>\n\n<div\n class=\"pg-cubic-bezier-control__point\"\n cdkDrag\n cdkDragBoundary=\".pg-cubic-bezier-control\"\n [cdkDragDisabled]=\"disabled()\"\n (cdkDragMoved)=\"updateParam('p1')\"\n #p1\n>\n <button\n [matTooltip]=\"params() | cubicBezierParams: 'p1'\"\n matTooltipPosition=\"left\"\n [pgCubicBezierControlRipple]=\"!disabled()\"\n (pgCubicBezierControlDirection)=\"moveParams('p1', $event)\"\n >\n p1\n </button>\n</div>\n\n<div\n class=\"pg-cubic-bezier-control__point\"\n cdkDrag\n cdkDragBoundary=\".pg-cubic-bezier-control\"\n [cdkDragDisabled]=\"disabled()\"\n (cdkDragMoved)=\"updateParam('p2')\"\n #p2\n>\n <button\n [matTooltip]=\"params() | cubicBezierParams: 'p2'\"\n matTooltipPosition=\"right\"\n [pgCubicBezierControlRipple]=\"!disabled()\"\n (pgCubicBezierControlDirection)=\"moveParams('p2', $event)\"\n >\n p2\n </button>\n</div>\n\n<pg-cubic-bezier-control-colors [cssVarConfig]=\"cssVarConfig\" (colorMapChange)=\"colorMap.set($event)\" />\n", styles: [".pg-cubic-bezier-control{position:relative;display:block;width:var(--pg-cubic-bezier-control-canvas-size);height:var(--pg-cubic-bezier-control-canvas-size);transition:opacity ease .12s;text-align:left}.pg-cubic-bezier-control--disabled{opacity:.5}.pg-cubic-bezier-control__canvas{position:absolute;z-index:1;width:100%;height:100%;border-radius:.25rem;background-color:rgb(from var(--mat-sys-surface-variant) r g b/.24)}.pg-cubic-bezier-control__point{position:absolute!important;z-index:2;top:0;left:0;width:0;height:0}.pg-cubic-bezier-control__point button{content:\"\";cursor:pointer;outline:none;position:absolute;top:-.625rem;left:-.625rem;width:1.25rem;height:1.25rem;margin:0;padding:0;border-radius:99px;border:none;background:var(--mat-sys-primary);color:transparent;line-height:inherit;text-align:inherit;font:inherit;-webkit-user-select:none;user-select:none}\n"] }] }], ctorParameters: () => [] }); class PreferBlackForgroundColorPipe { transform(backgroundColor) { return contrastRatio(backgroundColor, '#fff') < contrastRatio(backgroundColor, '#000'); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: PreferBlackForgroundColorPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.0.0", ngImport: i0, type: PreferBlackForgroundColorPipe, isStandalone: true, name: "preferBlackForgroundColor" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: PreferBlackForgroundColorPipe, decorators: [{ type: Pipe, args: [{ name: 'preferBlackForgroundColor', }] }] }); class PaletteGenOverviewComponent { service = inject(PaletteGenService); percentages = MATERIAL_PALETTE_PERCENTAGES_MAP.default; overview = computed(() => { return Object.entries(this.service.dataMap).reduce((acc, [paletteName, data]) => { acc.push({ paletteName: paletteName, dataList: data().list }); return acc; }, []); }); compact = input(false); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: PaletteGenOverviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.0", type: PaletteGenOverviewComponent, isStandalone: true, selector: "pg-palette-gen-overview", inputs: { compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "pg-palette-gen-overview" }, ngImport: i0, template: "@for (overviewItem of overview(); track $index) {\n <h2 class=\"pg-palette-gen-overview__name\">{{ overviewItem.paletteName | titlecase }}</h2>\n\n <div class=\"pg-palette-gen-overview__list\">\n @for (dataListItem of overviewItem.dataList; track $index) {\n @if (percentages.includes(dataListItem.percentage)) {\n <span\n [matTooltip]=\"dataListItem.color\"\n class=\"pg-palette-gen-overview__item\"\n [class.pg-palette-gen-overview__item--prefer-black]=\"dataListItem.color | preferBlackForgroundColor\"\n [class.pg-palette-gen-overview__item--compact]=\"compact()\"\n [style.background-color]=\"dataListItem.color\"\n >\n {{ dataListItem.percentage }}\n </span>\n }\n }\n </div>\n}\n", styles: [".pg-palette-gen-overview__name{margin:1rem 0 .5rem;font:var(--mat-sys-title-medium);letter-spacing:var(--mat-sys-title-medium-tracking)}.pg-palette-gen-overview__list{display:flex}.pg-palette-gen-overview__item{height:4rem;line-height:4rem;flex-basis:3rem;text-align:center;font-size:var(--pl-font-size-monospace, .85rem);font-family:var(--pl-font-family-monospace, monospace);font-optical-sizing:auto;font-weight:400;font-style:normal;border:1px solid rgba(255,255,255,.1);border-width:0 0 0 1px;color:#fff;transition:all ease .1s;transform-origin:center 3.5rem}.pg-palette-gen-overview__item--prefer-black{border:1px solid rgba(0,0,0,.05);border-width:0 0 0 1px;color:#000}.pg-palette-gen-overview__item:first-child{border-radius:.25rem 0 0 .25rem}.pg-palette-gen-overview__item:last-child{border-radius:0 .25rem .25rem 0}.pg-palette-gen-overview__item:hover{z-index:1;border-radius:.25rem;border-width:1px;transform:scale(1.5)}.pg-palette-gen-overview__item--compact{height:3rem;line-height:3rem;transform-origin:center 2.5rem}\n"], dependencies: [{ kind: "pipe", type: TitleCasePipe, name: "titlecase" }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: PreferBlackForgroundColorPipe, name: "preferBlackForgroundColor" }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: PaletteGenOverviewComponent, decorators: [{ type: Component, args: [{ selector: 'pg-palette-gen-overview', host: { class: 'pg-palette-gen-overview' }, imports: [TitleCasePipe, MatTooltipModule, PreferBlackForgroundColorPipe], encapsulation: ViewEncapsulation.None, template: "@for (overviewItem of overview(); track $index) {\n <h2 class=\"pg-palette-gen-overview__name\">{{ overviewItem.paletteName | titlecase }}</h2>\n\n <div class=\"pg-palette-gen-overview__list\">\n @for (dataListItem of overviewItem.dataList; track $index) {\n @if (percentages.includes(dataListItem.percentage)) {\n <span\n [matTooltip]=\"dataListItem.color\"\n class=\"pg-palette-gen-overview__item\"\n [class.pg-palette-gen-overview__item--prefer-black]=\"dataListItem.color | preferBlackForgroundColor\"\n [class.pg-palette-gen-overview__item--compact]=\"compact()\"\n [style.background-color]=\"dataListItem.color\"\n >\n {{ dataListItem.percentage }}\n </span>\n }\n }\n </div>\n}\n", styles: [".pg-palette-gen-overview__name{margin:1rem 0 .5rem;font:var(--mat-sys-title-medium);letter-spacing:var(--mat-sys-title-medium-tracking)}.pg-palette-gen-overview__list{display:flex}.pg-palette-gen-overview__item{height:4rem;line-height:4rem;flex-basis:3rem;text-align:center;font-size:var(--pl-font-size-monospace, .85rem);font-family:var(--pl-font-family-monospace, monospace);font-optical-sizing:auto;font-weight:400;font-style:normal;border:1px solid rgba(255,255,255,.1);border-width:0 0 0 1px;color:#fff;transition:all ease .1s;transform-origin:center 3.5rem}.pg-palette-gen-overview__item--prefer-black{border:1px solid rgba(0,0,0,.05);border-width:0 0 0 1px;color:#000}.pg-palette-gen-overview__item:first-child{border-radius:.25rem 0 0 .25rem}.pg-palette-gen-overview__item:last-child{border-radius:0 .25rem .25rem 0}.pg-palette-gen-overview__item:hover{z-index:1;border-radius:.25rem;border-width:1px;transform:scale(1.5)}.pg-palette-gen-overview__item--compact{height:3rem;line-height:3rem;transform-origin:center 2.5rem}\n"] }] }] }); // According to WCAG 2.1 guidelines, the required contrast ratios for normal text are: // - AA: 4.5 // - AAA: 7 class WcagContrastRatioCompliancePipe { transform(colorA, colorB) { if (!colorB) { return undefined; } const ratio = contrastRatio(colorA, colorB); if (ratio >= 7) { return { icon: 'done_all', label: 'WCAG 2 AAA Compliant' }; } if (ratio >= 4.5) { return { icon: 'check', label: 'WCAG 2 AA Compliant' }; } return { icon: 'warning', label: 'Not WCAG Compliant' }; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: WcagContrastRatioCompliancePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.0.0", ngImport: i0, type: WcagContrastRatioCompliancePipe, isStandalone: true, name: "wcagContrastRatioCompliance" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: WcagContrastRatioCompliancePipe, decorators: [{ type: Pipe, args: [{ name: 'wcagContrastRatioCompliance', }] }] }); // DO NOT MODIFY THE FOLLOWING OBJECTS MANUALLY // -------------------------------------------- // The content of following objects has been generated from the script: // `<rootDir>/palette-matching/palette-matching.script.ts` const PALETTE_TOKEN_MATCHING_MAP = { primary: { light: { '10': ['on-primary-container', 'on-primary-fixed'], '30': ['on-primary-fixed-variant'], '40': ['primary', 'surface-tint'], '80': ['inverse-primary', 'primary-fixed-dim'], '90': ['primary-container', 'primary-fixed'], '100': ['on-primary'], }, dark: { '10': ['on-primary-fixed'], '20': ['on-primary'], '30': ['primary-container', 'on-primary-fixed-variant'], '40': ['inverse-primary'], '80': ['primary', 'primary-fixed-dim', 'surface-tint'], '90': ['on-primary-container', 'primary-fixed'], }, }, secondary: { light: { '10': ['on-secondary-container', 'on-secondary-fixed'], '30': ['on-secondary-fixed-variant'], '40': ['secondary'], '80': ['secondary-fixed-dim'], '90': ['secondary-container', 'secondary-fixed'], '100': ['on-secondary'], }, dark: { '10': ['on-secondary-fixed'], '20': ['on-secondary'], '30': ['secondary-container', 'on-secondary-fixed-variant'], '80': ['secondary', 'secondary-fixed-dim'], '90': ['on-secondary-container', 'secondary-fixed'], }, }, tertiary: { light: { '10': ['on-tertiary-container', 'on-tertiary-fixed'], '30': ['on-tertiary-fixed-variant'], '40': ['tertiary'], '80': ['tertiary-fixed-dim'], '90': ['tertiary-contai