@elementor/editor-elements 4.2.0-beta1 → 4.3.0-1000
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +49 -2
- package/dist/index.d.ts +49 -2
- package/dist/index.js +310 -56
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +291 -43
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -7
- package/src/children-dependencies/bind-settings-reconcile.ts +147 -0
- package/src/children-dependencies/index.ts +4 -0
- package/src/children-dependencies/reconcile-initial-children.ts +59 -0
- package/src/children-dependencies/stash.ts +40 -0
- package/src/children-dependencies/types.ts +20 -0
- package/src/children-dependencies/utils.ts +44 -0
- package/src/index.ts +12 -0
- package/src/sync/get-element-icon.ts +14 -0
- package/src/sync/get-element-title.ts +48 -0
- package/src/sync/resolve-insert-index.ts +34 -0
- package/src/sync/types.ts +13 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { resolveInsertIndex } from '../sync/resolve-insert-index';
|
|
2
|
+
import { type V1ElementConfig, type V1ElementData, type V1ElementSettingsProps } from '../sync/types';
|
|
3
|
+
import { createChildrenStash } from './stash';
|
|
4
|
+
import { type ChildDependencyRule } from './types';
|
|
5
|
+
import { evaluateWhen, resolveChildModelData } from './utils';
|
|
6
|
+
|
|
7
|
+
type ReconcileInitialChildrenArgs = {
|
|
8
|
+
elementId: string;
|
|
9
|
+
elementConfig: V1ElementConfig | undefined;
|
|
10
|
+
attributes: {
|
|
11
|
+
elements?: V1ElementData[];
|
|
12
|
+
settings?: V1ElementSettingsProps;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function reconcileInitialChildren( {
|
|
17
|
+
elementId,
|
|
18
|
+
elementConfig,
|
|
19
|
+
attributes,
|
|
20
|
+
}: ReconcileInitialChildrenArgs ): void {
|
|
21
|
+
const stash = createChildrenStash();
|
|
22
|
+
const rules = elementConfig?.children_dependencies;
|
|
23
|
+
|
|
24
|
+
if ( ! rules?.length ) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const elements = [ ...( attributes.elements ?? [] ) ];
|
|
29
|
+
const settings = attributes.settings ?? {};
|
|
30
|
+
|
|
31
|
+
rules.forEach( ( rule: ChildDependencyRule ) => {
|
|
32
|
+
const isMet = evaluateWhen( rule.when, settings );
|
|
33
|
+
const existingIndex = elements.findIndex( ( element ) => element.elType === rule.child_type );
|
|
34
|
+
const isPresent = existingIndex >= 0;
|
|
35
|
+
|
|
36
|
+
if ( isMet && ! isPresent ) {
|
|
37
|
+
const { modelData, wasStashed } = resolveChildModelData( elementId, rule, stash );
|
|
38
|
+
const insertAt = resolveInsertIndex( rule.position, elements );
|
|
39
|
+
|
|
40
|
+
elements.splice( insertAt, 0, modelData );
|
|
41
|
+
|
|
42
|
+
if ( wasStashed ) {
|
|
43
|
+
stash.clear( elementId, rule.child_type );
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if ( ! isMet && isPresent ) {
|
|
50
|
+
const [ removed ] = elements.splice( existingIndex, 1 );
|
|
51
|
+
|
|
52
|
+
if ( rule.stash && removed ) {
|
|
53
|
+
stash.save( elementId, rule.child_type, removed );
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} );
|
|
57
|
+
|
|
58
|
+
attributes.elements = elements;
|
|
59
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getSessionStorageItem, removeSessionStorageItem, setSessionStorageItem } from '@elementor/session';
|
|
2
|
+
|
|
3
|
+
import { type V1ElementData } from '../sync/types';
|
|
4
|
+
import { type ChildrenStash } from './types';
|
|
5
|
+
|
|
6
|
+
const STASH_KEY_PREFIX = 'elementor/editor-state';
|
|
7
|
+
const STASH_KEY_SEGMENT = 'children-deps';
|
|
8
|
+
|
|
9
|
+
export function createChildrenStash(): ChildrenStash {
|
|
10
|
+
return {
|
|
11
|
+
get( elementId, childType ) {
|
|
12
|
+
return getSessionStorageItem< V1ElementData >( buildStashKey( elementId, childType ) );
|
|
13
|
+
},
|
|
14
|
+
save( elementId, childType, data ) {
|
|
15
|
+
setSessionStorageItem( buildStashKey( elementId, childType ), data );
|
|
16
|
+
},
|
|
17
|
+
clear( elementId, childType ) {
|
|
18
|
+
removeSessionStorageItem( buildStashKey( elementId, childType ) );
|
|
19
|
+
},
|
|
20
|
+
clearAllForElement( elementId ) {
|
|
21
|
+
const prefix = buildElementStashPrefix( elementId );
|
|
22
|
+
|
|
23
|
+
for ( let index = sessionStorage.length - 1; index >= 0; index-- ) {
|
|
24
|
+
const key = sessionStorage.key( index );
|
|
25
|
+
|
|
26
|
+
if ( key?.startsWith( prefix ) ) {
|
|
27
|
+
removeSessionStorageItem( key );
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildStashKey( elementId: string, childType: string ): string {
|
|
35
|
+
return `${ STASH_KEY_PREFIX }/${ elementId }/${ STASH_KEY_SEGMENT }/${ childType }`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function buildElementStashPrefix( elementId: string ): string {
|
|
39
|
+
return `${ STASH_KEY_PREFIX }/${ elementId }/${ STASH_KEY_SEGMENT }/`;
|
|
40
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Dependency } from '@elementor/editor-props';
|
|
2
|
+
|
|
3
|
+
import { type ElementPosition, type V1ElementData } from '../sync/types';
|
|
4
|
+
|
|
5
|
+
export type ChildDependencyRule = {
|
|
6
|
+
child_type: string;
|
|
7
|
+
when: Dependency;
|
|
8
|
+
position: ElementPosition;
|
|
9
|
+
stash: boolean;
|
|
10
|
+
default_model: V1ElementData | null;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type ChildDependenciesConfig = ChildDependencyRule[];
|
|
14
|
+
|
|
15
|
+
export type ChildrenStash = {
|
|
16
|
+
get: ( elementId: string, childType: string ) => V1ElementData | undefined;
|
|
17
|
+
save: ( elementId: string, childType: string, data: V1ElementData ) => void;
|
|
18
|
+
clear: ( elementId: string, childType: string ) => void;
|
|
19
|
+
clearAllForElement: ( elementId: string ) => void;
|
|
20
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type Dependency, isDependencyMet, type PropValue } from '@elementor/editor-props';
|
|
2
|
+
|
|
3
|
+
import { generateElementId } from '../sync/generate-element-id';
|
|
4
|
+
import { type V1ElementData } from '../sync/types';
|
|
5
|
+
import { type ChildDependencyRule, type ChildrenStash } from './types';
|
|
6
|
+
|
|
7
|
+
export function evaluateWhen( when: Dependency | undefined, settings: Record< string, PropValue > ): boolean {
|
|
8
|
+
return isDependencyMet( when, settings ).isMet;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function ensureModelId( model: V1ElementData ): V1ElementData {
|
|
12
|
+
const { skipDefaultChildren: _skipDefaultChildren, ...rest } = model;
|
|
13
|
+
|
|
14
|
+
return rest.id ? rest : { ...rest, id: generateElementId() };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type ResolvedChildModel = {
|
|
18
|
+
modelData: V1ElementData;
|
|
19
|
+
wasStashed: boolean;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the model to attach for a child-dependency rule, preferring a previously-stashed
|
|
24
|
+
* (user-customized) model, then the rule's default, else a bare element.
|
|
25
|
+
*
|
|
26
|
+
* `default_model` and the bare fallback never carry an id, so they're run through
|
|
27
|
+
* `ensureModelId` before the model reaches Backbone.
|
|
28
|
+
*
|
|
29
|
+
* @param elementId Parent element id, used as the stash lookup key.
|
|
30
|
+
* @param rule The child-dependency rule being resolved.
|
|
31
|
+
* @param stash Session-backed stash for previously-detached child models.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveChildModelData(
|
|
34
|
+
elementId: string,
|
|
35
|
+
rule: ChildDependencyRule,
|
|
36
|
+
stash: ChildrenStash
|
|
37
|
+
): ResolvedChildModel {
|
|
38
|
+
const stashed = rule.stash ? stash.get( elementId, rule.child_type ) : undefined;
|
|
39
|
+
const modelData = ensureModelId(
|
|
40
|
+
stashed ?? rule.default_model ?? ( { elType: rule.child_type } as V1ElementData )
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return { modelData, wasStashed: Boolean( stashed ) };
|
|
44
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
export * from './types';
|
|
3
3
|
export type * from './sync/types';
|
|
4
4
|
|
|
5
|
+
// children dependencies (schema-driven settings <-> children sync)
|
|
6
|
+
export {
|
|
7
|
+
bindSettingsReconcile,
|
|
8
|
+
type ChildDependenciesConfig,
|
|
9
|
+
type ChildDependencyRule,
|
|
10
|
+
evaluateWhen,
|
|
11
|
+
reconcileInitialChildren,
|
|
12
|
+
} from './children-dependencies';
|
|
13
|
+
|
|
5
14
|
// hooks
|
|
6
15
|
export { useElementChildren, type ElementChildren, type ElementModel } from './hooks/use-element-children';
|
|
7
16
|
export { useElementEditorSettings } from './hooks/use-element-editor-settings';
|
|
@@ -27,7 +36,9 @@ export { addModelToParent, findModelInDocument, removeModelFromParent, resolveCo
|
|
|
27
36
|
export { getCurrentDocumentContainer } from './sync/get-current-document-container';
|
|
28
37
|
export { getCurrentDocumentId } from './sync/get-current-document-id';
|
|
29
38
|
export { getElementEditorSettings } from './sync/get-element-editor-settings';
|
|
39
|
+
export { getElementIcon } from './sync/get-element-icon';
|
|
30
40
|
export { getElementLabel } from './sync/get-element-label';
|
|
41
|
+
export { getElementTitle } from './sync/get-element-title';
|
|
31
42
|
export { getElementSetting, getElementSettings } from './sync/get-element-setting';
|
|
32
43
|
export { getElementStyles } from './sync/get-element-styles';
|
|
33
44
|
export { getElementType } from './sync/get-element-type';
|
|
@@ -44,6 +55,7 @@ export { moveElement, type MoveElementParams } from './sync/move-element';
|
|
|
44
55
|
export { moveElements } from './sync/move-elements';
|
|
45
56
|
export { removeElements } from './sync/remove-elements';
|
|
46
57
|
export { replaceElement } from './sync/replace-element';
|
|
58
|
+
export { resolveInsertIndex } from './sync/resolve-insert-index';
|
|
47
59
|
export { updateElementEditorSettings } from './sync/update-element-editor-settings';
|
|
48
60
|
export { updateElementSettings, type UpdateElementSettingsArgs } from './sync/update-element-settings';
|
|
49
61
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ElementID } from '../types';
|
|
2
|
+
import { getContainer } from './get-container';
|
|
3
|
+
import { getWidgetsCache } from './get-widgets-cache';
|
|
4
|
+
|
|
5
|
+
export function getElementIcon( elementId: ElementID ): string | null {
|
|
6
|
+
const container = getContainer( elementId );
|
|
7
|
+
const type = container?.model.get( 'widgetType' ) || container?.model.get( 'elType' );
|
|
8
|
+
|
|
9
|
+
if ( ! type ) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return getWidgetsCache()?.[ type ]?.icon ?? null;
|
|
14
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type ElementID } from '../types';
|
|
2
|
+
import { getElementEditorSettings } from './get-element-editor-settings';
|
|
3
|
+
import { getElementLabel } from './get-element-label';
|
|
4
|
+
import { getElementSetting } from './get-element-setting';
|
|
5
|
+
|
|
6
|
+
function extractString( value: unknown ): string | null {
|
|
7
|
+
if ( typeof value === 'string' ) {
|
|
8
|
+
return value || null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (
|
|
12
|
+
value &&
|
|
13
|
+
typeof value === 'object' &&
|
|
14
|
+
'value' in value &&
|
|
15
|
+
typeof ( value as { value: unknown } ).value === 'string'
|
|
16
|
+
) {
|
|
17
|
+
return ( value as { value: string } ).value || null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getElementTitle( elementId: ElementID ): string | null {
|
|
24
|
+
const editorSettings = getElementEditorSettings( elementId );
|
|
25
|
+
const editorTitle = extractString( editorSettings?.title );
|
|
26
|
+
|
|
27
|
+
if ( editorTitle ) {
|
|
28
|
+
return editorTitle;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const legacyTitle = extractString( getElementSetting( elementId, '_title' ) );
|
|
32
|
+
|
|
33
|
+
if ( legacyTitle ) {
|
|
34
|
+
return legacyTitle;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const presetTitle = extractString( getElementSetting( elementId, 'presetTitle' ) );
|
|
38
|
+
|
|
39
|
+
if ( presetTitle ) {
|
|
40
|
+
return presetTitle;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
return getElementLabel( elementId );
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type ElementPosition, type V1ElementData } from './types';
|
|
2
|
+
|
|
3
|
+
export function resolveInsertIndex( position: ElementPosition, elements: V1ElementData[] ): number {
|
|
4
|
+
const lastIndex = elements.length;
|
|
5
|
+
|
|
6
|
+
switch ( position.kind ) {
|
|
7
|
+
case 'first':
|
|
8
|
+
return 0;
|
|
9
|
+
|
|
10
|
+
case 'last':
|
|
11
|
+
return lastIndex;
|
|
12
|
+
|
|
13
|
+
case 'index': {
|
|
14
|
+
const index = typeof position.value === 'number' ? position.value : lastIndex;
|
|
15
|
+
|
|
16
|
+
return Math.max( 0, Math.min( index, lastIndex ) );
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
case 'after_type': {
|
|
20
|
+
const anchor = elements.findIndex( ( element ) => element.elType === position.value );
|
|
21
|
+
|
|
22
|
+
return anchor >= 0 ? anchor + 1 : lastIndex;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
case 'before_type': {
|
|
26
|
+
const anchor = elements.findIndex( ( element ) => element.elType === position.value );
|
|
27
|
+
|
|
28
|
+
return anchor >= 0 ? anchor : lastIndex;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
default:
|
|
32
|
+
return lastIndex;
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/sync/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type PropsSchema, type PropValue, type SizePropValue } from '@elementor/editor-props';
|
|
2
2
|
import { type ClassState, type StyleDefinition, type StyleDefinitionID } from '@elementor/editor-styles';
|
|
3
3
|
|
|
4
|
+
import { type ChildDependencyRule } from '../children-dependencies/types';
|
|
4
5
|
import { type ControlItem, type PseudoState } from '../types';
|
|
5
6
|
|
|
6
7
|
export type ExtendedWindow = Window & {
|
|
@@ -162,16 +163,25 @@ export type V1ElementModelProps = {
|
|
|
162
163
|
interactions?: string | ElementInteractions;
|
|
163
164
|
isGlobal?: boolean;
|
|
164
165
|
skipDefaultChildren?: boolean;
|
|
166
|
+
hydrateDefaultChildren?: boolean;
|
|
165
167
|
};
|
|
166
168
|
|
|
167
169
|
export type V1ElementData = Omit< V1ElementModelProps, 'elements' > & {
|
|
168
170
|
elements?: V1ElementData[];
|
|
169
171
|
};
|
|
170
172
|
|
|
173
|
+
export type ElementPositionKind = 'last' | 'first' | 'index' | 'after_type' | 'before_type';
|
|
174
|
+
|
|
175
|
+
export type ElementPosition = {
|
|
176
|
+
kind: ElementPositionKind;
|
|
177
|
+
value: number | string | null;
|
|
178
|
+
};
|
|
179
|
+
|
|
171
180
|
export type V1ElementEditorSettingsProps = {
|
|
172
181
|
title?: string;
|
|
173
182
|
initial_position?: number;
|
|
174
183
|
component_uid?: string;
|
|
184
|
+
grid_outline?: boolean;
|
|
175
185
|
};
|
|
176
186
|
|
|
177
187
|
export type V1ElementSettingsProps = Record< string, PropValue >;
|
|
@@ -196,6 +206,7 @@ export type V1ElementConfig< T = object, TChild = unknown > = {
|
|
|
196
206
|
show_in_panel?: boolean;
|
|
197
207
|
allowed_child_types?: string[];
|
|
198
208
|
default_children?: Array< Record< string, TChild > >;
|
|
209
|
+
children_dependencies?: ChildDependencyRule[];
|
|
199
210
|
meta?: { [ key: string ]: string | number | boolean | null | NonNullable< V1ElementConfig[ 'meta' ] > };
|
|
200
211
|
} & T;
|
|
201
212
|
|
|
@@ -204,4 +215,6 @@ type V1Model< T > = {
|
|
|
204
215
|
set: < K extends keyof T >( key: K, value: T[ K ] ) => void;
|
|
205
216
|
toJSON: ( options?: { remove?: string[] } ) => T;
|
|
206
217
|
trigger?: ( event: string, ...args: unknown[] ) => void;
|
|
218
|
+
on?: ( event: string, callback: ( ...args: unknown[] ) => void, context?: unknown ) => void;
|
|
219
|
+
off?: ( event: string, callback?: ( ...args: unknown[] ) => void, context?: unknown ) => void;
|
|
207
220
|
};
|