Add Craft.js site builder (v2) - complete rebuild from GrapesJS
Rebuilt the visual site builder from scratch using Craft.js, React 18, and TypeScript. The new editor renders directly in the DOM (no iframe), supports 40+ components, multi-page with shared header/footer, 16 templates, full-spectrum color/gradient controls, custom head code injection, save/publish workflow, and auto-save. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import React, { useCallback, CSSProperties } from 'react';
|
||||
import { useEditor } from '@craftjs/core';
|
||||
import {
|
||||
BG_COLORS,
|
||||
RADIUS_PRESETS,
|
||||
SPACING_PRESETS,
|
||||
} from '../../../constants/presets';
|
||||
import {
|
||||
StylePanelProps,
|
||||
SectionLabel,
|
||||
ColorSwatchGrid,
|
||||
PresetButtonGrid,
|
||||
TextInputField,
|
||||
autoTextColor,
|
||||
} from './shared';
|
||||
|
||||
/* ---------- BUTTON ---------- */
|
||||
export const ButtonStylePanel: 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 setButtonColor = useCallback(
|
||||
(bgColor: string) => {
|
||||
actions.setProp(selectedId, (props: any) => {
|
||||
props.style = {
|
||||
...props.style,
|
||||
backgroundColor: bgColor,
|
||||
color: autoTextColor(bgColor),
|
||||
};
|
||||
});
|
||||
},
|
||||
[actions, selectedId],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="guided-section">
|
||||
<SectionLabel>Button Color</SectionLabel>
|
||||
<ColorSwatchGrid
|
||||
colors={BG_COLORS}
|
||||
activeValue={style.backgroundColor as string}
|
||||
onSelect={setButtonColor}
|
||||
/>
|
||||
</div>
|
||||
<TextInputField
|
||||
label="Button Text"
|
||||
value={nodeProps.text || ''}
|
||||
placeholder="Click Me"
|
||||
onChange={(v) => {
|
||||
actions.setProp(selectedId, (props: any) => { props.text = v; });
|
||||
}}
|
||||
/>
|
||||
<TextInputField
|
||||
label="Link URL"
|
||||
value={nodeProps.href || ''}
|
||||
placeholder="https://..."
|
||||
onChange={(v) => {
|
||||
actions.setProp(selectedId, (props: any) => { props.href = v; });
|
||||
}}
|
||||
/>
|
||||
<div className="guided-section">
|
||||
<SectionLabel>Border Radius</SectionLabel>
|
||||
<PresetButtonGrid
|
||||
presets={RADIUS_PRESETS}
|
||||
activeValue={style.borderRadius as string}
|
||||
onSelect={(v) => setPropStyle('borderRadius', v)}
|
||||
/>
|
||||
</div>
|
||||
<div className="guided-section">
|
||||
<SectionLabel>Padding</SectionLabel>
|
||||
<PresetButtonGrid
|
||||
presets={SPACING_PRESETS}
|
||||
activeValue={style.padding as string}
|
||||
onSelect={(v) => setPropStyle('padding', v)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user