@elementor/editor-controls 3.32.0-22 → 3.32.0-24
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 +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +289 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +288 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +17 -15
- package/src/components/css-code-editor/css-editor.styles.ts +45 -0
- package/src/components/css-code-editor/css-editor.tsx +169 -0
- package/src/components/css-code-editor/css-validation.ts +62 -0
- package/src/components/css-code-editor/resize-handle.tsx +55 -0
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-controls",
|
|
3
3
|
"description": "This package contains the controls model and utils for the Elementor editor",
|
|
4
|
-
"version": "3.32.0-
|
|
4
|
+
"version": "3.32.0-24",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -40,24 +40,26 @@
|
|
|
40
40
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@elementor/editor-current-user": "3.32.0-
|
|
44
|
-
"@elementor/editor-elements": "3.32.0-
|
|
45
|
-
"@elementor/editor-props": "3.32.0-
|
|
46
|
-
"@elementor/editor-responsive": "3.32.0-
|
|
47
|
-
"@elementor/editor-ui": "3.32.0-
|
|
48
|
-
"@elementor/editor-v1-adapters": "3.32.0-
|
|
49
|
-
"@elementor/env": "3.32.0-
|
|
50
|
-
"@elementor/http-client": "3.32.0-
|
|
43
|
+
"@elementor/editor-current-user": "3.32.0-24",
|
|
44
|
+
"@elementor/editor-elements": "3.32.0-24",
|
|
45
|
+
"@elementor/editor-props": "3.32.0-24",
|
|
46
|
+
"@elementor/editor-responsive": "3.32.0-24",
|
|
47
|
+
"@elementor/editor-ui": "3.32.0-24",
|
|
48
|
+
"@elementor/editor-v1-adapters": "3.32.0-24",
|
|
49
|
+
"@elementor/env": "3.32.0-24",
|
|
50
|
+
"@elementor/http-client": "3.32.0-24",
|
|
51
51
|
"@elementor/icons": "^1.51.1",
|
|
52
|
-
"@elementor/locations": "3.32.0-
|
|
53
|
-
"@elementor/query": "3.32.0-
|
|
54
|
-
"@elementor/session": "3.32.0-
|
|
52
|
+
"@elementor/locations": "3.32.0-24",
|
|
53
|
+
"@elementor/query": "3.32.0-24",
|
|
54
|
+
"@elementor/session": "3.32.0-24",
|
|
55
55
|
"@elementor/ui": "1.36.2",
|
|
56
|
-
"@elementor/utils": "3.32.0-
|
|
57
|
-
"@elementor/wp-media": "3.32.0-
|
|
58
|
-
"@wordpress/i18n": "^5.13.0"
|
|
56
|
+
"@elementor/utils": "3.32.0-24",
|
|
57
|
+
"@elementor/wp-media": "3.32.0-24",
|
|
58
|
+
"@wordpress/i18n": "^5.13.0",
|
|
59
|
+
"@monaco-editor/react": "^4.7.0"
|
|
59
60
|
},
|
|
60
61
|
"devDependencies": {
|
|
62
|
+
"monaco-types": "^0.1.0",
|
|
61
63
|
"tsup": "^8.3.5"
|
|
62
64
|
},
|
|
63
65
|
"peerDependencies": {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Box, Button, styled } from '@elementor/ui';
|
|
2
|
+
|
|
3
|
+
export const EditorWrapper = styled( Box )`
|
|
4
|
+
border: 1px solid var( --e-a-border-color );
|
|
5
|
+
border-radius: 8px;
|
|
6
|
+
padding: 10px 12px;
|
|
7
|
+
position: relative;
|
|
8
|
+
height: 200px;
|
|
9
|
+
|
|
10
|
+
.monaco-editor .colorpicker-widget {
|
|
11
|
+
z-index: 99999999 !important;
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export const ResizeHandle = styled( Button )`
|
|
16
|
+
position: absolute;
|
|
17
|
+
bottom: 0;
|
|
18
|
+
left: 0;
|
|
19
|
+
right: 0;
|
|
20
|
+
height: 6px;
|
|
21
|
+
cursor: ns-resize;
|
|
22
|
+
background: transparent;
|
|
23
|
+
border: none;
|
|
24
|
+
padding: 0;
|
|
25
|
+
|
|
26
|
+
&:hover {
|
|
27
|
+
background: rgba( 0, 0, 0, 0.05 );
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&:active {
|
|
31
|
+
background: rgba( 0, 0, 0, 0.1 );
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&::after {
|
|
35
|
+
content: '';
|
|
36
|
+
position: absolute;
|
|
37
|
+
top: 50%;
|
|
38
|
+
left: 50%;
|
|
39
|
+
transform: translate( -50%, -50% );
|
|
40
|
+
width: 30px;
|
|
41
|
+
height: 2px;
|
|
42
|
+
background: var( --e-a-border-color );
|
|
43
|
+
border-radius: 1px;
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { editor, MonacoEditor } from 'monaco-types';
|
|
3
|
+
import { useActiveBreakpoint } from '@elementor/editor-responsive';
|
|
4
|
+
import { useTheme } from '@elementor/ui';
|
|
5
|
+
import { Editor } from '@monaco-editor/react';
|
|
6
|
+
|
|
7
|
+
import { EditorWrapper } from './css-editor.styles';
|
|
8
|
+
import { setCustomSyntaxRules, validate } from './css-validation';
|
|
9
|
+
import { ResizeHandleComponent } from './resize-handle';
|
|
10
|
+
|
|
11
|
+
type CssEditorProps = {
|
|
12
|
+
value: string;
|
|
13
|
+
onChange: ( value: string ) => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const setVisualContent = ( value: string ): string => {
|
|
17
|
+
const trimmed = value.trim();
|
|
18
|
+
return `element.style {\n${ trimmed ? ' ' + trimmed.replace( /\n/g, '\n ' ) + '\n' : ' \n' }}`;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const getActual = ( value: string ): string => {
|
|
22
|
+
const lines = value.split( '\n' );
|
|
23
|
+
if ( lines.length < 2 ) {
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
return lines
|
|
27
|
+
.slice( 1, -1 )
|
|
28
|
+
.map( ( line ) => line.replace( /^ {2}/, '' ) )
|
|
29
|
+
.join( '\n' );
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const preventChangeOnVisualContent = ( editor: editor.IStandaloneCodeEditor, monaco: MonacoEditor ) => {
|
|
33
|
+
const model = editor.getModel();
|
|
34
|
+
if ( ! model ) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
editor.onKeyDown( ( e ) => {
|
|
39
|
+
const position = editor.getPosition();
|
|
40
|
+
if ( ! position ) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const totalLines = model.getLineCount();
|
|
45
|
+
const isInProtectedRange = position.lineNumber === 1 || position.lineNumber === totalLines;
|
|
46
|
+
|
|
47
|
+
if ( isInProtectedRange ) {
|
|
48
|
+
const allowedKeys = [
|
|
49
|
+
monaco.KeyCode.UpArrow,
|
|
50
|
+
monaco.KeyCode.DownArrow,
|
|
51
|
+
monaco.KeyCode.LeftArrow,
|
|
52
|
+
monaco.KeyCode.RightArrow,
|
|
53
|
+
monaco.KeyCode.Home,
|
|
54
|
+
monaco.KeyCode.End,
|
|
55
|
+
monaco.KeyCode.PageUp,
|
|
56
|
+
monaco.KeyCode.PageDown,
|
|
57
|
+
monaco.KeyCode.Tab,
|
|
58
|
+
monaco.KeyCode.Escape,
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
if ( ! allowedKeys.includes( e.keyCode ) ) {
|
|
62
|
+
e.preventDefault();
|
|
63
|
+
e.stopPropagation();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} );
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const createEditorDidMountHandler = (
|
|
70
|
+
editorRef: React.MutableRefObject< editor.IStandaloneCodeEditor | null >,
|
|
71
|
+
monacoRef: React.MutableRefObject< MonacoEditor | null >,
|
|
72
|
+
debounceTimer: React.MutableRefObject< NodeJS.Timeout | null >,
|
|
73
|
+
onChange: ( value: string ) => void
|
|
74
|
+
) => {
|
|
75
|
+
return ( editor: editor.IStandaloneCodeEditor, monaco: MonacoEditor ) => {
|
|
76
|
+
editorRef.current = editor;
|
|
77
|
+
monacoRef.current = monaco;
|
|
78
|
+
|
|
79
|
+
preventChangeOnVisualContent( editor, monaco );
|
|
80
|
+
|
|
81
|
+
setCustomSyntaxRules( editor, monaco );
|
|
82
|
+
|
|
83
|
+
editor.onDidChangeModelContent( () => {
|
|
84
|
+
const code = editor.getModel()?.getValue() ?? '';
|
|
85
|
+
const userContent = getActual( code );
|
|
86
|
+
|
|
87
|
+
setCustomSyntaxRules( editor, monaco );
|
|
88
|
+
|
|
89
|
+
const currentTimer = debounceTimer.current;
|
|
90
|
+
if ( currentTimer ) {
|
|
91
|
+
clearTimeout( currentTimer );
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const newTimer = setTimeout( () => {
|
|
95
|
+
if ( ! editorRef.current || ! monacoRef.current ) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const hasNoErrors = validate( editorRef.current, monacoRef.current );
|
|
100
|
+
|
|
101
|
+
if ( hasNoErrors ) {
|
|
102
|
+
onChange( userContent );
|
|
103
|
+
}
|
|
104
|
+
}, 500 );
|
|
105
|
+
|
|
106
|
+
debounceTimer.current = newTimer;
|
|
107
|
+
} );
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const CssEditor = ( { value, onChange }: CssEditorProps ) => {
|
|
112
|
+
const theme = useTheme();
|
|
113
|
+
const containerRef = React.useRef< HTMLDivElement >( null );
|
|
114
|
+
const editorRef = React.useRef< editor.IStandaloneCodeEditor | null >( null );
|
|
115
|
+
const monacoRef = React.useRef< MonacoEditor | null >( null );
|
|
116
|
+
const debounceTimer = React.useRef< NodeJS.Timeout | null >( null );
|
|
117
|
+
const activeBreakpoint = useActiveBreakpoint();
|
|
118
|
+
|
|
119
|
+
const handleResize = React.useCallback( () => {
|
|
120
|
+
editorRef.current?.layout();
|
|
121
|
+
}, [] );
|
|
122
|
+
|
|
123
|
+
const handleHeightChange = React.useCallback( ( height: number ) => {
|
|
124
|
+
if ( containerRef.current ) {
|
|
125
|
+
containerRef.current.style.height = `${ height }px`;
|
|
126
|
+
}
|
|
127
|
+
}, [] );
|
|
128
|
+
|
|
129
|
+
const handleEditorDidMount = createEditorDidMountHandler( editorRef, monacoRef, debounceTimer, onChange );
|
|
130
|
+
|
|
131
|
+
React.useEffect( () => {
|
|
132
|
+
const timerRef = debounceTimer;
|
|
133
|
+
return () => {
|
|
134
|
+
const timer = timerRef.current;
|
|
135
|
+
if ( timer ) {
|
|
136
|
+
clearTimeout( timer );
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}, [] );
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<EditorWrapper ref={ containerRef }>
|
|
143
|
+
<Editor
|
|
144
|
+
key={ activeBreakpoint }
|
|
145
|
+
height="100%"
|
|
146
|
+
language="css"
|
|
147
|
+
theme={ theme.palette.mode === 'dark' ? 'vs-dark' : 'vs' }
|
|
148
|
+
defaultValue={ setVisualContent( value ) }
|
|
149
|
+
onMount={ handleEditorDidMount }
|
|
150
|
+
options={ {
|
|
151
|
+
lineNumbers: 'off',
|
|
152
|
+
folding: false,
|
|
153
|
+
showFoldingControls: 'never',
|
|
154
|
+
minimap: { enabled: false },
|
|
155
|
+
fontFamily: 'Roboto, Arial, Helvetica, Verdana, sans-serif',
|
|
156
|
+
fontSize: 12,
|
|
157
|
+
renderLineHighlight: 'none',
|
|
158
|
+
hideCursorInOverviewRuler: true,
|
|
159
|
+
fixedOverflowWidgets: true,
|
|
160
|
+
} }
|
|
161
|
+
/>
|
|
162
|
+
<ResizeHandleComponent
|
|
163
|
+
onResize={ handleResize }
|
|
164
|
+
containerRef={ containerRef }
|
|
165
|
+
onHeightChange={ handleHeightChange }
|
|
166
|
+
/>
|
|
167
|
+
</EditorWrapper>
|
|
168
|
+
);
|
|
169
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { editor, MonacoEditor } from 'monaco-types';
|
|
2
|
+
import { __ } from '@wordpress/i18n';
|
|
3
|
+
|
|
4
|
+
const forbiddenPatterns = [
|
|
5
|
+
{
|
|
6
|
+
pattern: ':hover',
|
|
7
|
+
message: __(
|
|
8
|
+
'The use of pseudo-states is not permitted. Instead, switch to the desired pseudo state and add your custom code there.',
|
|
9
|
+
'elementor'
|
|
10
|
+
),
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
pattern: ':active',
|
|
14
|
+
message: __(
|
|
15
|
+
'The use of pseudo-states is not permitted. Instead, switch to the desired pseudo state and add your custom code there.',
|
|
16
|
+
'elementor'
|
|
17
|
+
),
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
pattern: '@media',
|
|
21
|
+
message: __(
|
|
22
|
+
'The use of @media is not permitted. Instead, switch to the desired breakpoint and add your custom code there.',
|
|
23
|
+
'elementor'
|
|
24
|
+
),
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export function setCustomSyntaxRules( editor: editor.IStandaloneCodeEditor, monaco: MonacoEditor ): boolean {
|
|
29
|
+
const model = editor.getModel();
|
|
30
|
+
if ( ! model ) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const customMarkers: editor.IMarkerData[] = [];
|
|
35
|
+
|
|
36
|
+
forbiddenPatterns.forEach( ( rule ) => {
|
|
37
|
+
const matches = model.findMatches( rule.pattern, true, false, true, null, true );
|
|
38
|
+
matches.forEach( ( match ) => {
|
|
39
|
+
customMarkers.push( {
|
|
40
|
+
severity: monaco.MarkerSeverity.Error,
|
|
41
|
+
message: rule.message,
|
|
42
|
+
startLineNumber: match.range.startLineNumber,
|
|
43
|
+
startColumn: match.range.startColumn,
|
|
44
|
+
endLineNumber: match.range.endLineNumber,
|
|
45
|
+
endColumn: match.range.endColumn,
|
|
46
|
+
source: 'custom-css-rules',
|
|
47
|
+
} );
|
|
48
|
+
} );
|
|
49
|
+
} );
|
|
50
|
+
|
|
51
|
+
monaco.editor.setModelMarkers( model, 'custom-css-rules', customMarkers );
|
|
52
|
+
return customMarkers.length === 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function validate( editor: editor.IStandaloneCodeEditor, monaco: MonacoEditor ): boolean {
|
|
56
|
+
const model = editor.getModel();
|
|
57
|
+
if ( ! model ) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
const allMarkers = monaco.editor.getModelMarkers( { resource: model.uri } );
|
|
61
|
+
return allMarkers.filter( ( marker ) => marker.severity === monaco.MarkerSeverity.Error ).length === 0;
|
|
62
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { ResizeHandle } from './css-editor.styles';
|
|
4
|
+
|
|
5
|
+
type ResizeHandleProps = {
|
|
6
|
+
onResize: ( height: number ) => void;
|
|
7
|
+
containerRef: React.RefObject< HTMLDivElement >;
|
|
8
|
+
onHeightChange?: ( height: number ) => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const ResizeHandleComponent = ( { onResize, containerRef, onHeightChange }: ResizeHandleProps ) => {
|
|
12
|
+
const handleResizeMove = React.useCallback(
|
|
13
|
+
( e: MouseEvent ) => {
|
|
14
|
+
const container = containerRef.current;
|
|
15
|
+
if ( ! container ) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const containerRect = container.getBoundingClientRect();
|
|
19
|
+
const newHeight = Math.max( 100, e.clientY - containerRect.top );
|
|
20
|
+
onHeightChange?.( newHeight );
|
|
21
|
+
onResize( newHeight );
|
|
22
|
+
},
|
|
23
|
+
[ containerRef, onResize, onHeightChange ]
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const handleResizeEnd = React.useCallback( () => {
|
|
27
|
+
document.removeEventListener( 'mousemove', handleResizeMove );
|
|
28
|
+
document.removeEventListener( 'mouseup', handleResizeEnd );
|
|
29
|
+
}, [ handleResizeMove ] );
|
|
30
|
+
|
|
31
|
+
const handleResizeStart = React.useCallback(
|
|
32
|
+
( e: React.MouseEvent ) => {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
e.stopPropagation();
|
|
35
|
+
document.addEventListener( 'mousemove', handleResizeMove );
|
|
36
|
+
document.addEventListener( 'mouseup', handleResizeEnd );
|
|
37
|
+
},
|
|
38
|
+
[ handleResizeMove, handleResizeEnd ]
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
React.useEffect( () => {
|
|
42
|
+
return () => {
|
|
43
|
+
document.removeEventListener( 'mousemove', handleResizeMove );
|
|
44
|
+
document.removeEventListener( 'mouseup', handleResizeEnd );
|
|
45
|
+
};
|
|
46
|
+
}, [ handleResizeMove, handleResizeEnd ] );
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<ResizeHandle
|
|
50
|
+
onMouseDown={ handleResizeStart }
|
|
51
|
+
aria-label="Resize editor height"
|
|
52
|
+
title="Drag to resize editor height"
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -34,6 +34,7 @@ export { transitionProperties, transitionsItemsList } from './controls/transitio
|
|
|
34
34
|
// components
|
|
35
35
|
export { ControlFormLabel } from './components/control-form-label';
|
|
36
36
|
export { ControlToggleButtonGroup } from './components/control-toggle-button-group';
|
|
37
|
+
export { CssEditor } from './components/css-code-editor/css-editor';
|
|
37
38
|
|
|
38
39
|
// types
|
|
39
40
|
export type { ControlComponent } from './create-control';
|