|
2 | 2 | import { getContext, onDestroy, createEventDispatcher, tick } from "svelte"; |
3 | 3 |
|
4 | 4 | import type { HSV, RGB, FillChoice, MenuDirection } from "@graphite/messages"; |
5 | | - import { Color, contrastingOutlineFactor, Gradient } from "@graphite/messages"; |
| 5 | + import { |
| 6 | + type Color, |
| 7 | + contrastingOutlineFactor, |
| 8 | + isColor, |
| 9 | + isGradient, |
| 10 | + createColor, |
| 11 | + createNoneColor, |
| 12 | + createColorFromHSVA, |
| 13 | + colorFromCSS, |
| 14 | + colorToRgb255, |
| 15 | + colorToHSVA, |
| 16 | + colorToHexOptionalAlpha, |
| 17 | + colorToHexNoAlpha, |
| 18 | + colorToRgbCSS, |
| 19 | + colorContrastingColor, |
| 20 | + colorOpaque, |
| 21 | + colorEquals, |
| 22 | + gradientFirstColor, |
| 23 | + } from "@graphite/messages"; |
6 | 24 | import type { TooltipState } from "@graphite/state-providers/tooltip"; |
7 | 25 | import { clamp } from "@graphite/utility-functions/math"; |
8 | 26 | import { isDesktop } from "@graphite/utility-functions/platform"; |
|
51 | 69 | // TODO: See if this should be made to follow the pattern of DropdownInput.svelte so this could be removed |
52 | 70 | export let open: boolean; |
53 | 71 |
|
54 | | - const hsvaOrNone = colorOrGradient instanceof Color ? colorOrGradient.toHSVA() : colorOrGradient.firstColor()?.toHSVA(); |
| 72 | + const colorForHSVA = isColor(colorOrGradient) ? colorOrGradient : gradientFirstColor(colorOrGradient); |
| 73 | + const hsvaOrNone = colorForHSVA ? colorToHSVA(colorForHSVA) : undefined; |
55 | 74 | const hsva = hsvaOrNone || { h: 0, s: 0, v: 0, a: 1 }; |
56 | 75 |
|
57 | 76 | // Gradient color stops |
58 | | - $: gradient = colorOrGradient instanceof Gradient ? colorOrGradient : undefined; |
| 77 | + $: gradient = isGradient(colorOrGradient) ? colorOrGradient : undefined; |
59 | 78 | let activeIndex = 0 as number | undefined; |
60 | 79 | let activeIndexIsMidpoint = false; |
61 | | - $: selectedGradientColor = (activeIndex !== undefined && gradient?.color[activeIndex]) || (Color.fromCSS("black") as Color); |
| 80 | + $: selectedGradientColor = (activeIndex !== undefined && gradient?.color[activeIndex]) || (colorFromCSS("black") as Color); |
62 | 81 | // Currently viewed color |
63 | | - $: color = colorOrGradient instanceof Color ? colorOrGradient : selectedGradientColor; |
| 82 | + $: color = isColor(colorOrGradient) ? colorOrGradient : selectedGradientColor; |
64 | 83 | // New color components |
65 | 84 | let hue = hsva.h; |
66 | 85 | let saturation = hsva.s; |
|
97 | 116 |
|
98 | 117 | $: oldColor = generateColor(oldHue, oldSaturation, oldValue, oldAlpha, oldIsNone); |
99 | 118 | $: newColor = generateColor(hue, saturation, value, alpha, isNone); |
100 | | - $: rgbChannels = Object.entries(newColor.toRgb255() || { r: undefined, g: undefined, b: undefined }) as [keyof RGB, number | undefined][]; |
| 119 | + $: rgbChannels = Object.entries(colorToRgb255(newColor) || { r: undefined, g: undefined, b: undefined }) as [keyof RGB, number | undefined][]; |
101 | 120 | $: hsvChannels = Object.entries(!isNone ? { h: hue * 360, s: saturation * 100, v: value * 100 } : { h: undefined, s: undefined, v: undefined }) as [keyof HSV, number | undefined][]; |
102 | | - $: opaqueHueColor = new Color({ h: hue, s: 1, v: 1, a: 1 }); |
| 121 | + $: opaqueHueColor = createColorFromHSVA(hue, 1, 1, 1); |
103 | 122 | $: outlineFactor = Math.max(contrastingOutlineFactor(newColor, "--color-2-mildblack", 0.01), contrastingOutlineFactor(oldColor, "--color-2-mildblack", 0.01)); |
104 | 123 | $: outlined = outlineFactor > 0.0001; |
105 | 124 | $: transparency = newColor.alpha < 1 || oldColor.alpha < 1; |
106 | 125 |
|
107 | 126 | function generateColor(h: number, s: number, v: number, a: number, none: boolean) { |
108 | | - if (none) return new Color("none"); |
109 | | - return new Color({ h, s, v, a }); |
| 127 | + if (none) return createNoneColor(); |
| 128 | + return createColorFromHSVA(h, s, v, a); |
110 | 129 | } |
111 | 130 |
|
112 | 131 | async function watchOpen(open: boolean) { |
|
119 | 138 | } |
120 | 139 |
|
121 | 140 | function watchColor(color: Color) { |
122 | | - const hsva = color.toHSVA(); |
| 141 | + const hsva = colorToHSVA(color); |
123 | 142 |
|
124 | 143 | if (hsva === undefined) { |
125 | 144 | setNewHSVA(0, 0, 0, 1, true); |
|
185 | 204 | strayCloses = false; |
186 | 205 | } |
187 | 206 |
|
188 | | - const color = new Color({ h: hue, s: saturation, v: value, a: alpha }); |
| 207 | + const color = createColorFromHSVA(hue, saturation, value, alpha); |
189 | 208 | setColor(color); |
190 | 209 |
|
191 | 210 | if (!e.shiftKey) { |
|
226 | 245 | saturation = saturationRestoreWhenShiftReleased; |
227 | 246 | value = valueRestoreWhenShiftReleased; |
228 | 247 |
|
229 | | - const color = new Color({ h: hue, s: saturation, v: value, a: alpha }); |
| 248 | + const color = createColorFromHSVA(hue, saturation, value, alpha); |
230 | 249 | setColor(color); |
231 | 250 | } |
232 | 251 | } |
|
282 | 301 | value = valueBeforeDrag; |
283 | 302 | alpha = alphaBeforeDrag; |
284 | 303 |
|
285 | | - const color = new Color({ h: hue, s: saturation, v: value, a: alpha }); |
| 304 | + const color = createColorFromHSVA(hue, saturation, value, alpha); |
286 | 305 | setColor(color); |
287 | 306 | } |
288 | 307 |
|
289 | 308 | function setColor(color?: Color) { |
290 | | - const colorToEmit = color || new Color({ h: hue, s: saturation, v: value, a: alpha }); |
| 309 | + const colorToEmit = color || createColorFromHSVA(hue, saturation, value, alpha); |
291 | 310 |
|
292 | | - if (gradientSpectrumInputWidget && activeIndex !== undefined && gradient?.position[activeIndex] !== undefined && colorOrGradient instanceof Gradient) { |
| 311 | + if (gradientSpectrumInputWidget && activeIndex !== undefined && gradient?.position[activeIndex] !== undefined && isGradient(colorOrGradient)) { |
293 | 312 | colorOrGradient.color[activeIndex] = colorToEmit; |
294 | 313 | } |
295 | 314 |
|
|
312 | 331 | } |
313 | 332 |
|
314 | 333 | function setColorCode(colorCode: string) { |
315 | | - const color = Color.fromCSS(colorCode); |
| 334 | + const color = colorFromCSS(colorCode); |
316 | 335 | if (color) setColor(color); |
317 | 336 | } |
318 | 337 |
|
319 | 338 | function setColorRGB(channel: keyof RGB, strength: number | undefined) { |
320 | 339 | // Do nothing if the given value is undefined |
321 | 340 | if (strength === undefined) return undefined; |
322 | 341 | // Set the specified channel to the given value |
323 | | - else if (channel === "r") setColor(new Color(strength / 255, newColor.green, newColor.blue, newColor.alpha)); |
324 | | - else if (channel === "g") setColor(new Color(newColor.red, strength / 255, newColor.blue, newColor.alpha)); |
325 | | - else if (channel === "b") setColor(new Color(newColor.red, newColor.green, strength / 255, newColor.alpha)); |
| 342 | + else if (channel === "r") setColor(createColor(strength / 255, newColor.green, newColor.blue, newColor.alpha)); |
| 343 | + else if (channel === "g") setColor(createColor(newColor.red, strength / 255, newColor.blue, newColor.alpha)); |
| 344 | + else if (channel === "b") setColor(createColor(newColor.red, newColor.green, strength / 255, newColor.alpha)); |
326 | 345 | } |
327 | 346 |
|
328 | 347 | function setColorHSV(channel: keyof HSV, strength: number | undefined) { |
|
353 | 372 |
|
354 | 373 | if (preset === "none") { |
355 | 374 | setNewHSVA(0, 0, 0, 1, true); |
356 | | - setColor(new Color("none")); |
| 375 | + setColor(createNoneColor()); |
357 | 376 | } else { |
358 | | - const presetColor = new Color(...PURE_COLORS[preset], 1); |
359 | | - const hsva = presetColor.toHSVA() || { h: 0, s: 0, v: 0, a: 0 }; |
| 377 | + const presetColor = createColor(...PURE_COLORS[preset], 1); |
| 378 | + const hsva = colorToHSVA(presetColor) || { h: 0, s: 0, v: 0, a: 0 }; |
360 | 379 |
|
361 | 380 | setNewHSVA(hsva.h, hsva.s, hsva.v, hsva.a, false); |
362 | 381 | setColor(presetColor); |
|
406 | 425 | activeIndexIsMidpoint = activeMarkerIsMidpoint; |
407 | 426 |
|
408 | 427 | const color = activeMarkerIndex === undefined ? undefined : gradient?.color[activeMarkerIndex]; |
409 | | - const hsva = color?.toHSVA(); |
| 428 | + const hsva = color ? colorToHSVA(color) : undefined; |
410 | 429 | if (!color || !hsva) return; |
411 | 430 |
|
412 | 431 | setColor(color); |
|
427 | 446 | <FloatingMenu class="color-picker" classes={{ disabled }} {open} on:open {strayCloses} escapeCloses={strayCloses && !gradientSpectrumDragging} {direction} type="Popover" bind:this={self}> |
428 | 447 | <LayoutRow |
429 | 448 | styles={{ |
430 | | - "--new-color": newColor.toHexOptionalAlpha(), |
431 | | - "--new-color-contrasting": newColor.contrastingColor(), |
432 | | - "--old-color": oldColor.toHexOptionalAlpha(), |
433 | | - "--old-color-contrasting": oldColor.contrastingColor(), |
434 | | - "--hue-color": opaqueHueColor.toRgbCSS(), |
435 | | - "--hue-color-contrasting": opaqueHueColor.contrastingColor(), |
436 | | - "--opaque-color": (newColor.opaque() || new Color(0, 0, 0, 1)).toHexNoAlpha(), |
437 | | - "--opaque-color-contrasting": (newColor.opaque() || new Color(0, 0, 0, 1)).contrastingColor(), |
| 449 | + "--new-color": colorToHexOptionalAlpha(newColor), |
| 450 | + "--new-color-contrasting": colorContrastingColor(newColor), |
| 451 | + "--old-color": colorToHexOptionalAlpha(oldColor), |
| 452 | + "--old-color-contrasting": colorContrastingColor(oldColor), |
| 453 | + "--hue-color": colorToRgbCSS(opaqueHueColor), |
| 454 | + "--hue-color-contrasting": colorContrastingColor(opaqueHueColor), |
| 455 | + "--opaque-color": colorToHexNoAlpha(colorOpaque(newColor) || createColor(0, 0, 0, 1)), |
| 456 | + "--opaque-color-contrasting": colorContrastingColor(colorOpaque(newColor) || createColor(0, 0, 0, 1)), |
438 | 457 | }} |
439 | 458 | > |
440 | 459 | {@const hueDescription = "The shade along the spectrum of the rainbow."} |
|
528 | 547 | class="choice-preview" |
529 | 548 | classes={{ outlined, transparency }} |
530 | 549 | styles={{ "--outline-amount": outlineFactor }} |
531 | | - tooltipDescription={!newColor.equals(oldColor) ? "Comparison between the present color choice (left) and the color before it was changed (right)." : "The present color choice."} |
| 550 | + tooltipDescription={!colorEquals(newColor, oldColor) ? "Comparison between the present color choice (left) and the color before it was changed (right)." : "The present color choice."} |
532 | 551 | > |
533 | | - {#if !newColor.equals(oldColor) && !disabled} |
| 552 | + {#if !colorEquals(newColor, oldColor) && !disabled} |
534 | 553 | <div class="swap-button-background"></div> |
535 | 554 | <IconButton class="swap-button" icon="SwapHorizontal" size={16} action={swapNewWithOld} tooltipLabel="Swap" /> |
536 | 555 | {/if} |
537 | 556 | <LayoutCol class="new-color" classes={{ none: isNone }}> |
538 | | - {#if !newColor.equals(oldColor)} |
| 557 | + {#if !colorEquals(newColor, oldColor)} |
539 | 558 | <TextLabel>New</TextLabel> |
540 | 559 | {/if} |
541 | 560 | </LayoutCol> |
542 | | - {#if !newColor.equals(oldColor)} |
| 561 | + {#if !colorEquals(newColor, oldColor)} |
543 | 562 | <LayoutCol class="old-color" classes={{ none: oldIsNone }}> |
544 | 563 | <TextLabel>Old</TextLabel> |
545 | 564 | </LayoutCol> |
|
552 | 571 | <Separator style="Related" /> |
553 | 572 | <LayoutRow> |
554 | 573 | <TextInput |
555 | | - value={newColor.toHexOptionalAlpha() || "-"} |
| 574 | + value={colorToHexOptionalAlpha(newColor) || "-"} |
556 | 575 | {disabled} |
557 | 576 | on:commitText={({ detail }) => { |
558 | 577 | dispatch("startHistoryTransaction"); |
|
0 commit comments