Skip to content

Commit

Permalink
Make color picker more stable by storing hsva
Browse files Browse the repository at this point in the history
  • Loading branch information
dabbott committed Dec 18, 2022
1 parent 76103ee commit 1eb0d44
Show file tree
Hide file tree
Showing 21 changed files with 127 additions and 107 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,6 @@ module.exports = {
'testing-library/no-container': 'off',
'@typescript-eslint/no-namespace': 'off',
'jest/prefer-to-have-length': 'off',
'@typescript-eslint/require-await': 'off',
},
};
15 changes: 15 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@
"printWidth": 80,
"proseWrap": "always"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"dependencies": {
"@shopify/eslint-plugin": "^40.1.0",
"@testing-library/jest-dom": "^5.11.9",
Expand All @@ -62,6 +74,9 @@
"jest-image-snapshot": "^4.5.1",
"lint-staged": "^13.1.0",
"modular-scripts": "^3.6.0",
"modular-template-app": "^1.1.0",
"modular-template-package": "^1.1.0",
"modular-template-view": "^1.1.0",
"nock": "^13.1.1",
"prettier": "^2.2.1",
"react": "^17.0.1",
Expand Down
3 changes: 1 addition & 2 deletions packages/app/src/components/inspector/BorderRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import {
import { SetNumberMode } from 'noya-state';
import React, { memo, ReactNode, useCallback } from 'react';
import * as InspectorPrimitives from '../inspector/InspectorPrimitives';
import DimensionInput from './DimensionInput';
import { DimensionValue } from './DimensionsInspector';
import DimensionInput, { DimensionValue } from './DimensionInput';
import FillInputFieldWithPicker, {
ColorFillProps,
GradientFillProps,
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/components/inspector/ColorControlsRow.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SetNumberMode } from 'noya-state';
import React, { memo } from 'react';
import * as InspectorPrimitives from '../inspector/InspectorPrimitives';
import { DimensionValue } from './DimensionsInspector';
import { DimensionValue } from './DimensionInput';
import { DimensionSliderRow } from './DimensionSliderRow';

interface Props {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { Spacer } from 'noya-designsystem';
import { SetNumberMode } from 'noya-state';
import React from 'react';
import * as InspectorPrimitives from '../inspector/InspectorPrimitives';
import DimensionInput from './DimensionInput';
import { DimensionValue } from './DimensionsInspector';
import DimensionInput, { DimensionValue } from './DimensionInput';

function roundNumber(number: number, roundTo: number) {
return Number(number.toFixed(roundTo));
Expand Down
3 changes: 2 additions & 1 deletion packages/app/src/components/inspector/DimensionInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { InputField } from 'noya-designsystem';
import { SetNumberMode } from 'noya-state';
import { round } from 'noya-utils';
import React, { memo, useCallback } from 'react';
import { DimensionValue } from './DimensionsInspector';

export type DimensionValue = number | undefined;

interface Props {
id?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { InputField, Slider } from 'noya-designsystem';
import { SetNumberMode } from 'noya-state';
import React, { memo, useCallback } from 'react';
import * as InspectorPrimitives from '../inspector/InspectorPrimitives';
import { DimensionValue } from './DimensionsInspector';
import { DimensionValue } from './DimensionInput';

interface Props {
id: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import { IconButton, Spacer } from 'noya-designsystem';
import { SetNumberMode } from 'noya-state';
import React, { useCallback } from 'react';
import styled from 'styled-components';
import DimensionInput from './DimensionInput';
import DimensionInput, { DimensionValue } from './DimensionInput';
import FlipControls from './FlipControls';
import * as InspectorPrimitives from './InspectorPrimitives';

export type DimensionValue = number | undefined;

const Row = styled.div(({ theme }) => ({
flex: '0 0 auto',
display: 'flex',
Expand Down
3 changes: 1 addition & 2 deletions packages/app/src/components/inspector/FillRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import Sketch from 'noya-file-format';
import { SetNumberMode } from 'noya-state';
import React, { memo, ReactNode, useCallback, useMemo } from 'react';
import * as InspectorPrimitives from '../inspector/InspectorPrimitives';
import DimensionInput from './DimensionInput';
import { DimensionValue } from './DimensionsInspector';
import DimensionInput, { DimensionValue } from './DimensionInput';
import FillInputFieldWithPicker, {
ColorFillProps,
GradientFillProps,
Expand Down
3 changes: 1 addition & 2 deletions packages/app/src/components/inspector/ShadowRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { Label, LabeledElementView } from 'noya-designsystem';
import { SetNumberMode } from 'noya-state';
import React, { memo, ReactNode, useCallback } from 'react';
import * as InspectorPrimitives from '../inspector/InspectorPrimitives';
import DimensionInput from './DimensionInput';
import { DimensionValue } from './DimensionsInspector';
import DimensionInput, { DimensionValue } from './DimensionInput';
import FillInputFieldWithPicker, {
ColorFillProps,
} from './FillInputFieldWithPicker';
Expand Down
4 changes: 0 additions & 4 deletions packages/app/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
box-sizing: border-box;
}

/* html {
font-size: 62.5%;
} */

body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
Expand Down
19 changes: 14 additions & 5 deletions packages/noya-colorpicker/src/components/ColorPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { ReactNode } from 'react';
import { useDeepMemo } from 'noya-react-utils';
import React, { ReactNode, useCallback, useMemo } from 'react';
import styled from 'styled-components';
import { ColorPickerProvider } from '../contexts/ColorPickerContext';
import { useColorManipulation } from '../hooks/useColorManipulation';
import { AnyColor, ColorModel, ColorPickerBaseProps } from '../types';
import { ColorPickerContextValue, ColorPickerProvider } from '../contexts/ColorPickerContext';
import { AnyColor, ColorModel, ColorPickerBaseProps, HsvaColor } from '../types';

const Container = styled.div({
position: 'relative',
Expand All @@ -23,7 +23,16 @@ export default function ColorPicker<T extends AnyColor>({
onChange,
children,
}: Props<T>): JSX.Element {
const contextValue = useColorManipulation<T>(colorModel, color, onChange);
const hsva = useDeepMemo(colorModel.toHsva(color));

const handleChange = useCallback(
(params: Partial<HsvaColor>) => {
onChange?.(colorModel.fromHsva({ ...hsva, ...params }))
},
[colorModel, hsva, onChange],
);

const contextValue = useMemo((): ColorPickerContextValue => [hsva, handleChange], [handleChange, hsva])

return (
<ColorPickerProvider value={contextValue}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createContext, useContext } from 'react';
import { HsvaColor } from '../types';

type ColorPickerContextValue = [
export type ColorPickerContextValue = [
hsva: HsvaColor,
onChange: (
color: Partial<HsvaColor>,
Expand Down
62 changes: 0 additions & 62 deletions packages/noya-colorpicker/src/hooks/useColorManipulation.ts

This file was deleted.

42 changes: 30 additions & 12 deletions packages/noya-designsystem/src/components/ColorPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {
ColorModel,
ColorPicker as NoyaColorPicker,
equalColorObjects,
HsvaColor,
hsvaToRgba,
Hue,
RgbaColor,
rgbaToHsva,
Saturation,
} from 'noya-colorpicker';
Expand All @@ -19,27 +19,45 @@ interface Props {
onChange: (color: Sketch.Color) => void;
}

const colorModel: ColorModel<HsvaColor> = {
defaultColor: { h: 0, s: 0, v: 0, a: 1 },
equal: equalColorObjects,
toHsva: (a) => a,
fromHsva: (a) => a,
};

export default memo(function ColorPicker({ value, onChange }: Props) {
const colorModel: ColorModel<RgbaColor> = {
defaultColor: { r: 0, g: 0, b: 0, a: 1 },
toHsva: rgbaToHsva,
fromHsva: hsvaToRgba,
equal: equalColorObjects,
};
const rgbaColor: RgbaColor = useMemo(() => sketchColorToRgba(value), [value]);
const hsva = value.colorSpaces?.hsva;

const color = useMemo(() => {
return hsva
? { h: hsva.hue, s: hsva.saturation, v: hsva.value, a: hsva.alpha }
: rgbaToHsva(sketchColorToRgba(value));
}, [value, hsva]);

const handleChange = useCallback(
(value: RgbaColor) => {
onChange(rgbaToSketchColor(value));
(hsva: HsvaColor) => {
const updated = rgbaToSketchColor(hsvaToRgba(hsva));

updated.colorSpaces = {
hsva: {
hue: hsva.h,
saturation: hsva.s,
value: hsva.v,
alpha: hsva.a,
},
};

onChange(updated);
},
[onChange],
);

return (
<NoyaColorPicker
<NoyaColorPicker<HsvaColor>
color={color}
colorModel={colorModel}
onChange={handleChange}
color={rgbaColor}
>
<Saturation />
<Spacer.Vertical size={12} />
Expand Down
1 change: 1 addition & 0 deletions packages/noya-designsystem/src/components/GridView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const ItemContainer = styled.div<{ selected: boolean }>(
const GridContainer = styled.div(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
position: 'relative',
}));

const ItemTitle = styled.span(({ theme }) => ({
Expand Down
1 change: 1 addition & 0 deletions packages/noya-designsystem/src/components/InputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ type InputFieldNumberInputProps = Omit<
> & {
value: number | undefined;
onNudge?: (value: number) => void;
variant?: 'bare';
} & (
| {
onChange: (value: number) => void;
Expand Down
8 changes: 8 additions & 0 deletions packages/noya-file-format/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ export type Color = {
green: UnitInterval;
blue: UnitInterval;
swatchID?: Uuid;
colorSpaces?: {
hsva?: {
hue: number;
saturation: number;
value: number;
alpha: number;
};
};
};
/**
* The set of all real numbers that are greater than or equal to 0 and less than or equal to 1. Used within Sketch documents to encode normalised scalar values, for example RGB color components.
Expand Down
14 changes: 14 additions & 0 deletions packages/noya-react-utils/src/SuspendedValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,18 @@ export class SuspendedValue<T> {
return this.promiseState.value;
}
}

/**
* Resolve a SuspendedValue immediately, without waiting for the next
* cycle of the event loop. Useful for synchronous tests.
*/
static resolveInstantly<T>(value: T): SuspendedValue<T> {
const resolved: Promise<T> = Promise.resolve(value);
const suspended = new SuspendedValue(resolved);

// Replace the suspended promise internals
suspended.suspendedPromise = new Promise(() => {});
suspended.promiseState = { type: 'success', value: value };
return suspended;
}
}
27 changes: 18 additions & 9 deletions packages/noya-renderer/src/FontManagerContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,23 @@ const FontManagerContext = createContext<FontManagerContextValue | undefined>(
undefined,
);

const load = (name: string) => {
const path = getPublicPath() + 'fonts/' + name;
function loadPublicAsset(name: string) {
const path = getPublicPath() + name;

// Detect node vs browser
return typeof window !== 'undefined' && typeof fetch !== 'undefined'
? fetch(path).then((response) => response.arrayBuffer())
: (require('fs').promises.readFile(path) as Promise<ArrayBuffer>);
};
if (typeof window !== 'undefined' && typeof fetch !== 'undefined') {
return new SuspendedValue(
fetch(path).then((response) => response.arrayBuffer()),
);
} else {
return SuspendedValue.resolveInstantly(require('fs').readFileSync(path));
}
}

const suspendedDefaultFont = new SuspendedValue<ArrayBuffer>(
load('roboto/Roboto-Regular.ttf'),
);
// We don't start loading fonts until the Provider renders the first time,
// since we currently support setting the public path at runtime when the app starts,
// which needs to happen before `load` is called.
let suspendedDefaultFont: SuspendedValue<ArrayBuffer>;

const sharedFontManager = new FontManager(GoogleFontProvider);

Expand All @@ -55,6 +60,10 @@ interface Props {
export const FontManagerProvider = memo(function FontManagerProvider({
children,
}: Props) {
if (!suspendedDefaultFont) {
suspendedDefaultFont = loadPublicAsset('fonts/roboto/Roboto-Regular.ttf');
}

const CanvasKit = useCanvasKit();

const defaultFont = suspendedDefaultFont.getValueOrThrow();
Expand Down
Loading

0 comments on commit 1eb0d44

Please sign in to comment.