diff --git a/modules/atomic-widgets/styles/styles-renderer.php b/modules/atomic-widgets/styles/styles-renderer.php index b2202e71b6d9..4f34631526b1 100644 --- a/modules/atomic-widgets/styles/styles-renderer.php +++ b/modules/atomic-widgets/styles/styles-renderer.php @@ -111,16 +111,17 @@ private function get_base_selector( array $style_def ): ?string { } private function variant_to_css_string( string $base_selector, array $variant ): string { - $css = $this->props_to_css_string( $variant['props'] ); + $css = $this->props_to_css_string( $variant['props'] ) ?? ''; + $custom_css = isset( $variant['customCss']['raw'] ) ? $variant['customCss']['raw'] : ''; - if ( ! $css ) { + if ( ! $css && ! $custom_css ) { return ''; } $state = isset( $variant['meta']['state'] ) ? ':' . $variant['meta']['state'] : ''; $selector = $base_selector . $state; - $style_declaration = $selector . '{' . $css . '}'; + $style_declaration = $selector . '{' . $css . $custom_css . '}'; if ( isset( $variant['meta']['breakpoint'] ) ) { $style_declaration = $this->wrap_with_media_query( $variant['meta']['breakpoint'], $style_declaration ); diff --git a/packages/packages/core/editor-canvas/src/renderers/create-styles-renderer.ts b/packages/packages/core/editor-canvas/src/renderers/create-styles-renderer.ts index 080b49269b36..2dfafc991a48 100644 --- a/packages/packages/core/editor-canvas/src/renderers/create-styles-renderer.ts +++ b/packages/packages/core/editor-canvas/src/renderers/create-styles-renderer.ts @@ -42,7 +42,9 @@ export function createStylesRenderer( { resolve, breakpoints, selectorPrefix = ' return async ( { styles, signal }: StyleRendererArgs ): Promise< StyleItem[] > => { const stylesCssPromises = styles.map( async ( style ) => { const variantCssPromises = Object.values( style.variants ).map( async ( variant ) => { - const css = await propsToCss( { props: variant.props, resolve, signal } ); + const css = + ( await propsToCss( { props: variant.props, resolve, signal } ) ) + + ( variant.customCss?.raw ?? '' ); return createStyleWrapper() .for( style.cssName, style.type ) diff --git a/packages/packages/core/editor-canvas/src/style-commands/undoable-actions/paste-element-style.ts b/packages/packages/core/editor-canvas/src/style-commands/undoable-actions/paste-element-style.ts index 78c95770793f..b7c80065fae3 100644 --- a/packages/packages/core/editor-canvas/src/style-commands/undoable-actions/paste-element-style.ts +++ b/packages/packages/core/editor-canvas/src/style-commands/undoable-actions/paste-element-style.ts @@ -40,12 +40,13 @@ export const undoablePasteElementStyle = () => }; if ( styleId ) { - newStyle.variants.forEach( ( { meta, props } ) => { + newStyle.variants.forEach( ( { meta, props, customCss } ) => { updateElementStyle( { elementId, styleId, meta, props, + customCss, } ); } ); } else { diff --git a/packages/packages/core/editor-editing-panel/src/components/custom-css.tsx b/packages/packages/core/editor-editing-panel/src/components/custom-css.tsx new file mode 100644 index 000000000000..70277a970f31 --- /dev/null +++ b/packages/packages/core/editor-editing-panel/src/components/custom-css.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import { TextField } from '@elementor/ui'; + +import { useCustomCss } from '../hooks/use-custom-css'; +import { SectionContent } from './section-content'; + +export const CustomCss = () => { + const { customCss, setCustomCss } = useCustomCss(); + + return ( + + ) => + setCustomCss( ev.target.value, { history: { propDisplayName: 'Custom CSS' } } ) + } + /> + + ); +}; diff --git a/packages/packages/core/editor-editing-panel/src/components/style-tab.tsx b/packages/packages/core/editor-editing-panel/src/components/style-tab.tsx index 1631685adbdb..3a4ca7f9dcf2 100644 --- a/packages/packages/core/editor-editing-panel/src/components/style-tab.tsx +++ b/packages/packages/core/editor-editing-panel/src/components/style-tab.tsx @@ -14,6 +14,7 @@ import { StyleProvider } from '../contexts/style-context'; import { StyleInheritanceProvider } from '../contexts/styles-inheritance-context'; import { useActiveStyleDefId } from '../hooks/use-active-style-def-id'; import { CssClassSelector } from './css-classes/css-class-selector'; +import { CustomCss } from './custom-css'; import { SectionsList } from './sections-list'; import { BackgroundSection } from './style-sections/background-section/background-section'; import { BorderSection } from './style-sections/border-section/border-section'; @@ -157,6 +158,14 @@ export const StyleTab = () => { } } fields={ [ 'box-shadow', 'opacity', 'transform', 'filter', 'backdrop-filter' ] } /> + diff --git a/packages/packages/core/editor-editing-panel/src/hooks/use-custom-css.ts b/packages/packages/core/editor-editing-panel/src/hooks/use-custom-css.ts new file mode 100644 index 000000000000..e2ce6f10b97f --- /dev/null +++ b/packages/packages/core/editor-editing-panel/src/hooks/use-custom-css.ts @@ -0,0 +1,180 @@ +import { useMemo } from 'react'; +import { createElementStyle, deleteElementStyle, type ElementID } from '@elementor/editor-elements'; +import { + type CustomCss, + getVariantByMeta, + type StyleDefinition, + type StyleDefinitionVariant, +} from '@elementor/editor-styles'; +import { ELEMENTS_STYLES_RESERVED_LABEL, type StylesProvider } from '@elementor/editor-styles-repository'; +import { isExperimentActive, undoable } from '@elementor/editor-v1-adapters'; + +import { useClassesProp } from '../contexts/classes-prop-context'; +import { useElement } from '../contexts/element-context'; +import { useStyle } from '../contexts/style-context'; +import { StylesProviderCannotUpdatePropsError } from '../errors'; +import { EXPERIMENTAL_FEATURES } from '../sync/experiments-flags'; +import { getSubtitle, getTitle, HISTORY_DEBOUNCE_WAIT } from './use-styles-fields'; +import { useStylesRerender } from './use-styles-rerender'; + +export const useCustomCss = () => { + const { + element: { id: elementId }, + } = useElement(); + const { id: styleId, meta, provider } = useStyle(); + const style = provider?.actions.get( styleId, { elementId } ); + + const undoableUpdateStyle = useUndoableUpdateCustomCss( { elementId, meta } ); + + useStylesRerender(); + + const variant = style ? getVariantByMeta( style, meta ) : null; + const customCss = variant?.customCss?.raw ? variant?.customCss : null; + + const setCustomCss = ( + raw: string, + { history: { propDisplayName } }: { history: { propDisplayName: string } } + ) => { + if ( ! styleId ) { + undoableUpdateStyle( { styleId: null, provider: null, customCss: { raw }, propDisplayName } ); + } else { + undoableUpdateStyle( { styleId, provider, customCss: { raw }, propDisplayName } ); + } + }; + + return { + customCss, + setCustomCss, + }; +}; + +type UpdateStyleArgs = { + styleId: StyleDefinition[ 'id' ]; + provider: StylesProvider; + customCss: CustomCss | null; + propDisplayName: string; +}; + +type CreateStyleArgs = { + styleId: null; + provider: null; + customCss: CustomCss | null; + propDisplayName: string; +}; + +type UpdateStyleReturn = { + styleId: StyleDefinition[ 'id' ]; + provider: StylesProvider; + prevCustomCss: CustomCss | null; +}; + +type CreateStyleReturn = { + createdStyleId: StyleDefinition[ 'id' ]; +}; + +type UndoableUpdateStylePayload = UpdateStyleArgs | CreateStyleArgs; +type UndoableUpdateStyleReturn = UpdateStyleReturn | CreateStyleReturn; + +export function useUndoableUpdateCustomCss( { + elementId, + meta: { breakpoint, state }, +}: { + elementId: ElementID; + meta: StyleDefinitionVariant[ 'meta' ]; +} ) { + const classesProp = useClassesProp(); + + return useMemo( () => { + const isVersion331Active = isExperimentActive( EXPERIMENTAL_FEATURES.V_3_31 ); + + const meta = { breakpoint, state }; + + const createStyleArgs = { elementId, classesProp, meta, label: ELEMENTS_STYLES_RESERVED_LABEL }; + + return undoable( + { + do: ( payload: UndoableUpdateStylePayload ): UndoableUpdateStyleReturn => { + if ( shouldCreateNewLocalStyle( payload ) ) { + return createLocalStyle( payload as CreateStyleArgs ); + } + return updateStyleCustomCss( payload as UpdateStyleArgs ); + }, + undo: ( payload: UndoableUpdateStylePayload, doReturn: UndoableUpdateStyleReturn ) => { + const wasLocalStyleCreated = shouldCreateNewLocalStyle( payload ); + + if ( wasLocalStyleCreated ) { + return undoCreateLocalStyle( payload as CreateStyleArgs, doReturn as CreateStyleReturn ); + } + return undoUpdateStyleCustomCss( payload as UpdateStyleArgs, doReturn as UpdateStyleReturn ); + }, + redo: ( payload: UndoableUpdateStylePayload, doReturn: UndoableUpdateStyleReturn ) => { + const wasLocalStyleCreated = shouldCreateNewLocalStyle( payload ); + + if ( wasLocalStyleCreated ) { + return createLocalStyle( payload as CreateStyleArgs, doReturn as CreateStyleReturn ); + } + return updateStyleCustomCss( payload as UpdateStyleArgs ); + }, + }, + { + title: ( { provider, styleId } ) => getTitle( { provider, styleId, elementId, isVersion331Active } ), + subtitle: ( { provider, styleId, propDisplayName } ) => + getSubtitle( { provider, styleId, elementId, isVersion331Active, propDisplayName } ), + debounce: { wait: HISTORY_DEBOUNCE_WAIT }, + } + ); + + function shouldCreateNewLocalStyle( payload: UndoableUpdateStylePayload ) { + // If styleId and provider are nullish, it means that it's a local style that haven't been created yet. + // Local styles are created only when the user starts editing a style. + return ! payload.styleId && ! payload.provider; + } + + function createLocalStyle( { customCss }: CreateStyleArgs, redoArgs?: CreateStyleReturn ): CreateStyleReturn { + const createdStyle = createElementStyle( { + ...createStyleArgs, + props: {}, + customCss: customCss ?? null, + styleId: redoArgs?.createdStyleId, + } ); + + return { createdStyleId: createdStyle }; + } + + function undoCreateLocalStyle( _: UndoableUpdateStylePayload, { createdStyleId }: CreateStyleReturn ) { + deleteElementStyle( elementId, createdStyleId ); + } + + function updateStyleCustomCss( { provider, styleId, customCss }: UpdateStyleArgs ): UpdateStyleReturn { + if ( ! provider.actions.updateCustomCss ) { + throw new StylesProviderCannotUpdatePropsError( { + context: { providerKey: provider.getKey() }, + } ); + } + + const style = provider.actions.get( styleId, { elementId } ); + const prevCustomCss = getCurrentCustomCss( style, meta ); + + provider.actions.updateCustomCss( { id: styleId, meta, customCss }, { elementId } ); + + return { styleId, provider, prevCustomCss }; + } + + function undoUpdateStyleCustomCss( + _: UndoableUpdateStylePayload, + { styleId, provider, prevCustomCss }: UpdateStyleReturn + ) { + provider.actions.updateCustomCss?.( { id: styleId, meta, customCss: prevCustomCss }, { elementId } ); + } + }, [ elementId, breakpoint, state, classesProp ] ); +} + +function getCurrentCustomCss( style: StyleDefinition | null, meta: StyleDefinitionVariant[ 'meta' ] ) { + if ( ! style ) { + return null; + } + + const variant = getVariantByMeta( style, meta ); + + return variant?.customCss ?? null; +} diff --git a/packages/packages/core/editor-editing-panel/src/hooks/use-styles-fields.ts b/packages/packages/core/editor-editing-panel/src/hooks/use-styles-fields.ts index c662b86eb245..e1481036b4de 100644 --- a/packages/packages/core/editor-editing-panel/src/hooks/use-styles-fields.ts +++ b/packages/packages/core/editor-editing-panel/src/hooks/use-styles-fields.ts @@ -14,7 +14,7 @@ import { StyleNotFoundUnderProviderError, StylesProviderCannotUpdatePropsError } import { EXPERIMENTAL_FEATURES } from '../sync/experiments-flags'; import { useStylesRerender } from './use-styles-rerender'; -const HISTORY_DEBOUNCE_WAIT = 800; +export const HISTORY_DEBOUNCE_WAIT = 800; export function useStylesFields< T extends Props >( propNames: ( keyof T & string )[] ) { const { @@ -138,49 +138,9 @@ function useUndoableUpdateStyle( { }, }, { - title: ( { provider, styleId } ) => { - if ( isVersion331Active ) { - let title: string; - - const isLocal = isLocalStyle( provider, styleId ); - - if ( isLocal ) { - title = localStyleHistoryTitlesV331.title( { elementId } ); - } else { - // If the provider was nullish, `isLocalStyle` would return true. - provider = provider as StylesProvider; - - title = defaultHistoryTitlesV331.title( { provider } ); - } - - return title; - } - return historyTitlesV330.title( { elementId } ); - }, - subtitle: ( { provider, styleId, propDisplayName } ) => { - if ( isVersion331Active ) { - let subtitle: string; - - const isLocal = isLocalStyle( provider, styleId ); - - if ( isLocal ) { - subtitle = localStyleHistoryTitlesV331.subtitle( { propDisplayName } ); - } else { - // If the provider or styleId were nullish, `isLocalStyle` would return true. - provider = provider as StylesProvider; - styleId = styleId as StyleDefinition[ 'id' ]; - - subtitle = defaultHistoryTitlesV331.subtitle( { - provider, - styleId, - elementId, - propDisplayName, - } ); - } - return subtitle; - } - return historyTitlesV330.subtitle; - }, + title: ( { provider, styleId } ) => getTitle( { provider, styleId, elementId, isVersion331Active } ), + subtitle: ( { provider, styleId, propDisplayName } ) => + getSubtitle( { provider, styleId, elementId, isVersion331Active, propDisplayName } ), debounce: { wait: HISTORY_DEBOUNCE_WAIT }, } ); @@ -192,7 +152,12 @@ function useUndoableUpdateStyle( { } function createLocalStyle( { props }: CreateStyleArgs, redoArgs?: CreateStyleReturn ): CreateStyleReturn { - const createdStyle = createElementStyle( { ...createStyleArgs, props, styleId: redoArgs?.createdStyleId } ); + const createdStyle = createElementStyle( { + ...createStyleArgs, + props, + customCss: null, + styleId: redoArgs?.createdStyleId, + } ); return { createdStyleId: createdStyle }; } @@ -237,23 +202,23 @@ function getCurrentProps( style: StyleDefinition | null, meta: StyleDefinitionVa return structuredClone( props ); } -const historyTitlesV330 = { +export const historyTitlesV330 = { title: ( { elementId }: { elementId: ElementID } ) => getElementLabel( elementId ), subtitle: __( 'Style edited', 'elementor' ), }; -type DefaultHistoryTitleV331Args = { +export type DefaultHistoryTitleV331Args = { provider: StylesProvider; }; -type DefaultHistorySubtitleV331Args = { +export type DefaultHistorySubtitleV331Args = { provider: StylesProvider; styleId: StyleDefinition[ 'id' ]; elementId: ElementID; propDisplayName: string; }; -const defaultHistoryTitlesV331 = { +export const defaultHistoryTitlesV331 = { title: ( { provider }: DefaultHistoryTitleV331Args ) => { const providerLabel = provider.labels?.singular; return providerLabel ? capitalize( providerLabel ) : __( 'Style', 'elementor' ); @@ -270,24 +235,78 @@ const defaultHistoryTitlesV331 = { }, }; -type LocalStyleHistoryTitleV331Args = { +export type LocalStyleHistoryTitleV331Args = { elementId: ElementID; }; -type LocalStyleHistorySubtitleV331Args = { +export type LocalStyleHistorySubtitleV331Args = { propDisplayName: string; }; -const localStyleHistoryTitlesV331 = { +export const localStyleHistoryTitlesV331 = { title: ( { elementId }: LocalStyleHistoryTitleV331Args ) => getElementLabel( elementId ), subtitle: ( { propDisplayName }: LocalStyleHistorySubtitleV331Args ) => // translators: %s is the name of the style property being edited __( `%s edited`, 'elementor' ).replace( '%s', propDisplayName ), }; -function capitalize( str: string ) { +export function capitalize( str: string ) { return str.charAt( 0 ).toUpperCase() + str.slice( 1 ); } -const isLocalStyle = ( provider: StylesProvider | null, styleId: StyleDefinition[ 'id' ] | null ) => +export const isLocalStyle = ( provider: StylesProvider | null, styleId: StyleDefinition[ 'id' ] | null ) => ! provider || ! styleId || isElementsStylesProvider( provider.getKey() ); + +type GetTitle = { + provider: StylesProvider | null; + styleId: string | null; + isVersion331Active: boolean; + elementId: string; +}; + +type GetSubtitle = GetTitle & { propDisplayName: string }; + +export const getTitle = ( { provider, styleId, isVersion331Active, elementId }: GetTitle ) => { + if ( isVersion331Active ) { + let title: string; + + const isLocal = isLocalStyle( provider, styleId ); + + if ( isLocal ) { + title = localStyleHistoryTitlesV331.title( { elementId } ); + } else { + // If the provider was nullish, `isLocalStyle` would return true. + provider = provider as StylesProvider; + + title = defaultHistoryTitlesV331.title( { provider } ); + } + + return title; + } + return historyTitlesV330.title( { elementId } ); +}; + +export const getSubtitle = ( { provider, styleId, propDisplayName, isVersion331Active, elementId }: GetSubtitle ) => { + if ( isVersion331Active ) { + let subtitle: string; + + const isLocal = isLocalStyle( provider, styleId ); + + if ( isLocal ) { + subtitle = localStyleHistoryTitlesV331.subtitle( { propDisplayName } ); + } else { + // If the provider or styleId were nullish, `isLocalStyle` would return true. + provider = provider as StylesProvider; + styleId = styleId as StyleDefinition[ 'id' ]; + + subtitle = defaultHistoryTitlesV331.subtitle( { + provider, + styleId, + elementId, + propDisplayName, + } ); + } + return subtitle; + } + return historyTitlesV330.subtitle; +}; diff --git a/packages/packages/core/editor-global-classes/src/global-classes-styles-provider.ts b/packages/packages/core/editor-global-classes/src/global-classes-styles-provider.ts index 59b50d85adeb..49664bd9bb29 100644 --- a/packages/packages/core/editor-global-classes/src/global-classes-styles-provider.ts +++ b/packages/packages/core/editor-global-classes/src/global-classes-styles-provider.ts @@ -72,11 +72,22 @@ export const globalClassesStylesProvider = createStylesProvider( { updateProps: ( args ) => { dispatch( slice.actions.updateProps( { + ...args, id: args.id, meta: args.meta, props: args.props, } ) ); }, + updateCustomCss: ( args ) => { + console.log( 'slice', slice ); + dispatch( + slice.actions.updateCustomCss( { + id: args.id, + meta: args.meta, + customCss: args.customCss ?? null, + } ) + ); + }, }, } ); diff --git a/packages/packages/core/editor-global-classes/src/store.ts b/packages/packages/core/editor-global-classes/src/store.ts index 4aee41a8fe67..68e900244c7c 100644 --- a/packages/packages/core/editor-global-classes/src/store.ts +++ b/packages/packages/core/editor-global-classes/src/store.ts @@ -1,5 +1,6 @@ import { mergeProps, type Props } from '@elementor/editor-props'; import { + type CustomCss, getVariantByMeta, type StyleDefinition, type StyleDefinitionID, @@ -125,12 +126,46 @@ export const slice = createSlice( { if ( variant ) { variant.props = mergeProps( variant.props, payload.props ); - if ( Object.keys( variant.props ).length === 0 ) { + if ( Object.keys( variant.props ).length === 0 && ! variant.customCss?.raw ) { // If the props object is empty after merging, we remove the variant. style.variants = style.variants.filter( ( v ) => v !== variant ); } } else { - style.variants.push( { meta: payload.meta, props: payload.props } ); + style.variants.push( { meta: payload.meta, props: payload.props, customCss: null } ); + } + + state.isDirty = true; + }, + + updateCustomCss( + state, + { + payload, + }: PayloadAction< { + id: StyleDefinitionID; + meta: StyleDefinitionVariant[ 'meta' ]; + customCss: CustomCss | null; + } > + ) { + const style = state.data.items[ payload.id ]; + + if ( ! style ) { + throw new GlobalClassNotFoundError( { context: { styleId: payload.id } } ); + } + localHistory.next( state.data ); + + const variant = getVariantByMeta( style, payload.meta ); + const customCss = payload.customCss?.raw ? payload.customCss : null; + + if ( variant ) { + variant.customCss = customCss; + + if ( Object.keys( variant.props ).length === 0 && ! variant.customCss?.raw ) { + // If the props object is empty after merging, we remove the variant. + style.variants = style.variants.filter( ( v ) => v !== variant ); + } + } else { + style.variants.push( { meta: payload.meta, props: {}, customCss } ); } state.isDirty = true; diff --git a/packages/packages/core/editor-styles-repository/src/providers/document-elements-styles-provider.ts b/packages/packages/core/editor-styles-repository/src/providers/document-elements-styles-provider.ts index 658b83e5e907..f8567ed1d704 100644 --- a/packages/packages/core/editor-styles-repository/src/providers/document-elements-styles-provider.ts +++ b/packages/packages/core/editor-styles-repository/src/providers/document-elements-styles-provider.ts @@ -54,14 +54,29 @@ export const documentElementsStylesProvider = createStylesProvider( { if ( ! isValidElementsMeta( meta ) ) { throw new InvalidElementsStyleProviderMetaError( { context: { meta } } ); } - +console.log( 'args', args ); updateElementStyle( { + ...args, elementId: meta.elementId, styleId: args.id, meta: args.meta, props: args.props, } ); }, + + updateCustomCss: ( args, meta = {} ) => { + if ( ! isValidElementsMeta( meta ) ) { + throw new InvalidElementsStyleProviderMetaError( { context: { meta } } ); + } +console.log( 'args', args ); + updateElementStyle( { + ...args, + elementId: meta.elementId, + styleId: args.id, + meta: args.meta, + props: {}, + } ); + }, }, } ); diff --git a/packages/packages/core/editor-styles-repository/src/types.ts b/packages/packages/core/editor-styles-repository/src/types.ts index 8120eadf1a4d..551459625b61 100644 --- a/packages/packages/core/editor-styles-repository/src/types.ts +++ b/packages/packages/core/editor-styles-repository/src/types.ts @@ -1,5 +1,5 @@ import type { Props } from '@elementor/editor-props'; -import type { StyleDefinition, StyleDefinitionID, StyleDefinitionVariant } from '@elementor/editor-styles'; +import type { CustomCss, StyleDefinition, StyleDefinitionID, StyleDefinitionVariant } from "@elementor/editor-styles"; type MakeOptional< T, K extends keyof T > = Omit< T, K > & Partial< T >; @@ -18,6 +18,13 @@ export type UpdatePropsActionPayload = { id: StyleDefinitionID; meta: StyleDefinitionVariant[ 'meta' ]; props: Props; + customCss?: CustomCss | null; +}; + +export type UpdateCustomCssActionPayload = { + id: StyleDefinitionID; + meta: StyleDefinitionVariant[ 'meta' ]; + customCss: CustomCss | null; }; export type StylesProvider = { @@ -37,6 +44,7 @@ export type StylesProvider = { delete?: ( id: StyleDefinitionID ) => void; update?: ( data: UpdateActionPayload ) => void; updateProps?: ( args: UpdatePropsActionPayload, meta?: Meta ) => void; + updateCustomCss?: ( args: UpdateCustomCssActionPayload, meta?: Meta ) => void; }; capabilities?: UserCapabilities; }; diff --git a/packages/packages/core/editor-styles-repository/src/utils/create-styles-provider.ts b/packages/packages/core/editor-styles-repository/src/utils/create-styles-provider.ts index 6c14552a1df8..b862f43b70f7 100644 --- a/packages/packages/core/editor-styles-repository/src/utils/create-styles-provider.ts +++ b/packages/packages/core/editor-styles-repository/src/utils/create-styles-provider.ts @@ -17,6 +17,7 @@ export type CreateStylesProviderOptions = { delete?: StylesProvider[ 'actions' ][ 'delete' ]; update?: StylesProvider[ 'actions' ][ 'update' ]; updateProps?: StylesProvider[ 'actions' ][ 'updateProps' ]; + updateCustomCss?: StylesProvider[ 'actions' ][ 'updateCustomCss' ]; }; capabilities?: UserCapabilities; }; @@ -33,6 +34,7 @@ export function createStylesProvider( { actions, capabilities, }: CreateStylesProviderOptions ): StylesProvider { + console.log( 'actions', actions ); return { getKey: typeof key === 'string' ? () => key : key, priority, @@ -51,6 +53,7 @@ export function createStylesProvider( { delete: actions.delete, update: actions.update, updateProps: actions.updateProps, + updateCustomCss: actions.updateCustomCss, }, }; } diff --git a/packages/packages/core/editor-styles-repository/src/utils/create-styles-repository.ts b/packages/packages/core/editor-styles-repository/src/utils/create-styles-repository.ts index f2d825980a28..a28d0e278911 100644 --- a/packages/packages/core/editor-styles-repository/src/utils/create-styles-repository.ts +++ b/packages/packages/core/editor-styles-repository/src/utils/create-styles-repository.ts @@ -2,6 +2,7 @@ import { type Meta, type StylesProvider } from '../types'; export const createStylesRepository = () => { const providers: StylesProvider[] = []; + console.log( 'create-styles-repository.ts', providers ); const getProviders = () => { return providers.slice( 0 ).sort( ( a, b ) => ( a.priority > b.priority ? -1 : 1 ) ); diff --git a/packages/packages/libs/editor-elements/src/styles/create-element-style.ts b/packages/packages/libs/editor-elements/src/styles/create-element-style.ts index a4d0132930e8..a460e3706d6f 100644 --- a/packages/packages/libs/editor-elements/src/styles/create-element-style.ts +++ b/packages/packages/libs/editor-elements/src/styles/create-element-style.ts @@ -18,6 +18,7 @@ export type CreateElementStyleArgs = { label: string; meta: StyleDefinitionVariant[ 'meta' ]; props: StyleDefinitionVariant[ 'props' ]; + customCss: StyleDefinitionVariant[ 'customCss' ]; additionalVariants?: StyleDefinitionVariant[]; }; @@ -28,6 +29,7 @@ export function createElementStyle( { label, meta, props, + customCss, additionalVariants = [], }: CreateElementStyleArgs ): string { let id = styleId; @@ -35,7 +37,7 @@ export function createElementStyle( { mutateElementStyles( elementId, ( styles ) => { id ??= generateId( `e-${ elementId }-`, Object.keys( styles ) ); - const variants = [ { meta, props }, ...additionalVariants ]; + const variants = [ { meta, props, customCss }, ...additionalVariants ]; styles[ id ] = { id, diff --git a/packages/packages/libs/editor-elements/src/styles/mutate-element-styles.ts b/packages/packages/libs/editor-elements/src/styles/mutate-element-styles.ts index c56a935db5e4..3e459b300558 100644 --- a/packages/packages/libs/editor-elements/src/styles/mutate-element-styles.ts +++ b/packages/packages/libs/editor-elements/src/styles/mutate-element-styles.ts @@ -36,6 +36,7 @@ export function mutateElementStyles( elementId: ElementID, mutator: Mutator ) { function mutateStyles( container: V1Element, mutator: Mutator ) { const styles: StyleDefinitionsMap = structuredClone( container.model.get( 'styles' ) ) ?? {}; + console.log( 'mutate-element-styles.ts' ); const entries = Object.entries( mutator( styles ) ) .map( ( [ styleId, style ] ) => { @@ -55,7 +56,7 @@ function mutateStyles( container: V1Element, mutator: Mutator ) { } function removeEmptyVariants( style: StyleDefinition ) { - return style.variants.filter( ( { props } ) => Object.keys( props ).length > 0 ); + return style.variants.filter( ( { props, customCss } ) => Object.keys( props ).length > 0 || customCss?.raw ); } function isStyleEmpty( style: StyleDefinition ) { diff --git a/packages/packages/libs/editor-elements/src/styles/update-element-style.ts b/packages/packages/libs/editor-elements/src/styles/update-element-style.ts index 15db67ead36d..02c24530da09 100644 --- a/packages/packages/libs/editor-elements/src/styles/update-element-style.ts +++ b/packages/packages/libs/editor-elements/src/styles/update-element-style.ts @@ -10,6 +10,7 @@ export type UpdateElementStyleArgs = { styleId: StyleDefinition[ 'id' ]; meta: StyleDefinitionVariant[ 'meta' ]; props: StyleDefinitionVariant[ 'props' ]; + customCss?: StyleDefinitionVariant[ 'customCss' ]; }; export function updateElementStyle( args: UpdateElementStyleArgs ) { @@ -21,11 +22,13 @@ export function updateElementStyle( args: UpdateElementStyleArgs ) { } const variant = getVariantByMeta( style, args.meta ); + const newCustomCss = 'customCss' in args ? args.customCss ?? null : variant?.customCss ?? null; if ( variant ) { variant.props = mergeProps( variant.props, args.props ); + variant.customCss = newCustomCss; } else { - style.variants.push( { meta: args.meta, props: args.props } ); + style.variants.push( { meta: args.meta, props: args.props, customCss: newCustomCss } ); } return styles; diff --git a/packages/packages/libs/editor-elements/src/sync/update-element-settings.ts b/packages/packages/libs/editor-elements/src/sync/update-element-settings.ts index e42101ed6fa0..517f308a79da 100644 --- a/packages/packages/libs/editor-elements/src/sync/update-element-settings.ts +++ b/packages/packages/libs/editor-elements/src/sync/update-element-settings.ts @@ -1,4 +1,5 @@ import { type Props } from '@elementor/editor-props'; +import { type CustomCss } from '@elementor/editor-styles'; import { __privateRunCommandSync as runCommandSync } from '@elementor/editor-v1-adapters'; import { type ElementID } from '../types'; @@ -7,20 +8,28 @@ import { getContainer } from './get-container'; export type UpdateElementSettingsArgs = { id: ElementID; props: Props; + customCss?: CustomCss | null; withHistory?: boolean; }; -export const updateElementSettings = ( { id, props, withHistory = true }: UpdateElementSettingsArgs ) => { +export const updateElementSettings = ( { id, props, withHistory = true, ...args }: UpdateElementSettingsArgs ) => { const container = getContainer( id ); - const args = { - container, - settings: { ...props }, - }; + const settings = + 'customCss' in args + ? { + container, + settings: { ...props }, + customCss: args.customCss ?? null, + } + : { + container, + settings: { ...props }, + }; if ( withHistory ) { - runCommandSync( 'document/elements/settings', args ); + runCommandSync( 'document/elements/settings', settings ); } else { - runCommandSync( 'document/elements/set-settings', args, { internal: true } ); + runCommandSync( 'document/elements/set-settings', settings, { internal: true } ); } }; diff --git a/packages/packages/libs/editor-styles/src/types.ts b/packages/packages/libs/editor-styles/src/types.ts index 9a7f32e0a5ea..2c497242f8be 100644 --- a/packages/packages/libs/editor-styles/src/types.ts +++ b/packages/packages/libs/editor-styles/src/types.ts @@ -13,12 +13,17 @@ export type StyleDefinitionState = | 'hidden' | 'visible'; +export type CustomCss = { + raw: string; +}; + export type StyleDefinitionVariant = { meta: { breakpoint: null | BreakpointId; state: StyleDefinitionState; }; props: Props; + customCss: CustomCss | null; }; export type StyleDefinitionType = 'class';