From a0e60363e907ad86c953a608378e5be7fb882687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignas=20Lun=C4=97nas?= <34475426+lunenas@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:50:08 +0300 Subject: [PATCH] feat: allow removing individual style classes with update/reset Made-with: Cursor --- .../ycode/components/RightSidebar.tsx | 90 +++++++++++++++++-- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/app/(builder)/ycode/components/RightSidebar.tsx b/app/(builder)/ycode/components/RightSidebar.tsx index b7d4e9de..811013b7 100644 --- a/app/(builder)/ycode/components/RightSidebar.tsx +++ b/app/(builder)/ycode/components/RightSidebar.tsx @@ -85,6 +85,9 @@ import { useLayerLocks } from '@/hooks/use-layer-locks'; // 6. Utils, APIs, lib import { classesToDesign, mergeDesign, removeConflictsForClass, getRemovedPropertyClasses } from '@/lib/tailwind-class-mapper'; +import { resetLayerToStyle, hasStyleOverrides } from '@/lib/layer-style-utils'; +import { updateStyleAcrossStores } from '@/lib/layer-style-store-utils'; +import { useLiveLayerStyleUpdates } from '@/hooks/use-live-layer-style-updates'; import { cn } from '@/lib/utils'; import { sanitizeHtmlId } from '@/lib/html-utils'; import { isFieldVariable, getCollectionVariable, findParentCollectionLayer, findAllParentCollectionLayers, isTextEditable, isTextContentLayer, isRichTextLayer, isHeadingLayer, findLayerWithParent, resetBindingsOnCollectionSourceChange, isInputInsideFilter, resolveFilterInputId, getLayerIndexes, indexedFindLayerById, indexedFindLayerWithParent, indexedFindParentCollectionLayer } from '@/lib/layer-utils'; @@ -612,6 +615,8 @@ const RightSidebar = React.memo(function RightSidebar({ // Get applied layer style and its classes const getStyleById = useLayerStylesStore((state) => state.getStyleById); + const updateStyle = useLayerStylesStore((state) => state.updateStyle); + const liveLayerStyleUpdates = useLiveLayerStyleUpdates(); const appliedStyle = selectedLayer?.styleId ? getStyleById(selectedLayer.styleId) : undefined; const styleClassesArray = useMemo(() => { if (!appliedStyle || !appliedStyle.classes) return []; @@ -632,14 +637,18 @@ const RightSidebar = React.memo(function RightSidebar({ if (styleClassesArray.length === 0) return new Set(); const overridden = new Set(); - // 1. Check for classes overridden by layer's custom classes + // 1. Check for style classes explicitly removed (not present in layer classes at all) + for (const styleClass of styleClassesArray) { + if (!classesArray.includes(styleClass)) { + overridden.add(styleClass); + } + } + + // 2. Check for classes overridden by layer's custom classes (conflict detection) if (layerOnlyClasses.length > 0) { for (const layerClass of layerOnlyClasses) { - // Use the conflict detection utility - // If adding this layer class would remove any style classes, those are overridden const classesWithoutConflicts = removeConflictsForClass(styleClassesArray, layerClass); - // Find which style classes were removed (those are the overridden ones) for (const styleClass of styleClassesArray) { if (!classesWithoutConflicts.includes(styleClass)) { overridden.add(styleClass); @@ -648,7 +657,7 @@ const RightSidebar = React.memo(function RightSidebar({ } } - // 2. Check for classes from properties explicitly removed on the layer + // 3. Check for classes from properties explicitly removed on the layer if (appliedStyle?.design && selectedLayer) { const removedClasses = getRemovedPropertyClasses( selectedLayer.design, @@ -659,7 +668,7 @@ const RightSidebar = React.memo(function RightSidebar({ } return overridden; - }, [layerOnlyClasses, styleClassesArray, appliedStyle, selectedLayer]); + }, [classesArray, layerOnlyClasses, styleClassesArray, appliedStyle, selectedLayer]); // Update local state when selected layer changes (for settings fields) const [prevSelectedLayerId, setPrevSelectedLayerId] = useState(null); @@ -758,6 +767,63 @@ const RightSidebar = React.memo(function RightSidebar({ } }, [classesArray, handleClassesChange, selectedLayer, showTextStyleControls, activeTextStyleKey, handleLayerUpdate]); + // Remove a class that belongs to the applied style — tracks as styleOverrides + const removeStyleClass = useCallback((classToRemove: string) => { + if (!selectedLayer) return; + const newClasses = classesArray.filter(cls => cls !== classToRemove).join(' '); + setClassesInput(newClasses); + handleLayerUpdate(selectedLayer.id, { + classes: newClasses, + styleOverrides: { + classes: newClasses, + design: selectedLayer.styleOverrides?.design ?? selectedLayer.design, + }, + }); + }, [classesArray, handleLayerUpdate, selectedLayer]); + + // Whether the style has any overrides (classes or design) + const styleHasOverrides = useMemo(() => { + if (!appliedStyle || !selectedLayer) return false; + return hasStyleOverrides(selectedLayer, appliedStyle); + }, [appliedStyle, selectedLayer]); + + // Update the style definition with current layer values + const handleUpdateStyleFromClasses = useCallback(async () => { + if (!selectedLayer || !appliedStyle) return; + const currentClasses = classesInput; + const currentDesign = selectedLayer.design; + + await updateStyle(appliedStyle.id, { + classes: currentClasses, + design: currentDesign, + }); + + updateStyleAcrossStores(appliedStyle.id, currentClasses, currentDesign); + handleLayerUpdate(selectedLayer.id, { styleOverrides: undefined }); + + if (liveLayerStyleUpdates) { + liveLayerStyleUpdates.broadcastStyleUpdate(appliedStyle.id, { + classes: currentClasses, + design: currentDesign, + }); + } + }, [selectedLayer, appliedStyle, classesInput, updateStyle, handleLayerUpdate, liveLayerStyleUpdates]); + + // Reset overrides back to the style's original classes/design + const handleResetStyleOverrides = useCallback(() => { + if (!selectedLayer || !appliedStyle) return; + const updatedLayer = resetLayerToStyle(selectedLayer, appliedStyle); + const resetClasses = Array.isArray(updatedLayer.classes) + ? updatedLayer.classes.join(' ') + : updatedLayer.classes || ''; + setClassesInput(resetClasses); + handleLayerUpdate(selectedLayer.id, { + classes: updatedLayer.classes, + design: updatedLayer.design, + styleOverrides: undefined, + }); + }, [selectedLayer, appliedStyle, handleLayerUpdate]); + // Handle key press for adding classes const handleKeyPress = useCallback((e: React.KeyboardEvent) => { if (e.key === 'Enter') { @@ -1812,7 +1878,7 @@ const RightSidebar = React.memo(function RightSidebar({ )} - {/* Layer style classes (strikethrough if overridden) */} + {/* Layer style classes (removable, strikethrough if overridden) */} {styleClassesArray.length > 0 && (
@@ -1835,6 +1901,16 @@ const RightSidebar = React.memo(function RightSidebar({ {cls} + {!isOverridden && ( + + )} ); })}