소스 검색

holy moly

Jimmy Chion 3 년 전
부모
커밋
35a77e10ad

+ 11 - 8
src/components/CodeBlocks/Css.tsx

@@ -15,19 +15,22 @@ export const CssControls: React.FC = () => {
   const { brightness, contrast } = filterProps;
   const { gradients, showTransparency } = cssProps;
 
-  //temp
-  const firstGradient = gradients[0];
-  const { type: gradientType, stops } = firstGradient;
-
-  const gradientFirstParam = getGradientFirstParam(firstGradient);
+  const gradientsString = gradients
+    .filter((grad) => grad.isVisible)
+    .map(
+      (grad) =>
+        `${grad.type}-gradient(${getGradientFirstParam(grad)}, ${rgbToString(
+          grad.stops[0].color
+        )}, ${rgbToString(grad.stops[1].color)})`
+    );
 
   const gradientCss = `/* css gradient: second layer */
 {
   width: 250px;
   height: 250px;
-  background: ${gradientType}-gradient(${gradientFirstParam}, ${rgbToString(
-    stops[0].color
-  )}, ${rgbToString(stops[1].color)})${showTransparency ? ', url(/checkers.png)' : ''};
+  background: \n\t${gradientsString.join(',\n\t')}${
+    showTransparency ? ',\n\turl(/checkers.png)' : ''
+  };
   /* filter: contrast(${contrast}%) brightness(${brightness}%); */
 }`;
   return (

+ 2 - 2
src/components/CodeBlocks/Filter.tsx

@@ -32,10 +32,10 @@ export const FilterControls: React.FC = () => {
 {
   width: 250px;
   height: 250px;
-  background: ${gradientsString.join(', ')}, ${
+  filter: contrast(${contrast}%) brightness(${brightness}%)${invert ? ' invert(100%)' : ''};
+  background: \n\t${gradientsString.join(',\n\t')},\n\t${
     inlineSvg ? inlineSvgString : 'url(/👆/that/noise.svg)'
   };
-  filter: contrast(${contrast}%) brightness(${brightness}%)${invert ? ' invert(100%)' : ''};
 }
   `;
 

+ 6 - 2
src/components/CodeBlocks/subcomponents/SliderInput.tsx

@@ -5,6 +5,8 @@ import { breakpoints } from '~/components/layout';
 interface ISliderInput {
   label: string;
   name: string;
+  id?: string;
+  disabled?: boolean;
   onChange: (val: number) => void;
   value: number;
   min: number;
@@ -13,9 +15,9 @@ interface ISliderInput {
   tipFormatter?: (val: number) => string;
 }
 export const SliderInput: React.FC<ISliderInput> = (props) => {
-  const { label, name, onChange, value, min, max, step, tipFormatter } = props;
+  const { label, name, id, onChange, value, min, max, step, tipFormatter, disabled } = props;
   return (
-    <Form.Item label={label} name={name} style={{ width: '100%', margin: 0 }}>
+    <Form.Item label={label} name={[name, id]} style={{ width: '100%', margin: 0 }}>
       <SliderAndInput>
         <SliderContainer>
           <Slider
@@ -25,6 +27,7 @@ export const SliderInput: React.FC<ISliderInput> = (props) => {
             max={max}
             onChange={onChange}
             value={value}
+            disabled={disabled}
           />
         </SliderContainer>
         <InputNumber
@@ -34,6 +37,7 @@ export const SliderInput: React.FC<ISliderInput> = (props) => {
           value={value}
           onChange={onChange}
           formatter={tipFormatter}
+          disabled={disabled}
         />
       </SliderAndInput>
     </Form.Item>

+ 27 - 16
src/components/GradientControls/GradientControls.tsx

@@ -1,32 +1,43 @@
 import { PlusOutlined } from '@ant-design/icons';
 import { Button } from 'antd';
-import { useInputStore, AnyGradientType, defaultGradient } from '../store';
+import React from 'react';
+import shallow from 'zustand/shallow';
+import { useInputStore, AnyGradientType, getRandomGradient } from '../store';
 import { GradientRow } from './GradientRow';
 
 export const GradientControls: React.FC = () => {
-  const [cssProps, setCssProps] = useInputStore((state) => [state.cssProps, state.setCssProps]);
+  const [cssProps, setCssProps] = useInputStore(
+    (state) => [state.cssProps, state.setCssProps],
+    shallow
+  );
 
-  const updateStateOfSelf = (index: number, newData: AnyGradientType) => {
-    cssProps.gradients[index] = newData;
-    setCssProps({ ...cssProps });
-  };
+  const updateStateOfSelf = React.useCallback(
+    (index: number, newData: AnyGradientType) => {
+      cssProps.gradients[index] = newData;
+      setCssProps({ ...cssProps });
+    },
+    [cssProps, setCssProps]
+  );
 
-  const deleteSelf = (index: number) => {
-    const newGradients = [
-      ...cssProps.gradients.slice(0, index),
-      ...cssProps.gradients.slice(index + 1),
-    ];
-    setCssProps({ ...cssProps, gradients: newGradients });
-  };
+  const deleteSelf = React.useCallback(
+    (index: number) => {
+      const newGradients = [
+        ...cssProps.gradients.slice(0, index),
+        ...cssProps.gradients.slice(index + 1),
+      ];
+      setCssProps({ ...cssProps, gradients: newGradients });
+    },
+    [cssProps, setCssProps]
+  );
 
   const pushNewGradient = () => {
-    cssProps.gradients.push(defaultGradient);
-    setCssProps({ ...cssProps });
+    cssProps.gradients.push(getRandomGradient());
+    setCssProps(cssProps);
   };
 
   const gradientInterface = cssProps.gradients.map((grad, i) => (
     <GradientRow
-      key={`${i}` + grad.type + `${grad.stops[0].color.r}`}
+      key={i}
       gradient={grad}
       nGradients={cssProps.gradients.length}
       updateSelf={updateStateOfSelf}

+ 109 - 80
src/components/GradientControls/GradientRow.tsx

@@ -27,11 +27,11 @@ export const GradientRow: React.FC<IGradientRow> = ({
 }) => {
   const { type: gradientType, isVisible, stops } = gradient;
 
-  const updateProp = (key: string, value: string | boolean | ColorStopType[] | number) => {
-    const newGradient = gradient;
-    newGradient[key] = value;
+  const updateProp = (k: string, v: string | boolean | ColorStopType[] | number) => {
+    const newGradient = Object.assign({}, gradient);
+    newGradient[k] = v;
     // add default values for radial and conic gradients
-    if (key === 'type' && !('posX' in newGradient)) {
+    if (k === 'type' && !('posX' in newGradient)) {
       (newGradient as RadialGradientType | ConicGradientType).posX = 50;
       (newGradient as RadialGradientType | ConicGradientType).posY = 50;
     }
@@ -39,84 +39,97 @@ export const GradientRow: React.FC<IGradientRow> = ({
   };
 
   return (
-    <Form>
-      <RowContainer>
-        <Form.Item>
-          <Select
-            value={gradientType}
-            onChange={(v: string) => updateProp('type', v)}
-            style={{ width: 130 }}
-          >
-            <Select.Option value="linear">linear</Select.Option>
-            <Select.Option value="radial">radial</Select.Option>
-            <Select.Option value="conic">conic</Select.Option>
-          </Select>
-        </Form.Item>
-        <ColorPicker
-          label="Color 1"
-          color={stops[0].color}
-          style={{ padding: 0 }}
-          onChange={(c: ChromePickerColor) =>
-            updateProp('stops', [{ color: c.rgb, offset: 1 }, stops[1]])
-          }
-        />
-        <ColorPicker
-          label="Color 2"
-          color={stops[stops.length - 1].color}
-          onChange={(c: ChromePickerColor) =>
-            updateProp('stops', [stops[0], { color: c.rgb, offset: 1 }])
-          }
-        />
-        <VisibilityIcon onClick={() => updateProp('isVisible', !isVisible)}>
-          {isVisible ? (
-            <Tooltip title="Hide">
-              <EyeOutlined />
-            </Tooltip>
-          ) : (
-            <EyeInvisibleOutlined />
-          )}
-        </VisibilityIcon>
-        {nGradients > 1 && (
-          <VisibilityIcon onClick={() => deleteSelf(selfIndex)}>
-            <Tooltip title="Remove gradient">
-              <DeleteOutlined />
-            </Tooltip>
-          </VisibilityIcon>
-        )}
-      </RowContainer>
+    <Container>
+      <Form name={`gradient-row-${selfIndex}`}>
+        <TopRow>
+          <TopRowLeft>
+            <Form.Item style={{ marginBottom: 0 }}>
+              <Select
+                value={gradientType}
+                onChange={(v: string) => updateProp('type', v)}
+                style={{ width: 130 }}
+                disabled={!isVisible}
+              >
+                <Select.Option value="linear">linear</Select.Option>
+                <Select.Option value="radial">radial</Select.Option>
+                <Select.Option value="conic">conic</Select.Option>
+              </Select>
+            </Form.Item>
+            <ColorPicker
+              label="Color 1"
+              color={stops[0].color}
+              style={{ padding: 0 }}
+              onChange={(c: ChromePickerColor) =>
+                updateProp('stops', [{ color: c.rgb, offset: 1 }, stops[1]])
+              }
+            />
+            <ColorPicker
+              label="Color 2"
+              color={stops[stops.length - 1].color}
+              onChange={(c: ChromePickerColor) =>
+                updateProp('stops', [stops[0], { color: c.rgb, offset: 1 }])
+              }
+            />
+          </TopRowLeft>
+          <TopRowRight>
+            <VisibilityIcon onClick={() => updateProp('isVisible', !isVisible)}>
+              {isVisible ? (
+                <Tooltip title="Hide">
+                  <EyeOutlined />
+                </Tooltip>
+              ) : (
+                <EyeInvisibleOutlined />
+              )}
+            </VisibilityIcon>
+            {nGradients > 1 && (
+              <VisibilityIcon onClick={() => deleteSelf(selfIndex)}>
+                <Tooltip title="Remove gradient">
+                  <DeleteOutlined />
+                </Tooltip>
+              </VisibilityIcon>
+            )}
+          </TopRowRight>
+        </TopRow>
 
-      {['linear', 'conic'].includes(gradientType) && 'angle' in gradient && (
-        <SliderInput
-          label="angle"
-          name="angle"
-          min={0}
-          max={360}
-          tipFormatter={(v) => `${v}°`}
-          onChange={(val: number) => updateProp('angle', val)}
-          value={typeof gradient.angle === 'number' ? gradient.angle : 0}
-        />
-      )}
-      {['radial', 'conic'].includes(gradientType) && 'posX' in gradient && (
-        <>
-          <SliderInput
-            label="position X"
-            name="position X"
-            min={-50}
-            max={150}
-            onChange={(val: number) => updateProp('posX', val)}
-            value={typeof gradient.posX === 'number' ? gradient.posX : 0}
-          />
+        {['linear', 'conic'].includes(gradientType) && 'angle' in gradient && (
           <SliderInput
-            label="position Y"
-            name="position Y"
-            min={-50}
-            max={150}
-            onChange={(val: number) => updateProp('posY', val)}
-            value={typeof gradient.posY === 'number' ? gradient.posY : 0}
+            label="angle"
+            name="angle"
+            id={`${selfIndex}`}
+            min={0}
+            max={360}
+            tipFormatter={(v) => `${v}°`}
+            onChange={(val: number) => updateProp('angle', val)}
+            value={typeof gradient.angle === 'number' ? gradient.angle : 0}
+            disabled={!isVisible}
           />
-        </>
-      )}
-    </Form>
+        )}
+        {['radial', 'conic'].includes(gradientType) && 'posX' in gradient && (
+          <>
+            <SliderInput
+              label="position X"
+              name="position X"
+              id={`${selfIndex}`}
+              min={-50}
+              max={150}
+              onChange={(val: number) => updateProp('posX', val)}
+              value={typeof gradient.posX === 'number' ? gradient.posX : 0}
+              disabled={!isVisible}
+            />
+            <SliderInput
+              label="position Y"
+              name="position Y"
+              id={`${selfIndex}`}
+              min={-50}
+              max={150}
+              onChange={(val: number) => updateProp('posY', val)}
+              value={typeof gradient.posY === 'number' ? gradient.posY : 0}
+              disabled={!isVisible}
+            />
+          </>
+        )}
+      </Form>
+    </Container>
   );
 };
 
@@ -126,7 +139,23 @@ const VisibilityIcon = styled.div`
   cursor: pointer;
 `;
 
-const RowContainer = styled.div`
+const TopRow = styled.div`
   display: flex;
   align-items: center;
+  justify-content: space-between;
+`;
+
+const TopRowLeft = styled.div`
+  display: flex;
+`;
+
+const TopRowRight = styled.div`
+  display: flex;
+`;
+
+const Container = styled.div`
+  background-color: #eee;
+  border: 1px solid #ccc;
+  padding: 12px;
+  margin: 12px 0;
 `;

+ 35 - 0
src/components/store.tsx

@@ -1,5 +1,10 @@
 import create from 'zustand';
 
+function getRandomFrom<T>(arr: Array<T>): T {
+  const randIndex = Math.trunc(Math.random() * 10) % arr.length;
+  return arr[randIndex];
+}
+
 export type SvgPropsType = {
   size: number;
   baseFrequency: number;
@@ -79,6 +84,36 @@ export const defaultGradient = {
   ],
 };
 
+export function getRandomGradient(): AnyGradientType {
+  return {
+    type: getRandomFrom(['linear', 'radial', 'conic']),
+    isVisible: true,
+    angle: getRandomFrom([0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330]),
+    posX: 50,
+    posY: 50,
+    stops: [
+      {
+        color: {
+          r: getRandomFrom([0, 255]),
+          g: getRandomFrom([0, 255]),
+          b: getRandomFrom([0, 255]),
+          a: 1,
+        },
+        offset: 0,
+      },
+      {
+        color: {
+          r: getRandomFrom([0, 255]),
+          g: getRandomFrom([0, 255]),
+          b: getRandomFrom([0, 255]),
+          a: 0,
+        },
+        offset: 1,
+      },
+    ],
+  };
+}
+
 const initialCssProps = {
   showTransparency: false,
   gradients: [defaultGradient],