@elementor/editor-editing-panel 4.1.0-822 → 4.1.0-824

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/editor-editing-panel",
3
- "version": "4.1.0-822",
3
+ "version": "4.1.0-824",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,28 +39,31 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "4.1.0-822",
43
- "@elementor/editor-canvas": "4.1.0-822",
44
- "@elementor/editor-controls": "4.1.0-822",
45
- "@elementor/editor-documents": "4.1.0-822",
46
- "@elementor/editor-elements": "4.1.0-822",
47
- "@elementor/editor-interactions": "4.1.0-822",
48
- "@elementor/editor-panels": "4.1.0-822",
49
- "@elementor/editor-props": "4.1.0-822",
50
- "@elementor/editor-responsive": "4.1.0-822",
51
- "@elementor/editor-styles": "4.1.0-822",
52
- "@elementor/editor-styles-repository": "4.1.0-822",
53
- "@elementor/editor-ui": "4.1.0-822",
54
- "@elementor/editor-v1-adapters": "4.1.0-822",
42
+ "@elementor/editor": "4.1.0-824",
43
+ "@elementor/editor-canvas": "4.1.0-824",
44
+ "@elementor/editor-controls": "4.1.0-824",
45
+ "@elementor/editor-documents": "4.1.0-824",
46
+ "@elementor/editor-elements": "4.1.0-824",
47
+ "@elementor/editor-interactions": "4.1.0-824",
48
+ "@elementor/editor-notifications": "4.1.0-824",
49
+ "@elementor/editor-panels": "4.1.0-824",
50
+ "@elementor/editor-props": "4.1.0-824",
51
+ "@elementor/editor-responsive": "4.1.0-824",
52
+ "@elementor/editor-styles": "4.1.0-824",
53
+ "@elementor/editor-styles-repository": "4.1.0-824",
54
+ "@elementor/editor-ui": "4.1.0-824",
55
+ "@elementor/editor-v1-adapters": "4.1.0-824",
56
+ "@elementor/http-client": "4.1.0-824",
55
57
  "@elementor/icons": "^1.68.0",
56
- "@elementor/editor-variables": "4.1.0-822",
57
- "@elementor/locations": "4.1.0-822",
58
- "@elementor/menus": "4.1.0-822",
59
- "@elementor/schema": "4.1.0-822",
60
- "@elementor/session": "4.1.0-822",
58
+ "@elementor/editor-variables": "4.1.0-824",
59
+ "@elementor/locations": "4.1.0-824",
60
+ "@elementor/menus": "4.1.0-824",
61
+ "@elementor/query": "4.1.0-824",
62
+ "@elementor/schema": "4.1.0-824",
63
+ "@elementor/session": "4.1.0-824",
61
64
  "@elementor/ui": "1.37.5",
62
- "@elementor/utils": "4.1.0-822",
63
- "@elementor/wp-media": "4.1.0-822",
65
+ "@elementor/utils": "4.1.0-824",
66
+ "@elementor/wp-media": "4.1.0-824",
64
67
  "@wordpress/i18n": "^5.13.0"
65
68
  },
66
69
  "peerDependencies": {
@@ -0,0 +1,67 @@
1
+ import * as React from 'react';
2
+ import { Card, CardActionArea, Radio, RadioGroup, Stack, Typography } from '@elementor/ui';
3
+ import { __ } from '@wordpress/i18n';
4
+
5
+ import { type ConflictStrategy } from '../types';
6
+
7
+ type Option = {
8
+ value: ConflictStrategy;
9
+ title: string;
10
+ description: string;
11
+ };
12
+
13
+ const getOptions = (): Option[] => [
14
+ {
15
+ value: 'replace',
16
+ title: __( 'Replace existing values', 'elementor' ),
17
+ description: __( 'Imported design system values will overwrite existing variables and classes.', 'elementor' ),
18
+ },
19
+ {
20
+ value: 'keep',
21
+ title: __( 'Keep existing values', 'elementor' ),
22
+ description: __( 'Existing variables and classes will not change.', 'elementor' ),
23
+ },
24
+ ];
25
+
26
+ type Props = {
27
+ value: ConflictStrategy | null;
28
+ onChange: ( value: ConflictStrategy ) => void;
29
+ };
30
+
31
+ export const ConflictOptions = ( { value, onChange }: Props ) => {
32
+ const options = getOptions();
33
+
34
+ return (
35
+ <Stack spacing={ 1 }>
36
+ <Typography variant="body1">
37
+ { __( 'How to handle conflicts with existing variables or classes?', 'elementor' ) }
38
+ </Typography>
39
+ <RadioGroup
40
+ value={ value ?? '' }
41
+ onChange={ ( _: unknown, next: string ) => onChange( next as ConflictStrategy ) }
42
+ >
43
+ <Stack spacing={ 1 }>
44
+ { options.map( ( option ) => (
45
+ <Card key={ option.value } variant="outlined">
46
+ <CardActionArea onClick={ () => onChange( option.value ) }>
47
+ <Stack direction="row" alignItems="center" spacing={ 2 } padding={ 2 }>
48
+ <Radio
49
+ value={ option.value }
50
+ checked={ value === option.value }
51
+ inputProps={ { 'aria-label': option.title } }
52
+ />
53
+ <Stack direction="column" spacing={ 0.5 }>
54
+ <Typography variant="subtitle2">{ option.title }</Typography>
55
+ <Typography variant="caption" color="text.secondary">
56
+ { option.description }
57
+ </Typography>
58
+ </Stack>
59
+ </Stack>
60
+ </CardActionArea>
61
+ </Card>
62
+ ) ) }
63
+ </Stack>
64
+ </RadioGroup>
65
+ </Stack>
66
+ );
67
+ };
@@ -0,0 +1,33 @@
1
+ import * as React from 'react';
2
+ import { closeDialog, openDialog } from '@elementor/editor-ui';
3
+ import { UploadIcon } from '@elementor/icons';
4
+ import { useIsMutating } from '@elementor/query';
5
+ import { IconButton, Tooltip } from '@elementor/ui';
6
+ import { __ } from '@wordpress/i18n';
7
+
8
+ import { IMPORT_DESIGN_SYSTEM_MUTATION_KEY } from '../hooks/use-import-request';
9
+ import { ImportDesignSystemDialog } from '../import-design-system-dialog';
10
+
11
+ export const TriggerButton = () => {
12
+ const isImporting = useIsMutating( { mutationKey: [ ...IMPORT_DESIGN_SYSTEM_MUTATION_KEY ] } ) > 0;
13
+
14
+ const label = isImporting
15
+ ? __( 'Importing design system…', 'elementor' )
16
+ : __( 'Import Design System', 'elementor' );
17
+
18
+ const handleClick = () => {
19
+ openDialog( {
20
+ component: <ImportDesignSystemDialog onClose={ closeDialog } />,
21
+ } );
22
+ };
23
+
24
+ return (
25
+ <Tooltip title={ label } placement="top">
26
+ <span>
27
+ <IconButton size="tiny" disabled={ isImporting } aria-label={ label } onClick={ handleClick }>
28
+ <UploadIcon fontSize="tiny" />
29
+ </IconButton>
30
+ </span>
31
+ </Tooltip>
32
+ );
33
+ };
@@ -0,0 +1,24 @@
1
+ import { useState } from 'react';
2
+
3
+ import { type ConflictStrategy } from '../types';
4
+
5
+ export type DialogState = {
6
+ file: File | null;
7
+ conflictStrategy: ConflictStrategy | null;
8
+ };
9
+
10
+ const initialState: DialogState = {
11
+ file: null,
12
+ conflictStrategy: null,
13
+ };
14
+
15
+ export const useDialogState = () => {
16
+ const [ state, setState ] = useState< DialogState >( initialState );
17
+
18
+ const setFile = ( file: File | null ) => setState( ( prev ) => ( { ...prev, file } ) );
19
+
20
+ const setConflictStrategy = ( conflictStrategy: ConflictStrategy ) =>
21
+ setState( ( prev ) => ( { ...prev, conflictStrategy } ) );
22
+
23
+ return { ...state, setFile, setConflictStrategy };
24
+ };
@@ -0,0 +1,38 @@
1
+ import { GLOBAL_STYLES_IMPORTED_EVENT, type ImportedGlobalStylesPayload } from '@elementor/editor-canvas';
2
+ import { reloadCurrentDocument } from '@elementor/editor-documents';
3
+ import { httpService } from '@elementor/http-client';
4
+ import { useMutation } from '@elementor/query';
5
+
6
+ import { type ConflictStrategy } from '../types';
7
+
8
+ const IMPORT_ENDPOINT = '/design-system/import';
9
+
10
+ export const IMPORT_DESIGN_SYSTEM_MUTATION_KEY = [ 'design-system-import' ] as const;
11
+
12
+ type ImportRequestArgs = {
13
+ file: File;
14
+ conflictStrategy: ConflictStrategy;
15
+ };
16
+
17
+ export const useImportRequest = () => {
18
+ return useMutation( {
19
+ mutationKey: [ ...IMPORT_DESIGN_SYSTEM_MUTATION_KEY ],
20
+ mutationFn: async ( { file, conflictStrategy }: ImportRequestArgs ): Promise< void > => {
21
+ const formData = new FormData();
22
+ formData.append( 'file', file );
23
+ formData.append( 'conflict_strategy', conflictStrategy );
24
+
25
+ const response = await httpService().post< ImportedGlobalStylesPayload >( IMPORT_ENDPOINT, formData, {
26
+ headers: { 'Content-Type': 'multipart/form-data' },
27
+ } );
28
+
29
+ window.dispatchEvent(
30
+ new CustomEvent< ImportedGlobalStylesPayload >( GLOBAL_STYLES_IMPORTED_EVENT, {
31
+ detail: response?.data,
32
+ } )
33
+ );
34
+
35
+ await reloadCurrentDocument();
36
+ },
37
+ } );
38
+ };
@@ -0,0 +1,89 @@
1
+ import * as React from 'react';
2
+ import { closeDialog, FileUploadDropzone, FileUploadRow, openDialog } from '@elementor/editor-ui';
3
+ import { Button, DialogActions, DialogContent, DialogHeader, DialogTitle, Stack } from '@elementor/ui';
4
+ import { __, sprintf } from '@wordpress/i18n';
5
+
6
+ import { ConflictOptions } from './components/conflict-options';
7
+ import { useDialogState } from './hooks/use-dialog-state';
8
+ import { useImportRequest } from './hooks/use-import-request';
9
+ import { notifyImportFailure, notifyImportInProgress, notifyImportSuccess } from './import-notifications';
10
+
11
+ const ALLOWED_FILE_TYPES: `${ string }/${ string }`[] = [ 'application/zip' ];
12
+ const FILE_INPUT_ACCEPT = 'application/zip,.zip';
13
+ // TODO: Replace with the actual server-enforced limit once finalized.
14
+ const MAX_FILE_SIZE_MB = 3;
15
+
16
+ type Props = {
17
+ onClose: () => void;
18
+ };
19
+
20
+ const reopenSelf = () => {
21
+ openDialog( {
22
+ component: <ImportDesignSystemDialog onClose={ closeDialog } />,
23
+ } );
24
+ };
25
+
26
+ export const ImportDesignSystemDialog = ( { onClose }: Props ) => {
27
+ const { file, conflictStrategy, setFile, setConflictStrategy } = useDialogState();
28
+ const importMutation = useImportRequest();
29
+
30
+ const isImportEnabled = Boolean( file && conflictStrategy );
31
+
32
+ const handleImport = async () => {
33
+ if ( ! file || ! conflictStrategy ) {
34
+ return;
35
+ }
36
+
37
+ notifyImportInProgress();
38
+ onClose();
39
+
40
+ try {
41
+ await importMutation.mutateAsync( { file, conflictStrategy } );
42
+ notifyImportSuccess();
43
+ } catch {
44
+ notifyImportFailure( reopenSelf );
45
+ }
46
+ };
47
+
48
+ return (
49
+ <>
50
+ <DialogHeader logo={ false }>
51
+ <DialogTitle>{ __( 'Import Design System', 'elementor' ) }</DialogTitle>
52
+ </DialogHeader>
53
+ <DialogContent>
54
+ <Stack spacing={ 3 }>
55
+ { file ? (
56
+ <FileUploadRow file={ file } onRemove={ () => setFile( null ) } />
57
+ ) : (
58
+ <FileUploadDropzone
59
+ onFileSelected={ setFile }
60
+ allowedFileTypes={ ALLOWED_FILE_TYPES }
61
+ accept={ FILE_INPUT_ACCEPT }
62
+ regionLabel={ __( 'Design system file dropzone', 'elementor' ) }
63
+ helperText={ sprintf(
64
+ // translators: %d is the maximum file size in megabytes.
65
+ __( 'zip (max. %dMB)', 'elementor' ),
66
+ MAX_FILE_SIZE_MB
67
+ ) }
68
+ />
69
+ ) }
70
+ <ConflictOptions value={ conflictStrategy } onChange={ setConflictStrategy } />
71
+ </Stack>
72
+ </DialogContent>
73
+ <DialogActions>
74
+ <Button size="medium" color="secondary" onClick={ onClose }>
75
+ { __( 'Cancel', 'elementor' ) }
76
+ </Button>
77
+ <Button
78
+ size="medium"
79
+ variant="contained"
80
+ color="primary"
81
+ disabled={ ! isImportEnabled }
82
+ onClick={ handleImport }
83
+ >
84
+ { __( 'Import', 'elementor' ) }
85
+ </Button>
86
+ </DialogActions>
87
+ </>
88
+ );
89
+ };
@@ -0,0 +1,57 @@
1
+ import { dismissNotification, notify } from '@elementor/editor-notifications';
2
+ import { getQueryClient } from '@elementor/query';
3
+ import { __ } from '@wordpress/i18n';
4
+
5
+ import { IMPORT_DESIGN_SYSTEM_MUTATION_KEY } from './hooks/use-import-request';
6
+
7
+ const IMPORT_STARTED_NOTIFICATION_ID = 'design-system-import-started';
8
+ const SUCCESS_NOTIFICATION_ID = 'design-system-import-succeeded';
9
+ const FAILURE_NOTIFICATION_ID = 'design-system-import-failed';
10
+
11
+ export const notifyImportInProgress = () => {
12
+ notify( {
13
+ id: IMPORT_STARTED_NOTIFICATION_ID,
14
+ type: 'info',
15
+ message: __( 'Import in Progress. You will be notified when the import is complete.', 'elementor' ),
16
+ } );
17
+ };
18
+
19
+ export const notifyImportSuccess = () => {
20
+ dismissNotification( IMPORT_STARTED_NOTIFICATION_ID );
21
+
22
+ notify( {
23
+ id: SUCCESS_NOTIFICATION_ID,
24
+ type: 'success',
25
+ message: __( 'Design system imported', 'elementor' ),
26
+ } );
27
+ };
28
+
29
+ export const notifyImportFailure = ( onRetry: () => void ) => {
30
+ dismissNotification( IMPORT_STARTED_NOTIFICATION_ID );
31
+
32
+ notify( {
33
+ id: FAILURE_NOTIFICATION_ID,
34
+ type: 'error',
35
+ message: __( 'Your design system import failed', 'elementor' ),
36
+ additionalActionProps: [
37
+ {
38
+ size: 'small',
39
+ variant: 'outlined',
40
+ color: 'error',
41
+ children: __( 'Try again', 'elementor' ),
42
+ onClick: () => {
43
+ dismissNotification( FAILURE_NOTIFICATION_ID );
44
+
45
+ const isImporting =
46
+ getQueryClient().isMutating( { mutationKey: [ ...IMPORT_DESIGN_SYSTEM_MUTATION_KEY ] } ) > 0;
47
+
48
+ if ( isImporting ) {
49
+ return;
50
+ }
51
+
52
+ onRetry();
53
+ },
54
+ },
55
+ ],
56
+ } );
57
+ };
@@ -0,0 +1 @@
1
+ export type ConflictStrategy = 'replace' | 'keep';