@elementor/editor-controls 4.4.0-1076 → 4.4.0-1078

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.
@@ -0,0 +1,14 @@
1
+ import { useQuery } from '@elementor/query';
2
+
3
+ import { type FontAwesome7Icon, loadFontAwesome7Catalog } from './font-awesome-7-catalog';
4
+
5
+ const FONT_AWESOME_7_CATALOG_QUERY_KEY = [ 'font-awesome-7-catalog' ];
6
+
7
+ export function useFontAwesome7Catalog( enabled: boolean ) {
8
+ return useQuery< FontAwesome7Icon[] >( {
9
+ queryKey: FONT_AWESOME_7_CATALOG_QUERY_KEY,
10
+ queryFn: ( { signal } ) => loadFontAwesome7Catalog( signal ),
11
+ enabled,
12
+ staleTime: Infinity,
13
+ } );
14
+ }
@@ -1,43 +1,29 @@
1
1
  import * as React from 'react';
2
- import { useEffect, useState } from 'react';
2
+ import { useEffect, useRef, useState } from 'react';
3
3
  import { useCurrentUserCapabilities } from '@elementor/editor-current-user';
4
4
  import { iconPropTypeUtil, svgSrcPropTypeUtil, urlPropTypeUtil } from '@elementor/editor-props';
5
- import { UploadIcon } from '@elementor/icons';
6
- import {
7
- Box,
8
- Button,
9
- Card,
10
- CardMedia,
11
- CardOverlay,
12
- CircularProgress,
13
- Stack,
14
- styled,
15
- ThemeProvider,
16
- } from '@elementor/ui';
5
+ import { Box, Card, CardOverlay, Popover, Stack, styled, usePopupState } from '@elementor/ui';
17
6
  import { type OpenOptions, useWpMediaAttachment, useWpMediaFrame } from '@elementor/wp-media';
18
7
  import { __ } from '@wordpress/i18n';
19
8
 
20
9
  import { useBoundProp } from '../bound-prop-context';
21
- import { ConditionalControlInfotip } from '../components/conditional-control-infotip';
22
10
  import { EnableUnfilteredModal } from '../components/enable-unfiltered-modal';
23
11
  import ControlActions from '../control-actions/control-actions';
24
12
  import { createControl } from '../create-control';
25
13
  import { useUnfilteredFilesUpload } from '../hooks/use-unfiltered-files-upload';
26
- import {
27
- createIconPropValue,
28
- enqueueIconFonts,
29
- type IconLibrarySelection,
30
- isSvgLibrarySelection,
31
- openIconLibrary,
32
- } from './open-icon-library';
14
+ import { type IconLibraryAnchor, measureIconLibraryAnchor } from './icon-library/get-icon-library-anchor';
15
+ import { ICON_LIBRARY_POPOVER_WIDTH, IconLibraryPopover } from './icon-library/icon-library-popover';
16
+ import { createIconPropValue } from './open-icon-library';
17
+ import { SVG_MEDIA_CONTROL_CONTAINER_TEST_ID, SvgMediaOverlay } from './svg-media-overlay';
18
+ import { SvgMediaPreview } from './svg-media-preview';
33
19
 
34
20
  const TILE_SIZE = 8;
35
21
  const TILE_WHITE = 'transparent';
36
22
  const TILE_BLACK = '#c1c1c1';
37
- const ICON_PREVIEW_FONT_SIZE = 50;
38
23
  export const TILES_GRADIENT_FORMULA = `linear-gradient(45deg, ${ TILE_BLACK } 25%, ${ TILE_WHITE } 0, ${ TILE_WHITE } 75%, ${ TILE_BLACK } 0, ${ TILE_BLACK })`;
39
24
 
40
25
  const StyledCard = styled( Card )`
26
+ position: relative;
41
27
  background-color: white;
42
28
  background-image: ${ TILES_GRADIENT_FORMULA }, ${ TILES_GRADIENT_FORMULA };
43
29
  background-size: ${ TILE_SIZE }px ${ TILE_SIZE }px;
@@ -47,6 +33,8 @@ const StyledCard = styled( Card )`
47
33
  border: none;
48
34
  `;
49
35
 
36
+ const PREVIEW_ICON_COLOR = '#000000';
37
+
50
38
  const StyledCardMediaContainer = styled( Stack )`
51
39
  position: relative;
52
40
  height: 140px;
@@ -55,6 +43,7 @@ const StyledCardMediaContainer = styled( Stack )`
55
43
  justify-content: center;
56
44
  align-items: center;
57
45
  background-color: rgba( 255, 255, 255, 0.37 );
46
+ color: ${ PREVIEW_ICON_COLOR };
58
47
  `;
59
48
 
60
49
  const MODE_BROWSE: OpenOptions = { mode: 'browse' };
@@ -73,6 +62,10 @@ export const SvgMediaControl = createControl( ( { showIconLibrary = false }: Svg
73
62
  const src = attachment?.url ?? url?.value ?? null;
74
63
  const { data: allowSvgUpload } = useUnfilteredFilesUpload();
75
64
  const [ unfilteredModalOpenState, setUnfilteredModalOpenState ] = useState( false );
65
+ const iconLibraryPopoverState = usePopupState( { variant: 'popover' } );
66
+ const controlContainerRef = useRef< HTMLDivElement >( null );
67
+ const buttonGroupRef = useRef< HTMLDivElement >( null );
68
+ const [ iconLibraryAnchor, setIconLibraryAnchor ] = useState< IconLibraryAnchor | null >( null );
76
69
  const { isAdmin } = useCurrentUserCapabilities();
77
70
  const selectedIconClass =
78
71
  showIconLibrary && typeof iconValue?.value?.value === 'string' ? iconValue.value.value : null;
@@ -94,7 +87,7 @@ export const SvgMediaControl = createControl( ( { showIconLibrary = false }: Svg
94
87
  },
95
88
  } );
96
89
 
97
- const onCloseUnfilteredModal = ( enabled: boolean ) => {
90
+ const handleCloseUnfilteredModal = ( enabled: boolean ) => {
98
91
  setUnfilteredModalOpenState( false );
99
92
 
100
93
  if ( enabled ) {
@@ -110,152 +103,132 @@ export const SvgMediaControl = createControl( ( { showIconLibrary = false }: Svg
110
103
  }
111
104
  };
112
105
 
113
- const handleIconLibrarySelect = ( icon: IconLibrarySelection ) => {
114
- if ( ! showIconLibrary ) {
115
- return;
116
- }
117
- if ( isSvgLibrarySelection( icon ) ) {
118
- setSvgValue( {
119
- id: icon.value.id
120
- ? {
121
- $$type: 'image-attachment-id',
122
- value: icon.value.id,
123
- }
124
- : null,
125
- url: icon.value.url ? urlPropTypeUtil.create( icon.value.url ) : null,
126
- } );
127
- return;
128
- }
106
+ const handleSelectSvg = () => {
107
+ handleClick( MODE_BROWSE );
108
+ };
129
109
 
130
- if ( typeof icon.value === 'string' ) {
131
- setIconValue( createIconPropValue( icon.value, icon.library ) );
132
- }
110
+ const handleUpload = () => {
111
+ handleClick( MODE_UPLOAD );
133
112
  };
134
113
 
135
- const infotipProps = {
136
- title: __( "Sorry, you can't upload that file yet.", 'elementor' ),
137
- description: (
138
- <>
139
- { __( 'To upload them anyway, ask the site administrator to enable unfiltered', 'elementor' ) }
140
- <br />
141
- { __( 'file uploads.', 'elementor' ) }
142
- </>
143
- ),
144
- isEnabled: ! isAdmin,
114
+ const handleCloseIconLibrary = () => {
115
+ iconLibraryPopoverState.close();
116
+ setIconLibraryAnchor( null );
145
117
  };
146
118
 
147
- return (
148
- <Stack gap={ 1 } aria-label="SVG Control">
149
- <EnableUnfilteredModal open={ unfilteredModalOpenState } onClose={ onCloseUnfilteredModal } />
150
- <ControlActions>
151
- <StyledCard variant="outlined">
152
- <StyledCardMediaContainer>
153
- <SvgMediaPreview
154
- isFetching={ isFetching }
155
- src={ src }
156
- iconClassName={ selectedIconClass }
157
- iconLibrary={ selectedIconLibrary }
158
- />
159
- </StyledCardMediaContainer>
160
- <CardOverlay
161
- sx={ {
162
- '&:hover': {
163
- backgroundColor: 'rgba( 0, 0, 0, 0.75 )',
164
- },
165
- } }
166
- >
167
- <Stack gap={ 1 }>
168
- <Button
169
- size="tiny"
170
- color="inherit"
171
- variant="outlined"
172
- onClick={ () => handleClick( MODE_BROWSE ) }
173
- aria-label="Select SVG"
174
- >
175
- { __( 'Select SVG', 'elementor' ) }
176
- </Button>
177
- { showIconLibrary ? (
178
- <Button
179
- size="tiny"
180
- variant="text"
181
- color="inherit"
182
- onClick={ () =>
183
- openIconLibrary( {
184
- selected:
185
- selectedIconClass && selectedIconLibrary
186
- ? { value: selectedIconClass, library: selectedIconLibrary }
187
- : undefined,
188
- onSelect: handleIconLibrarySelect,
189
- } )
190
- }
191
- aria-label={ __( 'Icon library', 'elementor' ) }
192
- >
193
- { __( 'Icon library', 'elementor' ) }
194
- </Button>
195
- ) : null }
196
- <ConditionalControlInfotip { ...infotipProps }>
197
- <span>
198
- <ThemeProvider colorScheme={ isAdmin ? 'light' : 'dark' }>
199
- <Button
200
- size="tiny"
201
- variant="text"
202
- color="inherit"
203
- startIcon={ <UploadIcon /> }
204
- disabled={ ! isAdmin }
205
- onClick={ () => isAdmin && handleClick( MODE_UPLOAD ) }
206
- aria-label="Upload SVG"
207
- >
208
- { __( 'Upload', 'elementor' ) }
209
- </Button>
210
- </ThemeProvider>
211
- </span>
212
- </ConditionalControlInfotip>
213
- </Stack>
214
- </CardOverlay>
215
- </StyledCard>
216
- </ControlActions>
217
- </Stack>
218
- );
219
- } );
119
+ const handleIconLibrarySelect = ( icon: { value: string; library: string } ) => {
120
+ setIconValue( createIconPropValue( icon.value, icon.library ) );
121
+ };
122
+
123
+ const handleOpenIconLibrary = ( event: React.MouseEvent< HTMLElement > ) => {
124
+ const anchor = measureIconLibraryAnchor( controlContainerRef.current, buttonGroupRef.current );
125
+
126
+ if ( ! anchor ) {
127
+ return;
128
+ }
129
+
130
+ setIconLibraryAnchor( anchor );
131
+ iconLibraryPopoverState.open( event );
132
+ };
220
133
 
221
- function SvgMediaPreview( {
222
- isFetching,
223
- src,
224
- iconClassName,
225
- iconLibrary,
226
- }: {
227
- isFetching: boolean;
228
- src: string | null;
229
- iconClassName: string | null;
230
- iconLibrary: string | null;
231
- } ) {
232
134
  useEffect( () => {
233
- if ( iconLibrary ) {
234
- enqueueIconFonts( iconLibrary );
135
+ if ( ! iconLibraryPopoverState.isOpen ) {
136
+ return;
235
137
  }
236
- }, [ iconLibrary ] );
237
138
 
238
- if ( isFetching ) {
239
- return <CircularProgress role="progressbar" />;
240
- }
139
+ const handleResize = () => {
140
+ const nextAnchor = measureIconLibraryAnchor( controlContainerRef.current, buttonGroupRef.current );
241
141
 
242
- if ( iconClassName ) {
243
- return (
244
- <Box
245
- component="i"
246
- className={ iconClassName }
247
- aria-label={ __( 'Preview icon', 'elementor' ) }
248
- sx={ { fontSize: ICON_PREVIEW_FONT_SIZE } }
249
- />
250
- );
251
- }
142
+ if ( nextAnchor ) {
143
+ setIconLibraryAnchor( nextAnchor );
144
+ }
145
+ };
146
+
147
+ window.addEventListener( 'resize', handleResize );
148
+
149
+ return () => {
150
+ window.removeEventListener( 'resize', handleResize );
151
+ };
152
+ }, [ iconLibraryPopoverState.isOpen ] );
153
+
154
+ const iconLibraryWidth = iconLibraryAnchor?.width ?? ICON_LIBRARY_POPOVER_WIDTH;
252
155
 
253
156
  return (
254
- <CardMedia
255
- component="img"
256
- image={ src }
257
- alt={ __( 'Preview SVG', 'elementor' ) }
258
- sx={ { maxHeight: '140px', width: `${ ICON_PREVIEW_FONT_SIZE }px` } }
259
- />
157
+ <Stack gap={ 1 } aria-label={ __( 'SVG control', 'elementor' ) }>
158
+ <EnableUnfilteredModal open={ unfilteredModalOpenState } onClose={ handleCloseUnfilteredModal } />
159
+ { showIconLibrary && iconLibraryAnchor ? (
160
+ <Popover
161
+ disableScrollLock
162
+ open={ iconLibraryPopoverState.isOpen }
163
+ onClose={ handleCloseIconLibrary }
164
+ anchorReference="anchorPosition"
165
+ anchorPosition={ { top: iconLibraryAnchor.top, left: iconLibraryAnchor.left } }
166
+ anchorOrigin={ { vertical: 'top', horizontal: 'left' } }
167
+ transformOrigin={ { vertical: 'top', horizontal: 'left' } }
168
+ marginThreshold={ 0 }
169
+ PaperProps={ {
170
+ sx: {
171
+ width: iconLibraryWidth,
172
+ minWidth: iconLibraryWidth,
173
+ maxWidth: iconLibraryWidth,
174
+ m: 0,
175
+ },
176
+ } }
177
+ >
178
+ <IconLibraryPopover
179
+ open={ iconLibraryPopoverState.isOpen }
180
+ selectedIconClass={ selectedIconClass }
181
+ selectedIconLibrary={ selectedIconLibrary }
182
+ onSelect={ handleIconLibrarySelect }
183
+ onClose={ handleCloseIconLibrary }
184
+ width={ iconLibraryWidth }
185
+ />
186
+ </Popover>
187
+ ) : null }
188
+ <Box
189
+ ref={ controlContainerRef }
190
+ data-testid={ SVG_MEDIA_CONTROL_CONTAINER_TEST_ID }
191
+ sx={ { width: '100%' } }
192
+ >
193
+ <ControlActions>
194
+ <StyledCard variant="outlined">
195
+ <StyledCardMediaContainer>
196
+ <SvgMediaPreview
197
+ isFetching={ isFetching }
198
+ src={ src }
199
+ iconClassName={ selectedIconClass }
200
+ iconLibrary={ selectedIconLibrary }
201
+ />
202
+ </StyledCardMediaContainer>
203
+ <CardOverlay
204
+ sx={ {
205
+ '&:hover': {
206
+ backgroundColor: 'rgba( 0, 0, 0, 0.75 )',
207
+ },
208
+ } }
209
+ >
210
+ <SvgMediaOverlay
211
+ isAdmin={ isAdmin }
212
+ showIconLibrary={ showIconLibrary }
213
+ buttonGroupRef={ buttonGroupRef }
214
+ onSelectSvg={ handleSelectSvg }
215
+ onUpload={ handleUpload }
216
+ onOpenIconLibrary={ handleOpenIconLibrary }
217
+ infotipTitle={ __( "Sorry, you can't upload that file yet.", 'elementor' ) }
218
+ infotipDescription={ <UnfilteredUploadInfotipDescription /> }
219
+ />
220
+ </CardOverlay>
221
+ </StyledCard>
222
+ </ControlActions>
223
+ </Box>
224
+ </Stack>
260
225
  );
261
- }
226
+ } );
227
+
228
+ const UnfilteredUploadInfotipDescription = () => (
229
+ <>
230
+ { __( 'To upload them anyway, ask the site administrator to enable unfiltered', 'elementor' ) }
231
+ <br />
232
+ { __( 'file uploads.', 'elementor' ) }
233
+ </>
234
+ );
@@ -0,0 +1,137 @@
1
+ import * as React from 'react';
2
+ import { LibraryIcon, UploadIcon } from '@elementor/icons';
3
+ import { Box, Button, Stack, styled, type SxProps, ThemeProvider, Typography } from '@elementor/ui';
4
+ import { __ } from '@wordpress/i18n';
5
+
6
+ import { ConditionalControlInfotip } from '../components/conditional-control-infotip';
7
+
8
+ export const SVG_MEDIA_CONTROL_CONTAINER_TEST_ID = 'svg-media-control-container';
9
+ export const SVG_MEDIA_ACTION_GROUP_TEST_ID = 'svg-media-action-group';
10
+
11
+ const MEDIA_ACTION_PADDING_Y = 0.75;
12
+ const MEDIA_ACTION_PADDING_X = 2;
13
+ const ICON_LIBRARY_PADDING = 0.625;
14
+
15
+ const svgButtonSx = {
16
+ px: MEDIA_ACTION_PADDING_X,
17
+ py: MEDIA_ACTION_PADDING_Y,
18
+ };
19
+
20
+ const MediaActionGroup = styled( Stack )( ( { theme } ) => ( {
21
+ display: 'inline-flex',
22
+ flexDirection: 'row',
23
+ alignItems: 'stretch',
24
+ border: '1px solid currentColor',
25
+ borderRadius: theme.shape.borderRadius,
26
+ overflow: 'hidden',
27
+ '& .MuiButton-root': {
28
+ border: 'none',
29
+ borderRadius: 0,
30
+ lineHeight: 1,
31
+ },
32
+ } ) );
33
+
34
+ type SvgMediaOverlayProps = {
35
+ isAdmin: boolean;
36
+ showIconLibrary: boolean;
37
+ buttonGroupRef: React.Ref< HTMLDivElement >;
38
+ onSelectSvg: () => void;
39
+ onUpload: () => void;
40
+ onOpenIconLibrary: ( event: React.MouseEvent< HTMLElement > ) => void;
41
+ infotipTitle: string;
42
+ infotipDescription: React.ReactNode;
43
+ };
44
+
45
+ export const SvgMediaOverlay = ( {
46
+ isAdmin,
47
+ showIconLibrary,
48
+ buttonGroupRef,
49
+ onSelectSvg,
50
+ onUpload,
51
+ onOpenIconLibrary,
52
+ infotipTitle,
53
+ infotipDescription,
54
+ }: SvgMediaOverlayProps ) => (
55
+ <Stack alignItems="center" gap={ 1 }>
56
+ <MediaActionGroup ref={ buttonGroupRef } direction="row" data-testid={ SVG_MEDIA_ACTION_GROUP_TEST_ID }>
57
+ <Button
58
+ size="tiny"
59
+ color="inherit"
60
+ variant="text"
61
+ onClick={ onSelectSvg }
62
+ aria-label={ __( 'Select', 'elementor' ) }
63
+ sx={ svgButtonSx }
64
+ >
65
+ { __( 'Select', 'elementor' ) }
66
+ </Button>
67
+ <Box
68
+ sx={ {
69
+ width: '1px',
70
+ alignSelf: 'stretch',
71
+ bgcolor: 'currentColor',
72
+ flexShrink: 0,
73
+ } }
74
+ />
75
+ <ConditionalControlInfotip
76
+ title={ infotipTitle }
77
+ description={ infotipDescription }
78
+ isEnabled={ ! isAdmin }
79
+ >
80
+ <Box component="span" sx={ { display: 'inline-flex' } }>
81
+ <UploadControl isAdmin={ isAdmin } onUpload={ onUpload } />
82
+ </Box>
83
+ </ConditionalControlInfotip>
84
+ </MediaActionGroup>
85
+ { showIconLibrary ? (
86
+ <Button
87
+ size="tiny"
88
+ color="inherit"
89
+ variant="text"
90
+ startIcon={ <LibraryIcon sx={ { height: '18px', width: '16px' } } /> }
91
+ aria-label={ __( 'Icon library', 'elementor' ) }
92
+ onClick={ onOpenIconLibrary }
93
+ sx={ {
94
+ height: '28px',
95
+ p: ICON_LIBRARY_PADDING,
96
+ '.MuiButton-icon': { ml: 0 },
97
+ } }
98
+ >
99
+ <Typography>{ __( 'Icon library', 'elementor' ) }</Typography>
100
+ </Button>
101
+ ) : null }
102
+ </Stack>
103
+ );
104
+
105
+ const UploadControl = ( { isAdmin, onUpload }: { isAdmin: boolean; onUpload: () => void } ) => {
106
+ if ( isAdmin ) {
107
+ return <UploadButton sx={ svgButtonSx } onClick={ onUpload } />;
108
+ }
109
+
110
+ return (
111
+ <ThemeProvider colorScheme="dark">
112
+ <UploadButton disabled sx={ svgButtonSx } />
113
+ </ThemeProvider>
114
+ );
115
+ };
116
+
117
+ const UploadButton = ( {
118
+ disabled = false,
119
+ sx,
120
+ onClick,
121
+ }: {
122
+ disabled?: boolean;
123
+ sx?: SxProps;
124
+ onClick?: () => void;
125
+ } ) => (
126
+ <Button
127
+ sx={ sx }
128
+ size="tiny"
129
+ color="inherit"
130
+ variant="text"
131
+ disabled={ disabled }
132
+ onClick={ onClick }
133
+ aria-label={ __( 'Upload', 'elementor' ) }
134
+ >
135
+ <UploadIcon />
136
+ </Button>
137
+ );
@@ -0,0 +1,68 @@
1
+ import * as React from 'react';
2
+ import { Box, CardMedia, CircularProgress } from '@elementor/ui';
3
+ import { __ } from '@wordpress/i18n';
4
+
5
+ import { findFontAwesome7Icon, type FontAwesome7Icon } from './icon-library/font-awesome-7-catalog';
6
+ import { FontAwesomeGlyph } from './icon-library/font-awesome-glyph';
7
+ import { useFontAwesome7Catalog } from './icon-library/use-font-awesome-7-catalog';
8
+
9
+ const ICON_PREVIEW_SIZE = 50;
10
+ const ICON_PREVIEW_COLOR = '#000000';
11
+
12
+ type SvgMediaPreviewProps = {
13
+ isFetching: boolean;
14
+ src: string | null;
15
+ iconClassName: string | null;
16
+ iconLibrary: string | null;
17
+ };
18
+
19
+ export const SvgMediaPreview = ( { isFetching, src, iconClassName, iconLibrary }: SvgMediaPreviewProps ) => {
20
+ const shouldLoadIconCatalog = Boolean( iconClassName && iconLibrary );
21
+ const { data: icons = [], isLoading } = useFontAwesome7Catalog( shouldLoadIconCatalog );
22
+ const selectedIcon = findFontAwesome7Icon( icons, iconClassName, iconLibrary );
23
+
24
+ if ( isFetching || ( shouldLoadIconCatalog && isLoading ) ) {
25
+ return <CircularProgress role="progressbar" />;
26
+ }
27
+
28
+ if ( selectedIcon ) {
29
+ return <IconPreview icon={ selectedIcon } />;
30
+ }
31
+
32
+ if ( shouldLoadIconCatalog ) {
33
+ return <IconPreviewPlaceholder />;
34
+ }
35
+
36
+ return <SvgPreview src={ src } />;
37
+ };
38
+
39
+ const IconPreview = ( { icon }: { icon: FontAwesome7Icon } ) => (
40
+ <Box sx={ { color: ICON_PREVIEW_COLOR } }>
41
+ <FontAwesomeGlyph
42
+ icon={ icon }
43
+ size={ ICON_PREVIEW_SIZE }
44
+ color={ ICON_PREVIEW_COLOR }
45
+ label={ __( 'Preview icon', 'elementor' ) }
46
+ />
47
+ </Box>
48
+ );
49
+
50
+ const IconPreviewPlaceholder = () => (
51
+ <Box
52
+ aria-hidden
53
+ sx={ {
54
+ width: ICON_PREVIEW_SIZE,
55
+ height: ICON_PREVIEW_SIZE,
56
+ color: ICON_PREVIEW_COLOR,
57
+ } }
58
+ />
59
+ );
60
+
61
+ const SvgPreview = ( { src }: { src: string | null } ) => (
62
+ <CardMedia
63
+ component="img"
64
+ image={ src }
65
+ alt={ __( 'Preview SVG', 'elementor' ) }
66
+ sx={ { maxHeight: '140px', width: `${ ICON_PREVIEW_SIZE }px`, color: ICON_PREVIEW_COLOR } }
67
+ />
68
+ );
package/src/index.ts CHANGED
@@ -37,6 +37,12 @@ export { TransformSettingsControl } from './controls/transform-control/transform
37
37
  export { TransitionRepeaterControl } from './controls/transition-control/transition-repeater-control';
38
38
  export { PopoverContent } from './components/popover-content';
39
39
  export { enqueueFont } from './controls/font-family-control/enqueue-font';
40
+ export {
41
+ getFontAwesome7IconName,
42
+ resetFontAwesome7IconsCache,
43
+ resolveFontAwesome7Icon,
44
+ type FontAwesome7IconDefinition,
45
+ } from './controls/icon-library/font-awesome-7-data';
40
46
  export { transitionProperties, transitionsItemsList } from './controls/transition-control/data';
41
47
  export { DateTimeControl } from './controls/date-time-control';
42
48
  export { DateRangeControl } from './controls/date-range-control';