@@ -85,6 +85,9 @@ import { useLayerLocks } from '@/hooks/use-layer-locks';
8585
8686// 6. Utils, APIs, lib
8787import { classesToDesign , mergeDesign , removeConflictsForClass , getRemovedPropertyClasses } from '@/lib/tailwind-class-mapper' ;
88+ import { resetLayerToStyle , hasStyleOverrides } from '@/lib/layer-style-utils' ;
89+ import { updateStyleAcrossStores } from '@/lib/layer-style-store-utils' ;
90+ import { useLiveLayerStyleUpdates } from '@/hooks/use-live-layer-style-updates' ;
8891import { cn } from '@/lib/utils' ;
8992import { sanitizeHtmlId } from '@/lib/html-utils' ;
9093import { 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({
612615
613616 // Get applied layer style and its classes
614617 const getStyleById = useLayerStylesStore ( ( state ) => state . getStyleById ) ;
618+ const updateStyle = useLayerStylesStore ( ( state ) => state . updateStyle ) ;
619+ const liveLayerStyleUpdates = useLiveLayerStyleUpdates ( ) ;
615620 const appliedStyle = selectedLayer ?. styleId ? getStyleById ( selectedLayer . styleId ) : undefined ;
616621 const styleClassesArray = useMemo ( ( ) => {
617622 if ( ! appliedStyle || ! appliedStyle . classes ) return [ ] ;
@@ -632,14 +637,18 @@ const RightSidebar = React.memo(function RightSidebar({
632637 if ( styleClassesArray . length === 0 ) return new Set < string > ( ) ;
633638 const overridden = new Set < string > ( ) ;
634639
635- // 1. Check for classes overridden by layer's custom classes
640+ // 1. Check for style classes explicitly removed (not present in layer classes at all)
641+ for ( const styleClass of styleClassesArray ) {
642+ if ( ! classesArray . includes ( styleClass ) ) {
643+ overridden . add ( styleClass ) ;
644+ }
645+ }
646+
647+ // 2. Check for classes overridden by layer's custom classes (conflict detection)
636648 if ( layerOnlyClasses . length > 0 ) {
637649 for ( const layerClass of layerOnlyClasses ) {
638- // Use the conflict detection utility
639- // If adding this layer class would remove any style classes, those are overridden
640650 const classesWithoutConflicts = removeConflictsForClass ( styleClassesArray , layerClass ) ;
641651
642- // Find which style classes were removed (those are the overridden ones)
643652 for ( const styleClass of styleClassesArray ) {
644653 if ( ! classesWithoutConflicts . includes ( styleClass ) ) {
645654 overridden . add ( styleClass ) ;
@@ -648,7 +657,7 @@ const RightSidebar = React.memo(function RightSidebar({
648657 }
649658 }
650659
651- // 2 . Check for classes from properties explicitly removed on the layer
660+ // 3 . Check for classes from properties explicitly removed on the layer
652661 if ( appliedStyle ?. design && selectedLayer ) {
653662 const removedClasses = getRemovedPropertyClasses (
654663 selectedLayer . design ,
@@ -659,7 +668,7 @@ const RightSidebar = React.memo(function RightSidebar({
659668 }
660669
661670 return overridden ;
662- } , [ layerOnlyClasses , styleClassesArray , appliedStyle , selectedLayer ] ) ;
671+ } , [ classesArray , layerOnlyClasses , styleClassesArray , appliedStyle , selectedLayer ] ) ;
663672
664673 // Update local state when selected layer changes (for settings fields)
665674 const [ prevSelectedLayerId , setPrevSelectedLayerId ] = useState < string | null > ( null ) ;
@@ -758,6 +767,63 @@ const RightSidebar = React.memo(function RightSidebar({
758767 }
759768 } , [ classesArray , handleClassesChange , selectedLayer , showTextStyleControls , activeTextStyleKey , handleLayerUpdate ] ) ;
760769
770+ // Remove a class that belongs to the applied style — tracks as styleOverrides
771+ const removeStyleClass = useCallback ( ( classToRemove : string ) => {
772+ if ( ! selectedLayer ) return ;
773+ const newClasses = classesArray . filter ( cls => cls !== classToRemove ) . join ( ' ' ) ;
774+ setClassesInput ( newClasses ) ;
775+ handleLayerUpdate ( selectedLayer . id , {
776+ classes : newClasses ,
777+ styleOverrides : {
778+ classes : newClasses ,
779+ design : selectedLayer . styleOverrides ?. design ?? selectedLayer . design ,
780+ } ,
781+ } ) ;
782+ } , [ classesArray , handleLayerUpdate , selectedLayer ] ) ;
783+
784+ // Whether the style has any overrides (classes or design)
785+ const styleHasOverrides = useMemo ( ( ) => {
786+ if ( ! appliedStyle || ! selectedLayer ) return false ;
787+ return hasStyleOverrides ( selectedLayer , appliedStyle ) ;
788+ } , [ appliedStyle , selectedLayer ] ) ;
789+
790+ // Update the style definition with current layer values
791+ const handleUpdateStyleFromClasses = useCallback ( async ( ) => {
792+ if ( ! selectedLayer || ! appliedStyle ) return ;
793+ const currentClasses = classesInput ;
794+ const currentDesign = selectedLayer . design ;
795+
796+ await updateStyle ( appliedStyle . id , {
797+ classes : currentClasses ,
798+ design : currentDesign ,
799+ } ) ;
800+
801+ updateStyleAcrossStores ( appliedStyle . id , currentClasses , currentDesign ) ;
802+ handleLayerUpdate ( selectedLayer . id , { styleOverrides : undefined } ) ;
803+
804+ if ( liveLayerStyleUpdates ) {
805+ liveLayerStyleUpdates . broadcastStyleUpdate ( appliedStyle . id , {
806+ classes : currentClasses ,
807+ design : currentDesign ,
808+ } ) ;
809+ }
810+ } , [ selectedLayer , appliedStyle , classesInput , updateStyle , handleLayerUpdate , liveLayerStyleUpdates ] ) ;
811+
812+ // Reset overrides back to the style's original classes/design
813+ const handleResetStyleOverrides = useCallback ( ( ) => {
814+ if ( ! selectedLayer || ! appliedStyle ) return ;
815+ const updatedLayer = resetLayerToStyle ( selectedLayer , appliedStyle ) ;
816+ const resetClasses = Array . isArray ( updatedLayer . classes )
817+ ? updatedLayer . classes . join ( ' ' )
818+ : updatedLayer . classes || '' ;
819+ setClassesInput ( resetClasses ) ;
820+ handleLayerUpdate ( selectedLayer . id , {
821+ classes : updatedLayer . classes ,
822+ design : updatedLayer . design ,
823+ styleOverrides : undefined ,
824+ } ) ;
825+ } , [ selectedLayer , appliedStyle , handleLayerUpdate ] ) ;
826+
761827 // Handle key press for adding classes
762828 const handleKeyPress = useCallback ( ( e : React . KeyboardEvent < HTMLInputElement > ) => {
763829 if ( e . key === 'Enter' ) {
@@ -1690,21 +1756,27 @@ const RightSidebar = React.memo(function RightSidebar({
16901756 < hr className = "mt-4" />
16911757
16921758 { /* Design tab */ }
1693- < TabsContent value = "design" className = "flex-1 flex flex-col divide-y overflow-y-auto no-scrollbar data-[state=inactive]:hidden overflow-x-hidden mt-0" >
1759+ < TabsContent value = "design" className = "flex-1 flex flex-col divide-y data-[state=inactive]:hidden mt-0 overflow-hidden " >
16941760
1695- { /* Layer Styles Panel - hide in text style mode except for richText sublayers */ }
1696- { ( ! showTextStyleControls || ( selectedLayer && isRichTextLayer ( selectedLayer ) ) ) && (
1697- < LayerStylesPanel
1698- layer = { selectedLayer }
1699- pageId = { currentPageId }
1700- onLayerUpdate = { handleLayerUpdate }
1701- activeTextStyleKey = { selectedLayer && isRichTextLayer ( selectedLayer ) ? activeTextStyleKey : null }
1702- />
1703- ) }
1761+ < div className = "flex flex-col divide-y" >
17041762
1705- { activeTab === 'design' && (
1706- < UIStateSelector selectedLayer = { selectedLayer } />
1707- ) }
1763+ { /* Layer Styles Panel - hide in text style mode except for richText sublayers */ }
1764+ { ( ! showTextStyleControls || ( selectedLayer && isRichTextLayer ( selectedLayer ) ) ) && (
1765+ < LayerStylesPanel
1766+ layer = { selectedLayer }
1767+ pageId = { currentPageId }
1768+ onLayerUpdate = { handleLayerUpdate }
1769+ activeTextStyleKey = { selectedLayer && isRichTextLayer ( selectedLayer ) ? activeTextStyleKey : null }
1770+ />
1771+ ) }
1772+
1773+ { activeTab === 'design' && (
1774+ < UIStateSelector selectedLayer = { selectedLayer } />
1775+ ) }
1776+
1777+ </ div >
1778+
1779+ < div className = "overflow-y-auto no-scrollbar overflow-x-hidden divide-y " >
17081780
17091781 { shouldShowControl ( 'layout' , selectedLayer ) && ! showTextStyleControls && (
17101782 < LayoutControls layer = { selectedLayer } onLayerUpdate = { handleLayerUpdate } />
@@ -1789,9 +1861,10 @@ const RightSidebar = React.memo(function RightSidebar({
17891861 { layerOnlyClasses . map ( ( cls , index ) => (
17901862 < Badge
17911863 variant = "secondary"
1864+ className = "truncate max-w-50"
17921865 key = { `layer-${ index } ` }
17931866 >
1794- < span > { cls } </ span >
1867+ < span className = "truncate" > { cls } </ span >
17951868 < Button
17961869 onClick = { ( ) => removeClass ( cls ) }
17971870 className = "size-4! p-0! -mr-1"
@@ -1805,7 +1878,7 @@ const RightSidebar = React.memo(function RightSidebar({
18051878 </ div >
18061879 ) }
18071880
1808- { /* Layer style classes (strikethrough if overridden) */ }
1881+ { /* Layer style classes (removable, strikethrough if overridden) */ }
18091882 { styleClassesArray . length > 0 && (
18101883 < div className = "flex flex-col gap-2.5" >
18111884 < div className = "py-1 w-full flex items-center gap-2" >
@@ -1823,11 +1896,21 @@ const RightSidebar = React.memo(function RightSidebar({
18231896 < Badge
18241897 variant = "secondary"
18251898 key = { `style-${ index } ` }
1826- className = "opacity-60"
1899+ className = "opacity-60 truncate max-w-50 "
18271900 >
1828- < span className = { isOverridden ? 'line-through' : '' } >
1901+ < span className = { isOverridden ? 'line-through truncate ' : 'truncate ' } >
18291902 { cls }
18301903 </ span >
1904+ { ! isOverridden && (
1905+ < Button
1906+ onClick = { ( ) => removeStyleClass ( cls ) }
1907+ className = "size-4! p-0! -mr-1"
1908+ variant = "outline"
1909+ disabled = { isLockedByOther }
1910+ >
1911+ < Icon name = "x" className = "size-2" />
1912+ </ Button >
1913+ ) }
18311914 </ Badge >
18321915 ) ;
18331916 } ) }
@@ -1836,6 +1919,9 @@ const RightSidebar = React.memo(function RightSidebar({
18361919 ) }
18371920 </ div >
18381921 </ SettingsPanel >
1922+
1923+ </ div >
1924+
18391925 </ TabsContent >
18401926
18411927 < TabsContent value = "settings" className = "flex-1 overflow-y-auto no-scrollbar mt-0 data-[state=inactive]:hidden" >
0 commit comments