@elementor/editor-variables 3.32.0-49 → 3.32.0-50

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-variables",
3
- "version": "3.32.0-49",
3
+ "version": "3.32.0-50",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,18 +39,18 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "3.32.0-49",
43
- "@elementor/editor-canvas": "3.32.0-49",
44
- "@elementor/editor-controls": "3.32.0-49",
45
- "@elementor/editor-current-user": "3.32.0-49",
46
- "@elementor/editor-editing-panel": "3.32.0-49",
47
- "@elementor/editor-panels": "3.32.0-49",
48
- "@elementor/editor-props": "3.32.0-49",
49
- "@elementor/editor-ui": "3.32.0-49",
50
- "@elementor/editor-v1-adapters": "3.32.0-49",
51
- "@elementor/http-client": "3.32.0-49",
42
+ "@elementor/editor": "3.32.0-50",
43
+ "@elementor/editor-canvas": "3.32.0-50",
44
+ "@elementor/editor-controls": "3.32.0-50",
45
+ "@elementor/editor-current-user": "3.32.0-50",
46
+ "@elementor/editor-editing-panel": "3.32.0-50",
47
+ "@elementor/editor-panels": "3.32.0-50",
48
+ "@elementor/editor-props": "3.32.0-50",
49
+ "@elementor/editor-ui": "3.32.0-50",
50
+ "@elementor/editor-v1-adapters": "3.32.0-50",
51
+ "@elementor/http-client": "3.32.0-50",
52
52
  "@elementor/icons": "1.46.0",
53
- "@elementor/schema": "3.32.0-49",
53
+ "@elementor/schema": "3.32.0-50",
54
54
  "@elementor/ui": "1.36.8",
55
55
  "@wordpress/i18n": "^5.13.0"
56
56
  },
@@ -28,7 +28,7 @@ export const VariableSelectionPopover = ( { closePopover, propTypeKey, selectedV
28
28
  const [ currentView, setCurrentView ] = useState< View >( VIEW_LIST );
29
29
  const [ editId, setEditId ] = useState< string >( '' );
30
30
  const { open } = usePanelActions();
31
- const onSettingsAvailable = isExperimentActive( 'e_variables_settings' )
31
+ const onSettingsAvailable = isExperimentActive( 'e_variables_manager' )
32
32
  ? () => {
33
33
  open();
34
34
  }
@@ -98,8 +98,8 @@ function RenderView( props: ViewProps ): React.ReactNode {
98
98
 
99
99
  if ( userPermissions.canManageSettings() && props.onSettings ) {
100
100
  handlers.onSettings = () => {
101
- props.onSettings?.();
102
101
  props.closePopover();
102
+ props.onSettings?.();
103
103
  };
104
104
  }
105
105
 
@@ -0,0 +1,72 @@
1
+ import * as React from 'react';
2
+ import { createElement } from 'react';
3
+ import { DotsVerticalIcon } from '@elementor/icons';
4
+ import { bindMenu, bindTrigger, IconButton, Menu, MenuItem, type SvgIconProps, usePopupState } from '@elementor/ui';
5
+
6
+ export type VariableManagerMenuAction = {
7
+ name: string;
8
+ icon: React.ForwardRefExoticComponent< Omit< SvgIconProps, 'ref' > & React.RefAttributes< SVGSVGElement > >;
9
+ color: string;
10
+ onClick: () => void;
11
+ };
12
+
13
+ type VariableEditMenuProps = {
14
+ menuActions: VariableManagerMenuAction[];
15
+ disabled?: boolean;
16
+ };
17
+
18
+ export const VariableEditMenu = ( { menuActions, disabled }: VariableEditMenuProps ) => {
19
+ const menuState = usePopupState( {
20
+ variant: 'popover',
21
+ } );
22
+
23
+ return (
24
+ <>
25
+ <IconButton { ...bindTrigger( menuState ) } disabled={ disabled } size="tiny">
26
+ <DotsVerticalIcon fontSize="tiny" />
27
+ </IconButton>
28
+
29
+ <Menu
30
+ disablePortal
31
+ MenuListProps={ {
32
+ dense: true,
33
+ } }
34
+ PaperProps={ {
35
+ elevation: 6,
36
+ } }
37
+ { ...bindMenu( menuState ) }
38
+ anchorEl={ menuState.anchorEl }
39
+ anchorOrigin={ {
40
+ vertical: 'bottom',
41
+ horizontal: 'right',
42
+ } }
43
+ transformOrigin={ {
44
+ vertical: 'top',
45
+ horizontal: 'right',
46
+ } }
47
+ open={ menuState.isOpen }
48
+ onClose={ menuState.close }
49
+ >
50
+ { menuActions.map( ( action ) => (
51
+ <MenuItem
52
+ key={ action.name }
53
+ onClick={ () => {
54
+ action.onClick?.();
55
+ menuState.close();
56
+ } }
57
+ sx={ {
58
+ color: action.color,
59
+ gap: 1,
60
+ } }
61
+ >
62
+ { action.icon &&
63
+ createElement( action.icon, {
64
+ fontSize: 'inherit',
65
+ } ) }{ ' ' }
66
+ { action.name }
67
+ </MenuItem>
68
+ ) ) }
69
+ </Menu>
70
+ </>
71
+ );
72
+ };
@@ -0,0 +1,37 @@
1
+ import * as React from 'react';
2
+ import { type SxProps, TableCell } from '@elementor/ui';
3
+
4
+ type VariableTableCellProps = {
5
+ children?: React.ReactNode;
6
+ isHeader?: boolean;
7
+ width?: number;
8
+ maxWidth?: number;
9
+ align?: 'left' | 'right' | 'center';
10
+ noPadding?: boolean;
11
+ sx?: SxProps;
12
+ };
13
+
14
+ export const VariableTableCell = ( {
15
+ children,
16
+ isHeader,
17
+ width,
18
+ maxWidth,
19
+ align,
20
+ noPadding,
21
+ sx,
22
+ }: VariableTableCellProps ) => {
23
+ const baseSx: SxProps = {
24
+ maxWidth: maxWidth ?? 150,
25
+ cursor: 'initial',
26
+ typography: 'caption',
27
+ ...( isHeader && { color: 'text.primary', fontWeight: 'bold' } ),
28
+ ...( width && { width } ),
29
+ ...sx,
30
+ };
31
+
32
+ return (
33
+ <TableCell size="small" padding={ noPadding ? 'none' : undefined } align={ align } sx={ baseSx }>
34
+ { children }
35
+ </TableCell>
36
+ );
37
+ };
@@ -10,10 +10,12 @@ import {
10
10
  } from '@elementor/editor-panels';
11
11
  import { ThemeProvider } from '@elementor/editor-ui';
12
12
  import { changeEditMode } from '@elementor/editor-v1-adapters';
13
- import { FilterIcon, XIcon } from '@elementor/icons';
13
+ import { ColorFilterIcon, TrashIcon, XIcon } from '@elementor/icons';
14
14
  import { Alert, Box, Button, Divider, ErrorBoundary, IconButton, type IconButtonProps, Stack } from '@elementor/ui';
15
15
  import { __ } from '@wordpress/i18n';
16
16
 
17
+ import { VariablesManagerTable } from './variables-manager-table';
18
+
17
19
  const id = 'variables-manager';
18
20
 
19
21
  export const { panel, usePanelActions } = createPanel( {
@@ -34,6 +36,15 @@ export function VariablesManagerPanel() {
34
36
 
35
37
  usePreventUnload( isDirty );
36
38
 
39
+ const menuActions = [
40
+ {
41
+ name: __( 'Delete', 'elementor' ),
42
+ icon: TrashIcon,
43
+ color: 'error.main',
44
+ onClick: () => {},
45
+ },
46
+ ];
47
+
37
48
  return (
38
49
  <ThemeProvider>
39
50
  <ErrorBoundary fallback={ <ErrorBoundaryFallback /> }>
@@ -42,8 +53,8 @@ export function VariablesManagerPanel() {
42
53
  <Stack p={ 1 } pl={ 2 } width="100%" direction="row" alignItems="center">
43
54
  <Stack width="100%" direction="row" gap={ 1 }>
44
55
  <PanelHeaderTitle sx={ { display: 'flex', alignItems: 'center', gap: 0.5 } }>
45
- <FilterIcon fontSize="inherit" />
46
- { __( 'Variables Manager', 'elementor' ) }
56
+ <ColorFilterIcon fontSize="inherit" />
57
+ { __( 'Variable Manager', 'elementor' ) }
47
58
  </PanelHeaderTitle>
48
59
  </Stack>
49
60
  <CloseButton
@@ -62,15 +73,7 @@ export function VariablesManagerPanel() {
62
73
  } }
63
74
  >
64
75
  <Divider />
65
- <Box
66
- px={ 2 }
67
- sx={ {
68
- flexGrow: 1,
69
- overflowY: 'auto',
70
- } }
71
- >
72
- List
73
- </Box>
76
+ <VariablesManagerTable menuActions={ menuActions } />
74
77
  </PanelBody>
75
78
 
76
79
  <PanelFooter>
@@ -0,0 +1,172 @@
1
+ import * as React from 'react';
2
+ import { createElement, useState } from 'react';
3
+ import { EllipsisWithTooltip } from '@elementor/editor-ui';
4
+ import { GripVerticalIcon } from '@elementor/icons';
5
+ import {
6
+ IconButton,
7
+ Stack,
8
+ type SxProps,
9
+ Table,
10
+ TableBody,
11
+ TableContainer,
12
+ TableHead,
13
+ TableRow,
14
+ UnstableSortableItem,
15
+ type UnstableSortableItemRenderProps,
16
+ UnstableSortableProvider,
17
+ } from '@elementor/ui';
18
+ import { __ } from '@wordpress/i18n';
19
+
20
+ import { getVariables } from '../../hooks/use-prop-variables';
21
+ import { getVariableType } from '../../variables-registry/variable-type-registry';
22
+ import { VariableEditMenu, type VariableManagerMenuAction } from './variable-edit-menu';
23
+ import { VariableTableCell } from './variable-table-cell';
24
+
25
+ type Props = {
26
+ menuActions: VariableManagerMenuAction[];
27
+ };
28
+
29
+ export const VariablesManagerTable = ( { menuActions }: Props ) => {
30
+ const variables = getVariables( false );
31
+
32
+ const [ ids, setIds ] = useState< string[] >( Object.keys( variables ) );
33
+ const rows = ids.map( ( id ) => ( {
34
+ id,
35
+ name: variables[ id ].label,
36
+ value: variables[ id ].value,
37
+ type: variables[ id ].type,
38
+ icon: getVariableType( variables[ id ].type ).icon,
39
+ startIcon: getVariableType( variables[ id ].type ).startIcon,
40
+ } ) );
41
+
42
+ const tableSX: SxProps = {
43
+ minWidth: 250,
44
+ tableLayout: 'fixed',
45
+ };
46
+
47
+ return (
48
+ <TableContainer sx={ { overflow: 'initial' } }>
49
+ <Table sx={ tableSX } aria-label="Variables manager list with drag and drop reordering">
50
+ <TableHead>
51
+ <TableRow>
52
+ <VariableTableCell isHeader noPadding width={ 10 } maxWidth={ 10 } />
53
+ <VariableTableCell isHeader>{ __( 'Name', 'elementor' ) }</VariableTableCell>
54
+ <VariableTableCell isHeader>{ __( 'Value', 'elementor' ) }</VariableTableCell>
55
+ <VariableTableCell isHeader noPadding width={ 16 } maxWidth={ 16 } />
56
+ </TableRow>
57
+ </TableHead>
58
+ <TableBody>
59
+ <UnstableSortableProvider
60
+ value={ ids }
61
+ onChange={ setIds }
62
+ variant="static"
63
+ restrictAxis
64
+ dragOverlay={ ( { children: dragOverlayChildren, ...dragOverlayProps } ) => (
65
+ <Table sx={ tableSX } { ...dragOverlayProps }>
66
+ <TableBody>{ dragOverlayChildren }</TableBody>
67
+ </Table>
68
+ ) }
69
+ >
70
+ { rows.map( ( row ) => (
71
+ <UnstableSortableItem
72
+ key={ row.id }
73
+ id={ row.id }
74
+ render={ ( {
75
+ itemProps,
76
+ showDropIndication,
77
+ triggerProps,
78
+ itemStyle,
79
+ triggerStyle,
80
+ isDragged,
81
+ dropPosition,
82
+ setTriggerRef,
83
+ isDragOverlay,
84
+ isSorting,
85
+ index,
86
+ }: UnstableSortableItemRenderProps ) => {
87
+ const showIndicationBefore = showDropIndication && dropPosition === 'before';
88
+ const showIndicationAfter = showDropIndication && dropPosition === 'after';
89
+
90
+ return (
91
+ <TableRow
92
+ { ...itemProps }
93
+ selected={ isDragged }
94
+ sx={ {
95
+ ...( showIndicationBefore && {
96
+ '& td, & th': {
97
+ borderTop: '2px solid',
98
+ borderTopColor: 'primary.main',
99
+ },
100
+ } ),
101
+ ...( showIndicationAfter && {
102
+ '& td, & th': {
103
+ borderBottom: '2px solid',
104
+ borderBottomColor: 'primary.main',
105
+ },
106
+ } ),
107
+ '& [role="toolbar"], & [draggable]': {
108
+ opacity: 0,
109
+ },
110
+ '&:hover, &:focus-within': {
111
+ backgroundColor: 'action.hover',
112
+ '& [role="toolbar"], & [draggable]': {
113
+ opacity: 1,
114
+ },
115
+ },
116
+ } }
117
+ style={ { ...itemStyle, ...triggerStyle } }
118
+ disableDivider={ isDragOverlay || index === rows.length - 1 }
119
+ >
120
+ <VariableTableCell noPadding width={ 10 } maxWidth={ 10 }>
121
+ <IconButton
122
+ size="small"
123
+ ref={ setTriggerRef }
124
+ { ...triggerProps }
125
+ disabled={ isSorting }
126
+ draggable
127
+ >
128
+ <GripVerticalIcon fontSize="inherit" />
129
+ </IconButton>
130
+ </VariableTableCell>
131
+ <VariableTableCell>
132
+ <Stack direction="row" alignItems="center" gap={ 1 }>
133
+ { createElement( row.icon, { fontSize: 'inherit' } ) }
134
+ <EllipsisWithTooltip title={ row.name }>
135
+ { row.name }
136
+ </EllipsisWithTooltip>
137
+ </Stack>
138
+ </VariableTableCell>
139
+ <VariableTableCell>
140
+ <Stack direction="row" alignItems="center" gap={ 1 }>
141
+ { row.startIcon && row.startIcon( { value: row.value } ) }
142
+
143
+ <EllipsisWithTooltip title={ row.value }>
144
+ { row.value }
145
+ </EllipsisWithTooltip>
146
+ </Stack>
147
+ </VariableTableCell>
148
+ <VariableTableCell
149
+ align="right"
150
+ noPadding
151
+ width={ 16 }
152
+ maxWidth={ 16 }
153
+ sx={ { paddingInlineEnd: 1 } }
154
+ >
155
+ <Stack role="toolbar" direction="row" justifyContent="flex-end">
156
+ <VariableEditMenu
157
+ menuActions={ menuActions }
158
+ disabled={ isSorting }
159
+ />
160
+ </Stack>
161
+ </VariableTableCell>
162
+ </TableRow>
163
+ );
164
+ } }
165
+ />
166
+ ) ) }
167
+ </UnstableSortableProvider>
168
+ </TableBody>
169
+ </Table>
170
+ </TableContainer>
171
+ );
172
+ };
@@ -6,9 +6,19 @@ import { useVariableType } from '../context/variable-type-context';
6
6
  import { service } from '../service';
7
7
  import { type NormalizedVariable, type Variable } from '../types';
8
8
 
9
- export const useVariable = ( key: string ) => {
9
+ export const getVariables = ( includeDeleted = true ) => {
10
10
  const variables = service.variables();
11
11
 
12
+ if ( includeDeleted ) {
13
+ return variables;
14
+ }
15
+
16
+ return Object.fromEntries( Object.entries( variables ).filter( ( [ , variable ] ) => ! variable.deleted ) );
17
+ };
18
+
19
+ export const useVariable = ( key: string ) => {
20
+ const variables = getVariables();
21
+
12
22
  if ( ! variables?.[ key ] ) {
13
23
  return null;
14
24
  }
@@ -48,13 +58,11 @@ const usePropVariables = ( propKey: PropKey ): NormalizedVariable[] => {
48
58
  return useMemo( () => normalizeVariables( propKey ), [ propKey ] );
49
59
  };
50
60
 
51
- const isNotDeleted = ( { deleted }: { deleted?: boolean } ) => ! deleted;
52
-
53
- const normalizeVariables = ( propKey: string ): NormalizedVariable[] => {
54
- const variables = service.variables();
61
+ const normalizeVariables = ( propKey: string ) => {
62
+ const variables = getVariables( false );
55
63
 
56
64
  return Object.entries( variables )
57
- .filter( ( [ , variable ] ) => variable.type === propKey && isNotDeleted( variable ) )
65
+ .filter( ( [ , variable ] ) => variable.type === propKey )
58
66
  .map( ( [ key, { label, value } ] ) => ( {
59
67
  key,
60
68
  label,