@elementor/editor-global-classes 0.22.2 → 3.32.0-20

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +0 -21
  2. package/dist/index.js +1069 -327
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +1049 -279
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +18 -18
  7. package/src/api.ts +14 -2
  8. package/src/components/class-manager/class-item.tsx +40 -65
  9. package/src/components/class-manager/class-manager-button.tsx +4 -0
  10. package/src/components/class-manager/class-manager-panel.tsx +82 -74
  11. package/src/components/class-manager/delete-confirmation-dialog.tsx +26 -6
  12. package/src/components/class-manager/global-classes-list.tsx +76 -57
  13. package/src/components/class-manager/not-found.tsx +138 -0
  14. package/src/components/convert-local-class-to-global-class.tsx +62 -0
  15. package/src/components/css-class-usage/components/css-class-usage-popover.tsx +169 -0
  16. package/src/components/css-class-usage/components/css-class-usage-trigger.tsx +116 -0
  17. package/src/components/css-class-usage/components/index.ts +2 -0
  18. package/src/components/css-class-usage/types.ts +19 -0
  19. package/src/components/css-class-usage/utils.ts +10 -0
  20. package/src/components/search-and-filter/components/filter/active-filters.tsx +54 -0
  21. package/src/components/search-and-filter/components/filter/clear-icon-button.tsx +31 -0
  22. package/src/components/search-and-filter/components/filter/css-class-filter.tsx +74 -0
  23. package/src/components/search-and-filter/components/filter/filter-list.tsx +77 -0
  24. package/src/components/{class-manager → search-and-filter/components/search}/class-manager-search.tsx +12 -11
  25. package/src/components/search-and-filter/context.tsx +78 -0
  26. package/src/global-classes-styles-provider.ts +13 -8
  27. package/src/hooks/use-css-class-usage-by-id.ts +15 -0
  28. package/src/hooks/use-css-class-usage.ts +13 -0
  29. package/src/hooks/use-empty-css-class.ts +12 -0
  30. package/src/hooks/use-filtered-css-class-usage.tsx +67 -0
  31. package/src/hooks/use-filters.ts +30 -0
  32. package/src/hooks/use-prefetch-css-class-usage.ts +16 -0
  33. package/src/init.ts +11 -1
  34. package/src/store.ts +23 -6
  35. package/src/components/class-manager/class-manager-class-not-found.tsx +0 -56
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/editor-global-classes",
3
- "version": "0.22.2",
3
+ "version": "3.32.0-20",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -18,11 +18,11 @@
18
18
  },
19
19
  "repository": {
20
20
  "type": "git",
21
- "url": "git+https://github.com/elementor/elementor-packages.git",
21
+ "url": "git+https://github.com/elementor/elementor.git",
22
22
  "directory": "packages/core/editor-global-classes"
23
23
  },
24
24
  "bugs": {
25
- "url": "https://github.com/elementor/elementor-packages/issues"
25
+ "url": "https://github.com/elementor/elementor/issues"
26
26
  },
27
27
  "publishConfig": {
28
28
  "access": "public"
@@ -39,22 +39,22 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "0.21.1",
43
- "@elementor/editor-current-user": "0.6.1",
44
- "@elementor/editor-documents": "0.13.10",
45
- "@elementor/editor-editing-panel": "1.50.0",
46
- "@elementor/editor-panels": "0.17.1",
47
- "@elementor/editor-props": "0.18.0",
48
- "@elementor/editor-styles": "0.6.14",
49
- "@elementor/editor-styles-repository": "0.10.7",
50
- "@elementor/editor-ui": "0.14.2",
51
- "@elementor/editor-v1-adapters": "0.12.1",
52
- "@elementor/http-client": "0.3.0",
42
+ "@elementor/editor": "3.32.0-20",
43
+ "@elementor/editor-current-user": "3.32.0-20",
44
+ "@elementor/editor-documents": "3.32.0-20",
45
+ "@elementor/editor-editing-panel": "3.32.0-20",
46
+ "@elementor/editor-panels": "3.32.0-20",
47
+ "@elementor/editor-props": "3.32.0-20",
48
+ "@elementor/editor-styles": "3.32.0-20",
49
+ "@elementor/editor-styles-repository": "3.32.0-20",
50
+ "@elementor/editor-ui": "3.32.0-20",
51
+ "@elementor/editor-v1-adapters": "3.32.0-20",
52
+ "@elementor/http-client": "3.32.0-20",
53
53
  "@elementor/icons": "1.46.0",
54
- "@elementor/query": "0.2.4",
55
- "@elementor/store": "0.9.0",
56
- "@elementor/ui": "1.36.0",
57
- "@elementor/utils": "0.5.0",
54
+ "@elementor/query": "3.32.0-20",
55
+ "@elementor/store": "3.32.0-20",
56
+ "@elementor/ui": "1.36.2",
57
+ "@elementor/utils": "3.32.0-20",
58
58
  "@wordpress/i18n": "^5.13.0"
59
59
  },
60
60
  "peerDependencies": {
package/src/api.ts CHANGED
@@ -1,11 +1,21 @@
1
1
  import { type StyleDefinition, type StyleDefinitionID, type StyleDefinitionsMap } from '@elementor/editor-styles';
2
2
  import { type HttpResponse, httpService } from '@elementor/http-client';
3
3
 
4
+ import { type CssClassUsage } from './components/css-class-usage/types';
4
5
  import { type GlobalClasses } from './store';
5
6
 
6
7
  const RESOURCE_URL = '/global-classes';
8
+ const BASE_URL = 'elementor/v1';
9
+ const RESOURCE_USAGE_URL = `${ RESOURCE_URL }/usage`;
7
10
 
8
- export type GlobalClassesGetAllResponse = HttpResponse< StyleDefinitionsMap, { order: StyleDefinition[ 'id' ][] } >;
11
+ type GlobalClassesUsageResponse = HttpResponse< CssClassUsage >;
12
+
13
+ export type GlobalClassesGetAllResponse = HttpResponse<
14
+ StyleDefinitionsMap,
15
+ {
16
+ order: StyleDefinition[ 'id' ][];
17
+ }
18
+ >;
9
19
 
10
20
  type UpdatePayload = GlobalClasses & {
11
21
  changes: {
@@ -18,8 +28,10 @@ type UpdatePayload = GlobalClasses & {
18
28
  export type ApiContext = 'preview' | 'frontend';
19
29
 
20
30
  export const apiClient = {
31
+ usage: () => httpService().get< GlobalClassesUsageResponse >( `${ BASE_URL }${ RESOURCE_USAGE_URL }` ),
32
+
21
33
  all: ( context: ApiContext = 'preview' ) =>
22
- httpService().get< GlobalClassesGetAllResponse >( 'elementor/v1' + RESOURCE_URL, {
34
+ httpService().get< GlobalClassesGetAllResponse >( `${ BASE_URL }${ RESOURCE_URL }`, {
23
35
  params: { context },
24
36
  } ),
25
37
 
@@ -1,9 +1,7 @@
1
1
  import * as React from 'react';
2
- import { useRef } from 'react';
3
- import { EXPERIMENTAL_FEATURES } from '@elementor/editor-editing-panel';
2
+ import { useRef, useState } from 'react';
4
3
  import { validateStyleLabel } from '@elementor/editor-styles-repository';
5
4
  import { EditableField, EllipsisWithTooltip, MenuListItem, useEditable, WarningInfotip } from '@elementor/editor-ui';
6
- import { isExperimentActive } from '@elementor/editor-v1-adapters';
7
5
  import { DotsVerticalIcon } from '@elementor/icons';
8
6
  import {
9
7
  bindMenu,
@@ -22,11 +20,11 @@ import {
22
20
  } from '@elementor/ui';
23
21
  import { __ } from '@wordpress/i18n';
24
22
 
23
+ import { CssClassUsageTrigger } from '../css-class-usage/components';
24
+ import { useSearchAndFilters } from '../search-and-filter/context';
25
25
  import { useDeleteConfirmation } from './delete-confirmation-dialog';
26
26
  import { SortableTrigger, type SortableTriggerProps } from './sortable';
27
27
 
28
- const isVersion311IsActive = isExperimentActive( EXPERIMENTAL_FEATURES.V_3_31 );
29
-
30
28
  type ClassItemProps = React.PropsWithChildren< {
31
29
  id: string;
32
30
  label: string;
@@ -34,20 +32,13 @@ type ClassItemProps = React.PropsWithChildren< {
34
32
  selected?: boolean;
35
33
  disabled?: boolean;
36
34
  sortableTriggerProps: SortableTriggerProps;
37
- isSearchActive: boolean;
38
35
  } >;
39
36
 
40
- export const ClassItem = ( {
41
- id,
42
- label,
43
- renameClass,
44
- selected,
45
- disabled,
46
- sortableTriggerProps,
47
- isSearchActive,
48
- }: ClassItemProps ) => {
37
+ export const ClassItem = ( { id, label, renameClass, selected, disabled, sortableTriggerProps }: ClassItemProps ) => {
49
38
  const itemRef = useRef< HTMLElement >( null );
50
-
39
+ const {
40
+ search: { inputValue },
41
+ } = useSearchAndFilters();
51
42
  const {
52
43
  ref: editableRef,
53
44
  openEditMode,
@@ -59,7 +50,7 @@ export const ClassItem = ( {
59
50
  onSubmit: renameClass,
60
51
  validation: validateLabel,
61
52
  } );
62
-
53
+ const [ selectedCssUsage, setSelectedCssUsage ] = useState( '' );
63
54
  const { openDialog } = useDeleteConfirmation();
64
55
 
65
56
  const popupState = usePopupState( {
@@ -67,7 +58,8 @@ export const ClassItem = ( {
67
58
  disableAutoFocus: true,
68
59
  } );
69
60
 
70
- const isSelected = ( selected || popupState.isOpen ) && ! disabled;
61
+ const isSelected = ( selectedCssUsage === id || selected || popupState.isOpen ) && ! disabled;
62
+
71
63
  return (
72
64
  <>
73
65
  <Stack p={ 0 }>
@@ -82,7 +74,7 @@ export const ClassItem = ( {
82
74
  ref={ itemRef }
83
75
  dense
84
76
  disableGutters
85
- showSortIndicator={ isSearchActive }
77
+ showSortIndicator={ inputValue.length >= 2 }
86
78
  showActions={ isSelected || isEditing }
87
79
  shape="rounded"
88
80
  onDoubleClick={ openEditMode }
@@ -103,6 +95,9 @@ export const ClassItem = ( {
103
95
  <EllipsisWithTooltip title={ label } as={ Typography } variant="caption" />
104
96
  ) }
105
97
  </Indicator>
98
+ <Box className={ 'class-item-locator' }>
99
+ <CssClassUsageTrigger id={ id } onClick={ setSelectedCssUsage } />
100
+ </Box>
106
101
  <Tooltip
107
102
  placement="top"
108
103
  className={ 'class-item-more-actions' }
@@ -152,55 +147,35 @@ export const ClassItem = ( {
152
147
  );
153
148
  };
154
149
 
155
- // Custom styles for sortable list item, until the component is available in the UI package.
156
-
157
- // Experimental start
158
-
159
- const StyledListItemButtonV2 = styled( ListItemButton, {
160
- shouldForwardProp: ( prop: string ) => ! [ 'showActions', 'showSortIndicator' ].includes( prop ),
161
- } )< ListItemButtonProps & { showActions: boolean; showSortIndicator: boolean } >(
162
- ( { showActions, showSortIndicator } ) =>
163
- `
164
- min-height: 36px;
165
-
166
- &.visible-class-item {
167
- box-shadow: none !important;
168
- }
169
- .class-item-sortable-trigger {
170
- visibility: ${ showSortIndicator && showActions ? 'visible' : 'hidden' };
171
- }
172
- &:hover&:not(:disabled) {
173
- .class-item-sortable-trigger {
174
- visibility: ${ showSortIndicator ? 'visible' : 'hidden' };
175
- }
176
- }
177
- `
178
- );
179
-
180
- const StyledListItemButtonV1 = styled( ListItemButton, {
150
+ const StyledListItemButton = styled( ListItemButton, {
181
151
  shouldForwardProp: ( prop: string ) => ! [ 'showActions', 'showSortIndicator' ].includes( prop ),
182
152
  } )< ListItemButtonProps & { showActions: boolean; showSortIndicator: boolean } >(
183
- ( { showActions } ) => `
184
- min-height: 36px;
185
- &.visible-class-item {
186
- box-shadow: none !important;
187
- }
188
- .class-item-more-actions, .class-item-sortable-trigger {
189
- visibility: ${ showActions ? 'visible' : 'hidden' };
190
- }
191
- .class-item-sortable-trigger {
192
- visibility: ${ showActions ? 'visible' : 'hidden' };
193
- }
194
- &:hover&:not(:disabled) {
195
- .class-item-more-actions, .class-item-sortable-trigger {
196
- visibility: visible;
197
- }
198
- }
199
- `
153
+ ( { showActions, showSortIndicator } ) => `
154
+ min-height: 36px;
155
+
156
+ &.visible-class-item {
157
+ box-shadow: none !important;
158
+ }
159
+
160
+ .class-item-locator {
161
+ visibility: hidden;
162
+ }
163
+
164
+ .class-item-sortable-trigger {
165
+ visibility: ${ showSortIndicator && showActions ? 'visible' : 'hidden' };
166
+ }
167
+
168
+ &:hover:not(:disabled) {
169
+ .class-item-locator {
170
+ visibility: visible;
171
+ }
172
+
173
+ .class-item-sortable-trigger {
174
+ visibility: ${ showSortIndicator ? 'visible' : 'hidden' };
175
+ }
176
+ }
177
+ `
200
178
  );
201
- // Experimental start
202
-
203
- const StyledListItemButton = isVersion311IsActive ? StyledListItemButtonV2 : StyledListItemButtonV1;
204
179
 
205
180
  const Indicator = styled( Box, {
206
181
  shouldForwardProp: ( prop: string ) => ! [ 'isActive', 'isError' ].includes( prop ),
@@ -8,6 +8,7 @@ import { IconButton, Tooltip } from '@elementor/ui';
8
8
  import { __ } from '@wordpress/i18n';
9
9
 
10
10
  import { globalClassesStylesProvider } from '../../global-classes-styles-provider';
11
+ import { usePrefetchCssClassUsage } from '../../hooks/use-prefetch-css-class-usage';
11
12
  import { usePanelActions } from './class-manager-panel';
12
13
  import { FlippedColorSwatchIcon } from './flipped-color-swatch-icon';
13
14
  import { SaveChangesDialog, useDialog } from './save-changes-dialog';
@@ -17,6 +18,7 @@ export const ClassManagerButton = () => {
17
18
  const { open: openPanel } = usePanelActions();
18
19
  const { save: saveDocument } = useActiveDocumentActions();
19
20
  const { open: openSaveChangesDialog, close: closeSaveChangesDialog, isOpen: isSaveChangesDialogOpen } = useDialog();
21
+ const { prefetchClassesUsage } = usePrefetchCssClassUsage();
20
22
 
21
23
  const { userCan } = useUserStylesCapability();
22
24
 
@@ -33,6 +35,7 @@ export const ClassManagerButton = () => {
33
35
  }
34
36
 
35
37
  openPanel();
38
+ prefetchClassesUsage();
36
39
  };
37
40
 
38
41
  return (
@@ -65,6 +68,7 @@ export const ClassManagerButton = () => {
65
68
  await saveDocument();
66
69
  closeSaveChangesDialog();
67
70
  openPanel();
71
+ prefetchClassesUsage();
68
72
  },
69
73
  },
70
74
  } }
@@ -1,7 +1,6 @@
1
- import { useEffect } from 'react';
2
1
  import * as React from 'react';
2
+ import { useEffect } from 'react';
3
3
  import { setDocumentModifiedStatus } from '@elementor/editor-documents';
4
- import { EXPERIMENTAL_FEATURES } from '@elementor/editor-editing-panel';
5
4
  import {
6
5
  __createPanel as createPanel,
7
6
  Panel,
@@ -11,7 +10,7 @@ import {
11
10
  PanelHeaderTitle,
12
11
  } from '@elementor/editor-panels';
13
12
  import { ThemeProvider } from '@elementor/editor-ui';
14
- import { changeEditMode, isExperimentActive } from '@elementor/editor-v1-adapters';
13
+ import { changeEditMode } from '@elementor/editor-v1-adapters';
15
14
  import { XIcon } from '@elementor/icons';
16
15
  import { useMutation } from '@elementor/query';
17
16
  import { __dispatch as dispatch } from '@elementor/store';
@@ -19,6 +18,7 @@ import {
19
18
  Alert,
20
19
  Box,
21
20
  Button,
21
+ Chip,
22
22
  DialogHeader,
23
23
  Divider,
24
24
  ErrorBoundary,
@@ -26,22 +26,24 @@ import {
26
26
  type IconButtonProps,
27
27
  Stack,
28
28
  } from '@elementor/ui';
29
- import { useDebounceState } from '@elementor/utils';
30
29
  import { __ } from '@wordpress/i18n';
31
30
 
31
+ import { useClassesOrder } from '../../hooks/use-classes-order';
32
32
  import { useDirtyState } from '../../hooks/use-dirty-state';
33
+ import { useFilters } from '../../hooks/use-filters';
33
34
  import { saveGlobalClasses } from '../../save-global-classes';
34
35
  import { slice } from '../../store';
36
+ import { ActiveFilters } from '../search-and-filter/components/filter/active-filters';
37
+ import { CssClassFilter } from '../search-and-filter/components/filter/css-class-filter';
38
+ import { ClassManagerSearch } from '../search-and-filter/components/search/class-manager-search';
39
+ import { SearchAndFilterProvider } from '../search-and-filter/context';
35
40
  import { ClassManagerIntroduction } from './class-manager-introduction';
36
- import { ClassManagerSearch } from './class-manager-search';
37
41
  import { hasDeletedItems, onDelete } from './delete-class';
38
42
  import { FlippedColorSwatchIcon } from './flipped-color-swatch-icon';
39
43
  import { GlobalClassesList } from './global-classes-list';
40
44
  import { blockPanelInteractions, unblockPanelInteractions } from './panel-interactions';
41
45
  import { SaveChangesDialog, useDialog } from './save-changes-dialog';
42
46
 
43
- const isVersion311IsActive = isExperimentActive( EXPERIMENTAL_FEATURES.V_3_31 );
44
-
45
47
  const id = 'global-classes-manager';
46
48
 
47
49
  // We need to disable the app-bar buttons, and the elements overlays when opening the classes manager panel.
@@ -65,13 +67,7 @@ export const { panel, usePanelActions } = createPanel( {
65
67
  } );
66
68
 
67
69
  export function ClassManagerPanel() {
68
- const { debouncedValue, inputValue, handleChange } = useDebounceState( {
69
- delay: 300,
70
- initialValue: '',
71
- } );
72
-
73
70
  const isDirty = useDirtyState();
74
-
75
71
  const { close: closePanel } = usePanelActions();
76
72
  const { open: openSaveChangesDialog, close: closeSaveChangesDialog, isOpen: isSaveChangesDialogOpen } = useDialog();
77
73
 
@@ -88,72 +84,72 @@ export function ClassManagerPanel() {
88
84
  <ThemeProvider>
89
85
  <ErrorBoundary fallback={ <ErrorBoundaryFallback /> }>
90
86
  <Panel>
91
- <PanelHeader>
92
- <Stack p={ 1 } pl={ 2 } width="100%" direction="row" alignItems="center">
93
- <PanelHeaderTitle sx={ { display: 'flex', alignItems: 'center', gap: 0.5 } }>
94
- <FlippedColorSwatchIcon fontSize="inherit" />
95
- { __( 'Class Manager', 'elementor' ) }
96
- </PanelHeaderTitle>
97
- <CloseButton
98
- sx={ { marginLeft: 'auto' } }
99
- disabled={ isPublishing }
100
- onClose={ () => {
101
- if ( isDirty ) {
102
- openSaveChangesDialog();
103
- return;
104
- }
105
-
106
- closePanel();
107
- } }
108
- />
109
- </Stack>
110
- </PanelHeader>
111
- <PanelBody
112
- sx={ {
113
- display: 'flex',
114
- flexDirection: 'column',
115
- height: '100%',
116
- } }
117
- >
118
- { isVersion311IsActive && (
119
- <>
120
- <ClassManagerSearch searchValue={ inputValue } onChange={ handleChange } />
121
- <Divider
122
- sx={ {
123
- borderWidth: '1px 0 0 0',
87
+ <SearchAndFilterProvider>
88
+ <PanelHeader>
89
+ <Stack p={ 1 } pl={ 2 } width="100%" direction="row" alignItems="center">
90
+ <Stack width="100%" direction="row" gap={ 1 }>
91
+ <PanelHeaderTitle sx={ { display: 'flex', alignItems: 'center', gap: 0.5 } }>
92
+ <FlippedColorSwatchIcon fontSize="inherit" />
93
+ { __( 'Class Manager', 'elementor' ) }
94
+ </PanelHeaderTitle>
95
+ <TotalCssClassCounter />
96
+ </Stack>
97
+ <CloseButton
98
+ sx={ { marginLeft: 'auto' } }
99
+ disabled={ isPublishing }
100
+ onClose={ () => {
101
+ if ( isDirty ) {
102
+ openSaveChangesDialog();
103
+ return;
104
+ }
105
+
106
+ closePanel();
124
107
  } }
125
108
  />
126
- </>
127
- ) }
128
-
129
- <Box
130
- px={ 2 }
109
+ </Stack>
110
+ </PanelHeader>
111
+ <PanelBody
131
112
  sx={ {
132
- flexGrow: 1,
133
- overflowY: 'auto',
113
+ display: 'flex',
114
+ flexDirection: 'column',
115
+ height: '100%',
134
116
  } }
135
117
  >
136
- <GlobalClassesList
137
- disabled={ isPublishing }
138
- searchValue={ debouncedValue }
139
- onSearch={ handleChange }
140
- />
141
- </Box>
142
- </PanelBody>
143
-
144
- <PanelFooter>
145
- <Button
146
- fullWidth
147
- size="small"
148
- color="global"
149
- variant="contained"
150
- onClick={ publish }
151
- disabled={ ! isDirty }
152
- loading={ isPublishing }
153
- >
154
- { __( 'Save changes', 'elementor' ) }
155
- </Button>
156
- </PanelFooter>
118
+ <Box px={ 2 } pb={ 1 }>
119
+ <Stack direction="row" justifyContent="spaceBetween" gap={ 0.5 } sx={ { pb: 0.5 } }>
120
+ <Box sx={ { flexGrow: 1 } }>
121
+ <ClassManagerSearch />
122
+ </Box>
123
+ <CssClassFilter />
124
+ </Stack>
125
+ <ActiveFilters />
126
+ </Box>
127
+ <Divider />
128
+ <Box
129
+ px={ 2 }
130
+ sx={ {
131
+ flexGrow: 1,
132
+ overflowY: 'auto',
133
+ } }
134
+ >
135
+ <GlobalClassesList disabled={ isPublishing } />
136
+ </Box>
137
+ </PanelBody>
138
+
139
+ <PanelFooter>
140
+ <Button
141
+ fullWidth
142
+ size="small"
143
+ color="global"
144
+ variant="contained"
145
+ onClick={ publish }
146
+ disabled={ ! isDirty }
147
+ loading={ isPublishing }
148
+ >
149
+ { __( 'Save changes', 'elementor' ) }
150
+ </Button>
151
+ </PanelFooter>
152
+ </SearchAndFilterProvider>
157
153
  </Panel>
158
154
  </ErrorBoundary>
159
155
  <ClassManagerIntroduction />
@@ -240,3 +236,15 @@ const usePublish = () => {
240
236
  },
241
237
  } );
242
238
  };
239
+
240
+ const TotalCssClassCounter = () => {
241
+ const filters = useFilters();
242
+ const cssClasses = useClassesOrder();
243
+
244
+ return (
245
+ <Chip
246
+ size={ 'small' }
247
+ label={ filters ? `${ filters.length } / ${ cssClasses?.length }` : cssClasses?.length }
248
+ />
249
+ );
250
+ };
@@ -13,6 +13,7 @@ import {
13
13
  } from '@elementor/ui';
14
14
  import { __ } from '@wordpress/i18n';
15
15
 
16
+ import { useCssClassUsageByID } from '../../hooks/use-css-class-usage-by-id';
16
17
  import { deleteClass } from './delete-class';
17
18
 
18
19
  type DeleteConfirmationDialogProps = Pick< StyleDefinition, 'id' | 'label' >;
@@ -48,12 +49,27 @@ const TITLE_ID = 'delete-class-dialog';
48
49
 
49
50
  const DeleteConfirmationDialog = ( { label, id }: DeleteConfirmationDialogProps ) => {
50
51
  const { closeDialog } = useDeleteConfirmation();
51
-
52
+ const {
53
+ data: { total, content },
54
+ } = useCssClassUsageByID( id );
52
55
  const onConfirm = () => {
53
56
  deleteClass( id );
54
57
 
55
58
  closeDialog();
56
59
  };
60
+ // translators: %1: total usage count, %2: number of pages
61
+ const text =
62
+ total && content.length
63
+ ? __(
64
+ 'Will permanently remove it from your project and may affect the design across all elements using it. Used %1 times across %2 pages. This action cannot be undone.',
65
+ 'elementor'
66
+ )
67
+ .replace( '%1', total.toString() )
68
+ .replace( '%2', content.length.toString() )
69
+ : __(
70
+ 'Will permanently remove it from your project and may affect the design across all elements using it. This action cannot be undone.',
71
+ 'elementor'
72
+ );
57
73
 
58
74
  return (
59
75
  <Dialog open onClose={ closeDialog } aria-labelledby={ TITLE_ID } maxWidth="xs">
@@ -67,17 +83,21 @@ const DeleteConfirmationDialog = ( { label, id }: DeleteConfirmationDialogProps
67
83
  <Typography variant="subtitle2" component="span">
68
84
  &nbsp;{ label }&nbsp;
69
85
  </Typography>
70
- { __(
71
- 'will permanently remove it from your project and may affect the design across all elements using it. This action cannot be undone.',
72
- 'elementor'
73
- ) }
86
+ { text }
74
87
  </DialogContentText>
75
88
  </DialogContent>
76
89
  <DialogActions>
77
90
  <Button color="secondary" onClick={ closeDialog }>
78
91
  { __( 'Not now', 'elementor' ) }
79
92
  </Button>
80
- <Button variant="contained" color="error" onClick={ onConfirm }>
93
+
94
+ <Button
95
+ // eslint-disable-next-line jsx-a11y/no-autofocus
96
+ autoFocus
97
+ variant="contained"
98
+ color="error"
99
+ onClick={ onConfirm }
100
+ >
81
101
  { __( 'Delete', 'elementor' ) }
82
102
  </Button>
83
103
  </DialogActions>