From 4674a99ec9000de12799a57c74f7d6ce460b1cf3 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Sun, 12 Jul 2026 13:00:10 -0700 Subject: [PATCH] feat(builder): unify StylePanel image fields on AssetPicker Replace the ad-hoc upload/browse/URL blocks in ImageStylePanel, HeroStylePanel (bgImage + bgVideo), BackgroundSectionStylePanel, NavStylePanel (standalone Logo imageSrc + Navbar logoImage), MediaStylePanel (Gallery/ContentSlider array items), and FeaturesEditor (per-feature image) with the shared AssetPicker component, dropping each panel's duplicated local showBrowser/browserAssets/handleUpload state and inline asset-browser JSX. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../styles/BackgroundSectionStylePanel.tsx | 30 +--- .../panels/right/styles/FeaturesEditor.tsx | 42 +---- .../panels/right/styles/HeroStylePanel.tsx | 132 ++------------- .../panels/right/styles/ImageStylePanel.tsx | 153 +----------------- .../panels/right/styles/MediaStylePanel.tsx | 37 +++-- .../src/panels/right/styles/NavStylePanel.tsx | 9 +- 6 files changed, 56 insertions(+), 347 deletions(-) diff --git a/craft/src/panels/right/styles/BackgroundSectionStylePanel.tsx b/craft/src/panels/right/styles/BackgroundSectionStylePanel.tsx index f72b17f..ba6c09f 100644 --- a/craft/src/panels/right/styles/BackgroundSectionStylePanel.tsx +++ b/craft/src/panels/right/styles/BackgroundSectionStylePanel.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useRef } from 'react'; +import React, { useCallback } from 'react'; import { useEditor } from '@craftjs/core'; import { BG_COLORS, @@ -12,18 +12,15 @@ import { PresetButtonGrid, CollapsibleSection, ColorPickerField, - uploadToWhp, labelStyle, inputStyle, - smallInputStyle, - btnActiveStyle, sectionGap, } from './shared'; +import { AssetPicker } from '../../../ui/AssetPicker'; /* ---------- BACKGROUND SECTION ---------- */ export const BackgroundSectionStylePanel: React.FC = ({ selectedId, nodeProps }) => { const { actions } = useEditor(); - const fileInputRef = useRef(null); const setProp = useCallback((key: string, value: any) => { actions.setProp(selectedId, (props: any) => { props[key] = value; }); @@ -35,34 +32,13 @@ export const BackgroundSectionStylePanel: React.FC = ({ selecte }); }, [actions, selectedId]); - const handleUpload = useCallback(async (file: File) => { - const url = await uploadToWhp(file); - if (url) setProp('bgImage', url); - }, [setProp]); - const style = nodeProps.style || {}; return ( <> {/* Background Image */} - {nodeProps.bgImage && ( -
- - -
- )} -
- -
- { const f = e.target.files?.[0]; if (f) handleUpload(f); e.target.value = ''; }} /> - setProp('bgImage', e.target.value)} style={{ ...smallInputStyle, width: '100%', marginTop: 4 }} /> + setProp('bgImage', url)} variant="full" />
{/* Background Color */} diff --git a/craft/src/panels/right/styles/FeaturesEditor.tsx b/craft/src/panels/right/styles/FeaturesEditor.tsx index f1ab0f3..42db943 100644 --- a/craft/src/panels/right/styles/FeaturesEditor.tsx +++ b/craft/src/panels/right/styles/FeaturesEditor.tsx @@ -1,22 +1,7 @@ -import React, { useRef } from 'react'; +import React from 'react'; import { useEditor } from '@craftjs/core'; import { labelStyle, inputStyle, sectionGap } from './shared'; - -/* Upload helper (same contract as ImageBlock/Navbar): uploads to WHP if a - WHP_CONFIG is present, otherwise falls back to a local blob URL. */ -async function uploadToWhp(file: File): Promise { - const cfg = (window as any).WHP_CONFIG; - if (!cfg) return URL.createObjectURL(file); - const fd = new FormData(); - fd.append('file', file); - try { - const resp = await fetch(`${cfg.apiUrl}?action=upload_asset&site_id=${cfg.siteId}`, { - method: 'POST', headers: { 'X-CSRF-Token': cfg.csrfToken }, body: fd, - }); - const data = await resp.json(); - return data.success && data.url ? data.url : null; - } catch { return null; } -} +import { AssetPicker } from '../../../ui/AssetPicker'; interface Feature { title?: string; description?: string; icon?: string; @@ -33,7 +18,6 @@ interface Feature { export const FeaturesEditor: React.FC<{ selectedId: string; features: unknown }> = ({ selectedId, features }) => { const { actions } = useEditor(); const list: Feature[] = Array.isArray(features) ? (features as Feature[]) : []; - const fileRefs = useRef>({}); const mutate = (fn: (arr: Feature[]) => Feature[]) => actions.setProp(selectedId, (p: any) => { p.features = fn([...(p.features || [])]); }); @@ -42,9 +26,6 @@ export const FeaturesEditor: React.FC<{ selectedId: string; features: unknown }> const add = () => mutate((arr) => [...arr, { title: 'New Feature', description: 'Describe this feature.', icon: '🔧', image: '', imageAlt: '', buttonText: '', buttonUrl: '' }]); const remove = (i: number) => mutate((arr) => { arr.splice(i, 1); return arr; }); - const onUpload = async (i: number, file: File) => { const url = await uploadToWhp(file); if (url) update(i, 'image', url); }; - - const smallBtn: React.CSSProperties = { padding: '4px 8px', fontSize: 11, borderRadius: 4, cursor: 'pointer', border: '1px solid #3f3f46', background: '#27272a', color: '#e4e4e7' }; return (
@@ -57,21 +38,12 @@ export const FeaturesEditor: React.FC<{ selectedId: string; features: unknown }>