diff --git a/src/components/Creator/CreatorArea/ActionButtons/ActionButtons.tsx b/src/components/Creator/CreatorArea/ActionButtons/ActionButtons.tsx index c3224c0..209ec8e 100644 --- a/src/components/Creator/CreatorArea/ActionButtons/ActionButtons.tsx +++ b/src/components/Creator/CreatorArea/ActionButtons/ActionButtons.tsx @@ -1,7 +1,7 @@ import styles from './ActionButtons.module.scss' import Icon from "@/components/construction/Icon/Icon"; import classNames from "@/helpers/classNames"; -import {useCallback, useEffect, useRef} from "react"; +import {useCallback, useEffect, useRef, useState} from "react"; import {duplicateBlock, removeBlock} from "@/store/structureSlice"; import {useDispatch, useSelector} from "react-redux"; import ActionMoveButtons from "@/components/Creator/CreatorArea/ActionButtons/ActionMoveButtons/ActionMoveButtons"; @@ -13,13 +13,13 @@ export default function ActionButtons() { const selectedBlock = useSelector((state: any) => state.structure.selectedBlock); const pageYOffset = useSelector((state: any) => state.structure.pageYOffset); - const duplicate = useCallback(() => { - dispatch(duplicateBlock(selectedBlock)); + const duplicate = useCallback((block) => { + dispatch(duplicateBlock({...block})); }, [dispatch]); - const deleteBlk = useCallback(() => { + const deleteBlk = useCallback((block) => { if (confirm('Are you sure you want to delete this block?')) { - dispatch(removeBlock(selectedBlock)); + dispatch(removeBlock({...block})); } }, [dispatch]); @@ -55,9 +55,6 @@ export default function ActionButtons() { useEffect(() => { setComponentPosition(); }, [selectedBlock, pageYOffset]) - useEffect(() => { - console.log(pageYOffset) - }, [pageYOffset]) return ( <> {selectedBlock @@ -65,7 +62,6 @@ export default function ActionButtons() { - {/*${this.getAdditionalButtonsHtml()}*/} + onClick={()=>duplicate(selectedBlock)}/> + onClick={()=>deleteBlk(selectedBlock)}/> : ''} ) } diff --git a/src/components/Creator/LeftPanel/StylesPanel/Filter/Filter.module.scss b/src/components/Creator/LeftPanel/StylesPanel/Filter/Filter.module.scss new file mode 100644 index 0000000..6e36d98 --- /dev/null +++ b/src/components/Creator/LeftPanel/StylesPanel/Filter/Filter.module.scss @@ -0,0 +1,14 @@ +.singleFilter { + border-bottom: 1px solid #ccc; +} + +.icon { + width: 30px; + height: 30px; + align-self: flex-end; + cursor: pointer; + + &:hover { + color: #f00; + } +} diff --git a/src/components/Creator/LeftPanel/StylesPanel/Filter/Filter.tsx b/src/components/Creator/LeftPanel/StylesPanel/Filter/Filter.tsx new file mode 100644 index 0000000..358eee6 --- /dev/null +++ b/src/components/Creator/LeftPanel/StylesPanel/Filter/Filter.tsx @@ -0,0 +1,161 @@ +import styles from "@/components/Creator/LeftPanel/StylesPanel/StylesPanel.module.scss"; +import filterStyles from './Filter.module.scss' +import {Option, Select} from "@/components/construction/Select"; +import {useEffect, useState} from "react"; +import {getInheritedStyleWith} from "@/helpers/block-styles"; +import {useSelector} from "react-redux"; +import {Units} from "@/types/units"; +import FilterSingle from "@/components/Creator/LeftPanel/StylesPanel/Filter/FilterSingle/FilterSingle"; +import FilterDropShadow from "@/components/Creator/LeftPanel/StylesPanel/Filter/FilterDropShadow/FilterDropShadow"; +import classNames from "@/helpers/classNames"; +import Icon from "@/components/construction/Icon/Icon"; + +interface Props { + onChange: (value: string | null, property: string) => void +} + +type SelectedFilter = [id: string, value: any]; +const filterDefaultUnits: [name: string, unit: Units][] = [ + ['blur', 'px'], + ['brightness', '%'], + ['contrast', '%'], + ['grayscale', '%'], + ['hue-rotate', 'deg'], + ['invert', '%'], + ['opacity', '%'], + ['saturate', '%'], + ['sepia', '%'], +]; +const allFiltersNames: [id: string, name: string][] = [ + ['blur', 'Rozmycie'], + ['brightness', 'Jasność'], + ['contrast', 'Kontrast'], + ['drop-shadow', 'Cień'], + ['grayscale', 'Skala szarości'], + ['hue-rotate', 'Obracanie odcienia'], + ['invert', 'Odwrócenie kolorów'], + ['opacity', 'Nieprzezroczystość'], + ['saturate', 'Saturacja'], + ['sepia', 'Sepia'], +]; +const filtersDefaultValues: [id: string, value: any][] = [ + ['blur', 0], + ['brightness', 100], + ['contrast', 100], + ['drop-shadow', ['0px', '0px', '0px', '#000']], + ['grayscale', 0], + ['hue-rotate', 0], + ['invert', 0], + ['opacity', 100], + ['saturate', 0], + ['sepia', 0], +]; +export default function (props: Props) { + const selectedBlock = useSelector((state: any) => state.structure.selectedBlock); + const rwd = useSelector((state: any) => state.structure.rwdMode); + const styleState = useSelector((state: any) => state.structure.styleState); + const [filter, seFilter] = useState(''); + const [selectedFilters, setSelectedFilters] = useState([]); + + const changed = value => { + value = addFilterUnits(value); + props.onChange( + value.map(([id, value]: SelectedFilter) => `${id}(${value})`).join(' '), + 'filter' + ) + } + const addFilterUnits = value => { + return [...value].map(([id, val]) => { + if(id === 'drop-shadow' ){ + return [id, val.map((v,index)=>{ + if(index===3){ + return v; + } + return `${parseInt(v)}px` + }).join(' ')] + } + const [, unit] = filterDefaultUnits.find(([f_id, _unit]) => f_id === id)! + return [id, `${val}${unit}`] + }); + } + const mapFiltersToArray = (value: string): SelectedFilter[] => { + const filtersArr = value.match(/[\w-]+\([\w.,%\s]+[\(]{1}[\w.,%\s]+[\)]{1}\)|[\w-]+\([\w.,%\s\#]+\)/gm) ?? []; + const getFilterVal = name => filtersArr.find(filterName => filterName.startsWith(name)) + .trim().replace(`${name}(`, '').replace(/\)$/, ''); + const filters: SelectedFilter[] = []; + + filtersArr.forEach(filter => { + const [name] = filter.split('('); + + if (name.includes('drop-shadow')) { + const dropShadow = getFilterVal('drop-shadow'); + const [hShadow, vShadow, blur, color] = dropShadow.match(/\S+\([\d\s,\.]+\)|[\w\#]+/gm); + filters.push([name, [parseInt(hShadow), parseInt(vShadow), parseInt(blur), color]]); + } else { + filters.push([name, parseInt(getFilterVal(name))]); + } + }); + + return filters; + } + + useEffect(() => { + const style = getInheritedStyleWith( + selectedBlock.styles, + rwd, styleState, + ['filter'], + ) as any; + seFilter(style.filter || ''); + setSelectedFilters(mapFiltersToArray(style.filter || '')); + }, [selectedBlock, rwd, styleState]) + + const addFilter = (filterId: string) => { + const [, defaultValue] = filtersDefaultValues.find(([id, _]) => id === filterId); + const newFilter: SelectedFilter = [filterId, defaultValue]; + setSelectedFilters([...selectedFilters, newFilter]); + changed([...selectedFilters, newFilter]); + } + const removeFilter = index => { + selectedFilters.splice(index, 1); + setSelectedFilters([...selectedFilters]); + changed([...selectedFilters]); + } + const getFiltersList = () => { + return selectedFilters.map(([id, val], index) => { + return
+ {id === 'drop-shadow' + ? changedFilter(id, e)}/> + : changedFilter(id, e)}/> + } + removeFilter(index)} className={filterStyles.icon}/> +
+ } + ) + } + const isFilterChosen = (id: string): boolean => { + return selectedFilters.some(([filterId, _]) => id === filterId) + } + + const changedFilter = (id: string, val: any) => { + const index = selectedFilters.findIndex(([filterId, _]) => id === filterId) + selectedFilters[index] = [id, val]; + setSelectedFilters([...selectedFilters]); + changed([...selectedFilters]); + } + + return ( +
+
+ +
+ + {getFiltersList()} + +
+ ) +} diff --git a/src/components/Creator/LeftPanel/StylesPanel/Filter/FilterDropShadow/FilterDropShadow.tsx b/src/components/Creator/LeftPanel/StylesPanel/Filter/FilterDropShadow/FilterDropShadow.tsx new file mode 100644 index 0000000..0d0a7a0 --- /dev/null +++ b/src/components/Creator/LeftPanel/StylesPanel/Filter/FilterDropShadow/FilterDropShadow.tsx @@ -0,0 +1,39 @@ +import styles from "@/components/Creator/LeftPanel/StylesPanel/StylesPanel.module.scss"; +import InputWithUnits from "@/components/construction/InputWithUnits/InputWithUnits"; +import ColorPicker from "@/components/construction/ColorPicker/ColorPicker"; +import {useState} from "react"; + +type ValueType = [x: number, y: number, blur: number, color: string]; + +interface Props { + value: ValueType; + onChange: (value: ValueType) => void; + +} + +export default function (props: Props) { +const [value,setValue] =useState(props.value); + const onChange = (v, index) => { + const val = value; + val[index] = v; + setValue(val) + props.onChange(val); + } + + return ( +
+
+ onChange(e, 0)}/> + onChange(e, 1)}/> +
+
+ onChange(e, 2)}/> + onChange(e, 3)}/> +
+
+ ) + +} diff --git a/src/components/Creator/LeftPanel/StylesPanel/Filter/FilterSingle/FilterSingle.tsx b/src/components/Creator/LeftPanel/StylesPanel/Filter/FilterSingle/FilterSingle.tsx new file mode 100644 index 0000000..f0eabe7 --- /dev/null +++ b/src/components/Creator/LeftPanel/StylesPanel/Filter/FilterSingle/FilterSingle.tsx @@ -0,0 +1,53 @@ +import {Units} from "@/types/units"; +import styles from "@/components/Creator/LeftPanel/StylesPanel/StylesPanel.module.scss"; +import Slider from "@/components/construction/Slider/Slider"; +import {useEffect, useState} from "react"; + +interface Props { + value: number; + filterId: string; + onChange: (value: number) => void; +} + +type FilterDefaults = [id: string, name: string, unit: Units, min: number, max: number]; +export default function (props: Props) { + const [filterDefaults, setFilterDefaults] = useState<{ min: number, max: number }>(null); + const [label, setLabel] = useState(null); + const [unit, setUnit] = useState(null); + const displayValFn = val => `${val}${unit}`; + + const filters: FilterDefaults[] = [ + ['blur', 'Rozmycie', 'px', 0, 100], + ['brightness', 'Jasność', '%', 0, 200], + ['contrast', 'Kontrast', '%', 0, 200], + ['grayscale', 'Skala szarości', '%', 0, 100], + ['hue-rotate', 'Obracanie odcienia', 'deg', 0, 360], + ['invert', 'Odwrócenie kolorów', '%', 0, 100], + ['opacity', 'Nieprzezroczystość', '%', 0, 100], + ['saturate', 'Saturacja', '%', 0, 100], + ['sepia', 'Sepia', '%', 0, 100], + ]; + + const getSingleFilterDefaults = (filterId: string): FilterDefaults => { + return filters.find(([id, ..._]) => id === filterId) + } + + useEffect(() => { + const [, _name, _unit, min, max] = getSingleFilterDefaults(props.filterId); + setLabel(_name); + setUnit(_unit); + setFilterDefaults({min, max}); + }, [props.filterId]) + + return ( +
+ {filterDefaults ? : ''} +
+ ) + +} diff --git a/src/components/Creator/LeftPanel/StylesPanel/StylesPanel.tsx b/src/components/Creator/LeftPanel/StylesPanel/StylesPanel.tsx index dafdefa..78ca2db 100644 --- a/src/components/Creator/LeftPanel/StylesPanel/StylesPanel.tsx +++ b/src/components/Creator/LeftPanel/StylesPanel/StylesPanel.tsx @@ -19,6 +19,7 @@ import TextColumns from "@/components/Creator/LeftPanel/StylesPanel/TextColumns/ import Text from "@/components/Creator/LeftPanel/StylesPanel/Text/Text"; import TextShadow from "@/components/Creator/LeftPanel/StylesPanel/TextShadow/TextShadow"; import Background from "@/components/Creator/LeftPanel/StylesPanel/Background/Background"; +import Filter from "@/components/Creator/LeftPanel/StylesPanel/Filter/Filter"; export default function () { const selectedBlock = useSelector((state: any) => state.structure.selectedBlock); @@ -52,7 +53,7 @@ export default function () { ['quote', ], ['margin', ], ['padding', ], - // ['filter', ], + ['filter', ], // ['animations', ], ] const canShow = ([id,c]) => { diff --git a/src/store/functions/block.ts b/src/store/functions/block.ts index f3093dd..d4c2926 100644 --- a/src/store/functions/block.ts +++ b/src/store/functions/block.ts @@ -70,7 +70,7 @@ export const setAttributesFn = (state: StructureState, {payload}) => { export const duplicateBlockFn = (state: StructureState, {payload}: PayloadAction) => { - const {structure, selectedBlock} = state; + const {structure} = state; state.structure = duplicateBlk(structure, payload.id); };