From 23d1655721712ec9e2c902638d56e66e526416bf Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 14:06:06 -0400 Subject: [PATCH 01/14] reorder properties of SchemeNameMap and imports of schemes.ts --- src/utils/dynamic-color/schemes.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/utils/dynamic-color/schemes.ts b/src/utils/dynamic-color/schemes.ts index 690ba56..14a046a 100644 --- a/src/utils/dynamic-color/schemes.ts +++ b/src/utils/dynamic-color/schemes.ts @@ -1,22 +1,21 @@ import { - SchemeContent, - SchemeFidelity, - SchemeNeutral, SchemeMonochrome, + SchemeNeutral, + SchemeTonalSpot, SchemeVibrant, SchemeExpressive, - SchemeTonalSpot, - Hct, + SchemeFidelity, + SchemeContent, } from "@material/material-color-utilities"; export const SchemeNameMap = { - content: SchemeContent, - fidelity: SchemeFidelity, - neutral: SchemeNeutral, monochrome: SchemeMonochrome, + neutral: SchemeNeutral, + "tonal-spot": SchemeTonalSpot, vibrant: SchemeVibrant, expressive: SchemeExpressive, - "tonal-spot": SchemeTonalSpot, + fidelity: SchemeFidelity, + content: SchemeContent, } as const; export type SchemeName = keyof typeof SchemeNameMap; From d6d5c982a1a6662f4f12f69c4a737f225e750952 Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 14:12:10 -0400 Subject: [PATCH 02/14] export from schemes.ts --- src/utils/dynamic-color/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/dynamic-color/index.ts b/src/utils/dynamic-color/index.ts index 1498f69..cac701f 100644 --- a/src/utils/dynamic-color/index.ts +++ b/src/utils/dynamic-color/index.ts @@ -1 +1,2 @@ +export * from "./schemes"; export * from "./material-color"; From 8276e390a8ffdd83edd0b2cc9073d201aafa3b17 Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 14:16:58 -0400 Subject: [PATCH 03/14] add dynamic-scheme.ts --- .../__tests__/dynamic-scheme.spec.ts | 114 ++++++++++++++++++ src/utils/dynamic-color/dynamic-scheme.ts | 47 ++++++++ src/utils/dynamic-color/index.ts | 1 + 3 files changed, 162 insertions(+) create mode 100644 src/utils/dynamic-color/__tests__/dynamic-scheme.spec.ts create mode 100644 src/utils/dynamic-color/dynamic-scheme.ts diff --git a/src/utils/dynamic-color/__tests__/dynamic-scheme.spec.ts b/src/utils/dynamic-color/__tests__/dynamic-scheme.spec.ts new file mode 100644 index 0000000..541449e --- /dev/null +++ b/src/utils/dynamic-color/__tests__/dynamic-scheme.spec.ts @@ -0,0 +1,114 @@ +import { describe, it, expect } from "vitest"; +import { ref } from "vue"; +import { + argbFromHex, + SchemeFidelity, + SchemeExpressive, +} from "@material/material-color-utilities"; + +import { useDynamicScheme } from ".."; +import type { UseDynamicSchemeOptions, SchemeName } from ".."; + +describe("useDynamicScheme", () => { + it("returns readonly scheme", () => { + const options: UseDynamicSchemeOptions = { + schemeName: "expressive", + sourceColorArgb: argbFromHex("#2196F3"), + dark: true, + contrastLevel: 0.2, + }; + + const { schemeName, dark, scheme } = useDynamicScheme(options); + + expect(scheme.value).toBeInstanceOf(SchemeExpressive); + expect(scheme.value.sourceColorArgb).toBe(argbFromHex("#2196F3")); + expect(scheme.value.isDark).toBe(true); + expect(scheme.value.contrastLevel).toBe(0.2); + + schemeName.value = "fidelity"; + dark.value = false; + + expect(scheme.value).toBeInstanceOf(SchemeFidelity); + expect(scheme.value.sourceColorArgb).toBe(argbFromHex("#2196F3")); + expect(scheme.value.isDark).toBe(false); + expect(scheme.value.contrastLevel).toBe(0.2); + }); + + it("returns default options", () => { + const { schemeName, sourceColorArgb, dark, contrastLevel } = + useDynamicScheme(); + + expect(schemeName.value).toBe("fidelity"); + expect(sourceColorArgb.value).toBe(argbFromHex("#6750A4")); + expect(dark.value).toBe(false); + expect(contrastLevel.value).toBe(0.0); + }); + + it("returns reactive options", () => { + const options: UseDynamicSchemeOptions = { + schemeName: "expressive", + sourceColorArgb: 4280391411, // #2196F3 + dark: true, + contrastLevel: 0.2, + }; + + const { schemeName, sourceColorArgb, dark, contrastLevel } = + useDynamicScheme(options); + + expect(schemeName.value).toBe("expressive"); + expect(sourceColorArgb.value).toBe(4280391411); + expect(dark.value).toBe(true); + expect(contrastLevel.value).toBe(0.2); + }); + + it("keeps options reactive", () => { + const options = { + schemeName: ref("expressive"), + sourceColorArgb: ref(4280391411), // #2196F3 + dark: ref(true), + contrastLevel: ref(0.2), + }; + + const { schemeName, sourceColorArgb, dark, contrastLevel } = + useDynamicScheme(options); + + expect(schemeName.value).toBe("expressive"); + expect(sourceColorArgb.value).toBe(4280391411); + expect(dark.value).toBe(true); + expect(contrastLevel.value).toBe(0.2); + + options.schemeName.value = "fidelity"; + options.sourceColorArgb.value = argbFromHex("#3F51B5"); + options.dark.value = false; + options.contrastLevel.value = 0.3; + + expect(schemeName.value).toBe("fidelity"); + expect(sourceColorArgb.value).toBe(argbFromHex("#3F51B5")); + expect(dark.value).toBe(false); + expect(contrastLevel.value).toBe(0.3); + + schemeName.value = "neutral"; + sourceColorArgb.value = argbFromHex("#673AB7"); + dark.value = true; + contrastLevel.value = 0.4; + + expect(options.schemeName.value).toBe("neutral"); + expect(options.sourceColorArgb.value).toBe(argbFromHex("#673AB7")); + expect(options.dark.value).toBe(true); + expect(options.contrastLevel.value).toBe(0.4); + }); + + it("returns readonly scheme class", () => { + const { schemeName, schemeClass } = useDynamicScheme(); + expect(schemeClass.value.name).toBe("SchemeFidelity"); + schemeName.value = "expressive"; + expect(schemeClass.value.name).toBe("SchemeExpressive"); + }); + + it("returns readonly source color Hct", () => { + const { sourceColorArgb, sourceColorHct } = useDynamicScheme(); + expect(sourceColorHct.value.toInt()).toBe(argbFromHex("#6750A4")); + sourceColorArgb.value = argbFromHex("#2196F3"); + expect(sourceColorHct.value.toInt()).toBe(argbFromHex("#2196F3")); + }); +}); diff --git a/src/utils/dynamic-color/dynamic-scheme.ts b/src/utils/dynamic-color/dynamic-scheme.ts new file mode 100644 index 0000000..3217752 --- /dev/null +++ b/src/utils/dynamic-color/dynamic-scheme.ts @@ -0,0 +1,47 @@ +import { computed, ref } from "vue"; +import type { MaybeRef, UnwrapRef } from "vue"; +import { argbFromHex, Hct } from "@material/material-color-utilities"; +import { SchemeNameMap } from "./schemes"; +import type { SchemeName } from "./schemes"; + +export type UseDynamicSchemeOptions = { + schemeName?: MaybeRef; + sourceColorArgb?: MaybeRef; + dark?: MaybeRef; + contrastLevel?: MaybeRef; +}; + +const optDefault: Required> = { + schemeName: "fidelity", + sourceColorArgb: argbFromHex("#6750A4"), + dark: false, + contrastLevel: 0.0, +}; + +export function useDynamicScheme(options?: UseDynamicSchemeOptions) { + const schemeName = ref(options?.schemeName ?? optDefault.schemeName); + const sourceColorArgb = ref( + options?.sourceColorArgb ?? optDefault.sourceColorArgb + ); + const dark = ref(options?.dark ?? optDefault.dark); + const contrastLevel = ref(options?.contrastLevel ?? optDefault.contrastLevel); + const schemeClass = computed(() => SchemeNameMap[schemeName.value]); + const sourceColorHct = computed(() => Hct.fromInt(sourceColorArgb.value)); + const scheme = computed( + () => + new schemeClass.value( + sourceColorHct.value, + dark.value, + contrastLevel.value + ) + ); + return { + scheme, + schemeName, + sourceColorArgb, + dark, + contrastLevel, + schemeClass, + sourceColorHct, + }; +} diff --git a/src/utils/dynamic-color/index.ts b/src/utils/dynamic-color/index.ts index cac701f..15c9cde 100644 --- a/src/utils/dynamic-color/index.ts +++ b/src/utils/dynamic-color/index.ts @@ -1,2 +1,3 @@ export * from "./schemes"; +export * from "./dynamic-scheme"; export * from "./material-color"; From ba60252262e859931d3fe146de23867f70fe257b Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 14:39:58 -0400 Subject: [PATCH 04/14] rename useDynamicScheme() to useDynamicSchemeOld() --- src/utils/dynamic-color/material-color.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/dynamic-color/material-color.ts b/src/utils/dynamic-color/material-color.ts index ef535fb..e580c09 100644 --- a/src/utils/dynamic-color/material-color.ts +++ b/src/utils/dynamic-color/material-color.ts @@ -87,7 +87,7 @@ const replaceHctWithHex = (colorsHct: { [k in ColorName]: Hct }) => ) as { [k in ColorName]: string }; export function useDynamicColorsHct(options?: MaybeRef) { - const scheme = useDynamicScheme(options); + const scheme = useDynamicSchemeOld(options); const colorsHct = computed(() => generateColorsFromScheme(toValue(scheme))); return { colorsHct, scheme }; } @@ -103,7 +103,7 @@ export function hexFromHct(hct: Hct) { /** * Create a dynamic scheme reactively. */ -function useDynamicScheme(options?: MaybeRef) { +function useDynamicSchemeOld(options?: MaybeRef) { const opt = computed(() => ({ ...DEFAULT_OPTIONS, ...toValue(options) })); const schemeName = computed(() => opt.value.schemeName); const schemeClass = computed(() => SchemeNameMap[schemeName.value]); From 997248fcab22292f84de18ea2cb36124093aa0c1 Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 14:40:39 -0400 Subject: [PATCH 05/14] rename useDynamicColors() to useDynamicColorsOld() --- src/utils/color-theme/color-theme.ts | 8 ++++---- src/utils/dynamic-color/__tests__/material-color.spec.ts | 4 ++-- src/utils/dynamic-color/material-color.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/utils/color-theme/color-theme.ts b/src/utils/color-theme/color-theme.ts index 911bbf0..1ae192a 100644 --- a/src/utils/color-theme/color-theme.ts +++ b/src/utils/color-theme/color-theme.ts @@ -4,11 +4,11 @@ import { useTheme } from "vuetify"; import type { ThemeDefinition } from "vuetify"; import type { OmitIndexSignature } from "type-fest"; -import { useDynamicColors } from "@/utils/dynamic-color"; +import { useDynamicColorsOld } from "@/utils/dynamic-color"; import { useDarkMode } from "./dark-mode"; import { useMonacoEditorTheme } from "./monaco-editor"; -type DynamicColors = UnwrapRef["colors"]>; +type DynamicColors = UnwrapRef["colors"]>; type DynamicColorName = keyof DynamicColors; type VuetifyColors = NonNullable; @@ -37,8 +37,8 @@ export function useColorTheme() { dark: true, })); - const { colors: lightColors } = useDynamicColors(optionsLight); - const { colors: darkColors } = useDynamicColors(optionsDark); + const { colors: lightColors } = useDynamicColorsOld(optionsLight); + const { colors: darkColors } = useDynamicColorsOld(optionsDark); useSetDynamicColors(lightColors, false); useSetDynamicColors(darkColors, true); diff --git a/src/utils/dynamic-color/__tests__/material-color.spec.ts b/src/utils/dynamic-color/__tests__/material-color.spec.ts index 0937b74..bc68e61 100644 --- a/src/utils/dynamic-color/__tests__/material-color.spec.ts +++ b/src/utils/dynamic-color/__tests__/material-color.spec.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from "vitest"; import { ref, shallowRef } from "vue"; -import { useDynamicColors, useDynamicColorsHct, hctFromHex } from ".."; +import { useDynamicColorsOld, useDynamicColorsHct, hctFromHex } from ".."; describe("useDynamicColors", () => { it("returns the correct colors", () => { @@ -9,7 +9,7 @@ describe("useDynamicColors", () => { dark: false, contrastLevel: 0.0, }); - const { colors } = useDynamicColors(options); + const { colors } = useDynamicColorsOld(options); expect(colors.value).toHaveProperty("primary"); expect(colors.value).toMatchSnapshot(); }); diff --git a/src/utils/dynamic-color/material-color.ts b/src/utils/dynamic-color/material-color.ts index e580c09..deb8166 100644 --- a/src/utils/dynamic-color/material-color.ts +++ b/src/utils/dynamic-color/material-color.ts @@ -56,7 +56,7 @@ const DEFAULT_OPTIONS: Required = { schemeName: "fidelity", }; -export function useDynamicColors(options?: MaybeRef) { +export function useDynamicColorsOld(options?: MaybeRef) { const hex = computed(() => options && toValue(options)?.sourceColor); const hct = computed(() => From 896d3b4415c282c7761d119e1122101aa74dd1fc Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 15:07:39 -0400 Subject: [PATCH 06/14] add dynamic-colors.ts and its tests --- .../__snapshots__/dynamic-colors.spec.ts.snap | 60 +++++++++++++++++++ .../__tests__/dynamic-colors.spec.ts | 9 +++ src/utils/dynamic-color/dynamic-colors.ts | 29 +++++++++ src/utils/dynamic-color/index.ts | 1 + 4 files changed, 99 insertions(+) create mode 100644 src/utils/dynamic-color/__tests__/__snapshots__/dynamic-colors.spec.ts.snap create mode 100644 src/utils/dynamic-color/__tests__/dynamic-colors.spec.ts create mode 100644 src/utils/dynamic-color/dynamic-colors.ts diff --git a/src/utils/dynamic-color/__tests__/__snapshots__/dynamic-colors.spec.ts.snap b/src/utils/dynamic-color/__tests__/__snapshots__/dynamic-colors.spec.ts.snap new file mode 100644 index 0000000..d488305 --- /dev/null +++ b/src/utils/dynamic-color/__tests__/__snapshots__/dynamic-colors.spec.ts.snap @@ -0,0 +1,60 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`useDynamicColors > returns scheme 1`] = ` +{ + "background": 4294834175, + "error": 4290386458, + "error-container": 4294957782, + "inverse-on-surface": 4294307831, + "inverse-primary": 4291804415, + "inverse-surface": 4281478965, + "neutral-palette-key-color": 4286215805, + "neutral-variant-palette-key-color": 4286215554, + "on-background": 4280097568, + "on-error": 4294967295, + "on-error-container": 4282449922, + "on-primary": 4294967295, + "on-primary-container": 4294967295, + "on-primary-fixed": 4280418397, + "on-primary-fixed-variant": 4283381642, + "on-secondary": 4294967295, + "on-secondary-container": 4283056482, + "on-secondary-fixed": 4280227381, + "on-secondary-fixed-variant": 4283122275, + "on-surface": 4280097568, + "on-surface-variant": 4282991953, + "on-tertiary": 4294967295, + "on-tertiary-container": 4294967295, + "on-tertiary-fixed": 4280556032, + "on-tertiary-fixed-variant": 4284040192, + "outline": 4286215554, + "outline-variant": 4291544274, + "primary": 4283513485, + "primary-container": 4285947828, + "primary-fixed": 4293516799, + "primary-fixed-dim": 4291804415, + "primary-palette-key-color": 4286605759, + "scrim": 4278190080, + "secondary": 4284701052, + "secondary-container": 4293253631, + "secondary-fixed": 4293516799, + "secondary-fixed-dim": 4291674345, + "secondary-palette-key-color": 4286345878, + "shadow": 4278190080, + "surface": 4294834175, + "surface-bright": 4294834175, + "surface-container": 4294110452, + "surface-container-high": 4293715694, + "surface-container-highest": 4293320937, + "surface-container-low": 4294505210, + "surface-container-lowest": 4294967295, + "surface-dim": 4292794592, + "surface-tint": 4284960932, + "surface-variant": 4293386478, + "tertiary": 4284303104, + "tertiary-container": 4287064595, + "tertiary-fixed": 4294958995, + "tertiary-fixed-dim": 4293378917, + "tertiary-palette-key-color": 4291405645, +} +`; diff --git a/src/utils/dynamic-color/__tests__/dynamic-colors.spec.ts b/src/utils/dynamic-color/__tests__/dynamic-colors.spec.ts new file mode 100644 index 0000000..38d6606 --- /dev/null +++ b/src/utils/dynamic-color/__tests__/dynamic-colors.spec.ts @@ -0,0 +1,9 @@ +import { describe, it, expect } from "vitest"; +import { useDynamicColors } from ".."; + +describe("useDynamicColors", () => { + it("returns scheme", () => { + const { colors } = useDynamicColors(); + expect(colors.value).toMatchSnapshot(); + }); +}); diff --git a/src/utils/dynamic-color/dynamic-colors.ts b/src/utils/dynamic-color/dynamic-colors.ts new file mode 100644 index 0000000..3f5bb31 --- /dev/null +++ b/src/utils/dynamic-color/dynamic-colors.ts @@ -0,0 +1,29 @@ +import { computed } from "vue"; +import type { MaybeRef } from "vue"; +import { DynamicScheme } from "@material/material-color-utilities"; + +import { useDynamicScheme } from "./dynamic-scheme"; +import type { SchemeName } from "./schemes"; +import { ColorNameMap } from "./colors"; +import type { ColorName } from "./colors"; + +export type UseDynamicColorsOptions = { + schemeName?: MaybeRef; + sourceColorArgb?: MaybeRef; + dark?: MaybeRef; + contrastLevel?: MaybeRef; +}; + +export function useDynamicColors(options?: UseDynamicColorsOptions) { + const { scheme, ...rest } = useDynamicScheme(options); + const colors = computed(() => generateColors(scheme.value)); + return { colors, scheme, ...rest }; +} + +const generateColors = (scheme: DynamicScheme) => + Object.assign( + {}, + ...Object.entries(ColorNameMap).map(([colorName, dynamicColor]) => ({ + [colorName]: dynamicColor.getArgb(scheme), + })) + ) as { [k in ColorName]: number }; diff --git a/src/utils/dynamic-color/index.ts b/src/utils/dynamic-color/index.ts index 15c9cde..c0b38a5 100644 --- a/src/utils/dynamic-color/index.ts +++ b/src/utils/dynamic-color/index.ts @@ -1,3 +1,4 @@ export * from "./schemes"; export * from "./dynamic-scheme"; +export * from "./dynamic-colors"; export * from "./material-color"; From c6f7fc1859ff05bf88ed1d3b58e5222564226087 Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 15:29:58 -0400 Subject: [PATCH 07/14] use Hex instead of Argb --- .../__snapshots__/dynamic-colors.spec.ts.snap | 108 +++++++++--------- .../__tests__/dynamic-scheme.spec.ts | 30 ++--- src/utils/dynamic-color/dynamic-colors.ts | 9 +- src/utils/dynamic-color/dynamic-scheme.ts | 14 ++- 4 files changed, 82 insertions(+), 79 deletions(-) diff --git a/src/utils/dynamic-color/__tests__/__snapshots__/dynamic-colors.spec.ts.snap b/src/utils/dynamic-color/__tests__/__snapshots__/dynamic-colors.spec.ts.snap index d488305..b6245a3 100644 --- a/src/utils/dynamic-color/__tests__/__snapshots__/dynamic-colors.spec.ts.snap +++ b/src/utils/dynamic-color/__tests__/__snapshots__/dynamic-colors.spec.ts.snap @@ -2,59 +2,59 @@ exports[`useDynamicColors > returns scheme 1`] = ` { - "background": 4294834175, - "error": 4290386458, - "error-container": 4294957782, - "inverse-on-surface": 4294307831, - "inverse-primary": 4291804415, - "inverse-surface": 4281478965, - "neutral-palette-key-color": 4286215805, - "neutral-variant-palette-key-color": 4286215554, - "on-background": 4280097568, - "on-error": 4294967295, - "on-error-container": 4282449922, - "on-primary": 4294967295, - "on-primary-container": 4294967295, - "on-primary-fixed": 4280418397, - "on-primary-fixed-variant": 4283381642, - "on-secondary": 4294967295, - "on-secondary-container": 4283056482, - "on-secondary-fixed": 4280227381, - "on-secondary-fixed-variant": 4283122275, - "on-surface": 4280097568, - "on-surface-variant": 4282991953, - "on-tertiary": 4294967295, - "on-tertiary-container": 4294967295, - "on-tertiary-fixed": 4280556032, - "on-tertiary-fixed-variant": 4284040192, - "outline": 4286215554, - "outline-variant": 4291544274, - "primary": 4283513485, - "primary-container": 4285947828, - "primary-fixed": 4293516799, - "primary-fixed-dim": 4291804415, - "primary-palette-key-color": 4286605759, - "scrim": 4278190080, - "secondary": 4284701052, - "secondary-container": 4293253631, - "secondary-fixed": 4293516799, - "secondary-fixed-dim": 4291674345, - "secondary-palette-key-color": 4286345878, - "shadow": 4278190080, - "surface": 4294834175, - "surface-bright": 4294834175, - "surface-container": 4294110452, - "surface-container-high": 4293715694, - "surface-container-highest": 4293320937, - "surface-container-low": 4294505210, - "surface-container-lowest": 4294967295, - "surface-dim": 4292794592, - "surface-tint": 4284960932, - "surface-variant": 4293386478, - "tertiary": 4284303104, - "tertiary-container": 4287064595, - "tertiary-fixed": 4294958995, - "tertiary-fixed-dim": 4293378917, - "tertiary-palette-key-color": 4291405645, + "background": "#fdf7ff", + "error": "#ba1a1a", + "error-container": "#ffdad6", + "inverse-on-surface": "#f5eff7", + "inverse-primary": "#cfbcff", + "inverse-surface": "#322f35", + "neutral-palette-key-color": "#7a767d", + "neutral-variant-palette-key-color": "#7a7582", + "on-background": "#1d1b20", + "on-error": "#ffffff", + "on-error-container": "#410002", + "on-primary": "#ffffff", + "on-primary-container": "#ffffff", + "on-primary-fixed": "#22005d", + "on-primary-fixed-variant": "#4f378a", + "on-secondary": "#ffffff", + "on-secondary-container": "#4a4162", + "on-secondary-fixed": "#1f1635", + "on-secondary-fixed-variant": "#4b4263", + "on-surface": "#1d1b20", + "on-surface-variant": "#494551", + "on-tertiary": "#ffffff", + "on-tertiary-container": "#ffffff", + "on-tertiary-fixed": "#241a00", + "on-tertiary-fixed-variant": "#594400", + "outline": "#7a7582", + "outline-variant": "#cbc4d2", + "primary": "#513a8d", + "primary-container": "#765fb4", + "primary-fixed": "#e9ddff", + "primary-fixed-dim": "#cfbcff", + "primary-palette-key-color": "#8069bf", + "scrim": "#000000", + "secondary": "#63597c", + "secondary-container": "#e5d9ff", + "secondary-fixed": "#e9ddff", + "secondary-fixed-dim": "#cdc0e9", + "secondary-palette-key-color": "#7c7296", + "shadow": "#000000", + "surface": "#fdf7ff", + "surface-bright": "#fdf7ff", + "surface-container": "#f2ecf4", + "surface-container-high": "#ece6ee", + "surface-container-highest": "#e6e0e9", + "surface-container-low": "#f8f2fa", + "surface-container-lowest": "#ffffff", + "surface-dim": "#ded8e0", + "surface-tint": "#6750a4", + "surface-variant": "#e7e0ee", + "tertiary": "#5d4700", + "tertiary-container": "#876a13", + "tertiary-fixed": "#ffdf93", + "tertiary-fixed-dim": "#e7c365", + "tertiary-palette-key-color": "#c9a74d", } `; diff --git a/src/utils/dynamic-color/__tests__/dynamic-scheme.spec.ts b/src/utils/dynamic-color/__tests__/dynamic-scheme.spec.ts index 541449e..bf3a41f 100644 --- a/src/utils/dynamic-color/__tests__/dynamic-scheme.spec.ts +++ b/src/utils/dynamic-color/__tests__/dynamic-scheme.spec.ts @@ -13,7 +13,7 @@ describe("useDynamicScheme", () => { it("returns readonly scheme", () => { const options: UseDynamicSchemeOptions = { schemeName: "expressive", - sourceColorArgb: argbFromHex("#2196F3"), + sourceColorHex: "#2196F3", dark: true, contrastLevel: 0.2, }; @@ -35,11 +35,11 @@ describe("useDynamicScheme", () => { }); it("returns default options", () => { - const { schemeName, sourceColorArgb, dark, contrastLevel } = + const { schemeName, sourceColorHex, dark, contrastLevel } = useDynamicScheme(); expect(schemeName.value).toBe("fidelity"); - expect(sourceColorArgb.value).toBe(argbFromHex("#6750A4")); + expect(sourceColorHex.value).toBe("#6750A4"); expect(dark.value).toBe(false); expect(contrastLevel.value).toBe(0.0); }); @@ -47,16 +47,16 @@ describe("useDynamicScheme", () => { it("returns reactive options", () => { const options: UseDynamicSchemeOptions = { schemeName: "expressive", - sourceColorArgb: 4280391411, // #2196F3 + sourceColorHex: "#2196F3", dark: true, contrastLevel: 0.2, }; - const { schemeName, sourceColorArgb, dark, contrastLevel } = + const { schemeName, sourceColorHex, dark, contrastLevel } = useDynamicScheme(options); expect(schemeName.value).toBe("expressive"); - expect(sourceColorArgb.value).toBe(4280391411); + expect(sourceColorHex.value).toBe("#2196F3"); expect(dark.value).toBe(true); expect(contrastLevel.value).toBe(0.2); }); @@ -64,36 +64,36 @@ describe("useDynamicScheme", () => { it("keeps options reactive", () => { const options = { schemeName: ref("expressive"), - sourceColorArgb: ref(4280391411), // #2196F3 + sourceColorHex: ref("#2196F3"), dark: ref(true), contrastLevel: ref(0.2), }; - const { schemeName, sourceColorArgb, dark, contrastLevel } = + const { schemeName, sourceColorHex, dark, contrastLevel } = useDynamicScheme(options); expect(schemeName.value).toBe("expressive"); - expect(sourceColorArgb.value).toBe(4280391411); + expect(sourceColorHex.value).toBe("#2196F3"); expect(dark.value).toBe(true); expect(contrastLevel.value).toBe(0.2); options.schemeName.value = "fidelity"; - options.sourceColorArgb.value = argbFromHex("#3F51B5"); + options.sourceColorHex.value = "#3F51B5"; options.dark.value = false; options.contrastLevel.value = 0.3; expect(schemeName.value).toBe("fidelity"); - expect(sourceColorArgb.value).toBe(argbFromHex("#3F51B5")); + expect(sourceColorHex.value).toBe("#3F51B5"); expect(dark.value).toBe(false); expect(contrastLevel.value).toBe(0.3); schemeName.value = "neutral"; - sourceColorArgb.value = argbFromHex("#673AB7"); + sourceColorHex.value = "#673AB7"; dark.value = true; contrastLevel.value = 0.4; expect(options.schemeName.value).toBe("neutral"); - expect(options.sourceColorArgb.value).toBe(argbFromHex("#673AB7")); + expect(options.sourceColorHex.value).toBe("#673AB7"); expect(options.dark.value).toBe(true); expect(options.contrastLevel.value).toBe(0.4); }); @@ -106,9 +106,9 @@ describe("useDynamicScheme", () => { }); it("returns readonly source color Hct", () => { - const { sourceColorArgb, sourceColorHct } = useDynamicScheme(); + const { sourceColorHex, sourceColorHct } = useDynamicScheme(); expect(sourceColorHct.value.toInt()).toBe(argbFromHex("#6750A4")); - sourceColorArgb.value = argbFromHex("#2196F3"); + sourceColorHex.value = "#2196F3"; expect(sourceColorHct.value.toInt()).toBe(argbFromHex("#2196F3")); }); }); diff --git a/src/utils/dynamic-color/dynamic-colors.ts b/src/utils/dynamic-color/dynamic-colors.ts index 3f5bb31..9171449 100644 --- a/src/utils/dynamic-color/dynamic-colors.ts +++ b/src/utils/dynamic-color/dynamic-colors.ts @@ -1,6 +1,7 @@ import { computed } from "vue"; import type { MaybeRef } from "vue"; -import { DynamicScheme } from "@material/material-color-utilities"; +import { hexFromArgb } from "@material/material-color-utilities"; +import type { DynamicScheme } from "@material/material-color-utilities"; import { useDynamicScheme } from "./dynamic-scheme"; import type { SchemeName } from "./schemes"; @@ -9,7 +10,7 @@ import type { ColorName } from "./colors"; export type UseDynamicColorsOptions = { schemeName?: MaybeRef; - sourceColorArgb?: MaybeRef; + sourceColorHex?: MaybeRef; dark?: MaybeRef; contrastLevel?: MaybeRef; }; @@ -24,6 +25,6 @@ const generateColors = (scheme: DynamicScheme) => Object.assign( {}, ...Object.entries(ColorNameMap).map(([colorName, dynamicColor]) => ({ - [colorName]: dynamicColor.getArgb(scheme), + [colorName]: hexFromArgb(dynamicColor.getArgb(scheme)), })) - ) as { [k in ColorName]: number }; + ) as { [k in ColorName]: string }; diff --git a/src/utils/dynamic-color/dynamic-scheme.ts b/src/utils/dynamic-color/dynamic-scheme.ts index 3217752..34f9b45 100644 --- a/src/utils/dynamic-color/dynamic-scheme.ts +++ b/src/utils/dynamic-color/dynamic-scheme.ts @@ -6,27 +6,29 @@ import type { SchemeName } from "./schemes"; export type UseDynamicSchemeOptions = { schemeName?: MaybeRef; - sourceColorArgb?: MaybeRef; + sourceColorHex?: MaybeRef; dark?: MaybeRef; contrastLevel?: MaybeRef; }; const optDefault: Required> = { schemeName: "fidelity", - sourceColorArgb: argbFromHex("#6750A4"), + sourceColorHex: "#6750A4", dark: false, contrastLevel: 0.0, }; export function useDynamicScheme(options?: UseDynamicSchemeOptions) { const schemeName = ref(options?.schemeName ?? optDefault.schemeName); - const sourceColorArgb = ref( - options?.sourceColorArgb ?? optDefault.sourceColorArgb + const sourceColorHex = ref( + options?.sourceColorHex ?? optDefault.sourceColorHex ); const dark = ref(options?.dark ?? optDefault.dark); const contrastLevel = ref(options?.contrastLevel ?? optDefault.contrastLevel); const schemeClass = computed(() => SchemeNameMap[schemeName.value]); - const sourceColorHct = computed(() => Hct.fromInt(sourceColorArgb.value)); + const sourceColorHct = computed(() => + Hct.fromInt(argbFromHex(sourceColorHex.value)) + ); const scheme = computed( () => new schemeClass.value( @@ -38,7 +40,7 @@ export function useDynamicScheme(options?: UseDynamicSchemeOptions) { return { scheme, schemeName, - sourceColorArgb, + sourceColorHex, dark, contrastLevel, schemeClass, From 4af7c341faae0e31e683c24f1c778e174b0e38fc Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 15:30:21 -0400 Subject: [PATCH 08/14] update tests --- .../__snapshots__/dynamic-colors.spec.ts.snap | 2 +- .../dynamic-color/__tests__/dynamic-colors.spec.ts | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/utils/dynamic-color/__tests__/__snapshots__/dynamic-colors.spec.ts.snap b/src/utils/dynamic-color/__tests__/__snapshots__/dynamic-colors.spec.ts.snap index b6245a3..6a4ca6c 100644 --- a/src/utils/dynamic-color/__tests__/__snapshots__/dynamic-colors.spec.ts.snap +++ b/src/utils/dynamic-color/__tests__/__snapshots__/dynamic-colors.spec.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`useDynamicColors > returns scheme 1`] = ` +exports[`useDynamicColors > returns colors 1`] = ` { "background": "#fdf7ff", "error": "#ba1a1a", diff --git a/src/utils/dynamic-color/__tests__/dynamic-colors.spec.ts b/src/utils/dynamic-color/__tests__/dynamic-colors.spec.ts index 38d6606..eba6471 100644 --- a/src/utils/dynamic-color/__tests__/dynamic-colors.spec.ts +++ b/src/utils/dynamic-color/__tests__/dynamic-colors.spec.ts @@ -2,8 +2,18 @@ import { describe, it, expect } from "vitest"; import { useDynamicColors } from ".."; describe("useDynamicColors", () => { - it("returns scheme", () => { + it("returns colors", () => { const { colors } = useDynamicColors(); - expect(colors.value).toMatchSnapshot(); + expect(colors.value).toMatchSnapshot(); + }); + + it("colors are reactive", () => { + const { colors, sourceColorHex } = useDynamicColors({ + sourceColorHex: "#F44336", + }); + const colors1 = colors.value; + sourceColorHex.value = "#673AB7"; + const colors2 = colors.value; + expect(colors1).not.toEqual(colors2); }); }); From 3ebe8e3d82045c65b00df1565291dc211609d626 Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 15:41:31 -0400 Subject: [PATCH 09/14] copy doc from material-colors.ts to dynamic-colors.ts material-colors.ts is to be deleted. --- src/utils/dynamic-color/dynamic-colors.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/utils/dynamic-color/dynamic-colors.ts b/src/utils/dynamic-color/dynamic-colors.ts index 9171449..feb0a5f 100644 --- a/src/utils/dynamic-color/dynamic-colors.ts +++ b/src/utils/dynamic-color/dynamic-colors.ts @@ -1,3 +1,24 @@ +/** + * The code to generate Dynamic Color of Material Design: + * https://m3.material.io/styles/color/dynamic-color/ + * + * It uses material-color-utilities: + * https://github.com/material-foundation/material-color-utilities + * + * The code was originally based on dynamic_color_test.ts: + * https://github.com/material-foundation/material-color-utilities/blob/ce99247b6ba/typescript/dynamiccolor/dynamic_color_test.ts + * + * This issue has a link to the above file: + * https://github.com/material-foundation/material-color-utilities/issues/98 + * + * This YouTube video explains the dynamic scheme: + * https://youtu.be/vnDhq8W98O4?t=648 + * + * This blog post explains the surface colors: + * https://material.io/blog/tone-based-surface-color-m3 + * + */ + import { computed } from "vue"; import type { MaybeRef } from "vue"; import { hexFromArgb } from "@material/material-color-utilities"; From 75544e79bbc240788a4f26c71e0f782531aa5375 Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 15:46:46 -0400 Subject: [PATCH 10/14] replace useDynamicColorsOld() with useDynamicColors() --- src/utils/color-theme/color-theme.ts | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/utils/color-theme/color-theme.ts b/src/utils/color-theme/color-theme.ts index 1ae192a..8bb6611 100644 --- a/src/utils/color-theme/color-theme.ts +++ b/src/utils/color-theme/color-theme.ts @@ -4,11 +4,11 @@ import { useTheme } from "vuetify"; import type { ThemeDefinition } from "vuetify"; import type { OmitIndexSignature } from "type-fest"; -import { useDynamicColorsOld } from "@/utils/dynamic-color"; +import { useDynamicColors } from "@/utils/dynamic-color"; import { useDarkMode } from "./dark-mode"; import { useMonacoEditorTheme } from "./monaco-editor"; -type DynamicColors = UnwrapRef["colors"]>; +type DynamicColors = UnwrapRef["colors"]>; type DynamicColorName = keyof DynamicColors; type VuetifyColors = NonNullable; @@ -25,20 +25,13 @@ function useSourceColor() { export function useColorTheme() { // https://vuetifyjs.com/en/features/theme/ const { isDark } = useDarkMode(); - const { sourceColor } = useSourceColor(); - - const optionsLight = computed(() => ({ - sourceColor: toValue(sourceColor), - dark: false, - })); - - const optionsDark = computed(() => ({ - sourceColor: toValue(sourceColor), - dark: true, - })); + const { sourceColor: sourceColorHex } = useSourceColor(); + + const optionsLight = { sourceColorHex, dark: false }; + const optionsDark = { sourceColorHex, dark: true }; - const { colors: lightColors } = useDynamicColorsOld(optionsLight); - const { colors: darkColors } = useDynamicColorsOld(optionsDark); + const { colors: lightColors } = useDynamicColors(optionsLight); + const { colors: darkColors } = useDynamicColors(optionsDark); useSetDynamicColors(lightColors, false); useSetDynamicColors(darkColors, true); From bf9f775264ab428b5bd42930630f44b87739a343 Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 15:47:52 -0400 Subject: [PATCH 11/14] commit scratch.spec.ts --- src/utils/__tests__/scratch.spec.ts | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/utils/__tests__/scratch.spec.ts diff --git a/src/utils/__tests__/scratch.spec.ts b/src/utils/__tests__/scratch.spec.ts new file mode 100644 index 0000000..02670b6 --- /dev/null +++ b/src/utils/__tests__/scratch.spec.ts @@ -0,0 +1,37 @@ +import { test, expect } from "vitest"; +import { useTitle } from "@vueuse/core"; +import { ref } from "vue"; +import type { MaybeRef } from "vue"; + +// Test the argument of useTitle() is reactive +test("useTitle() should be reactive", () => { + const title = ref("Hello World"); + const t = useTitle(title); + expect(title.value).toBe("Hello World"); + title.value = "Hello Vue"; + expect(title.value).toBe("Hello Vue"); + t.value = "New Title"; + expect(t.value).toBe("New Title"); + expect(title.value).toBe("New Title"); +}); + +function useScratch(newScratch: MaybeRef) { + const scratch = ref(newScratch); + return scratch; +} + +test("useScratch() non ref arg", () => { + const newScratch = "Hello World"; + const scratch = useScratch(newScratch); + expect(scratch.value).toBe("Hello World"); +}); + +test("useScratch() should be reactive", () => { + const newScratch = ref("Hello World"); + const scratch = useScratch(newScratch); + expect(scratch.value).toBe("Hello World"); + scratch.value = "Hello Vue"; + expect(scratch.value).toBe("Hello Vue"); + expect(newScratch.value).toBe("Hello Vue"); + console.log(newScratch === scratch); +}); From 898072fff598ed3207d2f1b9192ff118e7adeb52 Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 15:59:31 -0400 Subject: [PATCH 12/14] update types --- src/utils/dynamic-color/dynamic-colors.ts | 10 ++-------- src/utils/dynamic-color/dynamic-scheme.ts | 4 ++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/utils/dynamic-color/dynamic-colors.ts b/src/utils/dynamic-color/dynamic-colors.ts index feb0a5f..90d36a2 100644 --- a/src/utils/dynamic-color/dynamic-colors.ts +++ b/src/utils/dynamic-color/dynamic-colors.ts @@ -20,21 +20,15 @@ */ import { computed } from "vue"; -import type { MaybeRef } from "vue"; import { hexFromArgb } from "@material/material-color-utilities"; import type { DynamicScheme } from "@material/material-color-utilities"; import { useDynamicScheme } from "./dynamic-scheme"; -import type { SchemeName } from "./schemes"; +import type { UseDynamicSchemeOptions } from "./dynamic-scheme"; import { ColorNameMap } from "./colors"; import type { ColorName } from "./colors"; -export type UseDynamicColorsOptions = { - schemeName?: MaybeRef; - sourceColorHex?: MaybeRef; - dark?: MaybeRef; - contrastLevel?: MaybeRef; -}; +export interface UseDynamicColorsOptions extends UseDynamicSchemeOptions {} export function useDynamicColors(options?: UseDynamicColorsOptions) { const { scheme, ...rest } = useDynamicScheme(options); diff --git a/src/utils/dynamic-color/dynamic-scheme.ts b/src/utils/dynamic-color/dynamic-scheme.ts index 34f9b45..c752680 100644 --- a/src/utils/dynamic-color/dynamic-scheme.ts +++ b/src/utils/dynamic-color/dynamic-scheme.ts @@ -4,12 +4,12 @@ import { argbFromHex, Hct } from "@material/material-color-utilities"; import { SchemeNameMap } from "./schemes"; import type { SchemeName } from "./schemes"; -export type UseDynamicSchemeOptions = { +export interface UseDynamicSchemeOptions { schemeName?: MaybeRef; sourceColorHex?: MaybeRef; dark?: MaybeRef; contrastLevel?: MaybeRef; -}; +} const optDefault: Required> = { schemeName: "fidelity", From 2d4cf5a77ba0864469e23e862be1e5bb0d5b0a54 Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 16:20:24 -0400 Subject: [PATCH 13/14] delete material-color.ts and its tests replaced with dynamic-colors.ts --- .../__snapshots__/material-color.spec.ts.snap | 389 ------------------ .../__tests__/material-color.spec.ts | 29 -- src/utils/dynamic-color/index.ts | 1 - src/utils/dynamic-color/material-color.ts | 130 ------ 4 files changed, 549 deletions(-) delete mode 100644 src/utils/dynamic-color/__tests__/__snapshots__/material-color.spec.ts.snap delete mode 100644 src/utils/dynamic-color/__tests__/material-color.spec.ts delete mode 100644 src/utils/dynamic-color/material-color.ts diff --git a/src/utils/dynamic-color/__tests__/__snapshots__/material-color.spec.ts.snap b/src/utils/dynamic-color/__tests__/__snapshots__/material-color.spec.ts.snap deleted file mode 100644 index 79b654d..0000000 --- a/src/utils/dynamic-color/__tests__/__snapshots__/material-color.spec.ts.snap +++ /dev/null @@ -1,389 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`useDynamicColors > returns the correct colors 1`] = ` -{ - "background": "#f8f9ff", - "error": "#ba1a1a", - "error-container": "#ffdad6", - "inverse-on-surface": "#eef1f9", - "inverse-primary": "#9ecaff", - "inverse-surface": "#2d3137", - "neutral-palette-key-color": "#73777e", - "neutral-variant-palette-key-color": "#707884", - "on-background": "#181c22", - "on-error": "#ffffff", - "on-error-container": "#410002", - "on-primary": "#ffffff", - "on-primary-container": "#001226", - "on-primary-fixed": "#001d36", - "on-primary-fixed-variant": "#00497d", - "on-secondary": "#ffffff", - "on-secondary-container": "#224365", - "on-secondary-fixed": "#001d36", - "on-secondary-fixed-variant": "#28496b", - "on-surface": "#181c22", - "on-surface-variant": "#404752", - "on-tertiary": "#ffffff", - "on-tertiary-container": "#200c00", - "on-tertiary-fixed": "#2e1500", - "on-tertiary-fixed-variant": "#6d3900", - "outline": "#707883", - "outline-variant": "#bfc7d4", - "primary": "#0061a4", - "primary-container": "#3ca3ff", - "primary-fixed": "#d1e4ff", - "primary-fixed-dim": "#9ecaff", - "primary-palette-key-color": "#038de9", - "scrim": "#000000", - "secondary": "#416084", - "secondary-container": "#bedaff", - "secondary-fixed": "#d1e4ff", - "secondary-fixed-dim": "#a9c9f2", - "secondary-palette-key-color": "#5a799e", - "shadow": "#000000", - "surface": "#f8f9ff", - "surface-bright": "#f8f9ff", - "surface-container": "#ebeef6", - "surface-container-high": "#e5e8f0", - "surface-container-highest": "#dfe2ea", - "surface-container-low": "#f1f3fc", - "surface-container-lowest": "#ffffff", - "surface-dim": "#d7dae2", - "surface-tint": "#0061a4", - "surface-variant": "#dbe3f0", - "tertiary": "#904d00", - "tertiary-container": "#ea8515", - "tertiary-fixed": "#ffdcc2", - "tertiary-fixed-dim": "#ffb77b", - "tertiary-palette-key-color": "#db7900", -} -`; - -exports[`useDynamicColorsHct > returns the correct colors 1`] = ` -{ - "background": Hct { - "argb": 4294507007, - "internalChroma": 5.782887097365252, - "internalHue": 254.23007904112848, - "internalTone": 98.00463144093008, - }, - "error": Hct { - "argb": 4290386458, - "internalChroma": 84.27740557298178, - "internalHue": 25.055818214141475, - "internalTone": 40.00248812068815, - }, - "error-container": Hct { - "argb": 4294957782, - "internalChroma": 13.234454405401584, - "internalHue": 23.79916793659701, - "internalTone": 89.9789231267891, - }, - "inverse-on-surface": Hct { - "argb": 4293849593, - "internalChroma": 7.414194867101687, - "internalHue": 253.03947065267846, - "internalTone": 95.13031661715617, - }, - "inverse-primary": Hct { - "argb": 4288596735, - "internalChroma": 37.18607759380727, - "internalHue": 253.4392269209713, - "internalTone": 80.02758180930664, - }, - "inverse-surface": Hct { - "argb": 4281151799, - "internalChroma": 7.348039755052786, - "internalHue": 253.88826043880863, - "internalTone": 20.1650352581582, - }, - "neutral-palette-key-color": Hct { - "argb": 4285757310, - "internalChroma": 7.474730049756462, - "internalHue": 252.7160033370452, - "internalTone": 49.91172715139082, - }, - "neutral-variant-palette-key-color": Hct { - "argb": 4285560964, - "internalChroma": 11.66441056936568, - "internalHue": 253.518331452986, - "internalTone": 50.148941881131066, - }, - "on-background": Hct { - "argb": 4279770146, - "internalChroma": 7.6382649690441555, - "internalHue": 255.94572303470926, - "internalTone": 10.108617253891556, - }, - "on-error": Hct { - "argb": 4294967295, - "internalChroma": 2.8690352036774005, - "internalHue": 209.49195947383808, - "internalTone": 100, - }, - "on-error-container": Hct { - "argb": 4282449922, - "internalChroma": 47.75361075053237, - "internalHue": 24.658029749720782, - "internalTone": 10.016770800355904, - }, - "on-primary": Hct { - "argb": 4294967295, - "internalChroma": 2.8690352036774005, - "internalHue": 209.49195947383808, - "internalTone": 100, - }, - "on-primary-container": Hct { - "argb": 4278194726, - "internalChroma": 20.82219487663626, - "internalHue": 255.14257075476212, - "internalTone": 5.1718517045296615, - }, - "on-primary-fixed": Hct { - "argb": 4278197558, - "internalChroma": 24.834944519214517, - "internalHue": 253.09732762984137, - "internalTone": 10.1458152722198, - }, - "on-primary-fixed-variant": Hct { - "argb": 4278208893, - "internalChroma": 40.87176608366732, - "internalHue": 253.80737609559577, - "internalTone": 30.02420740099906, - }, - "on-secondary": Hct { - "argb": 4294967295, - "internalChroma": 2.8690352036774005, - "internalHue": 209.49195947383808, - "internalTone": 100, - }, - "on-secondary-container": Hct { - "argb": 4280435557, - "internalChroma": 29.848376911392457, - "internalHue": 253.7358877235231, - "internalTone": 27.556400568746653, - }, - "on-secondary-fixed": Hct { - "argb": 4278197558, - "internalChroma": 24.834944519214517, - "internalHue": 253.09732762984137, - "internalTone": 10.1458152722198, - }, - "on-secondary-fixed-variant": Hct { - "argb": 4280830315, - "internalChroma": 29.812053627520267, - "internalHue": 253.11497802013886, - "internalTone": 30.1026370776791, - }, - "on-surface": Hct { - "argb": 4279770146, - "internalChroma": 7.6382649690441555, - "internalHue": 255.94572303470926, - "internalTone": 10.108617253891556, - }, - "on-surface-variant": Hct { - "argb": 4282402642, - "internalChroma": 11.402286190742394, - "internalHue": 256.44172854550817, - "internalTone": 29.92551279599261, - }, - "on-tertiary": Hct { - "argb": 4294967295, - "internalChroma": 2.8690352036774005, - "internalHue": 209.49195947383808, - "internalTone": 100, - }, - "on-tertiary-container": Hct { - "argb": 4280290304, - "internalChroma": 19.20624922631366, - "internalHue": 56.8706838741712, - "internalTone": 5.148968813486434, - }, - "on-tertiary-fixed": Hct { - "argb": 4281210112, - "internalChroma": 22.913913670877392, - "internalHue": 59.29033445341395, - "internalTone": 9.931727958841936, - }, - "on-tertiary-fixed-variant": Hct { - "argb": 4285348096, - "internalChroma": 38.270376061040864, - "internalHue": 58.55002070739536, - "internalTone": 29.855850283067326, - }, - "outline": Hct { - "argb": 4285560963, - "internalChroma": 11.122398236746955, - "internalHue": 251.18180560991004, - "internalTone": 50.11652442038704, - }, - "outline-variant": Hct { - "argb": 4290758612, - "internalChroma": 11.53769765472759, - "internalHue": 252.5502919534681, - "internalTone": 79.99739681273131, - }, - "primary": Hct { - "argb": 4278215076, - "internalChroma": 48.33836572079727, - "internalHue": 254.0404285721225, - "internalTone": 39.9647226078411, - }, - "primary-container": Hct { - "argb": 4282164223, - "internalChroma": 59.0053708678909, - "internalHue": 253.640844810992, - "internalTone": 65.25917552941185, - }, - "primary-fixed": Hct { - "argb": 4291945727, - "internalChroma": 20.64342844985456, - "internalHue": 253.51997372711435, - "internalTone": 89.98090008988925, - }, - "primary-fixed-dim": Hct { - "argb": 4288596735, - "internalChroma": 37.18607759380727, - "internalHue": 253.4392269209713, - "internalTone": 80.02758180930664, - }, - "primary-palette-key-color": Hct { - "argb": 4278423017, - "internalChroma": 59.72710724383374, - "internalHue": 253.5914684769191, - "internalTone": 57.028919294944956, - }, - "scrim": Hct { - "argb": 4278190080, - "internalChroma": 0, - "internalHue": 0, - "internalTone": 0, - }, - "secondary": Hct { - "argb": 4282474628, - "internalChroma": 29.955287027219693, - "internalHue": 254.27814993873622, - "internalTone": 39.84115218870693, - }, - "secondary-container": Hct { - "argb": 4290697983, - "internalChroma": 27.156821089278843, - "internalHue": 253.58013246510592, - "internalTone": 86.1612017361638, - }, - "secondary-fixed": Hct { - "argb": 4291945727, - "internalChroma": 20.64342844985456, - "internalHue": 253.51997372711435, - "internalTone": 89.98090008988925, - }, - "secondary-fixed-dim": Hct { - "argb": 4289317362, - "internalChroma": 30.01247837613234, - "internalHue": 253.67859136989472, - "internalTone": 79.96488213548392, - }, - "secondary-palette-key-color": Hct { - "argb": 4284119454, - "internalChroma": 29.854410229557516, - "internalHue": 253.8544030342028, - "internalTone": 49.87838963119367, - }, - "shadow": Hct { - "argb": 4278190080, - "internalChroma": 0, - "internalHue": 0, - "internalTone": 0, - }, - "surface": Hct { - "argb": 4294507007, - "internalChroma": 5.782887097365252, - "internalHue": 254.23007904112848, - "internalTone": 98.00463144093008, - }, - "surface-bright": Hct { - "argb": 4294507007, - "internalChroma": 5.782887097365252, - "internalHue": 254.23007904112848, - "internalTone": 98.00463144093008, - }, - "surface-container": Hct { - "argb": 4293652214, - "internalChroma": 7.416366897806944, - "internalHue": 253.2014771988429, - "internalTone": 94.08315204788983, - }, - "surface-container-high": Hct { - "argb": 4293257456, - "internalChroma": 7.421300508313794, - "internalHue": 253.52843281648867, - "internalTone": 91.98130347791135, - }, - "surface-container-highest": Hct { - "argb": 4292862698, - "internalChroma": 7.427056461079774, - "internalHue": 253.8593574894516, - "internalTone": 89.86918375703816, - }, - "surface-container-low": Hct { - "argb": 4294046716, - "internalChroma": 7.632286462326692, - "internalHue": 259.60728576445547, - "internalTone": 95.92630375247313, - }, - "surface-container-lowest": Hct { - "argb": 4294967295, - "internalChroma": 2.8690352036774005, - "internalHue": 209.49195947383808, - "internalTone": 100, - }, - "surface-dim": Hct { - "argb": 4292336354, - "internalChroma": 7.436090849950321, - "internalHue": 254.30686071593595, - "internalTone": 87.03651113758505, - }, - "surface-tint": Hct { - "argb": 4278215076, - "internalChroma": 48.33836572079727, - "internalHue": 254.0404285721225, - "internalTone": 39.9647226078411, - }, - "surface-variant": Hct { - "argb": 4292600816, - "internalChroma": 11.38361380140088, - "internalHue": 251.5739049725983, - "internalTone": 89.99358884418842, - }, - "tertiary": Hct { - "argb": 4287646976, - "internalChroma": 45.462470386169805, - "internalHue": 58.42623266506773, - "internalTone": 39.976883378533856, - }, - "tertiary-container": Hct { - "argb": 4293559573, - "internalChroma": 58.67054388358007, - "internalHue": 59.21208571633588, - "internalTone": 65.2170616608435, - }, - "tertiary-fixed": Hct { - "argb": 4294958274, - "internalChroma": 15.151818811795314, - "internalHue": 59.348592385913975, - "internalTone": 90.01763382779387, - }, - "tertiary-fixed-dim": Hct { - "argb": 4294948731, - "internalChroma": 35.30552710391083, - "internalHue": 58.879567452569596, - "internalTone": 79.92979775126585, - }, - "tertiary-palette-key-color": Hct { - "argb": 4292573440, - "internalChroma": 58.73805179607165, - "internalHue": 58.9929361549152, - "internalTone": 60.546872235591536, - }, -} -`; diff --git a/src/utils/dynamic-color/__tests__/material-color.spec.ts b/src/utils/dynamic-color/__tests__/material-color.spec.ts deleted file mode 100644 index bc68e61..0000000 --- a/src/utils/dynamic-color/__tests__/material-color.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { ref, shallowRef } from "vue"; -import { useDynamicColorsOld, useDynamicColorsHct, hctFromHex } from ".."; - -describe("useDynamicColors", () => { - it("returns the correct colors", () => { - const options = ref({ - sourceColor: "#2196F3", - dark: false, - contrastLevel: 0.0, - }); - const { colors } = useDynamicColorsOld(options); - expect(colors.value).toHaveProperty("primary"); - expect(colors.value).toMatchSnapshot(); - }); -}); - -describe("useDynamicColorsHct", () => { - it("returns the correct colors", () => { - const options = shallowRef({ - sourceColorHct: hctFromHex("#2196F3"), - dark: false, - contrastLevel: 0.0, - }); - const { colorsHct } = useDynamicColorsHct(options); - expect(colorsHct.value).toHaveProperty("primary"); - expect(colorsHct.value).toMatchSnapshot(); - }); -}); diff --git a/src/utils/dynamic-color/index.ts b/src/utils/dynamic-color/index.ts index c0b38a5..6761272 100644 --- a/src/utils/dynamic-color/index.ts +++ b/src/utils/dynamic-color/index.ts @@ -1,4 +1,3 @@ export * from "./schemes"; export * from "./dynamic-scheme"; export * from "./dynamic-colors"; -export * from "./material-color"; diff --git a/src/utils/dynamic-color/material-color.ts b/src/utils/dynamic-color/material-color.ts deleted file mode 100644 index deb8166..0000000 --- a/src/utils/dynamic-color/material-color.ts +++ /dev/null @@ -1,130 +0,0 @@ -/** - * The code to generate Dynamic Color of Material Design: - * https://m3.material.io/styles/color/dynamic-color/ - * - * It uses material-color-utilities: - * https://github.com/material-foundation/material-color-utilities - * - * The code was originally based on dynamic_color_test.ts: - * https://github.com/material-foundation/material-color-utilities/blob/ce99247b6ba/typescript/dynamiccolor/dynamic_color_test.ts - * - * This issue has a link to the above file: - * https://github.com/material-foundation/material-color-utilities/issues/98 - * - * This YouTube video explains the dynamic scheme: - * https://youtu.be/vnDhq8W98O4?t=648 - * - * This blog post explains the surface colors: - * https://material.io/blog/tone-based-surface-color-m3 - * - */ - -import { computed, toValue } from "vue"; -import type { MaybeRef } from "vue"; -import { - argbFromHex, - hexFromArgb, - Hct, -} from "@material/material-color-utilities"; -import { DynamicScheme } from "@material/material-color-utilities"; -import { ColorNameMap } from "./colors"; -import type { ColorName } from "./colors"; -import { SchemeNameMap } from "./schemes"; -import type { SchemeName } from "./schemes"; - -interface Options { - dark?: boolean; - contrastLevel?: number; - schemeName?: SchemeName; -} - -export interface OptionsHex extends Options { - sourceColor?: string; // e.g., "#6750A4" -} - -export interface OptionsHct extends Options { - sourceColorHct?: Hct; -} - -// Default seed in https://www.figma.com/community/file/1035197037666593721/Visualizing-dynamic-color-in-your-app-with-Material-Design -const DEFAULT_SOURCE_COLOR_HCT = hctFromHex("#6750A4"); - -const DEFAULT_OPTIONS: Required = { - sourceColorHct: DEFAULT_SOURCE_COLOR_HCT, - dark: false, - contrastLevel: 0.0, - schemeName: "fidelity", -}; - -export function useDynamicColorsOld(options?: MaybeRef) { - const hex = computed(() => options && toValue(options)?.sourceColor); - - const hct = computed(() => - hex.value === undefined ? undefined : hctFromHex(hex.value) - ); - - // Replace sourceColor with sourceColorHct. - const optionsHct = computed(() => - (({ sourceColor, ...o }) => o)({ - sourceColorHct: hct.value, - ...toValue(options), - }) - ); - - const { colorsHct, scheme } = useDynamicColorsHct(optionsHct); - const colors = computed(() => replaceHctWithHex(toValue(colorsHct))); - - return { colors, scheme }; -} - -const replaceHctWithHex = (colorsHct: { [k in ColorName]: Hct }) => - // replace the object values while keeping the keys - Object.assign( - {}, - ...Object.entries(colorsHct).map(([colorName, hct]) => ({ - [colorName]: hexFromHct(hct), - })) - ) as { [k in ColorName]: string }; - -export function useDynamicColorsHct(options?: MaybeRef) { - const scheme = useDynamicSchemeOld(options); - const colorsHct = computed(() => generateColorsFromScheme(toValue(scheme))); - return { colorsHct, scheme }; -} - -export function hctFromHex(hex: string) { - return Hct.fromInt(argbFromHex(hex)); -} - -export function hexFromHct(hct: Hct) { - return hexFromArgb(hct.toInt()); -} - -/** - * Create a dynamic scheme reactively. - */ -function useDynamicSchemeOld(options?: MaybeRef) { - const opt = computed(() => ({ ...DEFAULT_OPTIONS, ...toValue(options) })); - const schemeName = computed(() => opt.value.schemeName); - const schemeClass = computed(() => SchemeNameMap[schemeName.value]); - const sourceColorHct = computed(() => opt.value.sourceColorHct); - const dark = computed(() => opt.value.dark); - const contrastLevel = computed(() => opt.value.contrastLevel); - const scheme = computed( - () => - new schemeClass.value( - sourceColorHct.value, - dark.value, - contrastLevel.value - ) - ); - return scheme; -} - -const generateColorsFromScheme = (scheme: DynamicScheme) => - Object.assign( - {}, - ...Object.entries(ColorNameMap).map(([colorName, dynamicColor]) => ({ - [colorName]: dynamicColor.getHct(scheme), - })) - ) as { [k in ColorName]: Hct }; From 568e25f8088ed554149ba0e4f4e98f0cf93a3d9c Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 5 Sep 2023 16:20:57 -0400 Subject: [PATCH 14/14] add a comment --- src/utils/dynamic-color/dynamic-scheme.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/dynamic-color/dynamic-scheme.ts b/src/utils/dynamic-color/dynamic-scheme.ts index c752680..8959bdf 100644 --- a/src/utils/dynamic-color/dynamic-scheme.ts +++ b/src/utils/dynamic-color/dynamic-scheme.ts @@ -18,6 +18,8 @@ const optDefault: Required> = { contrastLevel: 0.0, }; +// "#6750A4" is the default seed in https://www.figma.com/community/file/1035197037666593721/Visualizing-dynamic-color-in-your-app-with-Material-Design + export function useDynamicScheme(options?: UseDynamicSchemeOptions) { const schemeName = ref(options?.schemeName ?? optDefault.schemeName); const sourceColorHex = ref(