Site builder: security & data-loss hardening + unified asset picker + audit backlog #3
@@ -1,5 +1,4 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import { useEditor } from '@craftjs/core';
|
|
||||||
import {
|
import {
|
||||||
BG_COLORS,
|
BG_COLORS,
|
||||||
SPACING_PRESETS,
|
SPACING_PRESETS,
|
||||||
@@ -15,22 +14,13 @@ import {
|
|||||||
labelStyle,
|
labelStyle,
|
||||||
inputStyle,
|
inputStyle,
|
||||||
sectionGap,
|
sectionGap,
|
||||||
|
useNodeProp,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
import { AssetPicker } from '../../../ui/AssetPicker';
|
import { AssetPicker } from '../../../ui/AssetPicker';
|
||||||
|
|
||||||
/* ---------- BACKGROUND SECTION ---------- */
|
/* ---------- BACKGROUND SECTION ---------- */
|
||||||
export const BackgroundSectionStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
export const BackgroundSectionStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||||
const { actions } = useEditor();
|
const { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||||
|
|
||||||
const setProp = useCallback((key: string, value: any) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => { props[key] = value; });
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const setPropStyle = useCallback((key: string, value: string) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => {
|
|
||||||
props.style = { ...props.style, [key]: value };
|
|
||||||
});
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const style = nodeProps.style || {};
|
const style = nodeProps.style || {};
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
PresetButtonGrid,
|
PresetButtonGrid,
|
||||||
TextInputField,
|
TextInputField,
|
||||||
autoTextColor,
|
autoTextColor,
|
||||||
|
useNodeProp,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
|
|
||||||
/* ---------- BUTTON ---------- */
|
/* ---------- BUTTON ---------- */
|
||||||
@@ -19,14 +20,7 @@ export const ButtonStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodePr
|
|||||||
const { actions } = useEditor();
|
const { actions } = useEditor();
|
||||||
const style: CSSProperties = nodeProps.style || {};
|
const style: CSSProperties = nodeProps.style || {};
|
||||||
|
|
||||||
const setPropStyle = useCallback(
|
const { setPropStyle } = useNodeProp(selectedId);
|
||||||
(property: string, value: string) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => {
|
|
||||||
props.style = { ...props.style, [property]: value };
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[actions, selectedId],
|
|
||||||
);
|
|
||||||
|
|
||||||
const setButtonColor = useCallback(
|
const setButtonColor = useCallback(
|
||||||
(bgColor: string) => {
|
(bgColor: string) => {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useCallback, CSSProperties } from 'react';
|
import React, { CSSProperties } from 'react';
|
||||||
import { useEditor } from '@craftjs/core';
|
|
||||||
import {
|
import {
|
||||||
BG_COLORS,
|
BG_COLORS,
|
||||||
SPACING_PRESETS,
|
SPACING_PRESETS,
|
||||||
@@ -14,28 +13,14 @@ import {
|
|||||||
labelStyle,
|
labelStyle,
|
||||||
inputStyle,
|
inputStyle,
|
||||||
sectionGap,
|
sectionGap,
|
||||||
|
useNodeProp,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
|
|
||||||
/* ---------- CONTAINER / SECTION ---------- */
|
/* ---------- CONTAINER / SECTION ---------- */
|
||||||
export const ContainerStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
export const ContainerStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||||
const { actions } = useEditor();
|
|
||||||
const style: CSSProperties = nodeProps.style || {};
|
const style: CSSProperties = nodeProps.style || {};
|
||||||
|
|
||||||
const setPropStyle = useCallback(
|
const { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||||
(property: string, value: string) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => {
|
|
||||||
props.style = { ...props.style, [property]: value };
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[actions, selectedId],
|
|
||||||
);
|
|
||||||
|
|
||||||
const setProp = useCallback(
|
|
||||||
(key: string, value: string) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => { props[key] = value; });
|
|
||||||
},
|
|
||||||
[actions, selectedId],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import { useEditor } from '@craftjs/core';
|
|
||||||
import {
|
import {
|
||||||
BG_COLORS,
|
BG_COLORS,
|
||||||
SPACING_PRESETS,
|
SPACING_PRESETS,
|
||||||
@@ -15,21 +14,12 @@ import {
|
|||||||
inputStyle,
|
inputStyle,
|
||||||
btnActiveStyle,
|
btnActiveStyle,
|
||||||
sectionGap,
|
sectionGap,
|
||||||
|
useNodeProp,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
|
|
||||||
/* ---------- FORM ---------- */
|
/* ---------- FORM ---------- */
|
||||||
export const FormStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
export const FormStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||||
const { actions } = useEditor();
|
const { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||||
|
|
||||||
const setProp = useCallback((key: string, value: any) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => { props[key] = value; });
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const setPropStyle = useCallback((key: string, value: string) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => {
|
|
||||||
props.style = { ...props.style, [key]: value };
|
|
||||||
});
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const style = nodeProps.style || {};
|
const style = nodeProps.style || {};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import { useEditor } from '@craftjs/core';
|
import { useEditor } from '@craftjs/core';
|
||||||
import {
|
import {
|
||||||
TEXT_COLORS,
|
TEXT_COLORS,
|
||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
inputStyle,
|
inputStyle,
|
||||||
smallInputStyle,
|
smallInputStyle,
|
||||||
sectionGap,
|
sectionGap,
|
||||||
|
useNodeProp,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
|
|
||||||
/* ---------- SMART GENERIC PROPS EDITOR (Fallback) ---------- */
|
/* ---------- SMART GENERIC PROPS EDITOR (Fallback) ---------- */
|
||||||
@@ -27,15 +28,7 @@ export const GenericPropsEditor: React.FC<{ selectedId: string; nodeProps: Recor
|
|||||||
|
|
||||||
const SKIP_PROPS = new Set(['style', 'children', 'cssId', 'cssClass']);
|
const SKIP_PROPS = new Set(['style', 'children', 'cssId', 'cssClass']);
|
||||||
|
|
||||||
const setPropValue = useCallback((key: string, value: any) => {
|
const { setProp: setPropValue, setPropStyle: setStyleValue } = useNodeProp(selectedId);
|
||||||
actions.setProp(selectedId, (props: any) => { props[key] = value; });
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const setStyleValue = useCallback((key: string, value: string) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => {
|
|
||||||
props.style = { ...props.style, [key]: value };
|
|
||||||
});
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
// Categorize all props
|
// Categorize all props
|
||||||
const allProps = Object.entries(nodeProps).filter(([key]) => !SKIP_PROPS.has(key));
|
const allProps = Object.entries(nodeProps).filter(([key]) => !SKIP_PROPS.has(key));
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import { useEditor } from '@craftjs/core';
|
|
||||||
import {
|
import {
|
||||||
StylePanelProps,
|
StylePanelProps,
|
||||||
CollapsibleSection,
|
CollapsibleSection,
|
||||||
@@ -8,16 +7,13 @@ import {
|
|||||||
inputStyle,
|
inputStyle,
|
||||||
btnActiveStyle,
|
btnActiveStyle,
|
||||||
sectionGap,
|
sectionGap,
|
||||||
|
useNodeProp,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
import { AssetPicker } from '../../../ui/AssetPicker';
|
import { AssetPicker } from '../../../ui/AssetPicker';
|
||||||
|
|
||||||
/* ---------- HERO STYLE PANEL ---------- */
|
/* ---------- HERO STYLE PANEL ---------- */
|
||||||
export const HeroStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
export const HeroStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||||
const { actions } = useEditor();
|
const { setProp } = useNodeProp(selectedId);
|
||||||
|
|
||||||
const setProp = useCallback((key: string, value: any) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => { props[key] = value; });
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const bgType = nodeProps.bgType || 'color';
|
const bgType = nodeProps.bgType || 'color';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, CSSProperties } from 'react';
|
import React, { CSSProperties } from 'react';
|
||||||
import { useEditor } from '@craftjs/core';
|
import { useEditor } from '@craftjs/core';
|
||||||
import {
|
import {
|
||||||
IMAGE_RADIUS_PRESETS,
|
IMAGE_RADIUS_PRESETS,
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
SectionLabel,
|
SectionLabel,
|
||||||
PresetButtonGrid,
|
PresetButtonGrid,
|
||||||
TextInputField,
|
TextInputField,
|
||||||
|
useNodeProp,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
import { AssetPicker } from '../../../ui/AssetPicker';
|
import { AssetPicker } from '../../../ui/AssetPicker';
|
||||||
|
|
||||||
@@ -16,14 +17,7 @@ export const ImageStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodePro
|
|||||||
const { actions } = useEditor();
|
const { actions } = useEditor();
|
||||||
const style: CSSProperties = nodeProps.style || {};
|
const style: CSSProperties = nodeProps.style || {};
|
||||||
|
|
||||||
const setPropStyle = useCallback(
|
const { setPropStyle } = useNodeProp(selectedId);
|
||||||
(property: string, value: string) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => {
|
|
||||||
props.style = { ...props.style, [property]: value };
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[actions, selectedId],
|
|
||||||
);
|
|
||||||
|
|
||||||
const maxWidthPresets = [
|
const maxWidthPresets = [
|
||||||
{ label: '25%', value: '25%' },
|
{ label: '25%', value: '25%' },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import { useEditor } from '@craftjs/core';
|
import { useEditor } from '@craftjs/core';
|
||||||
import {
|
import {
|
||||||
BG_COLORS,
|
BG_COLORS,
|
||||||
@@ -17,22 +17,14 @@ import {
|
|||||||
inputStyle,
|
inputStyle,
|
||||||
smallInputStyle,
|
smallInputStyle,
|
||||||
sectionGap,
|
sectionGap,
|
||||||
|
useNodeProp,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
import { AssetPicker } from '../../../ui/AssetPicker';
|
import { AssetPicker } from '../../../ui/AssetPicker';
|
||||||
|
|
||||||
/* ---------- MEDIA (Video / Gallery / Map / Slider) ---------- */
|
/* ---------- MEDIA (Video / Gallery / Map / Slider) ---------- */
|
||||||
export const MediaStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
export const MediaStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||||
const { actions } = useEditor();
|
const { actions } = useEditor();
|
||||||
|
const { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||||
const setProp = useCallback((key: string, value: any) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => { props[key] = value; });
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const setPropStyle = useCallback((key: string, value: string) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => {
|
|
||||||
props.style = { ...props.style, [key]: value };
|
|
||||||
});
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const style = nodeProps.style || {};
|
const style = nodeProps.style || {};
|
||||||
|
|
||||||
|
|||||||
@@ -15,22 +15,14 @@ import {
|
|||||||
inputStyle,
|
inputStyle,
|
||||||
smallInputStyle,
|
smallInputStyle,
|
||||||
sectionGap,
|
sectionGap,
|
||||||
|
useNodeProp,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
import { AssetPicker } from '../../../ui/AssetPicker';
|
import { AssetPicker } from '../../../ui/AssetPicker';
|
||||||
|
|
||||||
/* ---------- NAV / MENU / LOGO ---------- */
|
/* ---------- NAV / MENU / LOGO ---------- */
|
||||||
export const NavStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
export const NavStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||||
const { actions } = useEditor();
|
const { actions } = useEditor();
|
||||||
|
const { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||||
const setProp = useCallback((key: string, value: any) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => { props[key] = value; });
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const setPropStyle = useCallback((key: string, value: string) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => {
|
|
||||||
props.style = { ...props.style, [key]: value };
|
|
||||||
});
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const links: any[] = nodeProps.links || [];
|
const links: any[] = nodeProps.links || [];
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import { useEditor } from '@craftjs/core';
|
import { useEditor } from '@craftjs/core';
|
||||||
import {
|
import {
|
||||||
BG_COLORS,
|
BG_COLORS,
|
||||||
@@ -17,22 +17,14 @@ import {
|
|||||||
inputStyle,
|
inputStyle,
|
||||||
smallInputStyle,
|
smallInputStyle,
|
||||||
sectionGap,
|
sectionGap,
|
||||||
|
useNodeProp,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
import { FeaturesEditor } from './FeaturesEditor';
|
import { FeaturesEditor } from './FeaturesEditor';
|
||||||
|
|
||||||
/* ---------- SECTION-TYPE (Accordion, Tabs, Pricing, Testimonials, etc.) ---------- */
|
/* ---------- SECTION-TYPE (Accordion, Tabs, Pricing, Testimonials, etc.) ---------- */
|
||||||
export const SectionTypePanel: React.FC<StylePanelProps & { typeName: string }> = ({ selectedId, nodeProps, typeName }) => {
|
export const SectionTypePanel: React.FC<StylePanelProps & { typeName: string }> = ({ selectedId, nodeProps, typeName }) => {
|
||||||
const { actions } = useEditor();
|
const { actions } = useEditor();
|
||||||
|
const { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||||
const setProp = useCallback((key: string, value: any) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => { props[key] = value; });
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const setPropStyle = useCallback((key: string, value: string) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => {
|
|
||||||
props.style = { ...props.style, [key]: value };
|
|
||||||
});
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const style = nodeProps.style || {};
|
const style = nodeProps.style || {};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import { useEditor } from '@craftjs/core';
|
import { useEditor } from '@craftjs/core';
|
||||||
import {
|
import {
|
||||||
BG_COLORS,
|
BG_COLORS,
|
||||||
@@ -16,21 +16,13 @@ import {
|
|||||||
smallInputStyle,
|
smallInputStyle,
|
||||||
btnActiveStyle,
|
btnActiveStyle,
|
||||||
sectionGap,
|
sectionGap,
|
||||||
|
useNodeProp,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
|
|
||||||
/* ---------- SOCIAL / ICON / STAR RATING ---------- */
|
/* ---------- SOCIAL / ICON / STAR RATING ---------- */
|
||||||
export const SocialStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
export const SocialStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||||
const { actions } = useEditor();
|
const { actions } = useEditor();
|
||||||
|
const { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||||
const setProp = useCallback((key: string, value: any) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => { props[key] = value; });
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const setPropStyle = useCallback((key: string, value: string) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => {
|
|
||||||
props.style = { ...props.style, [key]: value };
|
|
||||||
});
|
|
||||||
}, [actions, selectedId]);
|
|
||||||
|
|
||||||
const style = nodeProps.style || {};
|
const style = nodeProps.style || {};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useCallback, CSSProperties } from 'react';
|
import React, { CSSProperties } from 'react';
|
||||||
import { useEditor } from '@craftjs/core';
|
|
||||||
import {
|
import {
|
||||||
TEXT_COLORS,
|
TEXT_COLORS,
|
||||||
FONT_FAMILIES,
|
FONT_FAMILIES,
|
||||||
@@ -11,21 +10,14 @@ import {
|
|||||||
SectionLabel,
|
SectionLabel,
|
||||||
ColorSwatchGrid,
|
ColorSwatchGrid,
|
||||||
PresetButtonGrid,
|
PresetButtonGrid,
|
||||||
|
useNodeProp,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
|
|
||||||
/* ---------- TEXT ---------- */
|
/* ---------- TEXT ---------- */
|
||||||
export const TextStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
export const TextStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||||
const { actions } = useEditor();
|
|
||||||
const style: CSSProperties = nodeProps.style || {};
|
const style: CSSProperties = nodeProps.style || {};
|
||||||
|
|
||||||
const setPropStyle = useCallback(
|
const { setPropStyle } = useNodeProp(selectedId);
|
||||||
(property: string, value: string) => {
|
|
||||||
actions.setProp(selectedId, (props: any) => {
|
|
||||||
props.style = { ...props.style, [property]: value };
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[actions, selectedId],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -288,6 +288,18 @@ export interface StylePanelProps {
|
|||||||
nodeProps: Record<string, any>;
|
nodeProps: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- useNodeProp ----------
|
||||||
|
Shared setProp/setPropStyle boilerplate repeated across many *StylePanel.tsx
|
||||||
|
files. Only adopted where a panel's inline definitions were byte-equivalent
|
||||||
|
to this (some panels use different param names or extra logic and keep
|
||||||
|
their own local versions). */
|
||||||
|
export function useNodeProp(selectedId: string) {
|
||||||
|
const { actions } = useEditor();
|
||||||
|
const setProp = useCallback((key: string, value: any) => actions.setProp(selectedId, (p: any) => { p[key] = value; }), [actions, selectedId]);
|
||||||
|
const setPropStyle = useCallback((prop: string, value: string) => actions.setProp(selectedId, (p: any) => { p.style = { ...p.style, [prop]: value }; }), [actions, selectedId]);
|
||||||
|
return { setProp, setPropStyle };
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- Array Prop Editor (reusable for features, items, plans, etc.) ---------- */
|
/* ---------- Array Prop Editor (reusable for features, items, plans, etc.) ---------- */
|
||||||
interface ArrayPropEditorProps {
|
interface ArrayPropEditorProps {
|
||||||
selectedId: string;
|
selectedId: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user