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';