Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 83 additions & 7 deletions app/(builder)/ycode/components/RightSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 [];
Expand All @@ -632,14 +637,18 @@ const RightSidebar = React.memo(function RightSidebar({
if (styleClassesArray.length === 0) return new Set<string>();
const overridden = new Set<string>();

// 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);
Expand All @@ -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,
Expand All @@ -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<string | null>(null);
Expand Down Expand Up @@ -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<HTMLInputElement>) => {
if (e.key === 'Enter') {
Expand Down Expand Up @@ -1812,7 +1878,7 @@ const RightSidebar = React.memo(function RightSidebar({
</div>
)}

{/* Layer style classes (strikethrough if overridden) */}
{/* Layer style classes (removable, strikethrough if overridden) */}
{styleClassesArray.length > 0 && (
<div className="flex flex-col gap-2.5">
<div className="py-1 w-full flex items-center gap-2">
Expand All @@ -1835,6 +1901,16 @@ const RightSidebar = React.memo(function RightSidebar({
<span className={isOverridden ? 'line-through truncate' : 'truncate'}>
{cls}
</span>
{!isOverridden && (
<Button
onClick={() => removeStyleClass(cls)}
className="size-4! p-0! -mr-1"
variant="outline"
disabled={isLockedByOther}
>
<Icon name="x" className="size-2" />
</Button>
)}
</Badge>
);
})}
Expand Down