Jimmy Chion преди 3 години
родител
ревизия
0f14c18116

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

@@ -3,7 +3,7 @@ import hljs from 'highlight.js/lib/core';
 import React from 'react';
 import { rgbToString } from './Output';
 import { SectionTitle } from './subcomponents';
-import { GradientPicker } from '~/components/GradientPicker';
+import { GradientControls } from '~/components/GradientControls';
 import { AnyGradientType, useInputStore } from '~/components/store';
 
 export const CssControls: React.FC = () => {
@@ -41,7 +41,7 @@ export const CssControls: React.FC = () => {
           }}
         />
       </pre>
-      <GradientPicker />
+      <GradientControls />
       <Form>
         <Form.Item label="Show checkered">
           <Switch

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

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

+ 10 - 7
src/components/CodeBlocks/Output.tsx

@@ -31,24 +31,27 @@ const Output: React.FC = () => {
   <rect width='100%' height='100%' filter='url(#noiseFilter)'/>
 </svg>`;
 
-  const gradientsString = gradients.map((grad) => {
-    return `${grad.type}-gradient(${getGradientFirstParam(grad)}, ${rgbToString(
-      grad.stops[0].color
-    )}, ${rgbToString(grad.stops[1].color)})`;
-  });
+  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: ${gradientsString.join(' ')} ${showTransparency ? ', url(/checkers.png)' : ''};
+  background: ${gradientsString.join(', ')} ${showTransparency ? ', url(/checkers.png)' : ''};
   /* filter: contrast(${contrast}%) brightness(${brightness}%)${invert ? ' invert(100%)' : ''}; */
 }`;
 
   const liveCss = `
 width: 250px;
 height: 250px;
-background: ${gradientsString.join(' ')}, url("data:image/svg+xml,${svgString.replace(
+background: ${gradientsString.join(', ')}, url("data:image/svg+xml,${svgString.replace(
     symbols,
     encodeURIComponent
   )}");

+ 0 - 0
src/components/GradientPicker/ColorPicker.tsx → src/components/GradientControls/ColorPicker.tsx


+ 46 - 0
src/components/GradientControls/GradientControls.tsx

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

+ 47 - 10
src/components/GradientPicker/GradientRow.tsx → src/components/GradientControls/GradientRow.tsx

@@ -1,32 +1,46 @@
-import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons';
-import { Form, Select } from 'antd';
+import { EyeOutlined, EyeInvisibleOutlined, DeleteOutlined } from '@ant-design/icons';
+import { Form, Select, Tooltip } from 'antd';
 import React from 'react';
 import styled from 'styled-components';
-import { Row } from '../layout';
 import { ColorPicker, ChromePickerColor } from './ColorPicker';
 import { SliderInput } from '~/components/CodeBlocks/subcomponents';
-import { AnyGradientType, ColorStopType } from '~/components/store';
+import {
+  AnyGradientType,
+  ColorStopType,
+  ConicGradientType,
+  RadialGradientType,
+} from '~/components/store';
 
 interface IGradientRow {
   gradient: AnyGradientType;
   selfIndex: number;
+  nGradients: number;
   updateSelf: (index: number, newData: AnyGradientType) => void;
+  deleteSelf: (index: number) => void;
 }
-export const GradientRow: React.FC<IGradientRow> = ({ gradient, selfIndex, updateSelf }) => {
+export const GradientRow: React.FC<IGradientRow> = ({
+  gradient,
+  selfIndex,
+  nGradients,
+  updateSelf,
+  deleteSelf,
+}) => {
   const { type: gradientType, isVisible, stops } = gradient;
 
   const updateProp = (key: string, value: string | boolean | ColorStopType[] | number) => {
     const newGradient = gradient;
     newGradient[key] = value;
+    // add default values for radial and conic gradients
+    if (key === 'type' && !('posX' in newGradient)) {
+      (newGradient as RadialGradientType | ConicGradientType).posX = 50;
+      (newGradient as RadialGradientType | ConicGradientType).posY = 50;
+    }
     updateSelf(selfIndex, newGradient);
   };
 
   return (
     <Form>
-      <Row>
-        <VisibilityIcon onClick={() => updateProp('isVisible', !isVisible)}>
-          {isVisible ? <EyeOutlined /> : <EyeInvisibleOutlined />}
-        </VisibilityIcon>
+      <RowContainer>
         <Form.Item>
           <Select
             value={gradientType}
@@ -53,7 +67,23 @@ export const GradientRow: React.FC<IGradientRow> = ({ gradient, selfIndex, updat
             updateProp('stops', [stops[0], { color: c.rgb, offset: 1 }])
           }
         />
-      </Row>
+        <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>
 
       {['linear', 'conic'].includes(gradientType) && 'angle' in gradient && (
         <SliderInput
@@ -92,4 +122,11 @@ export const GradientRow: React.FC<IGradientRow> = ({ gradient, selfIndex, updat
 
 const VisibilityIcon = styled.div`
   padding: 3px;
+  margin: 0 8px;
+  cursor: pointer;
+`;
+
+const RowContainer = styled.div`
+  display: flex;
+  align-items: center;
 `;

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

@@ -0,0 +1 @@
+export * from './GradientControls';

+ 0 - 27
src/components/GradientPicker/GradientPicker.tsx

@@ -1,27 +0,0 @@
-import { PlusOutlined } from '@ant-design/icons';
-import { Button } from 'antd';
-import { useInputStore, AnyGradientType } from '../store';
-import { GradientRow } from './GradientRow';
-
-export const GradientPicker: React.FC = () => {
-  const [cssProps, setCssProps] = useInputStore((state) => [state.cssProps, state.setCssProps]);
-
-  const updateStateOfSelf = (index: number, newData: AnyGradientType) => {
-    setCssProps({ ...cssProps, gradients: cssProps.gradients.splice(index, 1, newData) });
-  };
-  const gradientInterface = cssProps.gradients.map((grad, i) => (
-    <GradientRow
-      key={`${i}` + grad.type + `${grad.stops[0].color.r}`}
-      gradient={grad}
-      updateSelf={updateStateOfSelf}
-      selfIndex={i}
-    />
-  ));
-
-  return (
-    <div>
-      {gradientInterface}
-      <Button icon={<PlusOutlined />}>Add gradient</Button>
-    </div>
-  );
-};

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

@@ -1 +0,0 @@
-export * from './GradientPicker';

+ 17 - 16
src/components/store.tsx

@@ -62,26 +62,27 @@ const initialSvgProps = {
   baseFrequency: 0.65,
   numOctaves: 3,
 };
-const initialCssProps = {
-  showTransparency: false,
-  gradients: [
+
+export const defaultGradient = {
+  type: 'linear',
+  isVisible: true,
+  angle: 0,
+  stops: [
+    {
+      color: { r: 0, g: 0, b: 255, a: 1 },
+      offset: 0,
+    },
     {
-      type: 'linear',
-      isVisible: true,
-      angle: 0,
-      stops: [
-        {
-          color: { r: 0, g: 0, b: 255, a: 1 },
-          offset: 0,
-        },
-        {
-          color: { r: 0, g: 0, b: 0, a: 0 },
-          offset: 1,
-        },
-      ],
+      color: { r: 0, g: 0, b: 0, a: 0 },
+      offset: 1,
     },
   ],
 };
+
+const initialCssProps = {
+  showTransparency: false,
+  gradients: [defaultGradient],
+};
 const initialFilterProps = {
   contrast: 170,
   brightness: 1000,