@elementor/menus 4.0.0-manual → 4.0.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.
package/dist/index.js CHANGED
@@ -41,7 +41,7 @@ var import_utils = require("@elementor/utils");
41
41
 
42
42
  // src/create-register-item.tsx
43
43
  var React = __toESM(require("react"));
44
- function createRegisterItem(locations, component) {
44
+ function createRegisterItem(locations, component, notify) {
45
45
  return ({ id, group = "default", priority = 10, overwrite = false, props: _props, useProps: _useProps }) => {
46
46
  if (!(group in locations)) {
47
47
  return;
@@ -60,36 +60,55 @@ function createRegisterItem(locations, component) {
60
60
  overwrite
61
61
  }
62
62
  });
63
+ notify();
63
64
  };
64
65
  }
65
66
 
66
67
  // src/create-use-menu-items.ts
67
68
  var import_react = require("react");
68
- function createUseMenuItems(locations) {
69
- return () => {
70
- return (0, import_react.useMemo)(() => {
71
- return Object.entries(locations).reduce((carry, [groupName, location]) => {
72
- const items = location.getInjections().map((injection) => ({
73
- id: injection.id,
74
- MenuItem: injection.component
75
- }));
76
- return {
77
- ...carry,
78
- [groupName]: items
79
- };
80
- }, {});
81
- }, []);
69
+ function createUseMenuItems(locations, subscribe) {
70
+ let snapshot = null;
71
+ subscribe(() => {
72
+ snapshot = null;
73
+ });
74
+ const getMenuItems = () => {
75
+ if (snapshot) {
76
+ return snapshot;
77
+ }
78
+ snapshot = Object.entries(locations).reduce((carry, [groupName, location]) => {
79
+ const items = location.getInjections().map((injection) => ({
80
+ id: injection.id,
81
+ MenuItem: injection.component
82
+ }));
83
+ return {
84
+ ...carry,
85
+ [groupName]: items
86
+ };
87
+ }, {});
88
+ return snapshot;
82
89
  };
90
+ return () => (0, import_react.useSyncExternalStore)(subscribe, getMenuItems);
83
91
  }
84
92
 
85
93
  // src/create-menu.ts
94
+ function createSubscription() {
95
+ const listeners = /* @__PURE__ */ new Set();
96
+ return {
97
+ subscribe: (listener) => {
98
+ listeners.add(listener);
99
+ return () => listeners.delete(listener);
100
+ },
101
+ notify: () => listeners.forEach((listener) => listener())
102
+ };
103
+ }
86
104
  function createMenu({
87
105
  groups = [],
88
106
  components
89
107
  }) {
90
108
  const locations = createLocations([...groups, "default"]);
91
- const registerFns = createRegisterFns(locations, components);
92
- const useMenuItems = createUseMenuItems(locations);
109
+ const { subscribe, notify } = createSubscription();
110
+ const registerFns = createRegisterFns(locations, components, notify);
111
+ const useMenuItems = createUseMenuItems(locations, subscribe);
93
112
  return {
94
113
  useMenuItems,
95
114
  ...registerFns
@@ -101,13 +120,13 @@ function createLocations(groups) {
101
120
  return acc;
102
121
  }, {});
103
122
  }
104
- function createRegisterFns(locations, components) {
123
+ function createRegisterFns(locations, components, notify) {
105
124
  return Object.entries(components).reduce(
106
125
  (acc, [key, component]) => {
107
126
  const name = `register${(0, import_utils.capitalize)(key)}`;
108
127
  return {
109
128
  ...acc,
110
- [name]: createRegisterItem(locations, component)
129
+ [name]: createRegisterItem(locations, component, notify)
111
130
  };
112
131
  },
113
132
  {}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/create-menu.ts","../src/create-register-item.tsx","../src/create-use-menu-items.ts","../src/controls-actions.ts","../src/action.tsx"],"sourcesContent":["export { createMenu, type Menu } from './create-menu';\nexport { type Components } from './types';\nexport { controlActionsMenu } from './controls-actions';\n","import { createLocation } from '@elementor/locations';\nimport { capitalize } from '@elementor/utils';\n\nimport { createRegisterItem, type RegisterItem } from './create-register-item';\nimport { createUseMenuItems, type UseMenuItems } from './create-use-menu-items';\nimport { type Components, type LocationsMap, type MenuGroups } from './types';\n\nexport type Menu< TComponents extends Components, TGroups extends string > = {\n\tuseMenuItems: UseMenuItems< TGroups >;\n} & RegisterFns< TGroups, TComponents >;\n\nexport function createMenu< TComponents extends Components, TGroups extends string = 'default' >( {\n\tgroups = [],\n\tcomponents,\n}: {\n\tgroups?: TGroups[];\n\tcomponents: TComponents;\n} ): Menu< TComponents, TGroups > {\n\tconst locations = createLocations< MenuGroups< TGroups > >( [ ...groups, 'default' ] );\n\n\tconst registerFns = createRegisterFns( locations, components );\n\tconst useMenuItems = createUseMenuItems( locations );\n\n\treturn {\n\t\tuseMenuItems,\n\t\t...registerFns,\n\t};\n}\n\nfunction createLocations< TGroups extends string >( groups: TGroups[] ) {\n\treturn groups.reduce( ( acc, group ) => {\n\t\tacc[ group ] = createLocation();\n\n\t\treturn acc;\n\t}, {} as LocationsMap< TGroups > );\n}\n\ntype RegisterFns< TGroups extends string, TComponents extends Components > = {\n\t[ K in keyof TComponents as `register${ Capitalize< K & string > }` ]: RegisterItem< TGroups, TComponents[ K ] >;\n};\n\nfunction createRegisterFns< TGroups extends string, TComponents extends Components >(\n\tlocations: LocationsMap< MenuGroups< TGroups > >,\n\tcomponents: TComponents\n) {\n\treturn Object.entries( components ).reduce(\n\t\t( acc, [ key, component ] ) => {\n\t\t\tconst name = `register${ capitalize( key ) }`;\n\n\t\t\treturn {\n\t\t\t\t...acc,\n\t\t\t\t[ name ]: createRegisterItem( locations, component ),\n\t\t\t};\n\t\t},\n\t\t{} as RegisterFns< TGroups, TComponents >\n\t);\n}\n","import * as React from 'react';\nimport { type ComponentPropsWithoutRef, type ComponentType } from 'react';\n\nimport { type LocationsMap, type MenuGroups } from './types';\n\nexport type RegisterItem< TGroups extends string, TComponent extends ComponentType > = (\n\targs: {\n\t\tid: string;\n\t\tgroup?: MenuGroups< TGroups >;\n\t\tpriority?: number;\n\t\toverwrite?: boolean;\n\t} & Props< ComponentPropsWithoutRef< TComponent > >\n) => void;\n\ntype Props< TProps extends object > = unknown extends TProps ? NoProps : PropsOrUseProps< TProps >;\n\ntype NoProps = { props?: never; useProps?: never };\n\ntype PropsOrUseProps< TProps extends object > =\n\t| { props: TProps; useProps?: never }\n\t| {\n\t\t\tuseProps: () => TProps;\n\t\t\tprops?: never;\n\t };\n\nexport function createRegisterItem< TGroups extends string, TComponent extends ComponentType >(\n\tlocations: LocationsMap< MenuGroups< TGroups > >,\n\tcomponent: TComponent\n): RegisterItem< TGroups, TComponent > {\n\treturn ( { id, group = 'default', priority = 10, overwrite = false, props: _props, useProps: _useProps } ) => {\n\t\tif ( ! ( group in locations ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst Component = component as ComponentType;\n\t\tconst useProps = _useProps || ( () => _props );\n\n\t\tconst InjectedComponent = ( props: object ) => {\n\t\t\tconst componentProps = useProps();\n\n\t\t\treturn <Component { ...props } { ...componentProps } />;\n\t\t};\n\n\t\tlocations[ group ].inject( {\n\t\t\tid,\n\t\t\tcomponent: InjectedComponent,\n\t\t\toptions: {\n\t\t\t\tpriority,\n\t\t\t\toverwrite,\n\t\t\t},\n\t\t} );\n\t};\n}\n","import { type ComponentType, useMemo } from 'react';\n\nimport { type LocationsMap, type MenuGroups } from './types';\n\nexport type UseMenuItems< TGroups extends string > = () => GroupedMenuItems< TGroups >;\n\ntype GroupedMenuItems< TGroups extends string > = Record<\n\tMenuGroups< TGroups >,\n\tArray< {\n\t\tid: string;\n\t\tMenuItem: ComponentType;\n\t} >\n>;\n\nexport function createUseMenuItems< TGroups extends string >(\n\tlocations: LocationsMap< MenuGroups< TGroups > >\n): UseMenuItems< TGroups > {\n\treturn () => {\n\t\t// Normalize the injections groups to an object with the groups as keys.\n\t\treturn useMemo( () => {\n\t\t\treturn Object.entries( locations ).reduce( ( carry, [ groupName, location ] ) => {\n\t\t\t\tconst items = location.getInjections().map( ( injection ) => ( {\n\t\t\t\t\tid: injection.id,\n\t\t\t\t\tMenuItem: injection.component,\n\t\t\t\t} ) );\n\n\t\t\t\treturn {\n\t\t\t\t\t...carry,\n\t\t\t\t\t[ groupName ]: items,\n\t\t\t\t};\n\t\t\t}, {} as GroupedMenuItems< TGroups > );\n\t\t}, [] );\n\t};\n}\n","import { PopoverAction } from '@elementor/editor-ui';\n\nimport Action from './action';\nimport { createMenu } from './create-menu';\n\nexport const controlActionsMenu = createMenu( {\n\tcomponents: {\n\t\tAction,\n\t\tPopoverAction,\n\t},\n} );\n","import * as React from 'react';\nimport { type ElementType as ReactElementType } from 'react';\nimport { IconButton, Tooltip } from '@elementor/ui';\n\nconst SIZE = 'tiny';\n\ntype ActionProps = {\n\ttitle: string;\n\tvisible?: boolean;\n\ticon: ReactElementType;\n\tonClick: () => void;\n};\n\nexport default function Action( { title, visible = true, icon: Icon, onClick }: ActionProps ) {\n\tif ( ! visible ) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<Tooltip placement=\"top\" title={ title } arrow={ true }>\n\t\t\t<IconButton aria-label={ title } size={ SIZE } onClick={ onClick }>\n\t\t\t\t<Icon fontSize={ SIZE } />\n\t\t\t</IconButton>\n\t\t</Tooltip>\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,uBAA+B;AAC/B,mBAA2B;;;ACD3B,YAAuB;AAyBhB,SAAS,mBACf,WACA,WACsC;AACtC,SAAO,CAAE,EAAE,IAAI,QAAQ,WAAW,WAAW,IAAI,YAAY,OAAO,OAAO,QAAQ,UAAU,UAAU,MAAO;AAC7G,QAAK,EAAI,SAAS,YAAc;AAC/B;AAAA,IACD;AAEA,UAAM,YAAY;AAClB,UAAM,WAAW,cAAe,MAAM;AAEtC,UAAM,oBAAoB,CAAE,UAAmB;AAC9C,YAAM,iBAAiB,SAAS;AAEhC,aAAO,oCAAC,aAAY,GAAG,OAAU,GAAG,gBAAiB;AAAA,IACtD;AAEA,cAAW,KAAM,EAAE,OAAQ;AAAA,MAC1B;AAAA,MACA,WAAW;AAAA,MACX,SAAS;AAAA,QACR;AAAA,QACA;AAAA,MACD;AAAA,IACD,CAAE;AAAA,EACH;AACD;;;ACpDA,mBAA4C;AAcrC,SAAS,mBACf,WAC0B;AAC1B,SAAO,MAAM;AAEZ,eAAO,sBAAS,MAAM;AACrB,aAAO,OAAO,QAAS,SAAU,EAAE,OAAQ,CAAE,OAAO,CAAE,WAAW,QAAS,MAAO;AAChF,cAAM,QAAQ,SAAS,cAAc,EAAE,IAAK,CAAE,eAAiB;AAAA,UAC9D,IAAI,UAAU;AAAA,UACd,UAAU,UAAU;AAAA,QACrB,EAAI;AAEJ,eAAO;AAAA,UACN,GAAG;AAAA,UACH,CAAE,SAAU,GAAG;AAAA,QAChB;AAAA,MACD,GAAG,CAAC,CAAiC;AAAA,IACtC,GAAG,CAAC,CAAE;AAAA,EACP;AACD;;;AFtBO,SAAS,WAAkF;AAAA,EACjG,SAAS,CAAC;AAAA,EACV;AACD,GAGkC;AACjC,QAAM,YAAY,gBAA0C,CAAE,GAAG,QAAQ,SAAU,CAAE;AAErF,QAAM,cAAc,kBAAmB,WAAW,UAAW;AAC7D,QAAM,eAAe,mBAAoB,SAAU;AAEnD,SAAO;AAAA,IACN;AAAA,IACA,GAAG;AAAA,EACJ;AACD;AAEA,SAAS,gBAA2C,QAAoB;AACvE,SAAO,OAAO,OAAQ,CAAE,KAAK,UAAW;AACvC,QAAK,KAAM,QAAI,iCAAe;AAE9B,WAAO;AAAA,EACR,GAAG,CAAC,CAA6B;AAClC;AAMA,SAAS,kBACR,WACA,YACC;AACD,SAAO,OAAO,QAAS,UAAW,EAAE;AAAA,IACnC,CAAE,KAAK,CAAE,KAAK,SAAU,MAAO;AAC9B,YAAM,OAAO,eAAY,yBAAY,GAAI,CAAE;AAE3C,aAAO;AAAA,QACN,GAAG;AAAA,QACH,CAAE,IAAK,GAAG,mBAAoB,WAAW,SAAU;AAAA,MACpD;AAAA,IACD;AAAA,IACA,CAAC;AAAA,EACF;AACD;;;AGxDA,uBAA8B;;;ACA9B,IAAAA,SAAuB;AAEvB,gBAAoC;AAEpC,IAAM,OAAO;AASE,SAAR,OAAyB,EAAE,OAAO,UAAU,MAAM,MAAM,MAAM,QAAQ,GAAiB;AAC7F,MAAK,CAAE,SAAU;AAChB,WAAO;AAAA,EACR;AAEA,SACC,qCAAC,qBAAQ,WAAU,OAAM,OAAgB,OAAQ,QAChD,qCAAC,wBAAW,cAAa,OAAQ,MAAO,MAAO,WAC9C,qCAAC,QAAK,UAAW,MAAO,CACzB,CACD;AAEF;;;ADpBO,IAAM,qBAAqB,WAAY;AAAA,EAC7C,YAAY;AAAA,IACX;AAAA,IACA;AAAA,EACD;AACD,CAAE;","names":["React"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/create-menu.ts","../src/create-register-item.tsx","../src/create-use-menu-items.ts","../src/controls-actions.ts","../src/action.tsx"],"sourcesContent":["export { createMenu, type Menu } from './create-menu';\nexport { type Components } from './types';\nexport { controlActionsMenu } from './controls-actions';\n","import { createLocation } from '@elementor/locations';\nimport { capitalize } from '@elementor/utils';\n\nimport { createRegisterItem, type RegisterItem } from './create-register-item';\nimport { createUseMenuItems, type UseMenuItems } from './create-use-menu-items';\nimport { type Components, type LocationsMap, type MenuGroups } from './types';\n\nexport type Menu< TComponents extends Components, TGroups extends string > = {\n\tuseMenuItems: UseMenuItems< TGroups >;\n} & RegisterFns< TGroups, TComponents >;\n\ntype Subscription = {\n\tsubscribe: ( listener: () => void ) => () => void;\n\tnotify: () => void;\n};\n\nfunction createSubscription(): Subscription {\n\tconst listeners = new Set< () => void >();\n\n\treturn {\n\t\tsubscribe: ( listener ) => {\n\t\t\tlisteners.add( listener );\n\t\t\treturn () => listeners.delete( listener );\n\t\t},\n\t\tnotify: () => listeners.forEach( ( listener ) => listener() ),\n\t};\n}\n\nexport function createMenu< TComponents extends Components, TGroups extends string = 'default' >( {\n\tgroups = [],\n\tcomponents,\n}: {\n\tgroups?: TGroups[];\n\tcomponents: TComponents;\n} ): Menu< TComponents, TGroups > {\n\tconst locations = createLocations< MenuGroups< TGroups > >( [ ...groups, 'default' ] );\n\tconst { subscribe, notify } = createSubscription();\n\n\tconst registerFns = createRegisterFns( locations, components, notify );\n\tconst useMenuItems = createUseMenuItems( locations, subscribe );\n\n\treturn {\n\t\tuseMenuItems,\n\t\t...registerFns,\n\t};\n}\n\nfunction createLocations< TGroups extends string >( groups: TGroups[] ) {\n\treturn groups.reduce( ( acc, group ) => {\n\t\tacc[ group ] = createLocation();\n\n\t\treturn acc;\n\t}, {} as LocationsMap< TGroups > );\n}\n\ntype RegisterFns< TGroups extends string, TComponents extends Components > = {\n\t[ K in keyof TComponents as `register${ Capitalize< K & string > }` ]: RegisterItem< TGroups, TComponents[ K ] >;\n};\n\nfunction createRegisterFns< TGroups extends string, TComponents extends Components >(\n\tlocations: LocationsMap< MenuGroups< TGroups > >,\n\tcomponents: TComponents,\n\tnotify: () => void\n) {\n\treturn Object.entries( components ).reduce(\n\t\t( acc, [ key, component ] ) => {\n\t\t\tconst name = `register${ capitalize( key ) }`;\n\n\t\t\treturn {\n\t\t\t\t...acc,\n\t\t\t\t[ name ]: createRegisterItem( locations, component, notify ),\n\t\t\t};\n\t\t},\n\t\t{} as RegisterFns< TGroups, TComponents >\n\t);\n}\n","import * as React from 'react';\nimport { type ComponentPropsWithoutRef, type ComponentType } from 'react';\n\nimport { type LocationsMap, type MenuGroups } from './types';\n\nexport type RegisterItem< TGroups extends string, TComponent extends ComponentType > = (\n\targs: {\n\t\tid: string;\n\t\tgroup?: MenuGroups< TGroups >;\n\t\tpriority?: number;\n\t\toverwrite?: boolean;\n\t} & Props< ComponentPropsWithoutRef< TComponent > >\n) => void;\n\ntype Props< TProps extends object > = unknown extends TProps ? NoProps : PropsOrUseProps< TProps >;\n\ntype NoProps = { props?: never; useProps?: never };\n\ntype PropsOrUseProps< TProps extends object > =\n\t| { props: TProps; useProps?: never }\n\t| {\n\t\t\tuseProps: () => TProps;\n\t\t\tprops?: never;\n\t };\n\nexport function createRegisterItem< TGroups extends string, TComponent extends ComponentType >(\n\tlocations: LocationsMap< MenuGroups< TGroups > >,\n\tcomponent: TComponent,\n\tnotify: () => void\n): RegisterItem< TGroups, TComponent > {\n\treturn ( { id, group = 'default', priority = 10, overwrite = false, props: _props, useProps: _useProps } ) => {\n\t\tif ( ! ( group in locations ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst Component = component as ComponentType;\n\t\tconst useProps = _useProps || ( () => _props );\n\n\t\tconst InjectedComponent = ( props: object ) => {\n\t\t\tconst componentProps = useProps();\n\n\t\t\treturn <Component { ...props } { ...componentProps } />;\n\t\t};\n\n\t\tlocations[ group ].inject( {\n\t\t\tid,\n\t\t\tcomponent: InjectedComponent,\n\t\t\toptions: {\n\t\t\t\tpriority,\n\t\t\t\toverwrite,\n\t\t\t},\n\t\t} );\n\n\t\tnotify();\n\t};\n}\n","import { type ComponentType, useSyncExternalStore } from 'react';\n\nimport { type LocationsMap, type MenuGroups } from './types';\n\nexport type UseMenuItems< TGroups extends string > = () => GroupedMenuItems< TGroups >;\n\ntype GroupedMenuItems< TGroups extends string > = Record<\n\tMenuGroups< TGroups >,\n\tArray< {\n\t\tid: string;\n\t\tMenuItem: ComponentType;\n\t} >\n>;\n\nexport function createUseMenuItems< TGroups extends string >(\n\tlocations: LocationsMap< MenuGroups< TGroups > >,\n\tsubscribe: ( listener: () => void ) => () => void\n): UseMenuItems< TGroups > {\n\tlet snapshot: GroupedMenuItems< TGroups > | null = null;\n\n\tsubscribe( () => {\n\t\tsnapshot = null;\n\t} );\n\n\tconst getMenuItems = () => {\n\t\tif ( snapshot ) {\n\t\t\treturn snapshot;\n\t\t}\n\t\tsnapshot = Object.entries( locations ).reduce( ( carry, [ groupName, location ] ) => {\n\t\t\tconst items = location.getInjections().map( ( injection ) => ( {\n\t\t\t\tid: injection.id,\n\t\t\t\tMenuItem: injection.component,\n\t\t\t} ) );\n\n\t\t\treturn {\n\t\t\t\t...carry,\n\t\t\t\t[ groupName ]: items,\n\t\t\t};\n\t\t}, {} as GroupedMenuItems< TGroups > );\n\n\t\treturn snapshot;\n\t};\n\n\treturn () => useSyncExternalStore( subscribe, getMenuItems );\n}\n","import { PopoverAction } from '@elementor/editor-ui';\n\nimport Action from './action';\nimport { createMenu } from './create-menu';\n\nexport const controlActionsMenu = createMenu( {\n\tcomponents: {\n\t\tAction,\n\t\tPopoverAction,\n\t},\n} );\n","import * as React from 'react';\nimport { type ElementType as ReactElementType } from 'react';\nimport { IconButton, Tooltip } from '@elementor/ui';\n\nconst SIZE = 'tiny';\n\ntype ActionProps = {\n\ttitle: string;\n\tvisible?: boolean;\n\ticon: ReactElementType;\n\tonClick: () => void;\n};\n\nexport default function Action( { title, visible = true, icon: Icon, onClick }: ActionProps ) {\n\tif ( ! visible ) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<Tooltip placement=\"top\" title={ title } arrow={ true }>\n\t\t\t<IconButton aria-label={ title } size={ SIZE } onClick={ onClick }>\n\t\t\t\t<Icon fontSize={ SIZE } />\n\t\t\t</IconButton>\n\t\t</Tooltip>\n\t);\n}\n"],"mappings":"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","names":["React"]}
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import { capitalize } from "@elementor/utils";
4
4
 
5
5
  // src/create-register-item.tsx
6
6
  import * as React from "react";
7
- function createRegisterItem(locations, component) {
7
+ function createRegisterItem(locations, component, notify) {
8
8
  return ({ id, group = "default", priority = 10, overwrite = false, props: _props, useProps: _useProps }) => {
9
9
  if (!(group in locations)) {
10
10
  return;
@@ -23,36 +23,55 @@ function createRegisterItem(locations, component) {
23
23
  overwrite
24
24
  }
25
25
  });
26
+ notify();
26
27
  };
27
28
  }
28
29
 
29
30
  // src/create-use-menu-items.ts
30
- import { useMemo } from "react";
31
- function createUseMenuItems(locations) {
32
- return () => {
33
- return useMemo(() => {
34
- return Object.entries(locations).reduce((carry, [groupName, location]) => {
35
- const items = location.getInjections().map((injection) => ({
36
- id: injection.id,
37
- MenuItem: injection.component
38
- }));
39
- return {
40
- ...carry,
41
- [groupName]: items
42
- };
43
- }, {});
44
- }, []);
31
+ import { useSyncExternalStore } from "react";
32
+ function createUseMenuItems(locations, subscribe) {
33
+ let snapshot = null;
34
+ subscribe(() => {
35
+ snapshot = null;
36
+ });
37
+ const getMenuItems = () => {
38
+ if (snapshot) {
39
+ return snapshot;
40
+ }
41
+ snapshot = Object.entries(locations).reduce((carry, [groupName, location]) => {
42
+ const items = location.getInjections().map((injection) => ({
43
+ id: injection.id,
44
+ MenuItem: injection.component
45
+ }));
46
+ return {
47
+ ...carry,
48
+ [groupName]: items
49
+ };
50
+ }, {});
51
+ return snapshot;
45
52
  };
53
+ return () => useSyncExternalStore(subscribe, getMenuItems);
46
54
  }
47
55
 
48
56
  // src/create-menu.ts
57
+ function createSubscription() {
58
+ const listeners = /* @__PURE__ */ new Set();
59
+ return {
60
+ subscribe: (listener) => {
61
+ listeners.add(listener);
62
+ return () => listeners.delete(listener);
63
+ },
64
+ notify: () => listeners.forEach((listener) => listener())
65
+ };
66
+ }
49
67
  function createMenu({
50
68
  groups = [],
51
69
  components
52
70
  }) {
53
71
  const locations = createLocations([...groups, "default"]);
54
- const registerFns = createRegisterFns(locations, components);
55
- const useMenuItems = createUseMenuItems(locations);
72
+ const { subscribe, notify } = createSubscription();
73
+ const registerFns = createRegisterFns(locations, components, notify);
74
+ const useMenuItems = createUseMenuItems(locations, subscribe);
56
75
  return {
57
76
  useMenuItems,
58
77
  ...registerFns
@@ -64,13 +83,13 @@ function createLocations(groups) {
64
83
  return acc;
65
84
  }, {});
66
85
  }
67
- function createRegisterFns(locations, components) {
86
+ function createRegisterFns(locations, components, notify) {
68
87
  return Object.entries(components).reduce(
69
88
  (acc, [key, component]) => {
70
89
  const name = `register${capitalize(key)}`;
71
90
  return {
72
91
  ...acc,
73
- [name]: createRegisterItem(locations, component)
92
+ [name]: createRegisterItem(locations, component, notify)
74
93
  };
75
94
  },
76
95
  {}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/create-menu.ts","../src/create-register-item.tsx","../src/create-use-menu-items.ts","../src/controls-actions.ts","../src/action.tsx"],"sourcesContent":["import { createLocation } from '@elementor/locations';\nimport { capitalize } from '@elementor/utils';\n\nimport { createRegisterItem, type RegisterItem } from './create-register-item';\nimport { createUseMenuItems, type UseMenuItems } from './create-use-menu-items';\nimport { type Components, type LocationsMap, type MenuGroups } from './types';\n\nexport type Menu< TComponents extends Components, TGroups extends string > = {\n\tuseMenuItems: UseMenuItems< TGroups >;\n} & RegisterFns< TGroups, TComponents >;\n\nexport function createMenu< TComponents extends Components, TGroups extends string = 'default' >( {\n\tgroups = [],\n\tcomponents,\n}: {\n\tgroups?: TGroups[];\n\tcomponents: TComponents;\n} ): Menu< TComponents, TGroups > {\n\tconst locations = createLocations< MenuGroups< TGroups > >( [ ...groups, 'default' ] );\n\n\tconst registerFns = createRegisterFns( locations, components );\n\tconst useMenuItems = createUseMenuItems( locations );\n\n\treturn {\n\t\tuseMenuItems,\n\t\t...registerFns,\n\t};\n}\n\nfunction createLocations< TGroups extends string >( groups: TGroups[] ) {\n\treturn groups.reduce( ( acc, group ) => {\n\t\tacc[ group ] = createLocation();\n\n\t\treturn acc;\n\t}, {} as LocationsMap< TGroups > );\n}\n\ntype RegisterFns< TGroups extends string, TComponents extends Components > = {\n\t[ K in keyof TComponents as `register${ Capitalize< K & string > }` ]: RegisterItem< TGroups, TComponents[ K ] >;\n};\n\nfunction createRegisterFns< TGroups extends string, TComponents extends Components >(\n\tlocations: LocationsMap< MenuGroups< TGroups > >,\n\tcomponents: TComponents\n) {\n\treturn Object.entries( components ).reduce(\n\t\t( acc, [ key, component ] ) => {\n\t\t\tconst name = `register${ capitalize( key ) }`;\n\n\t\t\treturn {\n\t\t\t\t...acc,\n\t\t\t\t[ name ]: createRegisterItem( locations, component ),\n\t\t\t};\n\t\t},\n\t\t{} as RegisterFns< TGroups, TComponents >\n\t);\n}\n","import * as React from 'react';\nimport { type ComponentPropsWithoutRef, type ComponentType } from 'react';\n\nimport { type LocationsMap, type MenuGroups } from './types';\n\nexport type RegisterItem< TGroups extends string, TComponent extends ComponentType > = (\n\targs: {\n\t\tid: string;\n\t\tgroup?: MenuGroups< TGroups >;\n\t\tpriority?: number;\n\t\toverwrite?: boolean;\n\t} & Props< ComponentPropsWithoutRef< TComponent > >\n) => void;\n\ntype Props< TProps extends object > = unknown extends TProps ? NoProps : PropsOrUseProps< TProps >;\n\ntype NoProps = { props?: never; useProps?: never };\n\ntype PropsOrUseProps< TProps extends object > =\n\t| { props: TProps; useProps?: never }\n\t| {\n\t\t\tuseProps: () => TProps;\n\t\t\tprops?: never;\n\t };\n\nexport function createRegisterItem< TGroups extends string, TComponent extends ComponentType >(\n\tlocations: LocationsMap< MenuGroups< TGroups > >,\n\tcomponent: TComponent\n): RegisterItem< TGroups, TComponent > {\n\treturn ( { id, group = 'default', priority = 10, overwrite = false, props: _props, useProps: _useProps } ) => {\n\t\tif ( ! ( group in locations ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst Component = component as ComponentType;\n\t\tconst useProps = _useProps || ( () => _props );\n\n\t\tconst InjectedComponent = ( props: object ) => {\n\t\t\tconst componentProps = useProps();\n\n\t\t\treturn <Component { ...props } { ...componentProps } />;\n\t\t};\n\n\t\tlocations[ group ].inject( {\n\t\t\tid,\n\t\t\tcomponent: InjectedComponent,\n\t\t\toptions: {\n\t\t\t\tpriority,\n\t\t\t\toverwrite,\n\t\t\t},\n\t\t} );\n\t};\n}\n","import { type ComponentType, useMemo } from 'react';\n\nimport { type LocationsMap, type MenuGroups } from './types';\n\nexport type UseMenuItems< TGroups extends string > = () => GroupedMenuItems< TGroups >;\n\ntype GroupedMenuItems< TGroups extends string > = Record<\n\tMenuGroups< TGroups >,\n\tArray< {\n\t\tid: string;\n\t\tMenuItem: ComponentType;\n\t} >\n>;\n\nexport function createUseMenuItems< TGroups extends string >(\n\tlocations: LocationsMap< MenuGroups< TGroups > >\n): UseMenuItems< TGroups > {\n\treturn () => {\n\t\t// Normalize the injections groups to an object with the groups as keys.\n\t\treturn useMemo( () => {\n\t\t\treturn Object.entries( locations ).reduce( ( carry, [ groupName, location ] ) => {\n\t\t\t\tconst items = location.getInjections().map( ( injection ) => ( {\n\t\t\t\t\tid: injection.id,\n\t\t\t\t\tMenuItem: injection.component,\n\t\t\t\t} ) );\n\n\t\t\t\treturn {\n\t\t\t\t\t...carry,\n\t\t\t\t\t[ groupName ]: items,\n\t\t\t\t};\n\t\t\t}, {} as GroupedMenuItems< TGroups > );\n\t\t}, [] );\n\t};\n}\n","import { PopoverAction } from '@elementor/editor-ui';\n\nimport Action from './action';\nimport { createMenu } from './create-menu';\n\nexport const controlActionsMenu = createMenu( {\n\tcomponents: {\n\t\tAction,\n\t\tPopoverAction,\n\t},\n} );\n","import * as React from 'react';\nimport { type ElementType as ReactElementType } from 'react';\nimport { IconButton, Tooltip } from '@elementor/ui';\n\nconst SIZE = 'tiny';\n\ntype ActionProps = {\n\ttitle: string;\n\tvisible?: boolean;\n\ticon: ReactElementType;\n\tonClick: () => void;\n};\n\nexport default function Action( { title, visible = true, icon: Icon, onClick }: ActionProps ) {\n\tif ( ! visible ) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<Tooltip placement=\"top\" title={ title } arrow={ true }>\n\t\t\t<IconButton aria-label={ title } size={ SIZE } onClick={ onClick }>\n\t\t\t\t<Icon fontSize={ SIZE } />\n\t\t\t</IconButton>\n\t\t</Tooltip>\n\t);\n}\n"],"mappings":";AAAA,SAAS,sBAAsB;AAC/B,SAAS,kBAAkB;;;ACD3B,YAAY,WAAW;AAyBhB,SAAS,mBACf,WACA,WACsC;AACtC,SAAO,CAAE,EAAE,IAAI,QAAQ,WAAW,WAAW,IAAI,YAAY,OAAO,OAAO,QAAQ,UAAU,UAAU,MAAO;AAC7G,QAAK,EAAI,SAAS,YAAc;AAC/B;AAAA,IACD;AAEA,UAAM,YAAY;AAClB,UAAM,WAAW,cAAe,MAAM;AAEtC,UAAM,oBAAoB,CAAE,UAAmB;AAC9C,YAAM,iBAAiB,SAAS;AAEhC,aAAO,oCAAC,aAAY,GAAG,OAAU,GAAG,gBAAiB;AAAA,IACtD;AAEA,cAAW,KAAM,EAAE,OAAQ;AAAA,MAC1B;AAAA,MACA,WAAW;AAAA,MACX,SAAS;AAAA,QACR;AAAA,QACA;AAAA,MACD;AAAA,IACD,CAAE;AAAA,EACH;AACD;;;ACpDA,SAA6B,eAAe;AAcrC,SAAS,mBACf,WAC0B;AAC1B,SAAO,MAAM;AAEZ,WAAO,QAAS,MAAM;AACrB,aAAO,OAAO,QAAS,SAAU,EAAE,OAAQ,CAAE,OAAO,CAAE,WAAW,QAAS,MAAO;AAChF,cAAM,QAAQ,SAAS,cAAc,EAAE,IAAK,CAAE,eAAiB;AAAA,UAC9D,IAAI,UAAU;AAAA,UACd,UAAU,UAAU;AAAA,QACrB,EAAI;AAEJ,eAAO;AAAA,UACN,GAAG;AAAA,UACH,CAAE,SAAU,GAAG;AAAA,QAChB;AAAA,MACD,GAAG,CAAC,CAAiC;AAAA,IACtC,GAAG,CAAC,CAAE;AAAA,EACP;AACD;;;AFtBO,SAAS,WAAkF;AAAA,EACjG,SAAS,CAAC;AAAA,EACV;AACD,GAGkC;AACjC,QAAM,YAAY,gBAA0C,CAAE,GAAG,QAAQ,SAAU,CAAE;AAErF,QAAM,cAAc,kBAAmB,WAAW,UAAW;AAC7D,QAAM,eAAe,mBAAoB,SAAU;AAEnD,SAAO;AAAA,IACN;AAAA,IACA,GAAG;AAAA,EACJ;AACD;AAEA,SAAS,gBAA2C,QAAoB;AACvE,SAAO,OAAO,OAAQ,CAAE,KAAK,UAAW;AACvC,QAAK,KAAM,IAAI,eAAe;AAE9B,WAAO;AAAA,EACR,GAAG,CAAC,CAA6B;AAClC;AAMA,SAAS,kBACR,WACA,YACC;AACD,SAAO,OAAO,QAAS,UAAW,EAAE;AAAA,IACnC,CAAE,KAAK,CAAE,KAAK,SAAU,MAAO;AAC9B,YAAM,OAAO,WAAY,WAAY,GAAI,CAAE;AAE3C,aAAO;AAAA,QACN,GAAG;AAAA,QACH,CAAE,IAAK,GAAG,mBAAoB,WAAW,SAAU;AAAA,MACpD;AAAA,IACD;AAAA,IACA,CAAC;AAAA,EACF;AACD;;;AGxDA,SAAS,qBAAqB;;;ACA9B,YAAYA,YAAW;AAEvB,SAAS,YAAY,eAAe;AAEpC,IAAM,OAAO;AASE,SAAR,OAAyB,EAAE,OAAO,UAAU,MAAM,MAAM,MAAM,QAAQ,GAAiB;AAC7F,MAAK,CAAE,SAAU;AAChB,WAAO;AAAA,EACR;AAEA,SACC,qCAAC,WAAQ,WAAU,OAAM,OAAgB,OAAQ,QAChD,qCAAC,cAAW,cAAa,OAAQ,MAAO,MAAO,WAC9C,qCAAC,QAAK,UAAW,MAAO,CACzB,CACD;AAEF;;;ADpBO,IAAM,qBAAqB,WAAY;AAAA,EAC7C,YAAY;AAAA,IACX;AAAA,IACA;AAAA,EACD;AACD,CAAE;","names":["React"]}
1
+ {"version":3,"sources":["../src/create-menu.ts","../src/create-register-item.tsx","../src/create-use-menu-items.ts","../src/controls-actions.ts","../src/action.tsx"],"sourcesContent":["import { createLocation } from '@elementor/locations';\nimport { capitalize } from '@elementor/utils';\n\nimport { createRegisterItem, type RegisterItem } from './create-register-item';\nimport { createUseMenuItems, type UseMenuItems } from './create-use-menu-items';\nimport { type Components, type LocationsMap, type MenuGroups } from './types';\n\nexport type Menu< TComponents extends Components, TGroups extends string > = {\n\tuseMenuItems: UseMenuItems< TGroups >;\n} & RegisterFns< TGroups, TComponents >;\n\ntype Subscription = {\n\tsubscribe: ( listener: () => void ) => () => void;\n\tnotify: () => void;\n};\n\nfunction createSubscription(): Subscription {\n\tconst listeners = new Set< () => void >();\n\n\treturn {\n\t\tsubscribe: ( listener ) => {\n\t\t\tlisteners.add( listener );\n\t\t\treturn () => listeners.delete( listener );\n\t\t},\n\t\tnotify: () => listeners.forEach( ( listener ) => listener() ),\n\t};\n}\n\nexport function createMenu< TComponents extends Components, TGroups extends string = 'default' >( {\n\tgroups = [],\n\tcomponents,\n}: {\n\tgroups?: TGroups[];\n\tcomponents: TComponents;\n} ): Menu< TComponents, TGroups > {\n\tconst locations = createLocations< MenuGroups< TGroups > >( [ ...groups, 'default' ] );\n\tconst { subscribe, notify } = createSubscription();\n\n\tconst registerFns = createRegisterFns( locations, components, notify );\n\tconst useMenuItems = createUseMenuItems( locations, subscribe );\n\n\treturn {\n\t\tuseMenuItems,\n\t\t...registerFns,\n\t};\n}\n\nfunction createLocations< TGroups extends string >( groups: TGroups[] ) {\n\treturn groups.reduce( ( acc, group ) => {\n\t\tacc[ group ] = createLocation();\n\n\t\treturn acc;\n\t}, {} as LocationsMap< TGroups > );\n}\n\ntype RegisterFns< TGroups extends string, TComponents extends Components > = {\n\t[ K in keyof TComponents as `register${ Capitalize< K & string > }` ]: RegisterItem< TGroups, TComponents[ K ] >;\n};\n\nfunction createRegisterFns< TGroups extends string, TComponents extends Components >(\n\tlocations: LocationsMap< MenuGroups< TGroups > >,\n\tcomponents: TComponents,\n\tnotify: () => void\n) {\n\treturn Object.entries( components ).reduce(\n\t\t( acc, [ key, component ] ) => {\n\t\t\tconst name = `register${ capitalize( key ) }`;\n\n\t\t\treturn {\n\t\t\t\t...acc,\n\t\t\t\t[ name ]: createRegisterItem( locations, component, notify ),\n\t\t\t};\n\t\t},\n\t\t{} as RegisterFns< TGroups, TComponents >\n\t);\n}\n","import * as React from 'react';\nimport { type ComponentPropsWithoutRef, type ComponentType } from 'react';\n\nimport { type LocationsMap, type MenuGroups } from './types';\n\nexport type RegisterItem< TGroups extends string, TComponent extends ComponentType > = (\n\targs: {\n\t\tid: string;\n\t\tgroup?: MenuGroups< TGroups >;\n\t\tpriority?: number;\n\t\toverwrite?: boolean;\n\t} & Props< ComponentPropsWithoutRef< TComponent > >\n) => void;\n\ntype Props< TProps extends object > = unknown extends TProps ? NoProps : PropsOrUseProps< TProps >;\n\ntype NoProps = { props?: never; useProps?: never };\n\ntype PropsOrUseProps< TProps extends object > =\n\t| { props: TProps; useProps?: never }\n\t| {\n\t\t\tuseProps: () => TProps;\n\t\t\tprops?: never;\n\t };\n\nexport function createRegisterItem< TGroups extends string, TComponent extends ComponentType >(\n\tlocations: LocationsMap< MenuGroups< TGroups > >,\n\tcomponent: TComponent,\n\tnotify: () => void\n): RegisterItem< TGroups, TComponent > {\n\treturn ( { id, group = 'default', priority = 10, overwrite = false, props: _props, useProps: _useProps } ) => {\n\t\tif ( ! ( group in locations ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst Component = component as ComponentType;\n\t\tconst useProps = _useProps || ( () => _props );\n\n\t\tconst InjectedComponent = ( props: object ) => {\n\t\t\tconst componentProps = useProps();\n\n\t\t\treturn <Component { ...props } { ...componentProps } />;\n\t\t};\n\n\t\tlocations[ group ].inject( {\n\t\t\tid,\n\t\t\tcomponent: InjectedComponent,\n\t\t\toptions: {\n\t\t\t\tpriority,\n\t\t\t\toverwrite,\n\t\t\t},\n\t\t} );\n\n\t\tnotify();\n\t};\n}\n","import { type ComponentType, useSyncExternalStore } from 'react';\n\nimport { type LocationsMap, type MenuGroups } from './types';\n\nexport type UseMenuItems< TGroups extends string > = () => GroupedMenuItems< TGroups >;\n\ntype GroupedMenuItems< TGroups extends string > = Record<\n\tMenuGroups< TGroups >,\n\tArray< {\n\t\tid: string;\n\t\tMenuItem: ComponentType;\n\t} >\n>;\n\nexport function createUseMenuItems< TGroups extends string >(\n\tlocations: LocationsMap< MenuGroups< TGroups > >,\n\tsubscribe: ( listener: () => void ) => () => void\n): UseMenuItems< TGroups > {\n\tlet snapshot: GroupedMenuItems< TGroups > | null = null;\n\n\tsubscribe( () => {\n\t\tsnapshot = null;\n\t} );\n\n\tconst getMenuItems = () => {\n\t\tif ( snapshot ) {\n\t\t\treturn snapshot;\n\t\t}\n\t\tsnapshot = Object.entries( locations ).reduce( ( carry, [ groupName, location ] ) => {\n\t\t\tconst items = location.getInjections().map( ( injection ) => ( {\n\t\t\t\tid: injection.id,\n\t\t\t\tMenuItem: injection.component,\n\t\t\t} ) );\n\n\t\t\treturn {\n\t\t\t\t...carry,\n\t\t\t\t[ groupName ]: items,\n\t\t\t};\n\t\t}, {} as GroupedMenuItems< TGroups > );\n\n\t\treturn snapshot;\n\t};\n\n\treturn () => useSyncExternalStore( subscribe, getMenuItems );\n}\n","import { PopoverAction } from '@elementor/editor-ui';\n\nimport Action from './action';\nimport { createMenu } from './create-menu';\n\nexport const controlActionsMenu = createMenu( {\n\tcomponents: {\n\t\tAction,\n\t\tPopoverAction,\n\t},\n} );\n","import * as React from 'react';\nimport { type ElementType as ReactElementType } from 'react';\nimport { IconButton, Tooltip } from '@elementor/ui';\n\nconst SIZE = 'tiny';\n\ntype ActionProps = {\n\ttitle: string;\n\tvisible?: boolean;\n\ticon: ReactElementType;\n\tonClick: () => void;\n};\n\nexport default function Action( { title, visible = true, icon: Icon, onClick }: ActionProps ) {\n\tif ( ! visible ) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<Tooltip placement=\"top\" title={ title } arrow={ true }>\n\t\t\t<IconButton aria-label={ title } size={ SIZE } onClick={ onClick }>\n\t\t\t\t<Icon fontSize={ SIZE } />\n\t\t\t</IconButton>\n\t\t</Tooltip>\n\t);\n}\n"],"mappings":"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","names":["React"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/menus",
3
3
  "description": "Add a menus registration mechanism for you React application",
4
- "version": "4.0.0-manual",
4
+ "version": "4.0.0",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -40,9 +40,9 @@
40
40
  "dev": "tsup --config=../../tsup.dev.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@elementor/locations": "4.0.0-manual",
44
- "@elementor/utils": "4.0.0-manual",
45
- "@elementor/editor-ui": "4.0.0-manual",
43
+ "@elementor/locations": "4.0.0",
44
+ "@elementor/utils": "4.0.0",
45
+ "@elementor/editor-ui": "4.0.0",
46
46
  "@elementor/ui": "1.36.17"
47
47
  },
48
48
  "peerDependencies": {
@@ -9,6 +9,23 @@ export type Menu< TComponents extends Components, TGroups extends string > = {
9
9
  useMenuItems: UseMenuItems< TGroups >;
10
10
  } & RegisterFns< TGroups, TComponents >;
11
11
 
12
+ type Subscription = {
13
+ subscribe: ( listener: () => void ) => () => void;
14
+ notify: () => void;
15
+ };
16
+
17
+ function createSubscription(): Subscription {
18
+ const listeners = new Set< () => void >();
19
+
20
+ return {
21
+ subscribe: ( listener ) => {
22
+ listeners.add( listener );
23
+ return () => listeners.delete( listener );
24
+ },
25
+ notify: () => listeners.forEach( ( listener ) => listener() ),
26
+ };
27
+ }
28
+
12
29
  export function createMenu< TComponents extends Components, TGroups extends string = 'default' >( {
13
30
  groups = [],
14
31
  components,
@@ -17,9 +34,10 @@ export function createMenu< TComponents extends Components, TGroups extends stri
17
34
  components: TComponents;
18
35
  } ): Menu< TComponents, TGroups > {
19
36
  const locations = createLocations< MenuGroups< TGroups > >( [ ...groups, 'default' ] );
37
+ const { subscribe, notify } = createSubscription();
20
38
 
21
- const registerFns = createRegisterFns( locations, components );
22
- const useMenuItems = createUseMenuItems( locations );
39
+ const registerFns = createRegisterFns( locations, components, notify );
40
+ const useMenuItems = createUseMenuItems( locations, subscribe );
23
41
 
24
42
  return {
25
43
  useMenuItems,
@@ -41,7 +59,8 @@ type RegisterFns< TGroups extends string, TComponents extends Components > = {
41
59
 
42
60
  function createRegisterFns< TGroups extends string, TComponents extends Components >(
43
61
  locations: LocationsMap< MenuGroups< TGroups > >,
44
- components: TComponents
62
+ components: TComponents,
63
+ notify: () => void
45
64
  ) {
46
65
  return Object.entries( components ).reduce(
47
66
  ( acc, [ key, component ] ) => {
@@ -49,7 +68,7 @@ function createRegisterFns< TGroups extends string, TComponents extends Componen
49
68
 
50
69
  return {
51
70
  ...acc,
52
- [ name ]: createRegisterItem( locations, component ),
71
+ [ name ]: createRegisterItem( locations, component, notify ),
53
72
  };
54
73
  },
55
74
  {} as RegisterFns< TGroups, TComponents >
@@ -25,7 +25,8 @@ type PropsOrUseProps< TProps extends object > =
25
25
 
26
26
  export function createRegisterItem< TGroups extends string, TComponent extends ComponentType >(
27
27
  locations: LocationsMap< MenuGroups< TGroups > >,
28
- component: TComponent
28
+ component: TComponent,
29
+ notify: () => void
29
30
  ): RegisterItem< TGroups, TComponent > {
30
31
  return ( { id, group = 'default', priority = 10, overwrite = false, props: _props, useProps: _useProps } ) => {
31
32
  if ( ! ( group in locations ) ) {
@@ -49,5 +50,7 @@ export function createRegisterItem< TGroups extends string, TComponent extends C
49
50
  overwrite,
50
51
  },
51
52
  } );
53
+
54
+ notify();
52
55
  };
53
56
  }
@@ -1,4 +1,4 @@
1
- import { type ComponentType, useMemo } from 'react';
1
+ import { type ComponentType, useSyncExternalStore } from 'react';
2
2
 
3
3
  import { type LocationsMap, type MenuGroups } from './types';
4
4
 
@@ -13,22 +13,33 @@ type GroupedMenuItems< TGroups extends string > = Record<
13
13
  >;
14
14
 
15
15
  export function createUseMenuItems< TGroups extends string >(
16
- locations: LocationsMap< MenuGroups< TGroups > >
16
+ locations: LocationsMap< MenuGroups< TGroups > >,
17
+ subscribe: ( listener: () => void ) => () => void
17
18
  ): UseMenuItems< TGroups > {
18
- return () => {
19
- // Normalize the injections groups to an object with the groups as keys.
20
- return useMemo( () => {
21
- return Object.entries( locations ).reduce( ( carry, [ groupName, location ] ) => {
22
- const items = location.getInjections().map( ( injection ) => ( {
23
- id: injection.id,
24
- MenuItem: injection.component,
25
- } ) );
26
-
27
- return {
28
- ...carry,
29
- [ groupName ]: items,
30
- };
31
- }, {} as GroupedMenuItems< TGroups > );
32
- }, [] );
19
+ let snapshot: GroupedMenuItems< TGroups > | null = null;
20
+
21
+ subscribe( () => {
22
+ snapshot = null;
23
+ } );
24
+
25
+ const getMenuItems = () => {
26
+ if ( snapshot ) {
27
+ return snapshot;
28
+ }
29
+ snapshot = Object.entries( locations ).reduce( ( carry, [ groupName, location ] ) => {
30
+ const items = location.getInjections().map( ( injection ) => ( {
31
+ id: injection.id,
32
+ MenuItem: injection.component,
33
+ } ) );
34
+
35
+ return {
36
+ ...carry,
37
+ [ groupName ]: items,
38
+ };
39
+ }, {} as GroupedMenuItems< TGroups > );
40
+
41
+ return snapshot;
33
42
  };
43
+
44
+ return () => useSyncExternalStore( subscribe, getMenuItems );
34
45
  }