Skip to content

Commit a25a69e

Browse files
committed
feat(Color Converter): Many enhancements
Add XYZ and Lab colors values Add Saturation/Bightness sliders Add Grayscale/Invert options
1 parent 9eac9cb commit a25a69e

File tree

2 files changed

+105
-27
lines changed

2 files changed

+105
-27
lines changed

components.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,10 @@ declare module '@vue/runtime-core' {
127127
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
128128
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
129129
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
130+
NCheckbox: typeof import('naive-ui')['NCheckbox']
130131
NCode: typeof import('naive-ui')['NCode']
131132
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
133+
NColorPicker: typeof import('naive-ui')['NColorPicker']
132134
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
133135
NDivider: typeof import('naive-ui')['NDivider']
134136
NEllipsis: typeof import('naive-ui')['NEllipsis']
@@ -144,6 +146,8 @@ declare module '@vue/runtime-core' {
144146
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
145147
NMenu: typeof import('naive-ui')['NMenu']
146148
NScrollbar: typeof import('naive-ui')['NScrollbar']
149+
NSlider: typeof import('naive-ui')['NSlider']
150+
NSpace: typeof import('naive-ui')['NSpace']
147151
NSpin: typeof import('naive-ui')['NSpin']
148152
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
149153
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
@@ -159,6 +163,7 @@ declare module '@vue/runtime-core' {
159163
RouterLink: typeof import('vue-router')['RouterLink']
160164
RouterView: typeof import('vue-router')['RouterView']
161165
RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
166+
SafelinkDecoder: typeof import('./src/tools/safelink-decoder/safelink-decoder.vue')['default']
162167
SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
163168
SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
164169
SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']

src/tools/color-converter/color-converter.vue

Lines changed: 100 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import cmykPlugin from 'colord/plugins/cmyk';
66
import hwbPlugin from 'colord/plugins/hwb';
77
import namesPlugin from 'colord/plugins/names';
88
import lchPlugin from 'colord/plugins/lch';
9+
import xyzPlugin from 'colord/plugins/xyz';
10+
import labPlugin from 'colord/plugins/lab';
911
import { buildColorFormat } from './color-converter.models';
1012
11-
extend([cmykPlugin, hwbPlugin, namesPlugin, lchPlugin]);
13+
extend([cmykPlugin, hwbPlugin, namesPlugin, lchPlugin, xyzPlugin, labPlugin]);
1214
1315
const formats = {
1416
picker: buildColorFormat({
@@ -46,14 +48,36 @@ const formats = {
4648
format: (v: Colord) => v.toCmykString(),
4749
placeholder: 'e.g. cmyk(0, 100%, 100%, 0)',
4850
}),
51+
lab: buildColorFormat({
52+
label: 'lab',
53+
format: (v: Colord) => JSON.stringify(v.toLab()),
54+
placeholder: 'e.g. { l: 14.89, a: 5.77, b: 14.41, alpha: 0.5 }',
55+
parse: value => colord(JSON.parse(value)),
56+
}),
57+
xyz: buildColorFormat({
58+
label: 'xyz',
59+
format: (v: Colord) => JSON.stringify(v.toXyz()),
60+
placeholder: 'e.g. { x: 95.047, y: 100, z: 108.883, a: 1 }',
61+
parse: value => colord(JSON.parse(value)),
62+
}),
4963
name: buildColorFormat({
5064
label: 'name',
5165
format: (v: Colord) => v.toName({ closest: true }) ?? 'Unknown',
5266
placeholder: 'e.g. red',
5367
}),
5468
};
5569
56-
updateColorValue(colord('#1ea54c'));
70+
const saturation = ref(0);
71+
const brightness = ref(0);
72+
const grayscale = ref(false);
73+
const invert = ref(false);
74+
75+
let lastColor = colord('#1ea54c');
76+
watch([saturation, brightness, grayscale, invert],
77+
() => updateColorValue(lastColor),
78+
);
79+
80+
updateColorValue(lastColor);
5781
5882
function updateColorValue(value: Colord | undefined, omitLabel?: string) {
5983
if (value === undefined) {
@@ -64,40 +88,89 @@ function updateColorValue(value: Colord | undefined, omitLabel?: string) {
6488
return;
6589
}
6690
91+
lastColor = value;
92+
93+
let correctedValue = value;
94+
if (grayscale.value) {
95+
correctedValue = correctedValue.grayscale();
96+
}
97+
if (invert.value) {
98+
correctedValue = correctedValue.invert();
99+
}
100+
101+
const saturationFloat = saturation.value / 100.0;
102+
if (saturationFloat > 0) {
103+
correctedValue = correctedValue.saturate(saturationFloat);
104+
}
105+
else if (saturationFloat < 0) {
106+
correctedValue = correctedValue.desaturate(-saturationFloat);
107+
}
108+
109+
const brightnessFloat = brightness.value / 100.0;
110+
if (brightnessFloat > 0) {
111+
correctedValue = correctedValue.lighten(brightnessFloat);
112+
}
113+
else if (brightnessFloat < 0) {
114+
correctedValue = correctedValue.darken(-brightnessFloat);
115+
}
116+
67117
_.forEach(formats, ({ value: valueRef, format }, key) => {
68118
if (key !== omitLabel) {
69-
valueRef.value = format(value);
119+
valueRef.value = format(correctedValue);
70120
}
71121
});
72122
}
73123
</script>
74124

75125
<template>
76-
<c-card>
77-
<template v-for="({ label, parse, placeholder, validation, type }, key) in formats" :key="key">
78-
<input-copyable
79-
v-if="type === 'text'"
80-
v-model:value="formats[key].value.value"
81-
:test-id="`input-${key}`"
82-
:label="`${label}:`"
83-
label-position="left"
84-
label-width="100px"
85-
label-align="right"
86-
:placeholder="placeholder"
87-
:validation="validation"
88-
raw-text
89-
clearable
90-
mt-2
91-
@update:value="(v:string) => updateColorValue(parse(v), key)"
92-
/>
93-
94-
<n-form-item v-else-if="type === 'color-picker'" :label="`${label}:`" label-width="100" label-placement="left" :show-feedback="false">
95-
<n-color-picker
126+
<div>
127+
<c-card title="Transformations">
128+
<n-form-item label="Saturation" label-placement="left">
129+
<n-slider v-model:value="saturation" :step="1" :min="-100" :max="100" mr-2 />
130+
<n-input-number v-model:value="saturation" size="small" />
131+
</n-form-item>
132+
133+
<n-form-item label="Brightness" label-placement="left">
134+
<n-slider v-model:value="brightness" :step="1" :min="-100" :max="100" mr-2 />
135+
<n-input-number v-model:value="brightness" size="small" />
136+
</n-form-item>
137+
138+
<n-space>
139+
<n-form-item label="Grayscale" label-placement="left">
140+
<n-checkbox v-model:checked="grayscale" mr-2 />
141+
</n-form-item>
142+
143+
<n-form-item label="Invert" label-placement="left">
144+
<n-checkbox v-model:checked="invert" mr-2 />
145+
</n-form-item>
146+
</n-space>
147+
</c-card>
148+
<c-card>
149+
<template v-for="({ label, parse, placeholder, validation, type }, key) in formats" :key="key">
150+
<input-copyable
151+
v-if="type === 'text'"
96152
v-model:value="formats[key].value.value"
97-
placement="bottom-end"
153+
:test-id="`input-${key}`"
154+
:label="`${label}:`"
155+
label-position="left"
156+
label-width="100px"
157+
label-align="right"
158+
:placeholder="placeholder"
159+
:validation="validation"
160+
raw-text
161+
clearable
162+
mt-2
98163
@update:value="(v:string) => updateColorValue(parse(v), key)"
99164
/>
100-
</n-form-item>
101-
</template>
102-
</c-card>
165+
166+
<n-form-item v-else-if="type === 'color-picker'" :label="`${label}:`" label-width="100" label-placement="left" :show-feedback="false">
167+
<n-color-picker
168+
v-model:value="formats[key].value.value"
169+
placement="bottom-end"
170+
@update:value="(v:string) => updateColorValue(parse(v), key)"
171+
/>
172+
</n-form-item>
173+
</template>
174+
</c-card>
175+
</div>
103176
</template>

0 commit comments

Comments
 (0)