@elementor/locations 4.3.0-985 → 4.3.0-987

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
@@ -75,11 +75,31 @@ var flushInjectionsFns = [];
75
75
  function flushAllInjections() {
76
76
  flushInjectionsFns.forEach((flush) => flush());
77
77
  }
78
+ function createSubscription() {
79
+ const listeners = /* @__PURE__ */ new Set();
80
+ return {
81
+ subscribe: (listener) => {
82
+ listeners.add(listener);
83
+ return () => listeners.delete(listener);
84
+ },
85
+ notify: () => listeners.forEach((listener) => listener())
86
+ };
87
+ }
78
88
  function createGetInjections(injections) {
79
89
  return () => [...injections.values()].sort((a, b) => a.priority - b.priority);
80
90
  }
81
- function createUseInjections(getInjections) {
82
- return () => (0, import_react3.useMemo)(() => getInjections(), []);
91
+ function createUseInjections(getInjections, subscribe) {
92
+ let snapshot = null;
93
+ subscribe(() => {
94
+ snapshot = null;
95
+ });
96
+ const getSnapshot = () => {
97
+ if (!snapshot) {
98
+ snapshot = getInjections();
99
+ }
100
+ return snapshot;
101
+ };
102
+ return () => (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot);
83
103
  }
84
104
  function wrapInjectedComponent(Component2) {
85
105
  return (props) => /* @__PURE__ */ React2.createElement(InjectedComponentWrapper, null, /* @__PURE__ */ React2.createElement(Component2, { ...props }));
@@ -88,11 +108,15 @@ function wrapInjectedComponent(Component2) {
88
108
  // src/create-location.tsx
89
109
  function createLocation() {
90
110
  const injections = /* @__PURE__ */ new Map();
111
+ const { subscribe, notify } = createSubscription();
91
112
  const getInjections = createGetInjections(injections);
92
- const useInjections = createUseInjections(getInjections);
113
+ const useInjections = createUseInjections(getInjections, subscribe);
93
114
  const Slot = createSlot(useInjections);
94
- const inject = createInject(injections);
95
- flushInjectionsFns.push(() => injections.clear());
115
+ const inject = createInject(injections, notify);
116
+ flushInjectionsFns.push(() => {
117
+ injections.clear();
118
+ notify();
119
+ });
96
120
  return {
97
121
  inject,
98
122
  getInjections,
@@ -106,7 +130,7 @@ function createSlot(useInjections) {
106
130
  return /* @__PURE__ */ React3.createElement(React3.Fragment, null, injections.map(({ id, component: Component2 }) => /* @__PURE__ */ React3.createElement(Component2, { ...props, key: id })));
107
131
  };
108
132
  }
109
- function createInject(injections) {
133
+ function createInject(injections, notify) {
110
134
  return ({ component, id, options = {} }) => {
111
135
  if (injections.has(id) && !options?.overwrite) {
112
136
  console.warn(
@@ -119,6 +143,7 @@ function createInject(injections) {
119
143
  component: wrapInjectedComponent(component),
120
144
  priority: options.priority ?? DEFAULT_PRIORITY
121
145
  });
146
+ notify();
122
147
  };
123
148
  }
124
149
 
@@ -126,11 +151,15 @@ function createInject(injections) {
126
151
  var React4 = __toESM(require("react"));
127
152
  function createReplaceableLocation() {
128
153
  const injections = /* @__PURE__ */ new Map();
154
+ const { subscribe, notify } = createSubscription();
129
155
  const getInjections = createGetInjections(injections);
130
- const useInjections = createUseInjections(getInjections);
156
+ const useInjections = createUseInjections(getInjections, subscribe);
131
157
  const Slot = createReplaceable(useInjections);
132
- const inject = createRegister(injections);
133
- flushInjectionsFns.push(() => injections.clear());
158
+ const inject = createRegister(injections, notify);
159
+ flushInjectionsFns.push(() => {
160
+ injections.clear();
161
+ notify();
162
+ });
134
163
  return {
135
164
  getInjections,
136
165
  useInjections,
@@ -148,7 +177,7 @@ function createReplaceable(useInjections) {
148
177
  return /* @__PURE__ */ React4.createElement(Component2, { ...props });
149
178
  };
150
179
  }
151
- function createRegister(injections) {
180
+ function createRegister(injections, notify) {
152
181
  return ({ component, id, condition = () => true, options = {} }) => {
153
182
  injections.set(id, {
154
183
  id,
@@ -156,6 +185,7 @@ function createRegister(injections) {
156
185
  condition,
157
186
  priority: options.priority ?? DEFAULT_PRIORITY
158
187
  });
188
+ notify();
159
189
  };
160
190
  }
161
191
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/create-location.tsx","../src/injections.tsx","../src/components/injected-component-wrapper.tsx","../src/components/error-boundary.tsx","../src/create-replaceable-location.tsx"],"sourcesContent":["export * from './types';\n\nexport { createLocation } from './create-location';\nexport { createReplaceableLocation } from './create-replaceable-location';\nexport { flushAllInjections as __flushAllInjections } from './injections';\n","import * as React from 'react';\n\nimport {\n\tcreateGetInjections,\n\tcreateUseInjections,\n\tDEFAULT_PRIORITY,\n\tflushInjectionsFns,\n\twrapInjectedComponent,\n} from './injections';\nimport { type AnyProps, type Id, type InjectArgs, type Injection, type Location } from './types';\n\ntype InjectionsMap< TProps extends object = AnyProps > = Map< Id, Injection< TProps > >;\n\nexport function createLocation< TProps extends object = AnyProps >(): Location< TProps > {\n\tconst injections: InjectionsMap< TProps > = new Map();\n\n\tconst getInjections = createGetInjections( injections );\n\tconst useInjections = createUseInjections( getInjections );\n\tconst Slot = createSlot( useInjections );\n\tconst inject = createInject( injections );\n\n\t// Push the clear function to the flushInjectionsFns array, so we can flush all injections at once.\n\tflushInjectionsFns.push( () => injections.clear() );\n\n\treturn {\n\t\tinject,\n\t\tgetInjections,\n\t\tuseInjections,\n\t\tSlot,\n\t};\n}\n\nfunction createSlot< TProps extends object = AnyProps >( useInjections: Location< TProps >[ 'useInjections' ] ) {\n\treturn ( props: TProps ) => {\n\t\tconst injections = useInjections();\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t{ injections.map( ( { id, component: Component } ) => (\n\t\t\t\t\t<Component { ...props } key={ id } />\n\t\t\t\t) ) }\n\t\t\t</>\n\t\t);\n\t};\n}\n\nfunction createInject< TProps extends object = AnyProps >( injections: InjectionsMap< TProps > ) {\n\treturn ( { component, id, options = {} }: InjectArgs< TProps > ) => {\n\t\tif ( injections.has( id ) && ! options?.overwrite ) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn(\n\t\t\t\t`An injection with the id \"${ id }\" already exists. Did you mean to use \"options.overwrite\"?`\n\t\t\t);\n\n\t\t\treturn;\n\t\t}\n\n\t\tinjections.set( id, {\n\t\t\tid,\n\t\t\tcomponent: wrapInjectedComponent( component ),\n\t\t\tpriority: options.priority ?? DEFAULT_PRIORITY,\n\t\t} );\n\t};\n}\n","import * as React from 'react';\nimport { useMemo } from 'react';\n\nimport InjectedComponentWrapper from './components/injected-component-wrapper';\nimport { type AnyProps, type Id, type InjectedComponent, type Injection, type Location } from './types';\n\ntype InjectionsMap< TProps extends object = AnyProps > = Map< Id, Injection< TProps > >;\n\nexport const DEFAULT_PRIORITY = 10;\n\n// Allow flushing all injections at once, for testing purposes.\nexport const flushInjectionsFns: ( () => void )[] = [];\n\nexport function flushAllInjections() {\n\tflushInjectionsFns.forEach( ( flush ) => flush() );\n}\n\nexport function createGetInjections< TProps extends object = AnyProps >( injections: InjectionsMap< TProps > ) {\n\treturn () => [ ...injections.values() ].sort( ( a, b ) => a.priority - b.priority );\n}\n\nexport function createUseInjections< TProps extends object = AnyProps >(\n\tgetInjections: Location< TProps >[ 'getInjections' ]\n) {\n\treturn () => useMemo( () => getInjections(), [] );\n}\n\nexport function wrapInjectedComponent< TProps extends object = AnyProps >( Component: InjectedComponent< TProps > ) {\n\treturn ( props: TProps ) => (\n\t\t<InjectedComponentWrapper>\n\t\t\t<Component { ...props } />\n\t\t</InjectedComponentWrapper>\n\t);\n}\n","import * as React from 'react';\nimport { type ReactNode, Suspense } from 'react';\n\nimport ErrorBoundary from './error-boundary';\n\nexport default function InjectedComponentWrapper( { children }: { children: ReactNode } ) {\n\treturn (\n\t\t<ErrorBoundary fallback={ null }>\n\t\t\t<Suspense fallback={ null }>{ children }</Suspense>\n\t\t</ErrorBoundary>\n\t);\n}\n","import { Component, type ReactNode } from 'react';\n\ninterface Props {\n\tchildren?: ReactNode;\n\tfallback: ReactNode;\n}\n\ninterface State {\n\thasError: boolean;\n}\n\nexport default class ErrorBoundary extends Component< Props, State > {\n\tpublic state: State = {\n\t\thasError: false,\n\t};\n\n\tpublic static getDerivedStateFromError(): State {\n\t\t// Update state so the next render will show the fallback UI.\n\t\treturn { hasError: true };\n\t}\n\n\tpublic render() {\n\t\tif ( this.state.hasError ) {\n\t\t\treturn this.props.fallback;\n\t\t}\n\n\t\treturn this.props.children;\n\t}\n}\n","import * as React from 'react';\nimport { type PropsWithChildren } from 'react';\n\nimport {\n\tcreateGetInjections,\n\tcreateUseInjections,\n\tDEFAULT_PRIORITY,\n\tflushInjectionsFns,\n\twrapInjectedComponent,\n} from './injections';\nimport {\n\ttype AnyProps,\n\ttype Id,\n\ttype ReplaceableInjectArgs,\n\ttype ReplaceableInjection,\n\ttype ReplaceableLocation,\n} from './types';\n\ntype ReplaceableInjectionsMap< TProps extends object = AnyProps > = Map< Id, ReplaceableInjection< TProps > >;\n\nexport function createReplaceableLocation< TProps extends object = AnyProps >(): ReplaceableLocation< TProps > {\n\tconst injections: ReplaceableInjectionsMap< TProps > = new Map();\n\n\tconst getInjections = createGetInjections( injections );\n\tconst useInjections = createUseInjections( getInjections );\n\tconst Slot = createReplaceable( useInjections );\n\tconst inject = createRegister( injections );\n\n\t// Push the clear function to the flushInjectionsFns array, so we can flush all injections at once.\n\tflushInjectionsFns.push( () => injections.clear() );\n\n\treturn {\n\t\tgetInjections,\n\t\tuseInjections,\n\t\tinject,\n\t\tSlot,\n\t};\n}\n\nfunction createReplaceable< TProps extends PropsWithChildren< object > = AnyProps >(\n\tuseInjections: ReplaceableLocation< TProps >[ 'useInjections' ]\n) {\n\treturn ( props: TProps ) => {\n\t\tconst injections = useInjections();\n\n\t\tconst { component: Component } = injections.find( ( { condition } ) => condition?.( props ) ) ?? {};\n\n\t\tif ( ! Component ) {\n\t\t\treturn props.children;\n\t\t}\n\n\t\treturn <Component { ...props } />;\n\t};\n}\n\nfunction createRegister< TProps extends object = AnyProps >( injections: ReplaceableInjectionsMap< TProps > ) {\n\treturn ( { component, id, condition = () => true, options = {} }: ReplaceableInjectArgs< TProps > ) => {\n\t\tinjections.set( id, {\n\t\t\tid,\n\t\t\tcomponent: wrapInjectedComponent( component ),\n\t\t\tcondition,\n\t\t\tpriority: options.priority ?? DEFAULT_PRIORITY,\n\t\t} );\n\t};\n}\n"],"mappings":"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","names":["React","React","import_react","import_react","Component","Component","React","Component"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/create-location.tsx","../src/injections.tsx","../src/components/injected-component-wrapper.tsx","../src/components/error-boundary.tsx","../src/create-replaceable-location.tsx"],"sourcesContent":["export * from './types';\n\nexport { createLocation } from './create-location';\nexport { createReplaceableLocation } from './create-replaceable-location';\nexport { flushAllInjections as __flushAllInjections } from './injections';\n","import * as React from 'react';\n\nimport {\n\tcreateGetInjections,\n\tcreateSubscription,\n\tcreateUseInjections,\n\tDEFAULT_PRIORITY,\n\tflushInjectionsFns,\n\twrapInjectedComponent,\n} from './injections';\nimport { type AnyProps, type Id, type InjectArgs, type Injection, type Location } from './types';\n\ntype InjectionsMap< TProps extends object = AnyProps > = Map< Id, Injection< TProps > >;\n\nexport function createLocation< TProps extends object = AnyProps >(): Location< TProps > {\n\tconst injections: InjectionsMap< TProps > = new Map();\n\tconst { subscribe, notify } = createSubscription();\n\n\tconst getInjections = createGetInjections( injections );\n\tconst useInjections = createUseInjections( getInjections, subscribe );\n\tconst Slot = createSlot( useInjections );\n\tconst inject = createInject( injections, notify );\n\n\t// Push the clear function to the flushInjectionsFns array, so we can flush all injections at once.\n\t// `notify()` is called too, so any mounted `Slot` (and its cached snapshot) reflects the flush,\n\t// which matters for test isolation between test cases.\n\tflushInjectionsFns.push( () => {\n\t\tinjections.clear();\n\t\tnotify();\n\t} );\n\n\treturn {\n\t\tinject,\n\t\tgetInjections,\n\t\tuseInjections,\n\t\tSlot,\n\t};\n}\n\nfunction createSlot< TProps extends object = AnyProps >( useInjections: Location< TProps >[ 'useInjections' ] ) {\n\treturn ( props: TProps ) => {\n\t\tconst injections = useInjections();\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t{ injections.map( ( { id, component: Component } ) => (\n\t\t\t\t\t<Component { ...props } key={ id } />\n\t\t\t\t) ) }\n\t\t\t</>\n\t\t);\n\t};\n}\n\nfunction createInject< TProps extends object = AnyProps >( injections: InjectionsMap< TProps >, notify: () => void ) {\n\treturn ( { component, id, options = {} }: InjectArgs< TProps > ) => {\n\t\tif ( injections.has( id ) && ! options?.overwrite ) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn(\n\t\t\t\t`An injection with the id \"${ id }\" already exists. Did you mean to use \"options.overwrite\"?`\n\t\t\t);\n\n\t\t\treturn;\n\t\t}\n\n\t\tinjections.set( id, {\n\t\t\tid,\n\t\t\tcomponent: wrapInjectedComponent( component ),\n\t\t\tpriority: options.priority ?? DEFAULT_PRIORITY,\n\t\t} );\n\n\t\tnotify();\n\t};\n}\n","import * as React from 'react';\nimport { useSyncExternalStore } from 'react';\n\nimport InjectedComponentWrapper from './components/injected-component-wrapper';\nimport { type AnyProps, type Id, type InjectedComponent, type Injection, type Location } from './types';\n\ntype InjectionsMap< TProps extends object = AnyProps > = Map< Id, Injection< TProps > >;\n\nexport const DEFAULT_PRIORITY = 10;\n\n// Allow flushing all injections at once, for testing purposes.\nexport const flushInjectionsFns: ( () => void )[] = [];\n\nexport function flushAllInjections() {\n\tflushInjectionsFns.forEach( ( flush ) => flush() );\n}\n\nexport type Subscribe = ( listener: () => void ) => () => void;\n\nexport function createSubscription() {\n\tconst listeners = new Set< () => void >();\n\n\treturn {\n\t\tsubscribe: ( listener: () => void ) => {\n\t\t\tlisteners.add( listener );\n\n\t\t\treturn () => listeners.delete( listener );\n\t\t},\n\t\tnotify: () => listeners.forEach( ( listener ) => listener() ),\n\t};\n}\n\nexport function createGetInjections< TProps extends object = AnyProps >( injections: InjectionsMap< TProps > ) {\n\treturn () => [ ...injections.values() ].sort( ( a, b ) => a.priority - b.priority );\n}\n\n// Injections registered after the `Slot` has already mounted (e.g. by an external plugin's\n// script that loads after the editor has rendered) must still be reflected in the UI, so the\n// snapshot is invalidated and re-read whenever `notify()` is called.\nexport function createUseInjections< TProps extends object = AnyProps >(\n\tgetInjections: Location< TProps >[ 'getInjections' ],\n\tsubscribe: Subscribe\n) {\n\tlet snapshot: ReturnType< Location< TProps >[ 'getInjections' ] > | null = null;\n\n\tsubscribe( () => {\n\t\tsnapshot = null;\n\t} );\n\n\tconst getSnapshot = () => {\n\t\tif ( ! snapshot ) {\n\t\t\tsnapshot = getInjections();\n\t\t}\n\n\t\treturn snapshot;\n\t};\n\n\treturn () => useSyncExternalStore( subscribe, getSnapshot );\n}\n\nexport function wrapInjectedComponent< TProps extends object = AnyProps >( Component: InjectedComponent< TProps > ) {\n\treturn ( props: TProps ) => (\n\t\t<InjectedComponentWrapper>\n\t\t\t<Component { ...props } />\n\t\t</InjectedComponentWrapper>\n\t);\n}\n","import * as React from 'react';\nimport { type ReactNode, Suspense } from 'react';\n\nimport ErrorBoundary from './error-boundary';\n\nexport default function InjectedComponentWrapper( { children }: { children: ReactNode } ) {\n\treturn (\n\t\t<ErrorBoundary fallback={ null }>\n\t\t\t<Suspense fallback={ null }>{ children }</Suspense>\n\t\t</ErrorBoundary>\n\t);\n}\n","import { Component, type ReactNode } from 'react';\n\ninterface Props {\n\tchildren?: ReactNode;\n\tfallback: ReactNode;\n}\n\ninterface State {\n\thasError: boolean;\n}\n\nexport default class ErrorBoundary extends Component< Props, State > {\n\tpublic state: State = {\n\t\thasError: false,\n\t};\n\n\tpublic static getDerivedStateFromError(): State {\n\t\t// Update state so the next render will show the fallback UI.\n\t\treturn { hasError: true };\n\t}\n\n\tpublic render() {\n\t\tif ( this.state.hasError ) {\n\t\t\treturn this.props.fallback;\n\t\t}\n\n\t\treturn this.props.children;\n\t}\n}\n","import * as React from 'react';\nimport { type PropsWithChildren } from 'react';\n\nimport {\n\tcreateGetInjections,\n\tcreateSubscription,\n\tcreateUseInjections,\n\tDEFAULT_PRIORITY,\n\tflushInjectionsFns,\n\twrapInjectedComponent,\n} from './injections';\nimport {\n\ttype AnyProps,\n\ttype Id,\n\ttype ReplaceableInjectArgs,\n\ttype ReplaceableInjection,\n\ttype ReplaceableLocation,\n} from './types';\n\ntype ReplaceableInjectionsMap< TProps extends object = AnyProps > = Map< Id, ReplaceableInjection< TProps > >;\n\nexport function createReplaceableLocation< TProps extends object = AnyProps >(): ReplaceableLocation< TProps > {\n\tconst injections: ReplaceableInjectionsMap< TProps > = new Map();\n\tconst { subscribe, notify } = createSubscription();\n\n\tconst getInjections = createGetInjections( injections );\n\tconst useInjections = createUseInjections( getInjections, subscribe );\n\tconst Slot = createReplaceable( useInjections );\n\tconst inject = createRegister( injections, notify );\n\n\t// Push the clear function to the flushInjectionsFns array, so we can flush all injections at once.\n\t// `notify()` is called too, so any mounted `Slot` (and its cached snapshot) reflects the flush,\n\t// which matters for test isolation between test cases.\n\tflushInjectionsFns.push( () => {\n\t\tinjections.clear();\n\t\tnotify();\n\t} );\n\n\treturn {\n\t\tgetInjections,\n\t\tuseInjections,\n\t\tinject,\n\t\tSlot,\n\t};\n}\n\nfunction createReplaceable< TProps extends PropsWithChildren< object > = AnyProps >(\n\tuseInjections: ReplaceableLocation< TProps >[ 'useInjections' ]\n) {\n\treturn ( props: TProps ) => {\n\t\tconst injections = useInjections();\n\n\t\tconst { component: Component } = injections.find( ( { condition } ) => condition?.( props ) ) ?? {};\n\n\t\tif ( ! Component ) {\n\t\t\treturn props.children;\n\t\t}\n\n\t\treturn <Component { ...props } />;\n\t};\n}\n\nfunction createRegister< TProps extends object = AnyProps >(\n\tinjections: ReplaceableInjectionsMap< TProps >,\n\tnotify: () => void\n) {\n\treturn ( { component, id, condition = () => true, options = {} }: ReplaceableInjectArgs< TProps > ) => {\n\t\tinjections.set( id, {\n\t\t\tid,\n\t\t\tcomponent: wrapInjectedComponent( component ),\n\t\t\tcondition,\n\t\t\tpriority: options.priority ?? DEFAULT_PRIORITY,\n\t\t} );\n\n\t\tnotify();\n\t};\n}\n"],"mappings":"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","names":["React","React","import_react","import_react","Component","Component","React","Component"]}
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import * as React3 from "react";
3
3
 
4
4
  // src/injections.tsx
5
5
  import * as React2 from "react";
6
- import { useMemo } from "react";
6
+ import { useSyncExternalStore } from "react";
7
7
 
8
8
  // src/components/injected-component-wrapper.tsx
9
9
  import * as React from "react";
@@ -37,11 +37,31 @@ var flushInjectionsFns = [];
37
37
  function flushAllInjections() {
38
38
  flushInjectionsFns.forEach((flush) => flush());
39
39
  }
40
+ function createSubscription() {
41
+ const listeners = /* @__PURE__ */ new Set();
42
+ return {
43
+ subscribe: (listener) => {
44
+ listeners.add(listener);
45
+ return () => listeners.delete(listener);
46
+ },
47
+ notify: () => listeners.forEach((listener) => listener())
48
+ };
49
+ }
40
50
  function createGetInjections(injections) {
41
51
  return () => [...injections.values()].sort((a, b) => a.priority - b.priority);
42
52
  }
43
- function createUseInjections(getInjections) {
44
- return () => useMemo(() => getInjections(), []);
53
+ function createUseInjections(getInjections, subscribe) {
54
+ let snapshot = null;
55
+ subscribe(() => {
56
+ snapshot = null;
57
+ });
58
+ const getSnapshot = () => {
59
+ if (!snapshot) {
60
+ snapshot = getInjections();
61
+ }
62
+ return snapshot;
63
+ };
64
+ return () => useSyncExternalStore(subscribe, getSnapshot);
45
65
  }
46
66
  function wrapInjectedComponent(Component2) {
47
67
  return (props) => /* @__PURE__ */ React2.createElement(InjectedComponentWrapper, null, /* @__PURE__ */ React2.createElement(Component2, { ...props }));
@@ -50,11 +70,15 @@ function wrapInjectedComponent(Component2) {
50
70
  // src/create-location.tsx
51
71
  function createLocation() {
52
72
  const injections = /* @__PURE__ */ new Map();
73
+ const { subscribe, notify } = createSubscription();
53
74
  const getInjections = createGetInjections(injections);
54
- const useInjections = createUseInjections(getInjections);
75
+ const useInjections = createUseInjections(getInjections, subscribe);
55
76
  const Slot = createSlot(useInjections);
56
- const inject = createInject(injections);
57
- flushInjectionsFns.push(() => injections.clear());
77
+ const inject = createInject(injections, notify);
78
+ flushInjectionsFns.push(() => {
79
+ injections.clear();
80
+ notify();
81
+ });
58
82
  return {
59
83
  inject,
60
84
  getInjections,
@@ -68,7 +92,7 @@ function createSlot(useInjections) {
68
92
  return /* @__PURE__ */ React3.createElement(React3.Fragment, null, injections.map(({ id, component: Component2 }) => /* @__PURE__ */ React3.createElement(Component2, { ...props, key: id })));
69
93
  };
70
94
  }
71
- function createInject(injections) {
95
+ function createInject(injections, notify) {
72
96
  return ({ component, id, options = {} }) => {
73
97
  if (injections.has(id) && !options?.overwrite) {
74
98
  console.warn(
@@ -81,6 +105,7 @@ function createInject(injections) {
81
105
  component: wrapInjectedComponent(component),
82
106
  priority: options.priority ?? DEFAULT_PRIORITY
83
107
  });
108
+ notify();
84
109
  };
85
110
  }
86
111
 
@@ -88,11 +113,15 @@ function createInject(injections) {
88
113
  import * as React4 from "react";
89
114
  function createReplaceableLocation() {
90
115
  const injections = /* @__PURE__ */ new Map();
116
+ const { subscribe, notify } = createSubscription();
91
117
  const getInjections = createGetInjections(injections);
92
- const useInjections = createUseInjections(getInjections);
118
+ const useInjections = createUseInjections(getInjections, subscribe);
93
119
  const Slot = createReplaceable(useInjections);
94
- const inject = createRegister(injections);
95
- flushInjectionsFns.push(() => injections.clear());
120
+ const inject = createRegister(injections, notify);
121
+ flushInjectionsFns.push(() => {
122
+ injections.clear();
123
+ notify();
124
+ });
96
125
  return {
97
126
  getInjections,
98
127
  useInjections,
@@ -110,7 +139,7 @@ function createReplaceable(useInjections) {
110
139
  return /* @__PURE__ */ React4.createElement(Component2, { ...props });
111
140
  };
112
141
  }
113
- function createRegister(injections) {
142
+ function createRegister(injections, notify) {
114
143
  return ({ component, id, condition = () => true, options = {} }) => {
115
144
  injections.set(id, {
116
145
  id,
@@ -118,6 +147,7 @@ function createRegister(injections) {
118
147
  condition,
119
148
  priority: options.priority ?? DEFAULT_PRIORITY
120
149
  });
150
+ notify();
121
151
  };
122
152
  }
123
153
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/create-location.tsx","../src/injections.tsx","../src/components/injected-component-wrapper.tsx","../src/components/error-boundary.tsx","../src/create-replaceable-location.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport {\n\tcreateGetInjections,\n\tcreateUseInjections,\n\tDEFAULT_PRIORITY,\n\tflushInjectionsFns,\n\twrapInjectedComponent,\n} from './injections';\nimport { type AnyProps, type Id, type InjectArgs, type Injection, type Location } from './types';\n\ntype InjectionsMap< TProps extends object = AnyProps > = Map< Id, Injection< TProps > >;\n\nexport function createLocation< TProps extends object = AnyProps >(): Location< TProps > {\n\tconst injections: InjectionsMap< TProps > = new Map();\n\n\tconst getInjections = createGetInjections( injections );\n\tconst useInjections = createUseInjections( getInjections );\n\tconst Slot = createSlot( useInjections );\n\tconst inject = createInject( injections );\n\n\t// Push the clear function to the flushInjectionsFns array, so we can flush all injections at once.\n\tflushInjectionsFns.push( () => injections.clear() );\n\n\treturn {\n\t\tinject,\n\t\tgetInjections,\n\t\tuseInjections,\n\t\tSlot,\n\t};\n}\n\nfunction createSlot< TProps extends object = AnyProps >( useInjections: Location< TProps >[ 'useInjections' ] ) {\n\treturn ( props: TProps ) => {\n\t\tconst injections = useInjections();\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t{ injections.map( ( { id, component: Component } ) => (\n\t\t\t\t\t<Component { ...props } key={ id } />\n\t\t\t\t) ) }\n\t\t\t</>\n\t\t);\n\t};\n}\n\nfunction createInject< TProps extends object = AnyProps >( injections: InjectionsMap< TProps > ) {\n\treturn ( { component, id, options = {} }: InjectArgs< TProps > ) => {\n\t\tif ( injections.has( id ) && ! options?.overwrite ) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn(\n\t\t\t\t`An injection with the id \"${ id }\" already exists. Did you mean to use \"options.overwrite\"?`\n\t\t\t);\n\n\t\t\treturn;\n\t\t}\n\n\t\tinjections.set( id, {\n\t\t\tid,\n\t\t\tcomponent: wrapInjectedComponent( component ),\n\t\t\tpriority: options.priority ?? DEFAULT_PRIORITY,\n\t\t} );\n\t};\n}\n","import * as React from 'react';\nimport { useMemo } from 'react';\n\nimport InjectedComponentWrapper from './components/injected-component-wrapper';\nimport { type AnyProps, type Id, type InjectedComponent, type Injection, type Location } from './types';\n\ntype InjectionsMap< TProps extends object = AnyProps > = Map< Id, Injection< TProps > >;\n\nexport const DEFAULT_PRIORITY = 10;\n\n// Allow flushing all injections at once, for testing purposes.\nexport const flushInjectionsFns: ( () => void )[] = [];\n\nexport function flushAllInjections() {\n\tflushInjectionsFns.forEach( ( flush ) => flush() );\n}\n\nexport function createGetInjections< TProps extends object = AnyProps >( injections: InjectionsMap< TProps > ) {\n\treturn () => [ ...injections.values() ].sort( ( a, b ) => a.priority - b.priority );\n}\n\nexport function createUseInjections< TProps extends object = AnyProps >(\n\tgetInjections: Location< TProps >[ 'getInjections' ]\n) {\n\treturn () => useMemo( () => getInjections(), [] );\n}\n\nexport function wrapInjectedComponent< TProps extends object = AnyProps >( Component: InjectedComponent< TProps > ) {\n\treturn ( props: TProps ) => (\n\t\t<InjectedComponentWrapper>\n\t\t\t<Component { ...props } />\n\t\t</InjectedComponentWrapper>\n\t);\n}\n","import * as React from 'react';\nimport { type ReactNode, Suspense } from 'react';\n\nimport ErrorBoundary from './error-boundary';\n\nexport default function InjectedComponentWrapper( { children }: { children: ReactNode } ) {\n\treturn (\n\t\t<ErrorBoundary fallback={ null }>\n\t\t\t<Suspense fallback={ null }>{ children }</Suspense>\n\t\t</ErrorBoundary>\n\t);\n}\n","import { Component, type ReactNode } from 'react';\n\ninterface Props {\n\tchildren?: ReactNode;\n\tfallback: ReactNode;\n}\n\ninterface State {\n\thasError: boolean;\n}\n\nexport default class ErrorBoundary extends Component< Props, State > {\n\tpublic state: State = {\n\t\thasError: false,\n\t};\n\n\tpublic static getDerivedStateFromError(): State {\n\t\t// Update state so the next render will show the fallback UI.\n\t\treturn { hasError: true };\n\t}\n\n\tpublic render() {\n\t\tif ( this.state.hasError ) {\n\t\t\treturn this.props.fallback;\n\t\t}\n\n\t\treturn this.props.children;\n\t}\n}\n","import * as React from 'react';\nimport { type PropsWithChildren } from 'react';\n\nimport {\n\tcreateGetInjections,\n\tcreateUseInjections,\n\tDEFAULT_PRIORITY,\n\tflushInjectionsFns,\n\twrapInjectedComponent,\n} from './injections';\nimport {\n\ttype AnyProps,\n\ttype Id,\n\ttype ReplaceableInjectArgs,\n\ttype ReplaceableInjection,\n\ttype ReplaceableLocation,\n} from './types';\n\ntype ReplaceableInjectionsMap< TProps extends object = AnyProps > = Map< Id, ReplaceableInjection< TProps > >;\n\nexport function createReplaceableLocation< TProps extends object = AnyProps >(): ReplaceableLocation< TProps > {\n\tconst injections: ReplaceableInjectionsMap< TProps > = new Map();\n\n\tconst getInjections = createGetInjections( injections );\n\tconst useInjections = createUseInjections( getInjections );\n\tconst Slot = createReplaceable( useInjections );\n\tconst inject = createRegister( injections );\n\n\t// Push the clear function to the flushInjectionsFns array, so we can flush all injections at once.\n\tflushInjectionsFns.push( () => injections.clear() );\n\n\treturn {\n\t\tgetInjections,\n\t\tuseInjections,\n\t\tinject,\n\t\tSlot,\n\t};\n}\n\nfunction createReplaceable< TProps extends PropsWithChildren< object > = AnyProps >(\n\tuseInjections: ReplaceableLocation< TProps >[ 'useInjections' ]\n) {\n\treturn ( props: TProps ) => {\n\t\tconst injections = useInjections();\n\n\t\tconst { component: Component } = injections.find( ( { condition } ) => condition?.( props ) ) ?? {};\n\n\t\tif ( ! Component ) {\n\t\t\treturn props.children;\n\t\t}\n\n\t\treturn <Component { ...props } />;\n\t};\n}\n\nfunction createRegister< TProps extends object = AnyProps >( injections: ReplaceableInjectionsMap< TProps > ) {\n\treturn ( { component, id, condition = () => true, options = {} }: ReplaceableInjectArgs< TProps > ) => {\n\t\tinjections.set( id, {\n\t\t\tid,\n\t\t\tcomponent: wrapInjectedComponent( component ),\n\t\t\tcondition,\n\t\t\tpriority: options.priority ?? DEFAULT_PRIORITY,\n\t\t} );\n\t};\n}\n"],"mappings":"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","names":["React","React","Component","Component","React","Component"]}
1
+ {"version":3,"sources":["../src/create-location.tsx","../src/injections.tsx","../src/components/injected-component-wrapper.tsx","../src/components/error-boundary.tsx","../src/create-replaceable-location.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport {\n\tcreateGetInjections,\n\tcreateSubscription,\n\tcreateUseInjections,\n\tDEFAULT_PRIORITY,\n\tflushInjectionsFns,\n\twrapInjectedComponent,\n} from './injections';\nimport { type AnyProps, type Id, type InjectArgs, type Injection, type Location } from './types';\n\ntype InjectionsMap< TProps extends object = AnyProps > = Map< Id, Injection< TProps > >;\n\nexport function createLocation< TProps extends object = AnyProps >(): Location< TProps > {\n\tconst injections: InjectionsMap< TProps > = new Map();\n\tconst { subscribe, notify } = createSubscription();\n\n\tconst getInjections = createGetInjections( injections );\n\tconst useInjections = createUseInjections( getInjections, subscribe );\n\tconst Slot = createSlot( useInjections );\n\tconst inject = createInject( injections, notify );\n\n\t// Push the clear function to the flushInjectionsFns array, so we can flush all injections at once.\n\t// `notify()` is called too, so any mounted `Slot` (and its cached snapshot) reflects the flush,\n\t// which matters for test isolation between test cases.\n\tflushInjectionsFns.push( () => {\n\t\tinjections.clear();\n\t\tnotify();\n\t} );\n\n\treturn {\n\t\tinject,\n\t\tgetInjections,\n\t\tuseInjections,\n\t\tSlot,\n\t};\n}\n\nfunction createSlot< TProps extends object = AnyProps >( useInjections: Location< TProps >[ 'useInjections' ] ) {\n\treturn ( props: TProps ) => {\n\t\tconst injections = useInjections();\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t{ injections.map( ( { id, component: Component } ) => (\n\t\t\t\t\t<Component { ...props } key={ id } />\n\t\t\t\t) ) }\n\t\t\t</>\n\t\t);\n\t};\n}\n\nfunction createInject< TProps extends object = AnyProps >( injections: InjectionsMap< TProps >, notify: () => void ) {\n\treturn ( { component, id, options = {} }: InjectArgs< TProps > ) => {\n\t\tif ( injections.has( id ) && ! options?.overwrite ) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn(\n\t\t\t\t`An injection with the id \"${ id }\" already exists. Did you mean to use \"options.overwrite\"?`\n\t\t\t);\n\n\t\t\treturn;\n\t\t}\n\n\t\tinjections.set( id, {\n\t\t\tid,\n\t\t\tcomponent: wrapInjectedComponent( component ),\n\t\t\tpriority: options.priority ?? DEFAULT_PRIORITY,\n\t\t} );\n\n\t\tnotify();\n\t};\n}\n","import * as React from 'react';\nimport { useSyncExternalStore } from 'react';\n\nimport InjectedComponentWrapper from './components/injected-component-wrapper';\nimport { type AnyProps, type Id, type InjectedComponent, type Injection, type Location } from './types';\n\ntype InjectionsMap< TProps extends object = AnyProps > = Map< Id, Injection< TProps > >;\n\nexport const DEFAULT_PRIORITY = 10;\n\n// Allow flushing all injections at once, for testing purposes.\nexport const flushInjectionsFns: ( () => void )[] = [];\n\nexport function flushAllInjections() {\n\tflushInjectionsFns.forEach( ( flush ) => flush() );\n}\n\nexport type Subscribe = ( listener: () => void ) => () => void;\n\nexport function createSubscription() {\n\tconst listeners = new Set< () => void >();\n\n\treturn {\n\t\tsubscribe: ( listener: () => void ) => {\n\t\t\tlisteners.add( listener );\n\n\t\t\treturn () => listeners.delete( listener );\n\t\t},\n\t\tnotify: () => listeners.forEach( ( listener ) => listener() ),\n\t};\n}\n\nexport function createGetInjections< TProps extends object = AnyProps >( injections: InjectionsMap< TProps > ) {\n\treturn () => [ ...injections.values() ].sort( ( a, b ) => a.priority - b.priority );\n}\n\n// Injections registered after the `Slot` has already mounted (e.g. by an external plugin's\n// script that loads after the editor has rendered) must still be reflected in the UI, so the\n// snapshot is invalidated and re-read whenever `notify()` is called.\nexport function createUseInjections< TProps extends object = AnyProps >(\n\tgetInjections: Location< TProps >[ 'getInjections' ],\n\tsubscribe: Subscribe\n) {\n\tlet snapshot: ReturnType< Location< TProps >[ 'getInjections' ] > | null = null;\n\n\tsubscribe( () => {\n\t\tsnapshot = null;\n\t} );\n\n\tconst getSnapshot = () => {\n\t\tif ( ! snapshot ) {\n\t\t\tsnapshot = getInjections();\n\t\t}\n\n\t\treturn snapshot;\n\t};\n\n\treturn () => useSyncExternalStore( subscribe, getSnapshot );\n}\n\nexport function wrapInjectedComponent< TProps extends object = AnyProps >( Component: InjectedComponent< TProps > ) {\n\treturn ( props: TProps ) => (\n\t\t<InjectedComponentWrapper>\n\t\t\t<Component { ...props } />\n\t\t</InjectedComponentWrapper>\n\t);\n}\n","import * as React from 'react';\nimport { type ReactNode, Suspense } from 'react';\n\nimport ErrorBoundary from './error-boundary';\n\nexport default function InjectedComponentWrapper( { children }: { children: ReactNode } ) {\n\treturn (\n\t\t<ErrorBoundary fallback={ null }>\n\t\t\t<Suspense fallback={ null }>{ children }</Suspense>\n\t\t</ErrorBoundary>\n\t);\n}\n","import { Component, type ReactNode } from 'react';\n\ninterface Props {\n\tchildren?: ReactNode;\n\tfallback: ReactNode;\n}\n\ninterface State {\n\thasError: boolean;\n}\n\nexport default class ErrorBoundary extends Component< Props, State > {\n\tpublic state: State = {\n\t\thasError: false,\n\t};\n\n\tpublic static getDerivedStateFromError(): State {\n\t\t// Update state so the next render will show the fallback UI.\n\t\treturn { hasError: true };\n\t}\n\n\tpublic render() {\n\t\tif ( this.state.hasError ) {\n\t\t\treturn this.props.fallback;\n\t\t}\n\n\t\treturn this.props.children;\n\t}\n}\n","import * as React from 'react';\nimport { type PropsWithChildren } from 'react';\n\nimport {\n\tcreateGetInjections,\n\tcreateSubscription,\n\tcreateUseInjections,\n\tDEFAULT_PRIORITY,\n\tflushInjectionsFns,\n\twrapInjectedComponent,\n} from './injections';\nimport {\n\ttype AnyProps,\n\ttype Id,\n\ttype ReplaceableInjectArgs,\n\ttype ReplaceableInjection,\n\ttype ReplaceableLocation,\n} from './types';\n\ntype ReplaceableInjectionsMap< TProps extends object = AnyProps > = Map< Id, ReplaceableInjection< TProps > >;\n\nexport function createReplaceableLocation< TProps extends object = AnyProps >(): ReplaceableLocation< TProps > {\n\tconst injections: ReplaceableInjectionsMap< TProps > = new Map();\n\tconst { subscribe, notify } = createSubscription();\n\n\tconst getInjections = createGetInjections( injections );\n\tconst useInjections = createUseInjections( getInjections, subscribe );\n\tconst Slot = createReplaceable( useInjections );\n\tconst inject = createRegister( injections, notify );\n\n\t// Push the clear function to the flushInjectionsFns array, so we can flush all injections at once.\n\t// `notify()` is called too, so any mounted `Slot` (and its cached snapshot) reflects the flush,\n\t// which matters for test isolation between test cases.\n\tflushInjectionsFns.push( () => {\n\t\tinjections.clear();\n\t\tnotify();\n\t} );\n\n\treturn {\n\t\tgetInjections,\n\t\tuseInjections,\n\t\tinject,\n\t\tSlot,\n\t};\n}\n\nfunction createReplaceable< TProps extends PropsWithChildren< object > = AnyProps >(\n\tuseInjections: ReplaceableLocation< TProps >[ 'useInjections' ]\n) {\n\treturn ( props: TProps ) => {\n\t\tconst injections = useInjections();\n\n\t\tconst { component: Component } = injections.find( ( { condition } ) => condition?.( props ) ) ?? {};\n\n\t\tif ( ! Component ) {\n\t\t\treturn props.children;\n\t\t}\n\n\t\treturn <Component { ...props } />;\n\t};\n}\n\nfunction createRegister< TProps extends object = AnyProps >(\n\tinjections: ReplaceableInjectionsMap< TProps >,\n\tnotify: () => void\n) {\n\treturn ( { component, id, condition = () => true, options = {} }: ReplaceableInjectArgs< TProps > ) => {\n\t\tinjections.set( id, {\n\t\t\tid,\n\t\t\tcomponent: wrapInjectedComponent( component ),\n\t\t\tcondition,\n\t\t\tpriority: options.priority ?? DEFAULT_PRIORITY,\n\t\t} );\n\n\t\tnotify();\n\t};\n}\n"],"mappings":"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","names":["React","React","Component","Component","React","Component"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/locations",
3
3
  "description": "Create & manage pluggable React applications",
4
- "version": "4.3.0-985",
4
+ "version": "4.3.0-987",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -2,6 +2,7 @@ import * as React from 'react';
2
2
 
3
3
  import {
4
4
  createGetInjections,
5
+ createSubscription,
5
6
  createUseInjections,
6
7
  DEFAULT_PRIORITY,
7
8
  flushInjectionsFns,
@@ -13,14 +14,20 @@ type InjectionsMap< TProps extends object = AnyProps > = Map< Id, Injection< TPr
13
14
 
14
15
  export function createLocation< TProps extends object = AnyProps >(): Location< TProps > {
15
16
  const injections: InjectionsMap< TProps > = new Map();
17
+ const { subscribe, notify } = createSubscription();
16
18
 
17
19
  const getInjections = createGetInjections( injections );
18
- const useInjections = createUseInjections( getInjections );
20
+ const useInjections = createUseInjections( getInjections, subscribe );
19
21
  const Slot = createSlot( useInjections );
20
- const inject = createInject( injections );
22
+ const inject = createInject( injections, notify );
21
23
 
22
24
  // Push the clear function to the flushInjectionsFns array, so we can flush all injections at once.
23
- flushInjectionsFns.push( () => injections.clear() );
25
+ // `notify()` is called too, so any mounted `Slot` (and its cached snapshot) reflects the flush,
26
+ // which matters for test isolation between test cases.
27
+ flushInjectionsFns.push( () => {
28
+ injections.clear();
29
+ notify();
30
+ } );
24
31
 
25
32
  return {
26
33
  inject,
@@ -44,7 +51,7 @@ function createSlot< TProps extends object = AnyProps >( useInjections: Location
44
51
  };
45
52
  }
46
53
 
47
- function createInject< TProps extends object = AnyProps >( injections: InjectionsMap< TProps > ) {
54
+ function createInject< TProps extends object = AnyProps >( injections: InjectionsMap< TProps >, notify: () => void ) {
48
55
  return ( { component, id, options = {} }: InjectArgs< TProps > ) => {
49
56
  if ( injections.has( id ) && ! options?.overwrite ) {
50
57
  // eslint-disable-next-line no-console
@@ -60,5 +67,7 @@ function createInject< TProps extends object = AnyProps >( injections: Injection
60
67
  component: wrapInjectedComponent( component ),
61
68
  priority: options.priority ?? DEFAULT_PRIORITY,
62
69
  } );
70
+
71
+ notify();
63
72
  };
64
73
  }
@@ -3,6 +3,7 @@ import { type PropsWithChildren } from 'react';
3
3
 
4
4
  import {
5
5
  createGetInjections,
6
+ createSubscription,
6
7
  createUseInjections,
7
8
  DEFAULT_PRIORITY,
8
9
  flushInjectionsFns,
@@ -20,14 +21,20 @@ type ReplaceableInjectionsMap< TProps extends object = AnyProps > = Map< Id, Rep
20
21
 
21
22
  export function createReplaceableLocation< TProps extends object = AnyProps >(): ReplaceableLocation< TProps > {
22
23
  const injections: ReplaceableInjectionsMap< TProps > = new Map();
24
+ const { subscribe, notify } = createSubscription();
23
25
 
24
26
  const getInjections = createGetInjections( injections );
25
- const useInjections = createUseInjections( getInjections );
27
+ const useInjections = createUseInjections( getInjections, subscribe );
26
28
  const Slot = createReplaceable( useInjections );
27
- const inject = createRegister( injections );
29
+ const inject = createRegister( injections, notify );
28
30
 
29
31
  // Push the clear function to the flushInjectionsFns array, so we can flush all injections at once.
30
- flushInjectionsFns.push( () => injections.clear() );
32
+ // `notify()` is called too, so any mounted `Slot` (and its cached snapshot) reflects the flush,
33
+ // which matters for test isolation between test cases.
34
+ flushInjectionsFns.push( () => {
35
+ injections.clear();
36
+ notify();
37
+ } );
31
38
 
32
39
  return {
33
40
  getInjections,
@@ -53,7 +60,10 @@ function createReplaceable< TProps extends PropsWithChildren< object > = AnyProp
53
60
  };
54
61
  }
55
62
 
56
- function createRegister< TProps extends object = AnyProps >( injections: ReplaceableInjectionsMap< TProps > ) {
63
+ function createRegister< TProps extends object = AnyProps >(
64
+ injections: ReplaceableInjectionsMap< TProps >,
65
+ notify: () => void
66
+ ) {
57
67
  return ( { component, id, condition = () => true, options = {} }: ReplaceableInjectArgs< TProps > ) => {
58
68
  injections.set( id, {
59
69
  id,
@@ -61,5 +71,7 @@ function createRegister< TProps extends object = AnyProps >( injections: Replace
61
71
  condition,
62
72
  priority: options.priority ?? DEFAULT_PRIORITY,
63
73
  } );
74
+
75
+ notify();
64
76
  };
65
77
  }
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { useMemo } from 'react';
2
+ import { useSyncExternalStore } from 'react';
3
3
 
4
4
  import InjectedComponentWrapper from './components/injected-component-wrapper';
5
5
  import { type AnyProps, type Id, type InjectedComponent, type Injection, type Location } from './types';
@@ -15,14 +15,47 @@ export function flushAllInjections() {
15
15
  flushInjectionsFns.forEach( ( flush ) => flush() );
16
16
  }
17
17
 
18
+ export type Subscribe = ( listener: () => void ) => () => void;
19
+
20
+ export function createSubscription() {
21
+ const listeners = new Set< () => void >();
22
+
23
+ return {
24
+ subscribe: ( listener: () => void ) => {
25
+ listeners.add( listener );
26
+
27
+ return () => listeners.delete( listener );
28
+ },
29
+ notify: () => listeners.forEach( ( listener ) => listener() ),
30
+ };
31
+ }
32
+
18
33
  export function createGetInjections< TProps extends object = AnyProps >( injections: InjectionsMap< TProps > ) {
19
34
  return () => [ ...injections.values() ].sort( ( a, b ) => a.priority - b.priority );
20
35
  }
21
36
 
37
+ // Injections registered after the `Slot` has already mounted (e.g. by an external plugin's
38
+ // script that loads after the editor has rendered) must still be reflected in the UI, so the
39
+ // snapshot is invalidated and re-read whenever `notify()` is called.
22
40
  export function createUseInjections< TProps extends object = AnyProps >(
23
- getInjections: Location< TProps >[ 'getInjections' ]
41
+ getInjections: Location< TProps >[ 'getInjections' ],
42
+ subscribe: Subscribe
24
43
  ) {
25
- return () => useMemo( () => getInjections(), [] );
44
+ let snapshot: ReturnType< Location< TProps >[ 'getInjections' ] > | null = null;
45
+
46
+ subscribe( () => {
47
+ snapshot = null;
48
+ } );
49
+
50
+ const getSnapshot = () => {
51
+ if ( ! snapshot ) {
52
+ snapshot = getInjections();
53
+ }
54
+
55
+ return snapshot;
56
+ };
57
+
58
+ return () => useSyncExternalStore( subscribe, getSnapshot );
26
59
  }
27
60
 
28
61
  export function wrapInjectedComponent< TProps extends object = AnyProps >( Component: InjectedComponent< TProps > ) {