2026-04-05 18:31:16 -07:00
|
|
|
import React from 'react';
|
|
|
|
|
import { useEditor } from '@craftjs/core';
|
|
|
|
|
import { SiteDesignPanel } from './SiteDesignPanel';
|
2026-05-25 12:43:28 -07:00
|
|
|
import { useSitesmithModal } from '../../state/SitesmithContext';
|
|
|
|
|
import { buildSitesmithTarget } from '../../utils/sitesmith-target';
|
2026-04-05 18:31:16 -07:00
|
|
|
import {
|
|
|
|
|
TextStylePanel,
|
|
|
|
|
ButtonStylePanel,
|
|
|
|
|
ImageStylePanel,
|
|
|
|
|
ContainerStylePanel,
|
|
|
|
|
HeroStylePanel,
|
|
|
|
|
NavStylePanel,
|
|
|
|
|
MediaStylePanel,
|
|
|
|
|
FormStylePanel,
|
|
|
|
|
SocialStylePanel,
|
|
|
|
|
SectionTypePanel,
|
|
|
|
|
PricingStylePanel,
|
|
|
|
|
BackgroundSectionStylePanel,
|
|
|
|
|
GenericPropsEditor,
|
|
|
|
|
} from './styles';
|
|
|
|
|
|
|
|
|
|
/* ================================================================
|
|
|
|
|
IMPORTANT: None of these panels use useNode().
|
|
|
|
|
All prop mutations go through useEditor().actions.setProp(nodeId, ...)
|
|
|
|
|
so they work from outside the node's render tree.
|
|
|
|
|
================================================================ */
|
|
|
|
|
|
|
|
|
|
/* ================================================================
|
|
|
|
|
Main GuidedStyles component
|
|
|
|
|
================================================================ */
|
|
|
|
|
|
|
|
|
|
export const GuidedStyles: React.FC = () => {
|
2026-05-25 12:43:28 -07:00
|
|
|
const { open: openSitesmith } = useSitesmithModal();
|
|
|
|
|
const { query } = useEditor();
|
2026-04-05 18:31:16 -07:00
|
|
|
|
2026-07-12 14:59:39 -07:00
|
|
|
const { selected, selectedType, nodeProps } = useEditor((state) => {
|
2026-04-05 18:31:16 -07:00
|
|
|
const currentNodeId = state.events.selected
|
|
|
|
|
? Array.from(state.events.selected)[0]
|
|
|
|
|
: undefined;
|
|
|
|
|
let selectedType: string | null = null;
|
|
|
|
|
let nodeProps: Record<string, any> = {};
|
|
|
|
|
|
|
|
|
|
if (currentNodeId) {
|
|
|
|
|
const node = state.nodes[currentNodeId];
|
|
|
|
|
if (node) {
|
|
|
|
|
selectedType = node.data.displayName || node.data.name || null;
|
|
|
|
|
nodeProps = node.data.props || {};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
selected: currentNodeId || null,
|
|
|
|
|
selectedType,
|
|
|
|
|
nodeProps,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!selected) {
|
|
|
|
|
return <SiteDesignPanel />;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Determine which panel to show based on displayName
|
|
|
|
|
const typeName = selectedType || '';
|
|
|
|
|
|
|
|
|
|
// ---- Type classification (covers ALL 40 component display names) ----
|
|
|
|
|
const isText = /^heading$|^text$/i.test(typeName);
|
|
|
|
|
const isButton = /^button$/i.test(typeName);
|
|
|
|
|
const isImage = /^image$/i.test(typeName);
|
|
|
|
|
const isBgSection = /^background section$/i.test(typeName);
|
2026-07-12 14:59:39 -07:00
|
|
|
const isContainer = /^container$|^section$|^columns$/i.test(typeName);
|
|
|
|
|
const isHero = /^hero/i.test(typeName);
|
2026-04-05 18:31:16 -07:00
|
|
|
const isNav = /^menu$|^logo$|^navbar$|^footer$/i.test(typeName);
|
|
|
|
|
const isMedia = /^video$|^gallery$|^map$|^content slider$/i.test(typeName);
|
|
|
|
|
const isForm = /^form$|^input$|^textarea$|^subscribe|^contact form$|^submit button$|^search bar$/i.test(typeName);
|
|
|
|
|
const isSocial = /^social links$|^icon$|^star rating$/i.test(typeName);
|
|
|
|
|
const isPricing = /^pricing/i.test(typeName);
|
|
|
|
|
const isSection = /^accordion$|^tabs$|^testimonial|^countdown$|^number counter$|^cta section$|^call to action$|^features grid$/i.test(typeName);
|
|
|
|
|
// Utility types that need minimal controls
|
|
|
|
|
const isUtility = /^divider$|^spacer$|^html$/i.test(typeName);
|
|
|
|
|
|
|
|
|
|
// Icon for the type badge
|
|
|
|
|
const typeIcon = isText ? 'fa-font'
|
|
|
|
|
: isButton ? 'fa-hand-pointer-o'
|
|
|
|
|
: isImage ? 'fa-image'
|
|
|
|
|
: isBgSection ? 'fa-picture-o'
|
|
|
|
|
: isContainer ? 'fa-object-group'
|
|
|
|
|
: isHero ? 'fa-star'
|
|
|
|
|
: isNav ? 'fa-bars'
|
|
|
|
|
: isMedia ? 'fa-play-circle'
|
|
|
|
|
: isForm ? 'fa-wpforms'
|
|
|
|
|
: isSocial ? 'fa-share-alt'
|
|
|
|
|
: isSection ? 'fa-th-large'
|
|
|
|
|
: isUtility ? 'fa-ellipsis-h'
|
|
|
|
|
: 'fa-cube';
|
|
|
|
|
|
2026-05-25 12:43:28 -07:00
|
|
|
const handleAskSitesmith = () => {
|
|
|
|
|
if (!selected || selected === 'ROOT') return;
|
|
|
|
|
const target = buildSitesmithTarget(query, selected);
|
|
|
|
|
if (target) openSitesmith(target);
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-05 18:31:16 -07:00
|
|
|
return (
|
|
|
|
|
<div className="guided-styles">
|
2026-05-25 12:43:28 -07:00
|
|
|
{/* Component type badge + Sitesmith shortcut */}
|
|
|
|
|
<div className="guided-section guided-type-header" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
|
|
|
<span className="guided-type-badge" style={{ flex: 1 }}>
|
2026-04-05 18:31:16 -07:00
|
|
|
<i className={`fa ${typeIcon}`} />
|
|
|
|
|
{' '}{typeName}
|
|
|
|
|
</span>
|
2026-05-25 12:43:28 -07:00
|
|
|
<button
|
|
|
|
|
onClick={handleAskSitesmith}
|
|
|
|
|
title="Ask Sitesmith to edit this block"
|
|
|
|
|
style={{
|
|
|
|
|
display: 'inline-flex', alignItems: 'center', gap: 4,
|
|
|
|
|
padding: '4px 8px', fontSize: 11, fontWeight: 600,
|
|
|
|
|
color: '#a78bfa', background: 'rgba(139,92,246,0.12)',
|
|
|
|
|
border: '1px solid rgba(139,92,246,0.4)',
|
|
|
|
|
borderRadius: 'var(--radius-sm)', cursor: 'pointer',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<i className="fa fa-magic" /> Ask Sitesmith
|
|
|
|
|
</button>
|
2026-04-05 18:31:16 -07:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* TEXT */}
|
|
|
|
|
{isText && <TextStylePanel selectedId={selected} nodeProps={nodeProps} />}
|
|
|
|
|
|
|
|
|
|
{/* BUTTON */}
|
|
|
|
|
{isButton && <ButtonStylePanel selectedId={selected} nodeProps={nodeProps} />}
|
|
|
|
|
|
|
|
|
|
{/* IMAGE */}
|
|
|
|
|
{isImage && <ImageStylePanel selectedId={selected} nodeProps={nodeProps} />}
|
|
|
|
|
|
|
|
|
|
{/* BACKGROUND SECTION */}
|
|
|
|
|
{isBgSection && <BackgroundSectionStylePanel selectedId={selected} nodeProps={nodeProps} />}
|
|
|
|
|
|
|
|
|
|
{/* CONTAINER / SECTION / COLUMNS */}
|
|
|
|
|
{isContainer && <ContainerStylePanel selectedId={selected} nodeProps={nodeProps} />}
|
|
|
|
|
|
|
|
|
|
{/* HERO */}
|
|
|
|
|
{isHero && <HeroStylePanel selectedId={selected} nodeProps={nodeProps} />}
|
|
|
|
|
|
|
|
|
|
{/* NAV / MENU / LOGO / FOOTER */}
|
|
|
|
|
{isNav && <NavStylePanel selectedId={selected} nodeProps={nodeProps} />}
|
|
|
|
|
|
|
|
|
|
{/* MEDIA (Video, Gallery, Map, Slider) */}
|
|
|
|
|
{isMedia && <MediaStylePanel selectedId={selected} nodeProps={nodeProps} />}
|
|
|
|
|
|
|
|
|
|
{/* FORM (Form, Input, Textarea, Subscribe, Contact, FormButton, Search) */}
|
|
|
|
|
{isForm && <FormStylePanel selectedId={selected} nodeProps={nodeProps} />}
|
|
|
|
|
|
|
|
|
|
{/* SOCIAL / ICON / STAR RATING */}
|
|
|
|
|
{isSocial && <SocialStylePanel selectedId={selected} nodeProps={nodeProps} />}
|
|
|
|
|
|
|
|
|
|
{/* PRICING TABLE */}
|
|
|
|
|
{isPricing && <PricingStylePanel selectedId={selected} nodeProps={nodeProps} />}
|
|
|
|
|
|
|
|
|
|
{/* SECTION-TYPE (Accordion, Tabs, Testimonials, Countdown, Counter, CTA, Features) */}
|
|
|
|
|
{isSection && <SectionTypePanel selectedId={selected} nodeProps={nodeProps} typeName={typeName} />}
|
|
|
|
|
|
|
|
|
|
{/* UTILITY (Divider, Spacer, HTML) -- use generic but it works well for these */}
|
|
|
|
|
{isUtility && <GenericPropsEditor selectedId={selected} nodeProps={nodeProps} typeName={typeName} />}
|
|
|
|
|
|
|
|
|
|
{/* FALLBACK: Anything not matched above */}
|
|
|
|
|
{!isText && !isButton && !isImage && !isBgSection && !isContainer && !isHero && !isNav && !isMedia && !isForm && !isSocial && !isPricing && !isSection && !isUtility && (
|
|
|
|
|
<GenericPropsEditor selectedId={selected} nodeProps={nodeProps} typeName={typeName} />
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|