@elementor/editor-editing-panel 1.5.1 → 1.7.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/index.d.mts +37 -3
  3. package/dist/index.d.ts +37 -3
  4. package/dist/index.js +816 -606
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +798 -570
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +6 -6
  9. package/src/components/css-class-menu.tsx +125 -0
  10. package/src/components/css-class-selector.tsx +156 -33
  11. package/src/components/multi-combobox.tsx +184 -0
  12. package/src/components/style-sections/border-section/border-field.tsx +1 -5
  13. package/src/components/style-sections/position-section/position-field.tsx +1 -0
  14. package/src/components/style-tab.tsx +13 -4
  15. package/src/contexts/css-class-item-context.tsx +31 -0
  16. package/src/contexts/style-context.tsx +4 -3
  17. package/src/controls-registry/create-top-level-object-type.ts +14 -0
  18. package/src/controls-registry/settings-field.tsx +12 -14
  19. package/src/controls-registry/styles-field.tsx +17 -5
  20. package/src/css-classes.ts +37 -0
  21. package/src/dynamics/components/dynamic-selection-control.tsx +1 -1
  22. package/src/dynamics/components/dynamic-selection.tsx +3 -4
  23. package/src/dynamics/dynamic-control.tsx +16 -11
  24. package/src/dynamics/hooks/use-dynamic-tag.ts +2 -3
  25. package/src/dynamics/hooks/use-prop-dynamic-action.tsx +1 -4
  26. package/src/dynamics/hooks/use-prop-dynamic-tags.ts +3 -6
  27. package/src/dynamics/utils.ts +1 -1
  28. package/src/hooks/use-styles-fields.ts +1 -0
  29. package/src/index.ts +1 -0
  30. package/src/init.ts +2 -0
  31. package/src/components/multi-combobox/index.ts +0 -3
  32. package/src/components/multi-combobox/multi-combobox.tsx +0 -122
  33. package/src/components/multi-combobox/types.ts +0 -29
  34. package/src/components/multi-combobox/use-combobox-actions.ts +0 -62
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import { createContext, type PropsWithChildren } from 'react';
3
+
4
+ type ClassItemContextType = {
5
+ styleId: string;
6
+ isGlobal: boolean;
7
+ isActive: boolean;
8
+ };
9
+ const ClassItemContext = createContext< ClassItemContextType >( {
10
+ styleId: '',
11
+ isGlobal: false,
12
+ isActive: false,
13
+ } );
14
+
15
+ type ClassItemProviderProps = PropsWithChildren< ClassItemContextType >;
16
+
17
+ export function CssClassItemProvider( { styleId, isGlobal, isActive, children }: ClassItemProviderProps ) {
18
+ return (
19
+ <ClassItemContext.Provider value={ { styleId, isGlobal, isActive } }>{ children }</ClassItemContext.Provider>
20
+ );
21
+ }
22
+
23
+ export function useCssClassItem() {
24
+ const context = React.useContext( ClassItemContext );
25
+
26
+ if ( ! context ) {
27
+ throw new Error( 'useCssClassItem must be used within a CssClassItemProvider' );
28
+ }
29
+
30
+ return context;
31
+ }
@@ -1,19 +1,20 @@
1
1
  import * as React from 'react';
2
2
  import { createContext, type Dispatch, type PropsWithChildren, useContext } from 'react';
3
- import { type StyleDefinition, type StyleVariant } from '@elementor/editor-styles';
3
+ import { type StyleDefinition, type StyleState, type StyleVariant } from '@elementor/editor-styles';
4
4
 
5
5
  type ContextValue = {
6
6
  id: StyleDefinition[ 'id' ] | null;
7
7
  setId: Dispatch< StyleDefinition[ 'id' ] | null >;
8
8
  meta: StyleVariant[ 'meta' ];
9
+ setMetaState: Dispatch< StyleState >;
9
10
  };
10
11
 
11
12
  const Context = createContext< ContextValue | null >( null );
12
13
 
13
14
  type Props = PropsWithChildren< ContextValue >;
14
15
 
15
- export function StyleProvider( { children, id, setId, meta }: Props ) {
16
- return <Context.Provider value={ { id, setId, meta } }>{ children }</Context.Provider>;
16
+ export function StyleProvider( { children, id, setId, meta, setMetaState }: Props ) {
17
+ return <Context.Provider value={ { id, setId, meta, setMetaState } }>{ children }</Context.Provider>;
17
18
  }
18
19
 
19
20
  export function useStyle() {
@@ -0,0 +1,14 @@
1
+ import { type ObjectPropType, type PropsSchema } from '@elementor/editor-props';
2
+
3
+ export const createTopLevelOjectType = ( { schema }: { schema: PropsSchema } ) => {
4
+ const schemaPropType: ObjectPropType = {
5
+ key: '',
6
+ kind: 'object',
7
+ meta: {},
8
+ settings: {},
9
+ default: null,
10
+ shape: schema,
11
+ };
12
+
13
+ return schemaPropType;
14
+ };
@@ -1,36 +1,34 @@
1
1
  import * as React from 'react';
2
- import { BoundPropProvider } from '@elementor/editor-controls';
2
+ import { PropKeyProvider, PropProvider } from '@elementor/editor-controls';
3
3
  import { updateSettings, useElementSetting } from '@elementor/editor-elements';
4
4
  import { type PropKey, type PropValue } from '@elementor/editor-props';
5
5
 
6
6
  import { useElement } from '../contexts/element-context';
7
+ import { createTopLevelOjectType } from './create-top-level-object-type';
7
8
 
8
9
  type Props = {
9
10
  bind: PropKey;
10
11
  children: React.ReactNode;
11
12
  };
12
13
 
13
- const SettingsField = ( { bind, children }: Props ) => {
14
+ export const SettingsField = ( { bind, children }: Props ) => {
14
15
  const { element, elementType } = useElement();
15
16
 
16
- const defaultValue = elementType.propsSchema[ bind ]?.default;
17
- const settingsValue = useElementSetting( element.id, bind );
18
- const value = settingsValue ?? defaultValue ?? null;
17
+ const settingsValue = useElementSetting< PropValue >( element.id, bind );
18
+ const value = { [ bind ]: settingsValue };
19
19
 
20
- const setValue = ( newValue: PropValue ) => {
20
+ const propType = createTopLevelOjectType( { schema: elementType.propsSchema } );
21
+
22
+ const setValue = ( newValue: Record< string, PropValue > ) => {
21
23
  updateSettings( {
22
24
  id: element.id,
23
- props: {
24
- [ bind ]: newValue,
25
- },
25
+ props: { ...newValue },
26
26
  } );
27
27
  };
28
28
 
29
29
  return (
30
- <BoundPropProvider setValue={ setValue } value={ value } bind={ bind }>
31
- { children }
32
- </BoundPropProvider>
30
+ <PropProvider propType={ propType } value={ value } setValue={ setValue }>
31
+ <PropKeyProvider bind={ bind }>{ children }</PropKeyProvider>
32
+ </PropProvider>
33
33
  );
34
34
  };
35
-
36
- export { SettingsField };
@@ -1,8 +1,10 @@
1
1
  import * as React from 'react';
2
- import { BoundPropProvider } from '@elementor/editor-controls';
3
- import { type PropKey } from '@elementor/editor-props';
2
+ import { PropKeyProvider, PropProvider } from '@elementor/editor-controls';
3
+ import { type PropKey, type PropValue } from '@elementor/editor-props';
4
+ import { getStylesSchema } from '@elementor/editor-styles';
4
5
 
5
6
  import { useStylesField } from '../hooks/use-styles-field';
7
+ import { createTopLevelOjectType } from './create-top-level-object-type';
6
8
 
7
9
  export type StylesFieldProps = {
8
10
  bind: PropKey;
@@ -12,9 +14,19 @@ export type StylesFieldProps = {
12
14
  export const StylesField = ( { bind, children }: StylesFieldProps ) => {
13
15
  const [ value, setValue ] = useStylesField( bind );
14
16
 
17
+ const stylesSchema = getStylesSchema();
18
+
19
+ const propType = createTopLevelOjectType( { schema: stylesSchema } );
20
+
21
+ const values = { [ bind ]: value };
22
+
23
+ const setValues = ( newValue: Record< string, PropValue > ) => {
24
+ setValue( newValue[ bind ] );
25
+ };
26
+
15
27
  return (
16
- <BoundPropProvider setValue={ setValue } value={ value } bind={ bind }>
17
- { children }
18
- </BoundPropProvider>
28
+ <PropProvider propType={ propType } value={ values } setValue={ setValues }>
29
+ <PropKeyProvider bind={ bind }>{ children }</PropKeyProvider>
30
+ </PropProvider>
19
31
  );
20
32
  };
@@ -0,0 +1,37 @@
1
+ import { type StyleState } from '@elementor/editor-styles';
2
+
3
+ import { registerStateMenuItem } from './components/css-class-menu';
4
+
5
+ const STATES: NonNullable< StyleState >[] = [ 'hover', 'focus', 'active' ];
6
+
7
+ export function initCssClasses() {
8
+ registerStateItems();
9
+ registerGlobalClassItems();
10
+ }
11
+
12
+ function registerStateItems() {
13
+ registerStateMenuItem( {
14
+ id: 'normal',
15
+ props: {
16
+ state: null,
17
+ },
18
+ } );
19
+
20
+ STATES.forEach( ( state ) => {
21
+ registerStateMenuItem( {
22
+ id: state,
23
+ props: {
24
+ state,
25
+ },
26
+ } );
27
+ } );
28
+ }
29
+
30
+ function registerGlobalClassItems() {
31
+ /**
32
+ * TODO - register the relevant global classes here
33
+ * change the import statement from 'css-class-menu' to -
34
+ * import { registerGlobalClassMenuItem, registerStateMenuItem } from './components/css-class-menu';
35
+ * and use registerGlobalClassMenuItem
36
+ */
37
+ }
@@ -42,7 +42,7 @@ export const DynamicSelectionControl = () => {
42
42
  const selectionPopoverId = useId();
43
43
  const selectionPopoverState = usePopupState( { variant: 'popover', popupId: selectionPopoverId } );
44
44
 
45
- const dynamicTag = useDynamicTag( bind, tagName );
45
+ const dynamicTag = useDynamicTag( tagName );
46
46
 
47
47
  const removeDynamicTag = () => {
48
48
  setAnyValue( propValueFromHistory ?? null );
@@ -1,7 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { Fragment, useState } from 'react';
3
3
  import { useBoundProp } from '@elementor/editor-controls';
4
- import { type PropKey } from '@elementor/editor-props';
5
4
  import { PhotoIcon, SearchIcon } from '@elementor/icons';
6
5
  import {
7
6
  Box,
@@ -46,7 +45,7 @@ export const DynamicSelection = ( { onSelect }: DynamicSelectionProps ) => {
46
45
 
47
46
  const isCurrentValueDynamic = !! dynamicValue;
48
47
 
49
- const options = useFilteredOptions( bind, searchValue );
48
+ const options = useFilteredOptions( searchValue );
50
49
 
51
50
  const handleSearch = ( event: React.ChangeEvent< HTMLInputElement > ) => {
52
51
  setSearchValue( event.target.value );
@@ -135,8 +134,8 @@ export const DynamicSelection = ( { onSelect }: DynamicSelectionProps ) => {
135
134
  );
136
135
  };
137
136
 
138
- const useFilteredOptions = ( bind: PropKey, searchValue: string ): OptionEntry[] => {
139
- const dynamicTags = usePropDynamicTags( bind );
137
+ const useFilteredOptions = ( searchValue: string ): OptionEntry[] => {
138
+ const dynamicTags = usePropDynamicTags();
140
139
 
141
140
  const options = dynamicTags.reduce< Map< string, Option[] > >( ( categories, { name, label, group } ) => {
142
141
  const isVisible = label.toLowerCase().includes( searchValue.trim().toLowerCase() );
@@ -1,40 +1,45 @@
1
1
  import * as React from 'react';
2
- import { BoundPropProvider, useBoundProp } from '@elementor/editor-controls';
3
- import { type PropKey, type PropValue } from '@elementor/editor-props';
2
+ import { PropKeyProvider, PropProvider, type SetValue, useBoundProp } from '@elementor/editor-controls';
3
+ import { type PropKey } from '@elementor/editor-props';
4
4
 
5
+ import { createTopLevelOjectType } from '../controls-registry/create-top-level-object-type';
5
6
  import { useDynamicTag } from './hooks/use-dynamic-tag';
6
- import { dynamicPropTypeUtil } from './utils';
7
+ import { dynamicPropTypeUtil, type DynamicPropValue } from './utils';
7
8
 
8
9
  export type DynamicControlProps = React.PropsWithChildren< {
9
10
  bind: PropKey;
10
11
  } >;
11
12
 
12
13
  export const DynamicControl = ( { bind, children }: DynamicControlProps ) => {
13
- const { value, setValue, bind: propName } = useBoundProp( dynamicPropTypeUtil );
14
+ const { value, setValue } = useBoundProp( dynamicPropTypeUtil );
14
15
  const { name = '', settings } = value ?? {};
15
16
 
16
- const dynamicTag = useDynamicTag( propName, name );
17
+ const dynamicTag = useDynamicTag( name );
17
18
 
18
19
  if ( ! dynamicTag ) {
19
20
  throw new Error( `Dynamic tag ${ name } not found` );
20
21
  }
21
22
 
22
- const defaultValue = dynamicTag.props_schema[ bind ]?.default;
23
+ const dynamicPropType = dynamicTag.props_schema[ bind ];
24
+
25
+ const defaultValue = dynamicPropType?.default;
23
26
  const dynamicValue = settings?.[ bind ] ?? defaultValue;
24
27
 
25
- const setDynamicValue = ( newValue: PropValue ) => {
28
+ const setDynamicValue: SetValue< Record< string, DynamicPropValue > > = ( newValues ) => {
26
29
  setValue( {
27
30
  name,
28
31
  settings: {
29
32
  ...settings,
30
- [ bind ]: newValue,
33
+ ...newValues,
31
34
  },
32
35
  } );
33
36
  };
34
37
 
38
+ const propType = createTopLevelOjectType( { schema: dynamicTag.props_schema } );
39
+
35
40
  return (
36
- <BoundPropProvider setValue={ setDynamicValue } value={ dynamicValue } bind={ bind }>
37
- { children }
38
- </BoundPropProvider>
41
+ <PropProvider propType={ propType } setValue={ setDynamicValue } value={ { [ bind ]: dynamicValue } }>
42
+ <PropKeyProvider bind={ bind }>{ children }</PropKeyProvider>
43
+ </PropProvider>
39
44
  );
40
45
  };
@@ -1,11 +1,10 @@
1
1
  import { useMemo } from 'react';
2
- import { type PropKey } from '@elementor/editor-props';
3
2
 
4
3
  import { type DynamicTag } from '../types';
5
4
  import { usePropDynamicTags } from './use-prop-dynamic-tags';
6
5
 
7
- export const useDynamicTag = ( propName: PropKey, tagName: string ): DynamicTag | null => {
8
- const dynamicTags = usePropDynamicTags( propName );
6
+ export const useDynamicTag = ( tagName: string ): DynamicTag | null => {
7
+ const dynamicTags = usePropDynamicTags();
9
8
 
10
9
  return useMemo( () => dynamicTags.find( ( tag ) => tag.name === tagName ) ?? null, [ dynamicTags, tagName ] );
11
10
  };
@@ -3,16 +3,13 @@ import { useBoundProp } from '@elementor/editor-controls';
3
3
  import { DatabaseIcon } from '@elementor/icons';
4
4
  import { __ } from '@wordpress/i18n';
5
5
 
6
- import { useElement } from '../../contexts/element-context';
7
6
  import { type PopoverActionProps } from '../../popover-action';
8
7
  import { DynamicSelection } from '../components/dynamic-selection';
9
8
  import { supportsDynamic } from '../utils';
10
9
 
11
10
  export const usePropDynamicAction = (): PopoverActionProps => {
12
- const { bind } = useBoundProp();
13
- const { elementType } = useElement();
11
+ const { propType } = useBoundProp();
14
12
 
15
- const propType = elementType.propsSchema[ bind ];
16
13
  const visible = !! propType && supportsDynamic( propType );
17
14
 
18
15
  return {
@@ -1,16 +1,13 @@
1
1
  import { useMemo } from 'react';
2
- import { type PropKey } from '@elementor/editor-props';
2
+ import { useBoundProp } from '@elementor/editor-controls';
3
3
 
4
- import { useElement } from '../../contexts/element-context';
5
4
  import { getAtomicDynamicTags } from '../sync/get-atomic-dynamic-tags';
6
5
  import { getDynamicPropType } from '../utils';
7
6
 
8
- export const usePropDynamicTags = ( propName: PropKey ) => {
7
+ export const usePropDynamicTags = () => {
9
8
  let categories: string[] = [];
10
9
 
11
- const { elementType } = useElement();
12
-
13
- const propType = elementType.propsSchema?.[ propName ];
10
+ const { propType } = useBoundProp();
14
11
 
15
12
  if ( propType ) {
16
13
  const propDynamicType = getDynamicPropType( propType );
@@ -32,7 +32,7 @@ export const dynamicPropTypeUtil = createPropUtils(
32
32
  DYNAMIC_PROP_TYPE_KEY,
33
33
  z.strictObject( {
34
34
  name: z.string(),
35
- settings: z.record( z.any() ).optional(),
35
+ settings: z.any().optional(),
36
36
  } )
37
37
  );
38
38
 
@@ -28,5 +28,6 @@ export function useStylesFields< T extends Props >( propNames: ( keyof T & strin
28
28
  label: __( 'local', 'elementor' ),
29
29
  } );
30
30
  };
31
+
31
32
  return [ value, setValue ] as const;
32
33
  }
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { useBoundProp } from '@elementor/editor-controls';
2
2
  export type { PopoverActionProps } from './popover-action';
3
3
  export { replaceControl } from './control-replacement';
4
+ export { registerGlobalClassMenuItem, registerStateMenuItem } from './components/css-class-menu';
4
5
 
5
6
  import init from './init';
6
7
 
package/src/init.ts CHANGED
@@ -3,6 +3,7 @@ import { __registerPanel as registerPanel } from '@elementor/editor-panels';
3
3
  import { __privateBlockDataCommand as blockDataCommand } from '@elementor/editor-v1-adapters';
4
4
 
5
5
  import { EditingPanelHooks } from './components/editing-panel-hooks';
6
+ import { initCssClasses } from './css-classes';
6
7
  import { init as initDynamics } from './dynamics/init';
7
8
  import { panel } from './panel';
8
9
  import { isAtomicWidgetSelected } from './sync/is-atomic-widget-selected';
@@ -18,6 +19,7 @@ export default function init() {
18
19
 
19
20
  // TODO: Move it from here once we have dynamic package.
20
21
  initDynamics();
22
+ initCssClasses();
21
23
  }
22
24
 
23
25
  const blockV1Panel = () => {
@@ -1,3 +0,0 @@
1
- export * from './types';
2
- export * from './multi-combobox';
3
- export * from './use-combobox-actions';
@@ -1,122 +0,0 @@
1
- import * as React from 'react';
2
- import {
3
- Autocomplete,
4
- type AutocompleteProps,
5
- type AutocompleteRenderGroupParams,
6
- Box,
7
- Chip,
8
- styled,
9
- TextField,
10
- } from '@elementor/ui';
11
- import { type FilterOptionsState } from '@mui/base';
12
-
13
- import { type ActionOption, type Actions, type Option } from './types';
14
- import { useComboboxActions } from './use-combobox-actions';
15
-
16
- type Props = Omit< AutocompleteProps< Option, true, true, true >, 'renderInput' | 'getLimitTagsText' | 'onSelect' > & {
17
- actions?: Actions;
18
- selected: Option[];
19
- options: Option[];
20
- optionsLabel?: string;
21
- onSelect?: ( value: Option[] ) => void;
22
- onCreate?: ( value: string ) => void;
23
- };
24
-
25
- export const MultiCombobox = ( {
26
- actions = {},
27
- selected,
28
- options,
29
- optionsLabel,
30
- onSelect,
31
- onCreate,
32
- ...props
33
- }: Props ) => {
34
- const { action: actionProps, option: optionProps } = useComboboxActions(
35
- selected,
36
- actions,
37
- // TODO: make the group mechanism more generic, allow passing list of groups.
38
- optionsLabel,
39
- onSelect
40
- );
41
-
42
- return (
43
- <Autocomplete
44
- { ...props }
45
- freeSolo
46
- multiple
47
- clearOnBlur
48
- selectOnFocus
49
- disableClearable
50
- handleHomeEndKeys
51
- value={ selected }
52
- options={ options }
53
- renderGroup={ renderGroup }
54
- renderInput={ ( params ) => <TextField { ...params } /> }
55
- // TODO: is it relevant for the combobox? or should be in the parent component?
56
- getLimitTagsText={ ( more ) => <Chip size="tiny" variant="standard" label={ `+${ more }` } clickable /> }
57
- onChange={ ( _, selectedOrTypedValue, reason ) => {
58
- if ( reason === 'createOption' ) {
59
- const typedValue = selectedOrTypedValue.find( ( option ) => typeof option === 'string' );
60
-
61
- return typedValue && onCreate?.( typedValue );
62
- }
63
-
64
- const action = selectedOrTypedValue.find( ( value ) => actionProps.is( value ) );
65
-
66
- if ( reason === 'selectOption' && action ) {
67
- return actionProps.onChange( action );
68
- }
69
-
70
- const selectedValues = selectedOrTypedValue.filter( ( v ) => typeof v !== 'string' );
71
- const fixedValues = options.filter( ( option ) => option.fixed );
72
-
73
- optionProps.onChange( [ ...new Set( [ ...fixedValues, ...selectedValues ] ) ] );
74
- } }
75
- getOptionLabel={ ( option ) => {
76
- if ( optionProps.is( option ) ) {
77
- return optionProps.getLabel( option );
78
- }
79
-
80
- if ( actionProps.is( option ) ) {
81
- return actionProps.getLabel( option );
82
- }
83
-
84
- return '';
85
- } }
86
- filterOptions={ ( optionList: Option[], params: FilterOptionsState< ActionOption | Option > ) => {
87
- const filteredOptions = optionProps.getFilteredOptions( optionList, params );
88
-
89
- const actionOptions = actionProps.getFilteredActions( optionList, params );
90
-
91
- return [ ...actionOptions, ...filteredOptions ];
92
- } }
93
- groupBy={ ( option ) =>
94
- ( optionProps.is( option ) ? optionProps.groupBy() : actionProps.groupBy( option ) ) ?? ''
95
- }
96
- />
97
- );
98
- };
99
-
100
- const renderGroup = ( params: AutocompleteRenderGroupParams ) => (
101
- <Group key={ params.key }>
102
- <GroupHeader>{ params.group }</GroupHeader>
103
- <GroupItems>{ params.children }</GroupItems>
104
- </Group>
105
- );
106
-
107
- const Group = styled( 'li' )`
108
- &:not( :last-of-type ) {
109
- border-bottom: 1px solid ${ ( { theme } ) => theme.palette.divider };
110
- }
111
- `;
112
-
113
- const GroupHeader = styled( Box )( ( { theme } ) => ( {
114
- position: 'sticky',
115
- top: '-8px',
116
- padding: theme.spacing( 1, 2 ),
117
- color: theme.palette.text.tertiary,
118
- } ) );
119
-
120
- const GroupItems = styled( 'ul' )`
121
- padding: 0;
122
- `;
@@ -1,29 +0,0 @@
1
- export type Option = {
2
- label: string;
3
- value: string;
4
- fixed?: boolean;
5
- // TODO: Should be remove from here or use some kind of `meta`
6
- color?: 'primary' | 'global';
7
- provider?: string;
8
- };
9
-
10
- export type Action = {
11
- getLabel: ( inputValue: string ) => string;
12
- apply: ( value: string ) => void;
13
- condition: ( options: Option[], inputValue: string ) => boolean;
14
- };
15
-
16
- export type ActionsGroup = {
17
- label: string;
18
- actions: Action[];
19
- };
20
-
21
- export type ActionOption = Option & {
22
- action: {
23
- groupLabel: string;
24
- apply: Action[ 'apply' ];
25
- getLabel: Action[ 'getLabel' ];
26
- };
27
- };
28
-
29
- export type Actions = Record< string, ActionsGroup >;
@@ -1,62 +0,0 @@
1
- import { createFilterOptions } from '@elementor/ui';
2
- import { type FilterOptionsState } from '@mui/base';
3
-
4
- import { type Action, type ActionOption, type Actions, type Option } from './types';
5
-
6
- export const useComboboxActions = (
7
- applied: Option[],
8
- actions: Actions,
9
- optionsLabel?: string,
10
- onSelect?: ( value: Option[] ) => void
11
- ) => ( {
12
- action: {
13
- is: ( opt: ActionOption | Option | string ): opt is ActionOption => typeof opt !== 'string' && 'action' in opt,
14
- getLabel: ( option: ActionOption ) => option.action.getLabel( option.label ),
15
- groupBy: ( option: ActionOption ) => option.action.groupLabel,
16
- onChange: ( { action, label }: ActionOption ) => action?.apply( label ),
17
- getFilteredActions: ( optionList: Option[], params: FilterOptionsState< ActionOption > ) => {
18
- const actionGroups = Object.values( actions );
19
-
20
- return actionGroups.reduce< Option[] >( ( groups, group ) => {
21
- const actionOptions = group.actions.reduce< Option[] >( ( groupActions, action ) => {
22
- const shouldShowAction = action.condition( optionList, params.inputValue );
23
-
24
- if ( shouldShowAction ) {
25
- const actionOption = createActionOption( group.label, action, params.inputValue );
26
- groupActions.unshift( actionOption );
27
- }
28
-
29
- return groupActions;
30
- }, [] );
31
-
32
- return [ ...groups, ...actionOptions ];
33
- }, [] );
34
- },
35
- },
36
- option: {
37
- is: ( opt: ActionOption | Option | string ): opt is Option => typeof opt !== 'string' && ! ( 'action' in opt ),
38
- getLabel: ( option: Option ) => option.label,
39
- groupBy: () => optionsLabel ?? '',
40
- onChange: ( optionValues: Option[] ) => onSelect?.( optionValues ),
41
- getFilteredOptions: ( optionList: Option[], params: FilterOptionsState< Option > ) => {
42
- const appliedValues = applied.map( ( option ) => option.value );
43
-
44
- const optionsWithoutApplied = optionList.filter( ( option ) => ! appliedValues.includes( option.value ) );
45
-
46
- return filter( optionsWithoutApplied, params );
47
- },
48
- },
49
- } );
50
-
51
- // Helper functions.
52
- const filter = createFilterOptions< Option >();
53
-
54
- const createActionOption = ( groupLabel: string, action: Action, inputValue: string ): ActionOption => ( {
55
- value: '',
56
- label: inputValue,
57
- action: {
58
- groupLabel,
59
- apply: action.apply,
60
- getLabel: action.getLabel,
61
- },
62
- } );