Site builder: security & data-loss hardening + unified asset picker + audit backlog #3
@@ -1,5 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useEditor } from '@craftjs/core';
|
||||
import React from 'react';
|
||||
import {
|
||||
BG_COLORS,
|
||||
SPACING_PRESETS,
|
||||
@@ -15,22 +14,13 @@ import {
|
||||
labelStyle,
|
||||
inputStyle,
|
||||
sectionGap,
|
||||
useNodeProp,
|
||||
} from './shared';
|
||||
import { AssetPicker } from '../../../ui/AssetPicker';
|
||||
|
||||
/* ---------- BACKGROUND SECTION ---------- */
|
||||
export const BackgroundSectionStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||
const { actions } = useEditor();
|
||||
|
||||
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 { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||
|
||||
const style = nodeProps.style || {};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
PresetButtonGrid,
|
||||
TextInputField,
|
||||
autoTextColor,
|
||||
useNodeProp,
|
||||
} from './shared';
|
||||
|
||||
/* ---------- BUTTON ---------- */
|
||||
@@ -19,14 +20,7 @@ export const ButtonStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodePr
|
||||
const { actions } = useEditor();
|
||||
const style: CSSProperties = nodeProps.style || {};
|
||||
|
||||
const setPropStyle = useCallback(
|
||||
(property: string, value: string) => {
|
||||
actions.setProp(selectedId, (props: any) => {
|
||||
props.style = { ...props.style, [property]: value };
|
||||
});
|
||||
},
|
||||
[actions, selectedId],
|
||||
);
|
||||
const { setPropStyle } = useNodeProp(selectedId);
|
||||
|
||||
const setButtonColor = useCallback(
|
||||
(bgColor: string) => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useCallback, CSSProperties } from 'react';
|
||||
import { useEditor } from '@craftjs/core';
|
||||
import React, { CSSProperties } from 'react';
|
||||
import {
|
||||
BG_COLORS,
|
||||
SPACING_PRESETS,
|
||||
@@ -14,28 +13,14 @@ import {
|
||||
labelStyle,
|
||||
inputStyle,
|
||||
sectionGap,
|
||||
useNodeProp,
|
||||
} from './shared';
|
||||
|
||||
/* ---------- CONTAINER / SECTION ---------- */
|
||||
export const ContainerStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||
const { actions } = useEditor();
|
||||
const style: CSSProperties = nodeProps.style || {};
|
||||
|
||||
const setPropStyle = useCallback(
|
||||
(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],
|
||||
);
|
||||
const { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useEditor } from '@craftjs/core';
|
||||
import React from 'react';
|
||||
import {
|
||||
BG_COLORS,
|
||||
SPACING_PRESETS,
|
||||
@@ -15,21 +14,12 @@ import {
|
||||
inputStyle,
|
||||
btnActiveStyle,
|
||||
sectionGap,
|
||||
useNodeProp,
|
||||
} from './shared';
|
||||
|
||||
/* ---------- FORM ---------- */
|
||||
export const FormStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||
const { actions } = useEditor();
|
||||
|
||||
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 { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||
|
||||
const style = nodeProps.style || {};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { useEditor } from '@craftjs/core';
|
||||
import {
|
||||
TEXT_COLORS,
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
inputStyle,
|
||||
smallInputStyle,
|
||||
sectionGap,
|
||||
useNodeProp,
|
||||
} from './shared';
|
||||
|
||||
/* ---------- 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 setPropValue = useCallback((key: string, value: any) => {
|
||||
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]);
|
||||
const { setProp: setPropValue, setPropStyle: setStyleValue } = useNodeProp(selectedId);
|
||||
|
||||
// Categorize all props
|
||||
const allProps = Object.entries(nodeProps).filter(([key]) => !SKIP_PROPS.has(key));
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useEditor } from '@craftjs/core';
|
||||
import React from 'react';
|
||||
import {
|
||||
StylePanelProps,
|
||||
CollapsibleSection,
|
||||
@@ -8,16 +7,13 @@ import {
|
||||
inputStyle,
|
||||
btnActiveStyle,
|
||||
sectionGap,
|
||||
useNodeProp,
|
||||
} from './shared';
|
||||
import { AssetPicker } from '../../../ui/AssetPicker';
|
||||
|
||||
/* ---------- HERO STYLE PANEL ---------- */
|
||||
export const HeroStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||
const { actions } = useEditor();
|
||||
|
||||
const setProp = useCallback((key: string, value: any) => {
|
||||
actions.setProp(selectedId, (props: any) => { props[key] = value; });
|
||||
}, [actions, selectedId]);
|
||||
const { setProp } = useNodeProp(selectedId);
|
||||
|
||||
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 {
|
||||
IMAGE_RADIUS_PRESETS,
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
SectionLabel,
|
||||
PresetButtonGrid,
|
||||
TextInputField,
|
||||
useNodeProp,
|
||||
} from './shared';
|
||||
import { AssetPicker } from '../../../ui/AssetPicker';
|
||||
|
||||
@@ -16,14 +17,7 @@ export const ImageStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodePro
|
||||
const { actions } = useEditor();
|
||||
const style: CSSProperties = nodeProps.style || {};
|
||||
|
||||
const setPropStyle = useCallback(
|
||||
(property: string, value: string) => {
|
||||
actions.setProp(selectedId, (props: any) => {
|
||||
props.style = { ...props.style, [property]: value };
|
||||
});
|
||||
},
|
||||
[actions, selectedId],
|
||||
);
|
||||
const { setPropStyle } = useNodeProp(selectedId);
|
||||
|
||||
const maxWidthPresets = [
|
||||
{ label: '25%', value: '25%' },
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { useEditor } from '@craftjs/core';
|
||||
import {
|
||||
BG_COLORS,
|
||||
@@ -17,22 +17,14 @@ import {
|
||||
inputStyle,
|
||||
smallInputStyle,
|
||||
sectionGap,
|
||||
useNodeProp,
|
||||
} from './shared';
|
||||
import { AssetPicker } from '../../../ui/AssetPicker';
|
||||
|
||||
/* ---------- MEDIA (Video / Gallery / Map / Slider) ---------- */
|
||||
export const MediaStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||
const { actions } = useEditor();
|
||||
|
||||
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 { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||
|
||||
const style = nodeProps.style || {};
|
||||
|
||||
|
||||
@@ -15,22 +15,14 @@ import {
|
||||
inputStyle,
|
||||
smallInputStyle,
|
||||
sectionGap,
|
||||
useNodeProp,
|
||||
} from './shared';
|
||||
import { AssetPicker } from '../../../ui/AssetPicker';
|
||||
|
||||
/* ---------- NAV / MENU / LOGO ---------- */
|
||||
export const NavStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||
const { actions } = useEditor();
|
||||
|
||||
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 { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||
|
||||
const links: any[] = nodeProps.links || [];
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { useEditor } from '@craftjs/core';
|
||||
import {
|
||||
BG_COLORS,
|
||||
@@ -17,22 +17,14 @@ import {
|
||||
inputStyle,
|
||||
smallInputStyle,
|
||||
sectionGap,
|
||||
useNodeProp,
|
||||
} from './shared';
|
||||
import { FeaturesEditor } from './FeaturesEditor';
|
||||
|
||||
/* ---------- SECTION-TYPE (Accordion, Tabs, Pricing, Testimonials, etc.) ---------- */
|
||||
export const SectionTypePanel: React.FC<StylePanelProps & { typeName: string }> = ({ selectedId, nodeProps, typeName }) => {
|
||||
const { actions } = useEditor();
|
||||
|
||||
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 { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||
|
||||
const style = nodeProps.style || {};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { useEditor } from '@craftjs/core';
|
||||
import {
|
||||
BG_COLORS,
|
||||
@@ -16,21 +16,13 @@ import {
|
||||
smallInputStyle,
|
||||
btnActiveStyle,
|
||||
sectionGap,
|
||||
useNodeProp,
|
||||
} from './shared';
|
||||
|
||||
/* ---------- SOCIAL / ICON / STAR RATING ---------- */
|
||||
export const SocialStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||
const { actions } = useEditor();
|
||||
|
||||
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 { setProp, setPropStyle } = useNodeProp(selectedId);
|
||||
|
||||
const style = nodeProps.style || {};
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useCallback, CSSProperties } from 'react';
|
||||
import { useEditor } from '@craftjs/core';
|
||||
import React, { CSSProperties } from 'react';
|
||||
import {
|
||||
TEXT_COLORS,
|
||||
FONT_FAMILIES,
|
||||
@@ -11,21 +10,14 @@ import {
|
||||
SectionLabel,
|
||||
ColorSwatchGrid,
|
||||
PresetButtonGrid,
|
||||
useNodeProp,
|
||||
} from './shared';
|
||||
|
||||
/* ---------- TEXT ---------- */
|
||||
export const TextStylePanel: React.FC<StylePanelProps> = ({ selectedId, nodeProps }) => {
|
||||
const { actions } = useEditor();
|
||||
const style: CSSProperties = nodeProps.style || {};
|
||||
|
||||
const setPropStyle = useCallback(
|
||||
(property: string, value: string) => {
|
||||
actions.setProp(selectedId, (props: any) => {
|
||||
props.style = { ...props.style, [property]: value };
|
||||
});
|
||||
},
|
||||
[actions, selectedId],
|
||||
);
|
||||
const { setPropStyle } = useNodeProp(selectedId);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -288,6 +288,18 @@ export interface StylePanelProps {
|
||||
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.) ---------- */
|
||||
interface ArrayPropEditorProps {
|
||||
selectedId: string;
|
||||
|
||||
Reference in New Issue
Block a user