From 51e5e65be7f834ec226be28d95a1b547b85b329c Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 14 Jan 2025 19:01:47 +0500 Subject: [PATCH] refactor(ColorPicker)!: migrate from `color` to `colortranslator` (#3097) --- docs/content/3.components/color-picker.md | 25 ++++++-- package.json | 5 +- pnpm-lock.yaml | 39 +++++-------- src/runtime/components/ColorPicker.vue | 57 ++++++++++++++----- test/components/ColorPicker.spec.ts | 3 +- .../ColorPicker-vue.spec.ts.snap | 31 +++++++--- .../__snapshots__/ColorPicker.spec.ts.snap | 31 +++++++--- 7 files changed, 128 insertions(+), 63 deletions(-) diff --git a/docs/content/3.components/color-picker.md b/docs/content/3.components/color-picker.md index 8495c96c..4e058789 100644 --- a/docs/content/3.components/color-picker.md +++ b/docs/content/3.components/color-picker.md @@ -68,9 +68,9 @@ props: --- :: -### HWB Format +### CMYK Format -Use the `format` prop to set `hwb` value of the ColorPicker. +Use the `format` prop to set `cmyk` value of the ColorPicker. ::component-code --- @@ -80,8 +80,25 @@ ignore: external: - modelValue props: - format: hwb - modelValue: 'hwb(150, 0%, 24%)' + format: cmyk + modelValue: 'cmyk(100%, 0%, 45.08%, 24.31%)' +--- +:: + +### CIELab Format + +Use the `format` prop to set `lab` value of the ColorPicker. + +::component-code +--- +ignore: + - modelValue + - format +external: + - modelValue +props: + format: lab + modelValue: 'lab(68.88% -60.41% 32.55%)' --- :: diff --git a/package.json b/package.json index 208bdef2..8f60fa44 100644 --- a/package.json +++ b/package.json @@ -85,11 +85,10 @@ "@tailwindcss/postcss": "4.0.0-beta.9", "@tailwindcss/vite": "4.0.0-beta.9", "@tanstack/vue-table": "^8.20.5", - "@types/color": "^4.2.0", "@unhead/vue": "^1.11.16", "@vueuse/core": "^12.4.0", "@vueuse/integrations": "^12.4.0", - "color": "^4.2.3", + "colortranslator": "^4.1.0", "consola": "^3.3.3", "defu": "^6.1.4", "embla-carousel-auto-height": "^8.5.2", @@ -105,8 +104,8 @@ "magic-string": "^0.30.17", "mlly": "^1.7.4", "ohash": "^1.1.4", - "reka-ui": "1.0.0-alpha.8", "pathe": "^2.0.1", + "reka-ui": "1.0.0-alpha.8", "scule": "^1.3.0", "sirv": "^3.0.0", "tailwind-variants": "^0.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ff74dc13..b57900ad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,9 +56,6 @@ importers: '@tanstack/vue-table': specifier: ^8.20.5 version: 8.20.5(vue@3.5.13(typescript@5.6.3)) - '@types/color': - specifier: ^4.2.0 - version: 4.2.0 '@unhead/vue': specifier: ^1.11.16 version: 1.11.16(vue@3.5.13(typescript@5.6.3)) @@ -68,9 +65,9 @@ importers: '@vueuse/integrations': specifier: ^12.4.0 version: 12.4.0(change-case@5.4.4)(fuse.js@7.0.0)(typescript@5.6.3) - color: - specifier: ^4.2.3 - version: 4.2.3 + colortranslator: + specifier: ^4.1.0 + version: 4.1.0 consola: specifier: ^3.3.3 version: 3.3.3 @@ -2498,15 +2495,6 @@ packages: resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} - '@types/color-convert@2.0.4': - resolution: {integrity: sha512-Ub1MmDdyZ7mX//g25uBAoH/mWGd9swVbt8BseymnaE18SU4po/PjmCrHxqIIRjBo3hV/vh1KGr0eMxUhp+t+dQ==} - - '@types/color-name@1.1.5': - resolution: {integrity: sha512-j2K5UJqGTxeesj6oQuGpMgifpT5k9HprgQd8D1Y0lOFqKHl3PJu5GMeS4Y5EgjS55AE6OQxf8mPED9uaGbf4Cg==} - - '@types/color@4.2.0': - resolution: {integrity: sha512-6+xrIRImMtGAL2X3qYkd02Mgs+gFGs+WsK0b7VVMaO4mYRISwyTjcqNrO0mNSmYEoq++rSLDB2F5HDNmqfOe+A==} - '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -3249,6 +3237,9 @@ packages: colorjs.io@0.5.2: resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==} + colortranslator@4.1.0: + resolution: {integrity: sha512-bwa5awaMnQ6dpm9D3nbsFwUr6x6FrTKmxPdolNtSYfxCNR7ZM93GG1OF5Y3Sy1LvYdalb3riKC9uTn0X5NB36g==} + comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -10215,16 +10206,6 @@ snapshots: '@trysound/sax@0.2.0': {} - '@types/color-convert@2.0.4': - dependencies: - '@types/color-name': 1.1.5 - - '@types/color-name@1.1.5': {} - - '@types/color@4.2.0': - dependencies: - '@types/color-convert': 2.0.4 - '@types/debug@4.1.12': dependencies: '@types/ms': 0.7.34 @@ -11099,11 +11080,13 @@ snapshots: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 + optional: true color@4.2.3: dependencies: color-convert: 2.0.1 color-string: 1.9.1 + optional: true colord@2.9.3: {} @@ -11111,6 +11094,8 @@ snapshots: colorjs.io@0.5.2: {} + colortranslator@4.1.0: {} + comma-separated-tokens@2.0.3: {} commander@10.0.1: {} @@ -12628,7 +12613,8 @@ snapshots: is-arrayish@0.2.1: {} - is-arrayish@0.3.2: {} + is-arrayish@0.3.2: + optional: true is-binary-path@2.1.0: dependencies: @@ -14968,6 +14954,7 @@ snapshots: simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 + optional: true sirv@3.0.0: dependencies: diff --git a/src/runtime/components/ColorPicker.vue b/src/runtime/components/ColorPicker.vue index 13c6dc76..43ff2c2e 100644 --- a/src/runtime/components/ColorPicker.vue +++ b/src/runtime/components/ColorPicker.vue @@ -5,6 +5,7 @@ import type { AppConfig } from '@nuxt/schema' import _appConfig from '#build/app.config' import theme from '#build/ui/color-picker' import { tv } from '../utils/tv' +import type { HSLObject } from 'colortranslator' const appConfig = _appConfig as AppConfig & { ui: { colorPicker: Partial } } @@ -18,6 +19,27 @@ type HSVColor = { v: number } +function HSLtoHSV(hsl: HSLObject): HSVColor { + const x = hsl.S * (hsl.L < 50 ? hsl.L : 100 - hsl.L) + const v = hsl.L + (x / 100) + + return { + h: hsl.H, + s: hsl.L === 0 ? hsl.S : 2 * x / v, + v + } +} + +function HSVtoHSL(hsv: HSVColor): HSLObject { + const x = (200 - hsv.s) * hsv.v / 100 + + return { + H: hsv.h, + S: x === 0 || x === 200 ? 0 : Math.round(hsv.s * hsv.v / (x <= 100 ? x : 200 - x)), + L: Math.round(x / 2) + } +} + export type ColorPickerProps = { /** * The element or component this component should render as. @@ -40,7 +62,7 @@ export type ColorPickerProps = { * Format of the color * @defaultValue 'hex' */ - format?: 'hex' | 'rgb' | 'hsl' | 'hwb' + format?: 'hex' | 'rgb' | 'hsl' | 'cmyk' | 'lab' size?: ColorPickerVariants['size'] class?: any ui?: Partial @@ -52,7 +74,7 @@ import { ref, nextTick, computed, toValue } from 'vue' import { Primitive } from 'reka-ui' import { useEventListener, useElementBounding, watchThrottled, watchPausable } from '@vueuse/core' import { isClient } from '@vueuse/shared' -import Color from 'color' +import { ColorTranslator } from 'colortranslator' const props = withDefaults(defineProps(), { format: 'hex', @@ -64,28 +86,37 @@ const modelValue = defineModel(undefined) const pickedColor = computed({ get() { try { - const color = Color(modelValue.value || props.defaultValue) - return color.hsv().object() as HSVColor + const color = new ColorTranslator(modelValue.value || props.defaultValue) + + return HSLtoHSV(color.HSLObject) } catch (_) { return { h: 0, s: 0, v: 100 } } }, set(value) { - const color = Color.hsv(value.h, value.s, value.v) + const color = new ColorTranslator(HSVtoHSL(value), { + decimals: 2, + labUnit: 'percent', + cmykUnit: 'percent', + cmykFunction: 'cmyk' + }) switch (props.format) { case 'rgb': - modelValue.value = color.rgb().string() + modelValue.value = color.RGB break case 'hsl': - modelValue.value = color.hsl().string() + modelValue.value = color.HSL break - case 'hwb': - modelValue.value = color.hwb().string() + case 'cmyk': + modelValue.value = color.CMYK + break + case 'lab': + modelValue.value = color.CIELab break case 'hex': default: - modelValue.value = color.hex() + modelValue.value = color.HEX } } }) @@ -202,18 +233,18 @@ watchThrottled([selectorThumbPosition, trackThumbPosition], () => { nextTick(resumeWatchColor) }, { throttle: () => props.throttle }) -const trackThumbColor = computed(() => Color({ +const trackThumbColor = computed(() => new ColorTranslator(HSVtoHSL({ h: normalizeHue(trackThumbPosition.value.y), s: 100, v: 100 -}).hex()) +})).HEX) const selectorStyle = computed(() => ({ backgroundColor: trackThumbColor.value })) const selectorThumbStyle = computed(() => ({ - backgroundColor: Color(modelValue.value || props.defaultValue).hex(), + backgroundColor: new ColorTranslator(modelValue.value || props.defaultValue).HEX, left: `${selectorThumbPosition.value.x}%`, top: `${selectorThumbPosition.value.y}%` })) diff --git a/test/components/ColorPicker.spec.ts b/test/components/ColorPicker.spec.ts index 8c706c24..46aa09dc 100644 --- a/test/components/ColorPicker.spec.ts +++ b/test/components/ColorPicker.spec.ts @@ -10,7 +10,8 @@ describe('ColorPicker', () => { ['hex', '#00C16A'], ['rgb', 'rgb(0, 193, 106)'], ['hsl', 'hsl(153, 100%, 37.8%)'], - ['hwb', 'hwb(150, 0%, 24%)'] + ['lab', 'lab(68.88% -60.41% 32.55%)'], + ['cmyk', 'cmyk(100%, 0%, 45.08%, 24.31%)'] ] it.each([ diff --git a/test/components/__snapshots__/ColorPicker-vue.spec.ts.snap b/test/components/__snapshots__/ColorPicker-vue.spec.ts.snap index 186b3c89..4b3cdd2c 100644 --- a/test/components/__snapshots__/ColorPicker-vue.spec.ts.snap +++ b/test/components/__snapshots__/ColorPicker-vue.spec.ts.snap @@ -45,16 +45,31 @@ exports[`ColorPicker > renders with disabled correctly 1`] = ` " `; +exports[`ColorPicker > renders with format cmyk correctly 1`] = ` +"
+
+
+
+
+
+
+
+
+
+
+
" +`; + exports[`ColorPicker > renders with format hex correctly 1`] = ` "
-
+
-
+
" @@ -75,16 +90,16 @@ exports[`ColorPicker > renders with format hsl correctly 1`] = ` " `; -exports[`ColorPicker > renders with format hwb correctly 1`] = ` +exports[`ColorPicker > renders with format lab correctly 1`] = ` "
-
+
-
+
-
+
" @@ -95,11 +110,11 @@ exports[`ColorPicker > renders with format rgb correctly 1`] = `
-
+
-
+
" diff --git a/test/components/__snapshots__/ColorPicker.spec.ts.snap b/test/components/__snapshots__/ColorPicker.spec.ts.snap index 927fbbc9..a44a2ef8 100644 --- a/test/components/__snapshots__/ColorPicker.spec.ts.snap +++ b/test/components/__snapshots__/ColorPicker.spec.ts.snap @@ -45,16 +45,31 @@ exports[`ColorPicker > renders with disabled correctly 1`] = ` " `; +exports[`ColorPicker > renders with format cmyk correctly 1`] = ` +"
+
+
+
+
+
+
+
+
+
+
+
" +`; + exports[`ColorPicker > renders with format hex correctly 1`] = ` "
-
+
-
+
" @@ -75,16 +90,16 @@ exports[`ColorPicker > renders with format hsl correctly 1`] = ` " `; -exports[`ColorPicker > renders with format hwb correctly 1`] = ` +exports[`ColorPicker > renders with format lab correctly 1`] = ` "
-
+
-
+
-
+
" @@ -95,11 +110,11 @@ exports[`ColorPicker > renders with format rgb correctly 1`] = `
-
+
-
+
"