@elementor/editor-controls 4.4.0-beta2 → 4.4.0-beta3

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,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": "4.4.0-beta2",
4
+ "version": "4.4.0-beta3",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -40,23 +40,23 @@
40
40
  "dev": "tsup --config=../../tsup.dev.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@elementor/editor-current-user": "4.4.0-beta2",
44
- "@elementor/editor-elements": "4.4.0-beta2",
45
- "@elementor/editor-props": "4.4.0-beta2",
46
- "@elementor/editor-responsive": "4.4.0-beta2",
47
- "@elementor/editor-ui": "4.4.0-beta2",
48
- "@elementor/editor-v1-adapters": "4.4.0-beta2",
49
- "@elementor/env": "4.4.0-beta2",
50
- "@elementor/events": "4.4.0-beta2",
51
- "@elementor/http-client": "4.4.0-beta2",
43
+ "@elementor/editor-current-user": "4.4.0-beta3",
44
+ "@elementor/editor-elements": "4.4.0-beta3",
45
+ "@elementor/editor-props": "4.4.0-beta3",
46
+ "@elementor/editor-responsive": "4.4.0-beta3",
47
+ "@elementor/editor-ui": "4.4.0-beta3",
48
+ "@elementor/editor-v1-adapters": "4.4.0-beta3",
49
+ "@elementor/env": "4.4.0-beta3",
50
+ "@elementor/events": "4.4.0-beta3",
51
+ "@elementor/http-client": "4.4.0-beta3",
52
52
  "@elementor/icons": "~1.78.1",
53
- "@elementor/locations": "4.4.0-beta2",
54
- "@elementor/query": "4.4.0-beta2",
55
- "@elementor/schema": "4.4.0-beta2",
56
- "@elementor/session": "4.4.0-beta2",
53
+ "@elementor/locations": "4.4.0-beta3",
54
+ "@elementor/query": "4.4.0-beta3",
55
+ "@elementor/schema": "4.4.0-beta3",
56
+ "@elementor/session": "4.4.0-beta3",
57
57
  "@elementor/ui": "1.41.4",
58
- "@elementor/utils": "4.4.0-beta2",
59
- "@elementor/wp-media": "4.4.0-beta2",
58
+ "@elementor/utils": "4.4.0-beta3",
59
+ "@elementor/wp-media": "4.4.0-beta3",
60
60
  "@monaco-editor/react": "^4.7.0",
61
61
  "@tanstack/react-virtual": "^3.13.3",
62
62
  "@tiptap/extension-bold": "^3.11.1",
@@ -1,7 +1,8 @@
1
1
  import * as React from 'react';
2
2
  import { useLayoutEffect, useRef, useState } from 'react';
3
+ import { useRovingFocus } from '@elementor/editor-ui';
3
4
  import { Box, Tooltip, useTheme } from '@elementor/ui';
4
- import { useVirtualizer } from '@tanstack/react-virtual';
5
+ import { defaultRangeExtractor, useVirtualizer } from '@tanstack/react-virtual';
5
6
  import { __ } from '@wordpress/i18n';
6
7
 
7
8
  import { type FontAwesome7Icon } from './font-awesome-7-catalog';
@@ -63,8 +64,6 @@ export const IconLibraryGrid = ( {
63
64
  }: IconLibraryGridProps ) => {
64
65
  const theme = useTheme();
65
66
  const containerRef = useRef< HTMLDivElement >( null );
66
- const cellRefs = useRef( new Map< string, HTMLButtonElement >() );
67
- const shouldRestoreFocusRef = useRef( false );
68
67
  const selectedIndex = items.findIndex( ( item ) => item.id === selectedValue );
69
68
  const [ focusedIndex, setFocusedIndex ] = useState( selectedIndex >= 0 ? selectedIndex : 0 );
70
69
  const [ { columnCount, cellSize }, setGridMetrics ] = useState( () => getGridMetrics( 0, 0, 0 ) );
@@ -79,6 +78,16 @@ export const IconLibraryGrid = ( {
79
78
  paddingStart: verticalPadding,
80
79
  paddingEnd: verticalPadding,
81
80
  overscan: GRID_OVERSCAN,
81
+ rangeExtractor: ( range ) => {
82
+ const rowIndexes = defaultRangeExtractor( range );
83
+ const focusedRow = Math.floor( focusedIndex / columnCount );
84
+
85
+ if ( items.length === 0 || rowIndexes.includes( focusedRow ) ) {
86
+ return rowIndexes;
87
+ }
88
+
89
+ return [ ...rowIndexes, focusedRow ].sort( ( a, b ) => a - b );
90
+ },
82
91
  } );
83
92
  const focusedItem = items[ focusedIndex ];
84
93
  const visibleRowIndexes = virtualizer.getVirtualIndexes().join( ',' );
@@ -103,20 +112,10 @@ export const IconLibraryGrid = ( {
103
112
  // eslint-disable-next-line react-hooks/exhaustive-deps
104
113
  }, [ cellSize, columnCount, items, selectedIndex, selectedValue ] );
105
114
 
106
- useLayoutEffect( () => {
107
- if ( ! shouldRestoreFocusRef.current || ! focusedItem ) {
108
- return;
109
- }
110
-
111
- const cell = cellRefs.current.get( focusedItem.id );
112
-
113
- if ( ! cell ) {
114
- return;
115
- }
116
-
117
- cell.focus();
118
- shouldRestoreFocusRef.current = false;
119
- }, [ focusedItem, visibleRowIndexes ] );
115
+ const { registerRef, requestFocus } = useRovingFocus< HTMLButtonElement >( {
116
+ tabbableKey: focusedItem?.id,
117
+ mountedSignature: visibleRowIndexes,
118
+ } );
120
119
 
121
120
  useLayoutEffect( () => {
122
121
  const container = containerRef.current;
@@ -152,7 +151,7 @@ export const IconLibraryGrid = ( {
152
151
  return;
153
152
  }
154
153
 
155
- shouldRestoreFocusRef.current = true;
154
+ requestFocus();
156
155
  setFocusedIndex( nextIndex );
157
156
  virtualizer.scrollToIndex( Math.floor( nextIndex / columnCount ) );
158
157
  };
@@ -281,13 +280,7 @@ export const IconLibraryGrid = ( {
281
280
  aria-label={ item.label }
282
281
  aria-selected={ isSelected }
283
282
  tabIndex={ tabIndex }
284
- ref={ ( node: HTMLButtonElement | null ) => {
285
- if ( node ) {
286
- cellRefs.current.set( item.id, node );
287
- } else {
288
- cellRefs.current.delete( item.id );
289
- }
290
- } }
283
+ ref={ registerRef( item.id ) }
291
284
  onClick={ () => {
292
285
  onSelect( item.id );
293
286
  onClose();
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { useMemo, useState } from 'react';
2
+ import { useMemo, useRef, useState } from 'react';
3
3
  import {
4
4
  PopoverBody,
5
5
  PopoverHeader,
@@ -103,6 +103,8 @@ export const IconLibraryPopover = ( {
103
103
  [ icons, selectedIconClass, selectedIconLibrary ]
104
104
  );
105
105
 
106
+ const contentRef = useRef< HTMLDivElement >( null );
107
+
106
108
  const handleClose = () => {
107
109
  setSearchValue( '' );
108
110
  setActiveLibraries( [] );
@@ -122,6 +124,19 @@ export const IconLibraryPopover = ( {
122
124
  } );
123
125
  };
124
126
 
127
+ const handleSearchKeyDown = ( event: React.KeyboardEvent ) => {
128
+ if ( event.key !== 'ArrowDown' || view !== 'list' ) {
129
+ return;
130
+ }
131
+
132
+ const option = contentRef.current?.querySelector< HTMLElement >( '[role="option"][tabindex="0"]' );
133
+
134
+ if ( option ) {
135
+ event.preventDefault();
136
+ option.focus();
137
+ }
138
+ };
139
+
125
140
  const handleClearSearch = () => {
126
141
  setSearchValue( '' );
127
142
  };
@@ -150,17 +165,19 @@ export const IconLibraryPopover = ( {
150
165
  gap={ 1 }
151
166
  sx={ { px: ICON_LIBRARY_INLINE_SPACING, pb: ICON_LIBRARY_SEARCH_PADDING_BOTTOM } }
152
167
  >
153
- <SearchField
154
- value={ searchInputValue }
155
- onSearch={ handleSearchChange }
156
- placeholder={ __( 'Search', 'elementor' ) }
157
- id="icon-library-search"
158
- sx={ { flex: 1, px: 0, pb: 0 } }
159
- />
168
+ <Box onKeyDown={ handleSearchKeyDown } sx={ { flex: 1, minWidth: 0 } }>
169
+ <SearchField
170
+ value={ searchInputValue }
171
+ onSearch={ handleSearchChange }
172
+ placeholder={ __( 'Search', 'elementor' ) }
173
+ id="icon-library-search"
174
+ sx={ { width: '100%', px: 0, pb: 0 } }
175
+ />
176
+ </Box>
160
177
  <IconLibraryFilter value={ activeLibraries } onChange={ setActiveLibraries } />
161
178
  </Stack>
162
179
  <Divider />
163
- <Box sx={ { flex: 1, overflow: 'hidden', minHeight: 0, minWidth: 0 } }>
180
+ <Box ref={ contentRef } sx={ { flex: 1, overflow: 'hidden', minHeight: 0, minWidth: 0 } }>
164
181
  <IconLibraryContent
165
182
  isLoading={ isLoading }
166
183
  items={ items }
@@ -235,6 +252,7 @@ const IconLibraryContent = ( {
235
252
  onClose={ onClose }
236
253
  itemHeight={ ICON_LIBRARY_ROW_HEIGHT }
237
254
  menuItemContentTemplate={ IconLibraryRow }
255
+ rovingFocus
238
256
  noResultsComponent={ emptyState }
239
257
  data-testid="icon-library-list"
240
258
  />