@elementor/editor-global-classes 0.7.1 → 0.8.0

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-global-classes",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -41,8 +41,8 @@
41
41
  "dependencies": {
42
42
  "@elementor/editor": "0.18.0",
43
43
  "@elementor/editor-current-user": "0.2.0",
44
- "@elementor/editor-editing-panel": "1.13.0",
45
- "@elementor/editor-panels": "0.11.1",
44
+ "@elementor/editor-editing-panel": "1.13.1",
45
+ "@elementor/editor-panels": "0.12.0",
46
46
  "@elementor/editor-props": "0.9.2",
47
47
  "@elementor/editor-styles": "0.5.7",
48
48
  "@elementor/editor-styles-repository": "0.7.2",
@@ -3,13 +3,16 @@ import {
3
3
  __createPanel as createPanel,
4
4
  Panel,
5
5
  PanelBody,
6
+ PanelFooter,
6
7
  PanelHeader,
7
8
  PanelHeaderTitle,
8
9
  } from '@elementor/editor-panels';
9
10
  import { ColorSwatchIcon, XIcon } from '@elementor/icons';
10
- import { Alert, Box, ErrorBoundary, IconButton, type IconButtonProps, Stack } from '@elementor/ui';
11
+ import { Alert, Box, Button, ErrorBoundary, IconButton, type IconButtonProps, Stack } from '@elementor/ui';
11
12
  import { __ } from '@wordpress/i18n';
12
13
 
14
+ import { useDirtyState } from '../../hooks/use-dirty-state';
15
+ import { publishGlobalClasses } from '../../publish-global-classes';
13
16
  import { ClassManagerIntroduction } from './class-manager-introduction';
14
17
  import { GlobalClassesList } from './global-classes-list';
15
18
 
@@ -18,7 +21,9 @@ export const { panel, usePanelActions } = createPanel( {
18
21
  component: ClassManagerPanel,
19
22
  } );
20
23
 
21
- function ClassManagerPanel() {
24
+ export function ClassManagerPanel() {
25
+ const isDirty = useDirtyState();
26
+
22
27
  return (
23
28
  <>
24
29
  <ErrorBoundary fallback={ <ErrorBoundaryFallback /> }>
@@ -35,6 +40,18 @@ function ClassManagerPanel() {
35
40
  <PanelBody px={ 2 }>
36
41
  <GlobalClassesList />
37
42
  </PanelBody>
43
+ <PanelFooter>
44
+ <Button
45
+ fullWidth
46
+ size="small"
47
+ variant="contained"
48
+ color="global"
49
+ disabled={ ! isDirty }
50
+ onClick={ publishGlobalClasses }
51
+ >
52
+ { __( 'Save changes', 'elementor' ) }
53
+ </Button>
54
+ </PanelFooter>
38
55
  </Panel>
39
56
  </ErrorBoundary>
40
57
  <ClassManagerIntroduction />
@@ -25,14 +25,15 @@ import {
25
25
  import { __ } from '@wordpress/i18n';
26
26
 
27
27
  import { globalClassesStylesProvider } from '../../global-classes-styles-provider';
28
- import { useGlobalClassesOrder, useOrderedGlobalClasses } from '../../store';
28
+ import { useClassesOrder } from '../../hooks/use-classes-order';
29
+ import { useOrderedClasses } from '../../hooks/use-ordered-classes';
29
30
  import { DeleteConfirmationProvider, useDeleteConfirmation } from './delete-confirmation-dialog';
30
31
  import { SortableItem, SortableItemIndicator, SortableProvider } from './sortable';
31
32
 
32
33
  export const GlobalClassesList = () => {
33
- const cssClasses = useOrderedGlobalClasses();
34
+ const cssClasses = useOrderedClasses();
34
35
 
35
- const [ classesOrder, reorderClasses ] = useClassesOrder();
36
+ const [ classesOrder, reorderClasses ] = useReorder();
36
37
 
37
38
  if ( ! cssClasses?.length ) {
38
39
  return <EmptyState />;
@@ -70,8 +71,8 @@ export const GlobalClassesList = () => {
70
71
  );
71
72
  };
72
73
 
73
- const useClassesOrder = () => {
74
- const order = useGlobalClassesOrder();
74
+ const useReorder = () => {
75
+ const order = useClassesOrder();
75
76
 
76
77
  const reorder = ( newIds: StyleDefinitionID[] ) => {
77
78
  globalClassesStylesProvider.actions.setOrder( newIds );
@@ -8,19 +8,13 @@ import {
8
8
  import { __ } from '@wordpress/i18n';
9
9
 
10
10
  import { GlobalClassLabelAlreadyExistsError } from './errors';
11
- import {
12
- selectClass,
13
- selectGlobalClasses,
14
- selectOrderedGlobalClasses,
15
- slice,
16
- type StateWithGlobalClasses,
17
- } from './store';
11
+ import { selectClass, selectGlobalClasses, selectOrderedClasses, slice, type StateWithGlobalClasses } from './store';
18
12
 
19
13
  export const globalClassesStylesProvider = {
20
14
  key: 'global-classes',
21
15
  priority: 30,
22
16
  actions: {
23
- get: () => selectOrderedGlobalClasses( getState() ),
17
+ get: () => selectOrderedClasses( getState() ),
24
18
  getById: ( id ) => selectClass( getState(), id ),
25
19
  create: ( label ) => {
26
20
  const classes = selectGlobalClasses( getState() );
@@ -0,0 +1,7 @@
1
+ import { __useSelector as useSelector } from '@elementor/store';
2
+
3
+ import { selectOrder } from '../store';
4
+
5
+ export const useClassesOrder = () => {
6
+ return useSelector( selectOrder );
7
+ };
@@ -0,0 +1,7 @@
1
+ import { __useSelector as useSelector } from '@elementor/store';
2
+
3
+ import { selectIsDirty } from '../store';
4
+
5
+ export const useDirtyState = () => {
6
+ return useSelector( selectIsDirty );
7
+ };
@@ -0,0 +1,7 @@
1
+ import { __useSelector as useSelector } from '@elementor/store';
2
+
3
+ import { selectOrderedClasses } from '../store';
4
+
5
+ export const useOrderedClasses = () => {
6
+ return useSelector( selectOrderedClasses );
7
+ };
@@ -0,0 +1,23 @@
1
+ import { __dispatch as dispatch, __getState as getState } from '@elementor/store';
2
+
3
+ import { apiClient } from './api';
4
+ import { type GlobalClassesState, selectIsDirty, slice } from './store';
5
+
6
+ export async function publishGlobalClasses() {
7
+ if ( ! isDirty() ) {
8
+ return;
9
+ }
10
+
11
+ const state: GlobalClassesState = getState().globalClasses;
12
+
13
+ await apiClient.update( {
14
+ items: state.items,
15
+ order: state.order,
16
+ } );
17
+
18
+ dispatch( slice.actions.setPristine() );
19
+ }
20
+
21
+ function isDirty() {
22
+ return selectIsDirty( getState() );
23
+ }
package/src/store.ts CHANGED
@@ -9,7 +9,6 @@ import { type UpdateActionPayload } from '@elementor/editor-styles-repository';
9
9
  import {
10
10
  __createSelector as createSelector,
11
11
  __createSlice as createSlice,
12
- __useSelector as useSelector,
13
12
  type PayloadAction,
14
13
  type SliceState,
15
14
  } from '@elementor/store';
@@ -58,6 +57,8 @@ export const slice = createSlice( {
58
57
  },
59
58
  setOrder( state, { payload }: PayloadAction< StyleDefinitionID[] > ) {
60
59
  state.order = payload;
60
+
61
+ state.isDirty = true;
61
62
  },
62
63
  update( state, { payload }: PayloadAction< { style: UpdateActionPayload } > ) {
63
64
  const style = state.items[ payload.style.id ];
@@ -101,11 +102,11 @@ export const slice = createSlice( {
101
102
  } );
102
103
 
103
104
  // Selectors
104
- const selectOrder = ( state: SliceState< typeof slice > ) => state[ SLICE_NAME ].order;
105
+ export const selectOrder = ( state: SliceState< typeof slice > ) => state[ SLICE_NAME ].order;
105
106
 
106
107
  export const selectGlobalClasses = ( state: SliceState< typeof slice > ) => state[ SLICE_NAME ].items;
107
108
 
108
- export const selectOrderedGlobalClasses = createSelector( selectGlobalClasses, selectOrder, ( items, order ) =>
109
+ export const selectOrderedClasses = createSelector( selectGlobalClasses, selectOrder, ( items, order ) =>
109
110
  order.map( ( id ) => items[ id ] )
110
111
  );
111
112
 
@@ -113,11 +114,3 @@ export const selectClass = ( state: SliceState< typeof slice >, id: StyleDefinit
113
114
  state[ SLICE_NAME ].items[ id ] ?? null;
114
115
 
115
116
  export const selectIsDirty = ( state: SliceState< typeof slice > ) => state.globalClasses.isDirty;
116
-
117
- export const useOrderedGlobalClasses = () => {
118
- const items = useSelector( selectOrderedGlobalClasses );
119
-
120
- return items;
121
- };
122
-
123
- export const useGlobalClassesOrder = () => useSelector( selectOrder );
@@ -1,8 +1,8 @@
1
1
  import { __privateRunCommandSync as runCommandSync, registerDataHook } from '@elementor/editor-v1-adapters';
2
- import { __dispatch, __getState as getState, __subscribeWithSelector as subscribeWithSelector } from '@elementor/store';
2
+ import { __getState as getState, __subscribeWithSelector as subscribeWithSelector } from '@elementor/store';
3
3
 
4
- import { apiClient } from './api';
5
- import { type GlobalClassesState, selectIsDirty, slice } from './store';
4
+ import { publishGlobalClasses } from './publish-global-classes';
5
+ import { selectIsDirty } from './store';
6
6
 
7
7
  export function syncWithDocumentSave() {
8
8
  const unsubscribe = syncDirtyState();
@@ -23,20 +23,7 @@ function syncDirtyState() {
23
23
  }
24
24
 
25
25
  function bindSaveAction() {
26
- registerDataHook( 'after', 'document/save/save', async () => {
27
- if ( ! isDirty() ) {
28
- return;
29
- }
30
-
31
- const state: GlobalClassesState = getState().globalClasses;
32
-
33
- await apiClient.update( {
34
- items: state.items,
35
- order: state.order,
36
- } );
37
-
38
- __dispatch( slice.actions.setPristine() );
39
- } );
26
+ registerDataHook( 'after', 'document/save/save', publishGlobalClasses );
40
27
  }
41
28
 
42
29
  function isDirty() {