Selaa lähdekoodia

bidirectional state binding. Reset button.

Jimmy Chion 3 vuotta sitten
vanhempi
commit
ef93bdeec8

+ 18 - 34
src/components/CodeBlocks/Css.tsx

@@ -1,6 +1,5 @@
 import { Form, Switch, Button, Popover, Select } from 'antd';
 import hljs from 'highlight.js/lib/core';
-import { useEffect, useState } from 'react';
 import { ChromePicker } from 'react-color';
 import styled from 'styled-components';
 import shallow from 'zustand/shallow';
@@ -10,41 +9,26 @@ import SliderInput from './SliderInput';
 import { useInputStore } from '~/components/store';
 
 export const CssControls = () => {
-  const [setCssProps, filterProps] = useInputStore(
-    (state) => [state.setCssProps, state.filterProps],
+  const [setCssProps, cssProps, filterProps] = useInputStore(
+    (state) => [state.setCssProps, state.cssProps, state.filterProps],
     shallow
   );
   const { brightness, contrast } = filterProps;
-  const [angle, setAngle] = useState(112);
-  const [posX, setPosX] = useState(50);
-  const [posY, setPosY] = useState(50);
-  const [showTransparency, setShowTransparency] = useState(true);
-  const [gradientType, setGradientType] = useState('linear');
-  const [color1, setColor1] = useState({ r: 0, g: 0, b: 255, a: 1 });
-  const [color2, setColor2] = useState({ r: 0, g: 0, b: 255, a: 0 });
+  const { gradientType, color1, color2, angle, showTransparency, posX, posY } = cssProps;
 
-  useEffect(() => {
-    setCssProps({
+  const setValues = (key: string, value: any) => {
+    const toSet = {
       gradientType,
-      color1: color1,
-      color2: color2,
+      color1,
+      color2,
       angle,
       showTransparency,
       posX,
       posY,
-    });
-  }, [
-    setCssProps,
-    showTransparency,
-    contrast,
-    brightness,
-    angle,
-    gradientType,
-    color1,
-    color2,
-    posX,
-    posY,
-  ]);
+    };
+    toSet[key] = value;
+    setCssProps(toSet);
+  };
 
   const gradientFirstParam =
     gradientType === 'linear'
@@ -79,7 +63,7 @@ export const CssControls = () => {
             <Form.Item label="Gradient type">
               <Select
                 value={gradientType}
-                onChange={(v) => setGradientType(v)}
+                onChange={(v: string) => setValues('gradientType', v)}
                 style={{ width: 120 }}
               >
                 <Select.Option value="linear">linear</Select.Option>
@@ -92,7 +76,7 @@ export const CssControls = () => {
             <Popover
               placement="bottom"
               style={{ padding: 0 }}
-              content={<ChromePicker color={color1} onChange={(c) => setColor1(c.rgb)} />}
+              content={<ChromePicker color={color1} onChange={(c) => setValues('color1', c.rgb)} />}
               trigger="click"
             >
               <Button>Color 1</Button>
@@ -101,7 +85,7 @@ export const CssControls = () => {
           <ColorPick>
             <Popover
               placement="bottom"
-              content={<ChromePicker color={color2} onChange={(c) => setColor2(c.rgb)} />}
+              content={<ChromePicker color={color2} onChange={(c) => setValues('color2', c.rgb)} />}
               trigger="click"
             >
               <Button>Color 2</Button>
@@ -116,7 +100,7 @@ export const CssControls = () => {
             min={0}
             max={360}
             tipFormatter={(v) => `${v}°`}
-            onChange={(val: number) => setAngle(val)}
+            onChange={(val: number) => setValues('angle', val)}
             value={typeof angle === 'number' ? angle : 0}
           />
         )}
@@ -127,7 +111,7 @@ export const CssControls = () => {
               name="position X"
               min={-50}
               max={150}
-              onChange={(val: number) => setPosX(val)}
+              onChange={(val: number) => setValues('posX', val)}
               value={typeof posX === 'number' ? posX : 0}
             />
             <SliderInput
@@ -135,7 +119,7 @@ export const CssControls = () => {
               name="position Y"
               min={-50}
               max={150}
-              onChange={(val: number) => setPosY(val)}
+              onChange={(val: number) => setValues('posY', val)}
               value={typeof posY === 'number' ? posY : 0}
             />
           </>
@@ -144,7 +128,7 @@ export const CssControls = () => {
           <Switch
             size="small"
             checked={showTransparency}
-            onChange={(e) => setShowTransparency(e)}
+            onChange={(e) => setValues('showTransparency', e)}
           />
         </Form.Item>
       </Form>

+ 18 - 15
src/components/CodeBlocks/Filter.tsx

@@ -1,6 +1,6 @@
 import { Form, Switch } from 'antd';
 import hljs from 'highlight.js/lib/core';
-import { useEffect, useState } from 'react';
+import { useState } from 'react';
 import shallow from 'zustand/shallow';
 import { symbols, rgbToString } from './Output';
 import { SectionTitle } from './SectionTitle';
@@ -8,23 +8,14 @@ import SliderInput from './SliderInput';
 import { useInputStore } from '~/components/store';
 
 export const FilterControls = () => {
-  const [svgProps, cssProps, setFilterProps] = useInputStore(
-    (state) => [state.svgProps, state.cssProps, state.setFilterProps],
+  const [svgProps, cssProps, filterProps, setFilterProps] = useInputStore(
+    (state) => [state.svgProps, state.cssProps, state.filterProps, state.setFilterProps],
     shallow
   );
-  const [contrast, setContrast] = useState(170);
-  const [brightness, setBrightness] = useState(1000);
+  const { contrast, brightness } = filterProps;
   const [inlineSvg, setInlineSvg] = useState(false);
   const { size, baseFrequency, numOctaves } = svgProps;
 
-  useEffect(() => {
-    setFilterProps({
-      brightness,
-      contrast,
-      inlineSvg,
-    });
-  }, [setFilterProps, contrast, brightness, inlineSvg]);
-
   const { gradientType, angle, color1, color2, posX, posY } = cssProps;
   const gradientFirstParam =
     gradientType === 'linear'
@@ -67,7 +58,13 @@ export const FilterControls = () => {
           max={1000}
           step={10}
           tipFormatter={(v) => `${v}%`}
-          onChange={(val: number) => setContrast(val)}
+          onChange={(val: number) =>
+            setFilterProps({
+              brightness,
+              contrast: val,
+              inlineSvg,
+            })
+          }
           value={typeof contrast === 'number' ? contrast : 10}
         />
         <SliderInput
@@ -77,7 +74,13 @@ export const FilterControls = () => {
           max={1500}
           step={50}
           tipFormatter={(v) => `${v}%`}
-          onChange={(val: number) => setBrightness(val)}
+          onChange={(val: number) =>
+            setFilterProps({
+              brightness: val,
+              contrast,
+              inlineSvg,
+            })
+          }
           value={typeof brightness === 'number' ? brightness : 0}
         />
         <Form.Item label="Inline the SVG">

+ 8 - 0
src/components/CodeBlocks/Reset.tsx

@@ -0,0 +1,8 @@
+import { Button } from 'antd';
+import shallow from 'zustand/shallow';
+import { useInputStore } from '~/components/store';
+
+export const Reset = () => {
+  const [resetAllProps] = useInputStore((state) => [state.resetAllProps], shallow);
+  return <Button onClick={() => resetAllProps()}>Reset all</Button>;
+};

+ 9 - 8
src/components/CodeBlocks/Svg.tsx

@@ -1,17 +1,18 @@
 import { Form } from 'antd';
 import hljs from 'highlight.js/lib/core';
-import { useEffect, useState } from 'react';
+import { useEffect } from 'react';
 import shallow from 'zustand/shallow';
 import { SectionTitle } from './SectionTitle';
 import SliderInput from './SliderInput';
 import { useInputStore } from '~/components/store';
 
 export const SvgControls = () => {
-  const [setSvgProps] = useInputStore((state) => [state.setSvgProps], shallow);
+  const [svgProps, setSvgProps] = useInputStore(
+    (state) => [state.svgProps, state.setSvgProps],
+    shallow
+  );
 
-  const [size, setSize] = useState(250);
-  const [baseFrequency, setBaseFrequency] = useState(0.65);
-  const [numOctaves, setNumOctaves] = useState(3);
+  const { size, baseFrequency, numOctaves } = svgProps;
 
   useEffect(() => {
     setSvgProps({ size, baseFrequency, numOctaves });
@@ -46,7 +47,7 @@ export const SvgControls = () => {
           name="size"
           min={1}
           max={400}
-          onChange={(val) => setSize(val)}
+          onChange={(newVal) => setSvgProps({ size: newVal, baseFrequency, numOctaves })}
           tipFormatter={(v) => `${v}px`}
           value={typeof size === 'number' ? size : 1}
         />
@@ -56,7 +57,7 @@ export const SvgControls = () => {
           min={0}
           max={10}
           step={0.01}
-          onChange={(val) => setBaseFrequency(val)}
+          onChange={(newVal) => setSvgProps({ size, baseFrequency: newVal, numOctaves })}
           value={typeof baseFrequency === 'number' ? baseFrequency : 1}
         />
         <SliderInput
@@ -64,7 +65,7 @@ export const SvgControls = () => {
           name="numOctaves"
           min={0}
           max={6}
-          onChange={(val) => setNumOctaves(val)}
+          onChange={(newVal) => setSvgProps({ size, baseFrequency, numOctaves: newVal })}
           value={typeof numOctaves === 'number' ? numOctaves : 1}
         />
       </Form>

+ 1 - 0
src/components/CodeBlocks/index.tsx

@@ -8,3 +8,4 @@ hljs.registerLanguage('xml', xmlLang);
 export * from './Svg';
 export * from './Css';
 export * from './Filter';
+export * from './Reset';

+ 24 - 1
src/components/store.tsx

@@ -9,6 +9,7 @@ export type InputState = {
   setCssProps: ({ gradientType, angle, color1, color2, showTransparency, posX, posY }) => void;
   filterProps: Record<string, number | boolean>;
   setFilterProps: ({ contrast, brightness, inlineSvg }) => void;
+  resetAllProps: () => void;
 };
 
 export const useInputStore = create<InputState>((set) => ({
@@ -23,7 +24,7 @@ export const useInputStore = create<InputState>((set) => ({
     }),
 
   cssProps: {
-    gradientType: 'linear-gradient',
+    gradientType: 'linear',
     angle: 112,
     color1: { r: 0, g: 0, b: 255, a: 1 },
     color2: { r: 0, g: 0, b: 0, a: 0 },
@@ -44,4 +45,26 @@ export const useInputStore = create<InputState>((set) => ({
     set({
       filterProps: { contrast, brightness, inlineSvg },
     }),
+  resetAllProps: () =>
+    set({
+      svgProps: {
+        size: 250,
+        baseFrequency: 0.65,
+        numOctaves: 3,
+      },
+      cssProps: {
+        gradientType: 'linear',
+        angle: 112,
+        color1: { r: 0, g: 0, b: 255, a: 1 },
+        color2: { r: 0, g: 0, b: 0, a: 0 },
+        showTransparency: true,
+        posX: 50,
+        posY: 50,
+      },
+      filterProps: {
+        contrast: 170,
+        brightness: 1000,
+        inlineSvg: false,
+      },
+    }),
 }));

+ 4 - 2
src/pages/index.tsx

@@ -1,7 +1,7 @@
 import Head from 'next/head';
 import React from 'react';
 import styled from 'styled-components';
-import { SvgControls, CssControls, FilterControls } from '~/components/CodeBlocks';
+import { SvgControls, CssControls, FilterControls, Reset } from '~/components/CodeBlocks';
 import Output from '~/components/CodeBlocks/Output';
 import { LinkOut } from '~/components/LinkOut';
 import { Row, LeftCol, Space, RightCol } from '~/components/layout';
@@ -19,7 +19,9 @@ const IndexPage = () => {
             <SvgControls />
             <CssControls />
             <FilterControls />
-            <Space h={60} />
+            <Space h={40} />
+            <Reset />
+            <Space h={20} />
             <hr />
             <footer>
               By <LinkOut href="https://twitter.com/jimmmy">@jimmmy</LinkOut>