@elementor/locations 0.1.0 → 0.3.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/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
+
6
+ # 0.3.0 (2023-05-21)
7
+
8
+
9
+ ### Features
10
+
11
+ * **locations:** change api to support props for slots [ED-10730] ([#38](https://github.com/elementor/elementor-packages/issues/38)) ([44bec3c](https://github.com/elementor/elementor-packages/commit/44bec3cda1020037ba7105c6f05ce4baa8e3b376))
12
+
13
+
14
+
15
+ # 0.2.0 (2023-05-09)
package/dist/index.d.ts CHANGED
@@ -1,38 +1,30 @@
1
1
  import { ComponentType } from 'react';
2
2
 
3
- type Location = string;
4
- type Filler = ComponentType;
5
- type Name = string;
3
+ type EmptyObject = Record<string, never>;
4
+ type Filler<TProps extends object = EmptyObject> = ComponentType<TProps>;
6
5
  type Id = string;
7
6
  type Priority = number;
8
- type InjectionOptions = {
9
- priority?: Priority;
10
- overwrite?: boolean;
11
- };
12
- type Injection = {
13
- location: Location;
14
- filler: Filler;
7
+ type Injection<TProps extends object = EmptyObject> = {
8
+ id: Id;
9
+ filler: Filler<TProps>;
15
10
  priority: Priority;
11
+ };
12
+ type InjectArgs<TProps extends object = EmptyObject> = {
16
13
  id: Id;
14
+ filler: Filler<TProps>;
15
+ options?: {
16
+ priority?: Priority;
17
+ overwrite?: boolean;
18
+ };
17
19
  };
18
-
19
- type InjectArgs = {
20
- location: Location;
21
- filler: Filler;
22
- name: Name;
23
- options?: InjectionOptions;
20
+ type Location<TProps extends object = EmptyObject> = {
21
+ inject: (args: InjectArgs<TProps>) => void;
22
+ getInjections: () => Injection<TProps>[];
23
+ useInjections: () => Injection<TProps>[];
24
+ Slot: ComponentType<TProps>;
24
25
  };
25
- declare function inject({ location, filler, name, options }: InjectArgs): void;
26
- declare function createInjectorFor(location: Location): ({ filler, name, options }: Omit<InjectArgs, 'location'>) => void;
27
- declare function getInjectionsOf(location: string): Injection[];
28
- declare function flushInjections(): void;
29
-
30
- declare function useInjectionsOf(locations: string[]): Injection[][];
31
- declare function useInjectionsOf(location: string): Injection[];
32
26
 
33
- type Props = {
34
- location: Location;
35
- };
36
- declare function Slot({ location }: Props): JSX.Element;
27
+ declare function createLocation<TProps extends object = EmptyObject>(): Location<TProps>;
28
+ declare function flushAllInjections(): void;
37
29
 
38
- export { Filler, Id, Injection, InjectionOptions, Location, Name, Priority, Slot, createInjectorFor, flushInjections, getInjectionsOf, inject, useInjectionsOf };
30
+ export { EmptyObject, Filler, Id, InjectArgs, Injection, Location, Priority, createLocation, flushAllInjections };
package/dist/index.js CHANGED
@@ -30,16 +30,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
- Slot: () => Slot,
34
- createInjectorFor: () => createInjectorFor,
35
- flushInjections: () => flushInjections,
36
- getInjectionsOf: () => getInjectionsOf,
37
- inject: () => inject,
38
- useInjectionsOf: () => useInjectionsOf
33
+ createLocation: () => createLocation,
34
+ flushAllInjections: () => flushAllInjections
39
35
  });
40
36
  module.exports = __toCommonJS(src_exports);
41
37
 
42
- // src/injections.tsx
38
+ // src/locations.tsx
43
39
  var React2 = __toESM(require("react"));
44
40
 
45
41
  // src/components/filler-wrapper.tsx
@@ -68,70 +64,60 @@ function FillerWrapper({ children }) {
68
64
  return /* @__PURE__ */ React.createElement(ErrorBoundary, { fallback: null }, /* @__PURE__ */ React.createElement(import_react2.Suspense, { fallback: null }, children));
69
65
  }
70
66
 
71
- // src/injections.tsx
67
+ // src/locations.tsx
68
+ var import_react3 = require("react");
72
69
  var DEFAULT_PRIORITY = 10;
73
- var injections = /* @__PURE__ */ new Map();
74
- function inject({ location, filler, name, options = {} }) {
75
- const id = generateId(location, name);
76
- if (injections.has(id) && !options?.overwrite) {
77
- console.error(
78
- `An injection named "${name}" under location "${location}" already exists. Did you mean to use "options.overwrite"?`
79
- );
80
- return;
81
- }
82
- const injection = {
83
- id,
84
- location,
85
- filler: wrapFiller(filler),
86
- priority: options.priority ?? DEFAULT_PRIORITY
87
- };
88
- injections.set(id, injection);
89
- }
90
- function createInjectorFor(location) {
91
- return ({ filler, name, options }) => {
92
- return inject({ location, name, filler, options });
70
+ var flushInjectionsFns = [];
71
+ function createLocation() {
72
+ const injections = /* @__PURE__ */ new Map();
73
+ const getInjections = createGetInjections(injections);
74
+ const useInjections = createUseInjections(getInjections);
75
+ const Slot = createSlot(useInjections);
76
+ const inject = createInject(injections);
77
+ flushInjectionsFns.push(() => injections.clear());
78
+ return {
79
+ inject,
80
+ getInjections,
81
+ useInjections,
82
+ Slot
93
83
  };
94
84
  }
95
- function getInjectionsOf(location) {
96
- return [...injections.values()].filter((injection) => injection.location === location).sort((a, b) => a.priority - b.priority);
97
- }
98
- function flushInjections() {
99
- injections.clear();
85
+ function flushAllInjections() {
86
+ flushInjectionsFns.forEach((flush) => flush());
100
87
  }
101
88
  function wrapFiller(FillerComponent) {
102
89
  return (props) => /* @__PURE__ */ React2.createElement(FillerWrapper, null, /* @__PURE__ */ React2.createElement(FillerComponent, { ...props }));
103
90
  }
104
- function generateId(location, name) {
105
- return `${location}::${name}`;
91
+ function createSlot(useInjections) {
92
+ return (props) => {
93
+ const injections = useInjections();
94
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, injections.map(({ id, filler: Component2 }) => /* @__PURE__ */ React2.createElement(Component2, { ...props, key: id })));
95
+ };
96
+ }
97
+ function createGetInjections(injections) {
98
+ return () => [...injections.values()].sort((a, b) => a.priority - b.priority);
106
99
  }
107
-
108
- // src/hooks/use-injections-of.ts
109
- var import_react3 = require("react");
110
- function useInjectionsOf(locations) {
111
- return (0, import_react3.useMemo)(
112
- () => {
113
- if (Array.isArray(locations)) {
114
- return locations.map((location) => getInjectionsOf(location));
115
- }
116
- return getInjectionsOf(locations);
117
- },
118
- [locations]
119
- );
100
+ function createInject(injections) {
101
+ return ({ filler, id, options = {} }) => {
102
+ if (injections.has(id) && !options?.overwrite) {
103
+ console.error(
104
+ `An injection with the id "${id}" already exists. Did you mean to use "options.overwrite"?`
105
+ );
106
+ return;
107
+ }
108
+ injections.set(id, {
109
+ id,
110
+ filler: wrapFiller(filler),
111
+ priority: options.priority ?? DEFAULT_PRIORITY
112
+ });
113
+ };
120
114
  }
121
-
122
- // src/components/slot.tsx
123
- var React3 = __toESM(require("react"));
124
- function Slot({ location }) {
125
- const injections2 = useInjectionsOf(location);
126
- return /* @__PURE__ */ React3.createElement(React3.Fragment, null, injections2.map(({ id, filler: Filler }) => /* @__PURE__ */ React3.createElement(Filler, { key: id })));
115
+ function createUseInjections(getInjections) {
116
+ return () => (0, import_react3.useMemo)(() => getInjections(), []);
127
117
  }
128
118
  // Annotate the CommonJS export names for ESM import in node:
129
119
  0 && (module.exports = {
130
- Slot,
131
- createInjectorFor,
132
- flushInjections,
133
- getInjectionsOf,
134
- inject,
135
- useInjectionsOf
120
+ createLocation,
121
+ flushAllInjections
136
122
  });
137
123
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/injections.tsx","../src/components/filler-wrapper.tsx","../src/components/error-boundary.tsx","../src/hooks/use-injections-of.ts","../src/components/slot.tsx"],"sourcesContent":["export * from './types';\nexport { inject, getInjectionsOf, flushInjections, createInjectorFor } from './injections';\nexport { default as useInjectionsOf } from './hooks/use-injections-of';\nexport { default as Slot } from './components/slot';\n","import * as React from 'react';\nimport { Location, Filler, Injection, InjectionOptions, Name, Id } from './types';\nimport FillerWrapper from './components/filler-wrapper';\n\nconst DEFAULT_PRIORITY = 10;\n\nconst injections: Map<Id, Injection> = new Map();\n\ntype InjectArgs = {\n\tlocation: Location;\n\tfiller: Filler;\n\tname: Name;\n\toptions?: InjectionOptions;\n}\n\nexport function inject( { location, filler, name, options = {} }: InjectArgs ) {\n\tconst id = generateId( location, name );\n\n\tif ( injections.has( id ) && ! options?.overwrite ) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error(\n\t\t\t`An injection named \"${ name }\" under location \"${ location }\" already exists. Did you mean to use \"options.overwrite\"?`\n\t\t);\n\n\t\treturn;\n\t}\n\n\tconst injection = {\n\t\tid,\n\t\tlocation,\n\t\tfiller: wrapFiller( filler ),\n\t\tpriority: options.priority ?? DEFAULT_PRIORITY,\n\t};\n\n\tinjections.set( id, injection );\n}\n\nexport function createInjectorFor( location: Location ) {\n\treturn ( { filler, name, options }: Omit<InjectArgs, 'location'> ) => {\n\t\treturn inject( { location, name, filler, options } );\n\t};\n}\n\nexport function getInjectionsOf( location: string ) {\n\treturn [ ...injections.values() ]\n\t\t.filter( ( injection ) => injection.location === location )\n\t\t.sort( ( a, b ) => a.priority - b.priority );\n}\n\nexport function flushInjections() {\n\tinjections.clear();\n}\n\nfunction wrapFiller( FillerComponent: Filler ) {\n\treturn ( props: object ) => (\n\t\t<FillerWrapper>\n\t\t\t<FillerComponent { ...props } />\n\t\t</FillerWrapper>\n\t);\n}\n\nfunction generateId( location: Location, name: Name ) {\n\treturn `${ location }::${ name }`;\n}\n","import * as React from 'react';\nimport { ReactNode, Suspense } from 'react';\nimport ErrorBoundary from './error-boundary';\n\nexport default function FillerWrapper( { children }: { children: ReactNode } ) {\n\treturn (\n\t\t<ErrorBoundary fallback={ null }>\n\t\t\t<Suspense fallback={ null }>\n\t\t\t\t{ children }\n\t\t\t</Suspense>\n\t\t</ErrorBoundary>\n\t);\n}\n","import { Component, 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 { useMemo } from 'react';\nimport { getInjectionsOf } from '../injections';\nimport { Injection } from '../types';\n\nexport default function useInjectionsOf( locations: string[] ): Injection[][];\nexport default function useInjectionsOf( location: string ): Injection[];\nexport default function useInjectionsOf( locations: string | string[] ) {\n\treturn useMemo(\n\t\t() => {\n\t\t\tif ( Array.isArray( locations ) ) {\n\t\t\t\treturn locations.map( ( location ) => getInjectionsOf( location ) );\n\t\t\t}\n\n\t\t\treturn getInjectionsOf( locations );\n\t\t},\n\t\t[ locations ]\n\t);\n}\n","import * as React from 'react';\nimport useInjectionsOf from '../hooks/use-injections-of';\nimport { Location } from '../types';\n\ntype Props = {\n\tlocation: Location\n}\n\nexport default function Slot( { location }: Props ) {\n\tconst injections = useInjectionsOf( location );\n\n\treturn (\n\t\t<>\n\t\t\t{ injections.map( ( { id, filler: Filler } ) => (\n\t\t\t\t<Filler key={ id } />\n\t\t\t) ) }\n\t\t</>\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,SAAuB;;;ACAvB,YAAuB;AACvB,IAAAC,gBAAoC;;;ACDpC,mBAAqC;AAWrC,IAAqB,gBAArB,cAA2C,uBAAwB;AAAA,EAC3D,QAAe;AAAA,IACrB,UAAU;AAAA,EACX;AAAA,EAEA,OAAc,2BAAkC;AAE/C,WAAO,EAAE,UAAU,KAAK;AAAA,EACzB;AAAA,EAEO,SAAS;AACf,QAAK,KAAK,MAAM,UAAW;AAC1B,aAAO,KAAK,MAAM;AAAA,IACnB;AAEA,WAAO,KAAK,MAAM;AAAA,EACnB;AACD;;;ADxBe,SAAR,cAAgC,EAAE,SAAS,GAA6B;AAC9E,SACC,oCAAC,iBAAc,UAAW,QACzB,oCAAC,0BAAS,UAAW,QAClB,QACH,CACD;AAEF;;;ADRA,IAAM,mBAAmB;AAEzB,IAAM,aAAiC,oBAAI,IAAI;AASxC,SAAS,OAAQ,EAAE,UAAU,QAAQ,MAAM,UAAU,CAAC,EAAE,GAAgB;AAC9E,QAAM,KAAK,WAAY,UAAU,IAAK;AAEtC,MAAK,WAAW,IAAK,EAAG,KAAK,CAAE,SAAS,WAAY;AAEnD,YAAQ;AAAA,MACP,uBAAwB,yBAA2B;AAAA,IACpD;AAEA;AAAA,EACD;AAEA,QAAM,YAAY;AAAA,IACjB;AAAA,IACA;AAAA,IACA,QAAQ,WAAY,MAAO;AAAA,IAC3B,UAAU,QAAQ,YAAY;AAAA,EAC/B;AAEA,aAAW,IAAK,IAAI,SAAU;AAC/B;AAEO,SAAS,kBAAmB,UAAqB;AACvD,SAAO,CAAE,EAAE,QAAQ,MAAM,QAAQ,MAAqC;AACrE,WAAO,OAAQ,EAAE,UAAU,MAAM,QAAQ,QAAQ,CAAE;AAAA,EACpD;AACD;AAEO,SAAS,gBAAiB,UAAmB;AACnD,SAAO,CAAE,GAAG,WAAW,OAAO,CAAE,EAC9B,OAAQ,CAAE,cAAe,UAAU,aAAa,QAAS,EACzD,KAAM,CAAE,GAAG,MAAO,EAAE,WAAW,EAAE,QAAS;AAC7C;AAEO,SAAS,kBAAkB;AACjC,aAAW,MAAM;AAClB;AAEA,SAAS,WAAY,iBAA0B;AAC9C,SAAO,CAAE,UACR,qCAAC,qBACA,qCAAC,mBAAkB,GAAG,OAAQ,CAC/B;AAEF;AAEA,SAAS,WAAY,UAAoB,MAAa;AACrD,SAAO,GAAI,aAAe;AAC3B;;;AG/DA,IAAAC,gBAAwB;AAMT,SAAR,gBAAkC,WAA+B;AACvE,aAAO;AAAA,IACN,MAAM;AACL,UAAK,MAAM,QAAS,SAAU,GAAI;AACjC,eAAO,UAAU,IAAK,CAAE,aAAc,gBAAiB,QAAS,CAAE;AAAA,MACnE;AAEA,aAAO,gBAAiB,SAAU;AAAA,IACnC;AAAA,IACA,CAAE,SAAU;AAAA,EACb;AACD;;;ACjBA,IAAAC,SAAuB;AAQR,SAAR,KAAuB,EAAE,SAAS,GAAW;AACnD,QAAMC,cAAa,gBAAiB,QAAS;AAE7C,SACC,4DACGA,YAAW,IAAK,CAAE,EAAE,IAAI,QAAQ,OAAO,MACxC,qCAAC,UAAO,KAAM,IAAK,CAClB,CACH;AAEF;","names":["React","import_react","import_react","React","injections"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/locations.tsx","../src/components/filler-wrapper.tsx","../src/components/error-boundary.tsx"],"sourcesContent":["export * from './types';\nexport { createLocation, flushAllInjections } from './locations';\n","import * as React from 'react';\nimport { Filler, Injection, InjectArgs, EmptyObject, Location, Id } from './types';\nimport FillerWrapper from './components/filler-wrapper';\nimport { useMemo } from 'react';\n\ntype InjectionsMap<TProps extends object = EmptyObject> = Map<Id, Injection<TProps>>\n\nconst DEFAULT_PRIORITY = 10;\n\n// Allow flushing all injections at once, for testing purposes.\nconst flushInjectionsFns: ( () => void )[] = [];\n\nexport function createLocation<TProps extends object = EmptyObject>(): 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\nexport function flushAllInjections() {\n\tflushInjectionsFns.forEach( ( flush ) => flush() );\n}\n\nfunction wrapFiller<TProps extends object = EmptyObject>( FillerComponent: Filler<TProps> ) {\n\treturn ( props: TProps ) => (\n\t\t<FillerWrapper>\n\t\t\t<FillerComponent { ...props } />\n\t\t</FillerWrapper>\n\t);\n}\n\nfunction createSlot<TProps extends object = EmptyObject>( 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, filler: 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 createGetInjections<TProps extends object = EmptyObject>( injections: InjectionsMap<TProps> ) {\n\treturn () => [ ...injections.values() ]\n\t\t.sort( ( a, b ) => a.priority - b.priority );\n}\n\nfunction createInject<TProps extends object = EmptyObject>( injections: InjectionsMap<TProps> ) {\n\treturn ( { filler, 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.error(\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\tfiller: wrapFiller( filler ),\n\t\t\tpriority: options.priority ?? DEFAULT_PRIORITY,\n\t\t} );\n\t};\n}\n\nfunction createUseInjections<TProps extends object = EmptyObject>( getInjections: Location<TProps>[ 'getInjections' ] ) {\n\treturn () => useMemo( () => getInjections(), [] );\n}\n","import * as React from 'react';\nimport { ReactNode, Suspense } from 'react';\nimport ErrorBoundary from './error-boundary';\n\nexport default function FillerWrapper( { children }: { children: ReactNode } ) {\n\treturn (\n\t\t<ErrorBoundary fallback={ null }>\n\t\t\t<Suspense fallback={ null }>\n\t\t\t\t{ children }\n\t\t\t</Suspense>\n\t\t</ErrorBoundary>\n\t);\n}\n","import { Component, 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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,SAAuB;;;ACAvB,YAAuB;AACvB,IAAAC,gBAAoC;;;ACDpC,mBAAqC;AAWrC,IAAqB,gBAArB,cAA2C,uBAAwB;AAAA,EAC3D,QAAe;AAAA,IACrB,UAAU;AAAA,EACX;AAAA,EAEA,OAAc,2BAAkC;AAE/C,WAAO,EAAE,UAAU,KAAK;AAAA,EACzB;AAAA,EAEO,SAAS;AACf,QAAK,KAAK,MAAM,UAAW;AAC1B,aAAO,KAAK,MAAM;AAAA,IACnB;AAEA,WAAO,KAAK,MAAM;AAAA,EACnB;AACD;;;ADxBe,SAAR,cAAgC,EAAE,SAAS,GAA6B;AAC9E,SACC,oCAAC,iBAAc,UAAW,QACzB,oCAAC,0BAAS,UAAW,QAClB,QACH,CACD;AAEF;;;ADTA,IAAAC,gBAAwB;AAIxB,IAAM,mBAAmB;AAGzB,IAAM,qBAAuC,CAAC;AAEvC,SAAS,iBAAwE;AACvF,QAAM,aAAoC,oBAAI,IAAI;AAElD,QAAM,gBAAgB,oBAAqB,UAAW;AACtD,QAAM,gBAAgB,oBAAqB,aAAc;AACzD,QAAM,OAAO,WAAY,aAAc;AACvC,QAAM,SAAS,aAAc,UAAW;AAGxC,qBAAmB,KAAM,MAAM,WAAW,MAAM,CAAE;AAElD,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;AAEO,SAAS,qBAAqB;AACpC,qBAAmB,QAAS,CAAE,UAAW,MAAM,CAAE;AAClD;AAEA,SAAS,WAAiD,iBAAkC;AAC3F,SAAO,CAAE,UACR,qCAAC,qBACA,qCAAC,mBAAkB,GAAG,OAAQ,CAC/B;AAEF;AAEA,SAAS,WAAiD,eAAqD;AAC9G,SAAO,CAAE,UAAmB;AAC3B,UAAM,aAAa,cAAc;AAEjC,WACC,4DACG,WAAW,IAAK,CAAE,EAAE,IAAI,QAAQC,WAAU,MAC3C,qCAACA,YAAA,EAAY,GAAG,OAAQ,KAAM,IAAK,CAClC,CACH;AAAA,EAEF;AACD;AAEA,SAAS,oBAA0D,YAAoC;AACtG,SAAO,MAAM,CAAE,GAAG,WAAW,OAAO,CAAE,EACpC,KAAM,CAAE,GAAG,MAAO,EAAE,WAAW,EAAE,QAAS;AAC7C;AAEA,SAAS,aAAmD,YAAoC;AAC/F,SAAO,CAAE,EAAE,QAAQ,IAAI,UAAU,CAAC,EAAE,MAA2B;AAC9D,QAAK,WAAW,IAAK,EAAG,KAAK,CAAE,SAAS,WAAY;AAEnD,cAAQ;AAAA,QACP,6BAA8B;AAAA,MAC/B;AAEA;AAAA,IACD;AAEA,eAAW,IAAK,IAAI;AAAA,MACnB;AAAA,MACA,QAAQ,WAAY,MAAO;AAAA,MAC3B,UAAU,QAAQ,YAAY;AAAA,IAC/B,CAAE;AAAA,EACH;AACD;AAEA,SAAS,oBAA0D,eAAqD;AACvH,SAAO,UAAM,uBAAS,MAAM,cAAc,GAAG,CAAC,CAAE;AACjD;","names":["React","import_react","import_react","Component"]}
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- // src/injections.tsx
1
+ // src/locations.tsx
2
2
  import * as React2 from "react";
3
3
 
4
4
  // src/components/filler-wrapper.tsx
@@ -27,69 +27,59 @@ function FillerWrapper({ children }) {
27
27
  return /* @__PURE__ */ React.createElement(ErrorBoundary, { fallback: null }, /* @__PURE__ */ React.createElement(Suspense, { fallback: null }, children));
28
28
  }
29
29
 
30
- // src/injections.tsx
30
+ // src/locations.tsx
31
+ import { useMemo } from "react";
31
32
  var DEFAULT_PRIORITY = 10;
32
- var injections = /* @__PURE__ */ new Map();
33
- function inject({ location, filler, name, options = {} }) {
34
- const id = generateId(location, name);
35
- if (injections.has(id) && !options?.overwrite) {
36
- console.error(
37
- `An injection named "${name}" under location "${location}" already exists. Did you mean to use "options.overwrite"?`
38
- );
39
- return;
40
- }
41
- const injection = {
42
- id,
43
- location,
44
- filler: wrapFiller(filler),
45
- priority: options.priority ?? DEFAULT_PRIORITY
46
- };
47
- injections.set(id, injection);
48
- }
49
- function createInjectorFor(location) {
50
- return ({ filler, name, options }) => {
51
- return inject({ location, name, filler, options });
33
+ var flushInjectionsFns = [];
34
+ function createLocation() {
35
+ const injections = /* @__PURE__ */ new Map();
36
+ const getInjections = createGetInjections(injections);
37
+ const useInjections = createUseInjections(getInjections);
38
+ const Slot = createSlot(useInjections);
39
+ const inject = createInject(injections);
40
+ flushInjectionsFns.push(() => injections.clear());
41
+ return {
42
+ inject,
43
+ getInjections,
44
+ useInjections,
45
+ Slot
52
46
  };
53
47
  }
54
- function getInjectionsOf(location) {
55
- return [...injections.values()].filter((injection) => injection.location === location).sort((a, b) => a.priority - b.priority);
56
- }
57
- function flushInjections() {
58
- injections.clear();
48
+ function flushAllInjections() {
49
+ flushInjectionsFns.forEach((flush) => flush());
59
50
  }
60
51
  function wrapFiller(FillerComponent) {
61
52
  return (props) => /* @__PURE__ */ React2.createElement(FillerWrapper, null, /* @__PURE__ */ React2.createElement(FillerComponent, { ...props }));
62
53
  }
63
- function generateId(location, name) {
64
- return `${location}::${name}`;
54
+ function createSlot(useInjections) {
55
+ return (props) => {
56
+ const injections = useInjections();
57
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, injections.map(({ id, filler: Component2 }) => /* @__PURE__ */ React2.createElement(Component2, { ...props, key: id })));
58
+ };
59
+ }
60
+ function createGetInjections(injections) {
61
+ return () => [...injections.values()].sort((a, b) => a.priority - b.priority);
65
62
  }
66
-
67
- // src/hooks/use-injections-of.ts
68
- import { useMemo } from "react";
69
- function useInjectionsOf(locations) {
70
- return useMemo(
71
- () => {
72
- if (Array.isArray(locations)) {
73
- return locations.map((location) => getInjectionsOf(location));
74
- }
75
- return getInjectionsOf(locations);
76
- },
77
- [locations]
78
- );
63
+ function createInject(injections) {
64
+ return ({ filler, id, options = {} }) => {
65
+ if (injections.has(id) && !options?.overwrite) {
66
+ console.error(
67
+ `An injection with the id "${id}" already exists. Did you mean to use "options.overwrite"?`
68
+ );
69
+ return;
70
+ }
71
+ injections.set(id, {
72
+ id,
73
+ filler: wrapFiller(filler),
74
+ priority: options.priority ?? DEFAULT_PRIORITY
75
+ });
76
+ };
79
77
  }
80
-
81
- // src/components/slot.tsx
82
- import * as React3 from "react";
83
- function Slot({ location }) {
84
- const injections2 = useInjectionsOf(location);
85
- return /* @__PURE__ */ React3.createElement(React3.Fragment, null, injections2.map(({ id, filler: Filler }) => /* @__PURE__ */ React3.createElement(Filler, { key: id })));
78
+ function createUseInjections(getInjections) {
79
+ return () => useMemo(() => getInjections(), []);
86
80
  }
87
81
  export {
88
- Slot,
89
- createInjectorFor,
90
- flushInjections,
91
- getInjectionsOf,
92
- inject,
93
- useInjectionsOf
82
+ createLocation,
83
+ flushAllInjections
94
84
  };
95
85
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/injections.tsx","../src/components/filler-wrapper.tsx","../src/components/error-boundary.tsx","../src/hooks/use-injections-of.ts","../src/components/slot.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Location, Filler, Injection, InjectionOptions, Name, Id } from './types';\nimport FillerWrapper from './components/filler-wrapper';\n\nconst DEFAULT_PRIORITY = 10;\n\nconst injections: Map<Id, Injection> = new Map();\n\ntype InjectArgs = {\n\tlocation: Location;\n\tfiller: Filler;\n\tname: Name;\n\toptions?: InjectionOptions;\n}\n\nexport function inject( { location, filler, name, options = {} }: InjectArgs ) {\n\tconst id = generateId( location, name );\n\n\tif ( injections.has( id ) && ! options?.overwrite ) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error(\n\t\t\t`An injection named \"${ name }\" under location \"${ location }\" already exists. Did you mean to use \"options.overwrite\"?`\n\t\t);\n\n\t\treturn;\n\t}\n\n\tconst injection = {\n\t\tid,\n\t\tlocation,\n\t\tfiller: wrapFiller( filler ),\n\t\tpriority: options.priority ?? DEFAULT_PRIORITY,\n\t};\n\n\tinjections.set( id, injection );\n}\n\nexport function createInjectorFor( location: Location ) {\n\treturn ( { filler, name, options }: Omit<InjectArgs, 'location'> ) => {\n\t\treturn inject( { location, name, filler, options } );\n\t};\n}\n\nexport function getInjectionsOf( location: string ) {\n\treturn [ ...injections.values() ]\n\t\t.filter( ( injection ) => injection.location === location )\n\t\t.sort( ( a, b ) => a.priority - b.priority );\n}\n\nexport function flushInjections() {\n\tinjections.clear();\n}\n\nfunction wrapFiller( FillerComponent: Filler ) {\n\treturn ( props: object ) => (\n\t\t<FillerWrapper>\n\t\t\t<FillerComponent { ...props } />\n\t\t</FillerWrapper>\n\t);\n}\n\nfunction generateId( location: Location, name: Name ) {\n\treturn `${ location }::${ name }`;\n}\n","import * as React from 'react';\nimport { ReactNode, Suspense } from 'react';\nimport ErrorBoundary from './error-boundary';\n\nexport default function FillerWrapper( { children }: { children: ReactNode } ) {\n\treturn (\n\t\t<ErrorBoundary fallback={ null }>\n\t\t\t<Suspense fallback={ null }>\n\t\t\t\t{ children }\n\t\t\t</Suspense>\n\t\t</ErrorBoundary>\n\t);\n}\n","import { Component, 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 { useMemo } from 'react';\nimport { getInjectionsOf } from '../injections';\nimport { Injection } from '../types';\n\nexport default function useInjectionsOf( locations: string[] ): Injection[][];\nexport default function useInjectionsOf( location: string ): Injection[];\nexport default function useInjectionsOf( locations: string | string[] ) {\n\treturn useMemo(\n\t\t() => {\n\t\t\tif ( Array.isArray( locations ) ) {\n\t\t\t\treturn locations.map( ( location ) => getInjectionsOf( location ) );\n\t\t\t}\n\n\t\t\treturn getInjectionsOf( locations );\n\t\t},\n\t\t[ locations ]\n\t);\n}\n","import * as React from 'react';\nimport useInjectionsOf from '../hooks/use-injections-of';\nimport { Location } from '../types';\n\ntype Props = {\n\tlocation: Location\n}\n\nexport default function Slot( { location }: Props ) {\n\tconst injections = useInjectionsOf( location );\n\n\treturn (\n\t\t<>\n\t\t\t{ injections.map( ( { id, filler: Filler } ) => (\n\t\t\t\t<Filler key={ id } />\n\t\t\t) ) }\n\t\t</>\n\t);\n}\n"],"mappings":";AAAA,YAAYA,YAAW;;;ACAvB,YAAY,WAAW;AACvB,SAAoB,gBAAgB;;;ACDpC,SAAS,iBAA4B;AAWrC,IAAqB,gBAArB,cAA2C,UAAwB;AAAA,EAC3D,QAAe;AAAA,IACrB,UAAU;AAAA,EACX;AAAA,EAEA,OAAc,2BAAkC;AAE/C,WAAO,EAAE,UAAU,KAAK;AAAA,EACzB;AAAA,EAEO,SAAS;AACf,QAAK,KAAK,MAAM,UAAW;AAC1B,aAAO,KAAK,MAAM;AAAA,IACnB;AAEA,WAAO,KAAK,MAAM;AAAA,EACnB;AACD;;;ADxBe,SAAR,cAAgC,EAAE,SAAS,GAA6B;AAC9E,SACC,oCAAC,iBAAc,UAAW,QACzB,oCAAC,YAAS,UAAW,QAClB,QACH,CACD;AAEF;;;ADRA,IAAM,mBAAmB;AAEzB,IAAM,aAAiC,oBAAI,IAAI;AASxC,SAAS,OAAQ,EAAE,UAAU,QAAQ,MAAM,UAAU,CAAC,EAAE,GAAgB;AAC9E,QAAM,KAAK,WAAY,UAAU,IAAK;AAEtC,MAAK,WAAW,IAAK,EAAG,KAAK,CAAE,SAAS,WAAY;AAEnD,YAAQ;AAAA,MACP,uBAAwB,yBAA2B;AAAA,IACpD;AAEA;AAAA,EACD;AAEA,QAAM,YAAY;AAAA,IACjB;AAAA,IACA;AAAA,IACA,QAAQ,WAAY,MAAO;AAAA,IAC3B,UAAU,QAAQ,YAAY;AAAA,EAC/B;AAEA,aAAW,IAAK,IAAI,SAAU;AAC/B;AAEO,SAAS,kBAAmB,UAAqB;AACvD,SAAO,CAAE,EAAE,QAAQ,MAAM,QAAQ,MAAqC;AACrE,WAAO,OAAQ,EAAE,UAAU,MAAM,QAAQ,QAAQ,CAAE;AAAA,EACpD;AACD;AAEO,SAAS,gBAAiB,UAAmB;AACnD,SAAO,CAAE,GAAG,WAAW,OAAO,CAAE,EAC9B,OAAQ,CAAE,cAAe,UAAU,aAAa,QAAS,EACzD,KAAM,CAAE,GAAG,MAAO,EAAE,WAAW,EAAE,QAAS;AAC7C;AAEO,SAAS,kBAAkB;AACjC,aAAW,MAAM;AAClB;AAEA,SAAS,WAAY,iBAA0B;AAC9C,SAAO,CAAE,UACR,qCAAC,qBACA,qCAAC,mBAAkB,GAAG,OAAQ,CAC/B;AAEF;AAEA,SAAS,WAAY,UAAoB,MAAa;AACrD,SAAO,GAAI,aAAe;AAC3B;;;AG/DA,SAAS,eAAe;AAMT,SAAR,gBAAkC,WAA+B;AACvE,SAAO;AAAA,IACN,MAAM;AACL,UAAK,MAAM,QAAS,SAAU,GAAI;AACjC,eAAO,UAAU,IAAK,CAAE,aAAc,gBAAiB,QAAS,CAAE;AAAA,MACnE;AAEA,aAAO,gBAAiB,SAAU;AAAA,IACnC;AAAA,IACA,CAAE,SAAU;AAAA,EACb;AACD;;;ACjBA,YAAYC,YAAW;AAQR,SAAR,KAAuB,EAAE,SAAS,GAAW;AACnD,QAAMC,cAAa,gBAAiB,QAAS;AAE7C,SACC,4DACGA,YAAW,IAAK,CAAE,EAAE,IAAI,QAAQ,OAAO,MACxC,qCAAC,UAAO,KAAM,IAAK,CAClB,CACH;AAEF;","names":["React","React","injections"]}
1
+ {"version":3,"sources":["../src/locations.tsx","../src/components/filler-wrapper.tsx","../src/components/error-boundary.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Filler, Injection, InjectArgs, EmptyObject, Location, Id } from './types';\nimport FillerWrapper from './components/filler-wrapper';\nimport { useMemo } from 'react';\n\ntype InjectionsMap<TProps extends object = EmptyObject> = Map<Id, Injection<TProps>>\n\nconst DEFAULT_PRIORITY = 10;\n\n// Allow flushing all injections at once, for testing purposes.\nconst flushInjectionsFns: ( () => void )[] = [];\n\nexport function createLocation<TProps extends object = EmptyObject>(): 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\nexport function flushAllInjections() {\n\tflushInjectionsFns.forEach( ( flush ) => flush() );\n}\n\nfunction wrapFiller<TProps extends object = EmptyObject>( FillerComponent: Filler<TProps> ) {\n\treturn ( props: TProps ) => (\n\t\t<FillerWrapper>\n\t\t\t<FillerComponent { ...props } />\n\t\t</FillerWrapper>\n\t);\n}\n\nfunction createSlot<TProps extends object = EmptyObject>( 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, filler: 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 createGetInjections<TProps extends object = EmptyObject>( injections: InjectionsMap<TProps> ) {\n\treturn () => [ ...injections.values() ]\n\t\t.sort( ( a, b ) => a.priority - b.priority );\n}\n\nfunction createInject<TProps extends object = EmptyObject>( injections: InjectionsMap<TProps> ) {\n\treturn ( { filler, 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.error(\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\tfiller: wrapFiller( filler ),\n\t\t\tpriority: options.priority ?? DEFAULT_PRIORITY,\n\t\t} );\n\t};\n}\n\nfunction createUseInjections<TProps extends object = EmptyObject>( getInjections: Location<TProps>[ 'getInjections' ] ) {\n\treturn () => useMemo( () => getInjections(), [] );\n}\n","import * as React from 'react';\nimport { ReactNode, Suspense } from 'react';\nimport ErrorBoundary from './error-boundary';\n\nexport default function FillerWrapper( { children }: { children: ReactNode } ) {\n\treturn (\n\t\t<ErrorBoundary fallback={ null }>\n\t\t\t<Suspense fallback={ null }>\n\t\t\t\t{ children }\n\t\t\t</Suspense>\n\t\t</ErrorBoundary>\n\t);\n}\n","import { Component, 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"],"mappings":";AAAA,YAAYA,YAAW;;;ACAvB,YAAY,WAAW;AACvB,SAAoB,gBAAgB;;;ACDpC,SAAS,iBAA4B;AAWrC,IAAqB,gBAArB,cAA2C,UAAwB;AAAA,EAC3D,QAAe;AAAA,IACrB,UAAU;AAAA,EACX;AAAA,EAEA,OAAc,2BAAkC;AAE/C,WAAO,EAAE,UAAU,KAAK;AAAA,EACzB;AAAA,EAEO,SAAS;AACf,QAAK,KAAK,MAAM,UAAW;AAC1B,aAAO,KAAK,MAAM;AAAA,IACnB;AAEA,WAAO,KAAK,MAAM;AAAA,EACnB;AACD;;;ADxBe,SAAR,cAAgC,EAAE,SAAS,GAA6B;AAC9E,SACC,oCAAC,iBAAc,UAAW,QACzB,oCAAC,YAAS,UAAW,QAClB,QACH,CACD;AAEF;;;ADTA,SAAS,eAAe;AAIxB,IAAM,mBAAmB;AAGzB,IAAM,qBAAuC,CAAC;AAEvC,SAAS,iBAAwE;AACvF,QAAM,aAAoC,oBAAI,IAAI;AAElD,QAAM,gBAAgB,oBAAqB,UAAW;AACtD,QAAM,gBAAgB,oBAAqB,aAAc;AACzD,QAAM,OAAO,WAAY,aAAc;AACvC,QAAM,SAAS,aAAc,UAAW;AAGxC,qBAAmB,KAAM,MAAM,WAAW,MAAM,CAAE;AAElD,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;AAEO,SAAS,qBAAqB;AACpC,qBAAmB,QAAS,CAAE,UAAW,MAAM,CAAE;AAClD;AAEA,SAAS,WAAiD,iBAAkC;AAC3F,SAAO,CAAE,UACR,qCAAC,qBACA,qCAAC,mBAAkB,GAAG,OAAQ,CAC/B;AAEF;AAEA,SAAS,WAAiD,eAAqD;AAC9G,SAAO,CAAE,UAAmB;AAC3B,UAAM,aAAa,cAAc;AAEjC,WACC,4DACG,WAAW,IAAK,CAAE,EAAE,IAAI,QAAQC,WAAU,MAC3C,qCAACA,YAAA,EAAY,GAAG,OAAQ,KAAM,IAAK,CAClC,CACH;AAAA,EAEF;AACD;AAEA,SAAS,oBAA0D,YAAoC;AACtG,SAAO,MAAM,CAAE,GAAG,WAAW,OAAO,CAAE,EACpC,KAAM,CAAE,GAAG,MAAO,EAAE,WAAW,EAAE,QAAS;AAC7C;AAEA,SAAS,aAAmD,YAAoC;AAC/F,SAAO,CAAE,EAAE,QAAQ,IAAI,UAAU,CAAC,EAAE,MAA2B;AAC9D,QAAK,WAAW,IAAK,EAAG,KAAK,CAAE,SAAS,WAAY;AAEnD,cAAQ;AAAA,QACP,6BAA8B;AAAA,MAC/B;AAEA;AAAA,IACD;AAEA,eAAW,IAAK,IAAI;AAAA,MACnB;AAAA,MACA,QAAQ,WAAY,MAAO;AAAA,MAC3B,UAAU,QAAQ,YAAY;AAAA,IAC/B,CAAE;AAAA,EACH;AACD;AAEA,SAAS,oBAA0D,eAAqD;AACvH,SAAO,MAAM,QAAS,MAAM,cAAc,GAAG,CAAC,CAAE;AACjD;","names":["React","Component"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/locations",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -19,7 +19,7 @@
19
19
  "repository": {
20
20
  "type": "git",
21
21
  "url": "https://github.com/elementor/elementor-packages.git",
22
- "directory": "packages/locations"
22
+ "directory": "packages/libs/locations"
23
23
  },
24
24
  "bugs": {
25
25
  "url": "https://github.com/elementor/elementor-packages/issues"
@@ -34,5 +34,5 @@
34
34
  "peerDependencies": {
35
35
  "react": "17.x"
36
36
  },
37
- "gitHead": "2ba9f13a9dbd085eb6ed8e6e303e9275ce626b8d"
37
+ "gitHead": "45afd449fda346eea0f8a431ebca7d6c1d518dd1"
38
38
  }
@@ -1,32 +1,31 @@
1
1
  import * as React from 'react';
2
2
  import { render } from '@testing-library/react';
3
3
  import { lazy } from 'react';
4
- import { inject, getInjectionsOf } from '../injections';
5
- import Slot from '../components/slot';
4
+ import { createLocation } from '../locations';
6
5
 
7
- describe( '@elementor/locations injections', () => {
6
+ describe( '@elementor/locations', () => {
8
7
  it( 'should render components based on the location name', () => {
9
8
  // Arrange.
10
- inject( {
11
- name: 'test-1',
12
- location: 'test',
9
+ const { inject: injectIntoTest, Slot: TestSlot } = createLocation();
10
+ const { inject: injectIntoTest2 } = createLocation();
11
+
12
+ injectIntoTest( {
13
+ id: 'test-1',
13
14
  filler: () => <div data-testid="element">First div</div>,
14
15
  } );
15
16
 
16
- inject( {
17
- name: 'test-2',
18
- location: 'test',
17
+ injectIntoTest( {
18
+ id: 'test-2',
19
19
  filler: () => <div data-testid="element">Second div</div>,
20
20
  } );
21
21
 
22
- inject( {
23
- name: 'test-3',
24
- location: 'test2',
22
+ injectIntoTest2( {
23
+ id: 'test-3',
25
24
  filler: () => <div data-testid="element">Should not exist</div>,
26
25
  } );
27
26
 
28
27
  // Act.
29
- const { getAllByTestId } = render( <Slot location="test" /> );
28
+ const { getAllByTestId } = render( <TestSlot /> );
30
29
 
31
30
  // Assert.
32
31
  const elements = getAllByTestId( 'element' );
@@ -38,29 +37,28 @@ describe( '@elementor/locations injections', () => {
38
37
 
39
38
  it( 'should render components based on priority', () => {
40
39
  // Arrange.
40
+ const { inject, Slot } = createLocation();
41
+
41
42
  inject( {
42
- name: 'test-1',
43
- location: 'test',
43
+ id: 'test-1',
44
44
  filler: () => <div data-testid="element">Third div</div>,
45
45
  // Default priority is 10.
46
46
  } );
47
47
 
48
48
  inject( {
49
- name: 'test-2',
50
- location: 'test',
49
+ id: 'test-2',
51
50
  filler: () => <div data-testid="element">First div</div>,
52
51
  options: { priority: 5 },
53
52
  } );
54
53
 
55
54
  inject( {
56
- name: 'test-3',
57
- location: 'test',
55
+ id: 'test-3',
58
56
  filler: () => <div data-testid="element">Second div</div>,
59
57
  options: { priority: 5 },
60
58
  } );
61
59
 
62
60
  // Act.
63
- const { getAllByTestId } = render( <Slot location="test" /> );
61
+ const { getAllByTestId } = render( <Slot /> );
64
62
 
65
63
  // Assert.
66
64
  const elements = getAllByTestId( 'element' );
@@ -72,8 +70,11 @@ describe( '@elementor/locations injections', () => {
72
70
  } );
73
71
 
74
72
  it( 'should render empty slot when there are no fills', () => {
75
- // Arrange + Act.
76
- const { container } = render( <Slot location="empty" /> );
73
+ // Arrange.
74
+ const { Slot } = createLocation();
75
+
76
+ // Act.
77
+ const { container } = render( <Slot /> );
77
78
 
78
79
  // Assert.
79
80
  expect( container.innerHTML ).toBe( '' );
@@ -81,22 +82,22 @@ describe( '@elementor/locations injections', () => {
81
82
 
82
83
  it( 'should render lazy components', async () => {
83
84
  // Arrange.
85
+ const { inject, Slot } = createLocation();
86
+
84
87
  inject( {
85
- name: 'test-1',
86
- location: 'test',
88
+ id: 'test-1',
87
89
  filler: () => <div>First div</div>,
88
90
  } );
89
91
 
90
92
  inject( {
91
- name: 'test-2',
92
- location: 'test',
93
+ id: 'test-2',
93
94
  filler: lazy( () => Promise.resolve( {
94
95
  default: () => <div>Second div</div>,
95
96
  } ) ),
96
97
  } );
97
98
 
98
99
  // Act.
99
- const { queryByText, findByText } = render( <Slot location="test" /> );
100
+ const { queryByText, findByText } = render( <Slot /> );
100
101
 
101
102
  // Assert.
102
103
  expect( queryByText( 'First div' ) ).toBeTruthy();
@@ -111,20 +112,20 @@ describe( '@elementor/locations injections', () => {
111
112
 
112
113
  it( 'should error when injecting filler with the same name (without overwrite option)', async () => {
113
114
  // Arrange.
115
+ const { inject, Slot } = createLocation();
116
+
114
117
  inject( {
115
- name: 'test',
116
- location: 'test',
118
+ id: 'test',
117
119
  filler: () => <div>First div</div>,
118
120
  } );
119
121
 
120
122
  inject( {
121
- name: 'test',
122
- location: 'test',
123
+ id: 'test',
123
124
  filler: () => <div>Second div</div>,
124
125
  } );
125
126
 
126
127
  // Act
127
- const { queryByText } = render( <Slot location="test" /> );
128
+ const { queryByText } = render( <Slot /> );
128
129
 
129
130
  // Assert.
130
131
  expect( queryByText( 'First div' ) ).toBeTruthy();
@@ -134,28 +135,27 @@ describe( '@elementor/locations injections', () => {
134
135
 
135
136
  it( 'should overwrite the filler if has same name', async () => {
136
137
  // Arrange.
138
+ const { inject, Slot } = createLocation();
139
+
137
140
  inject( {
138
- name: 'test',
139
- location: 'test',
141
+ id: 'test',
140
142
  filler: () => <div>First div</div>,
141
143
  } );
142
144
 
143
145
  inject( {
144
- name: 'test',
145
- location: 'test',
146
+ id: 'test',
146
147
  filler: () => <div>Second div</div>,
147
148
  options: { overwrite: true },
148
149
  } );
149
150
 
150
151
  inject( {
151
- name: 'test-2',
152
- location: 'test',
152
+ id: 'test-2',
153
153
  filler: () => <div>Third div</div>,
154
154
  options: { overwrite: true },
155
155
  } );
156
156
 
157
157
  // Act
158
- const { queryByText } = render( <Slot location="test" /> );
158
+ const { queryByText } = render( <Slot /> );
159
159
 
160
160
  // Assert.
161
161
  expect( queryByText( 'First div' ) ).toBeNull();
@@ -165,53 +165,68 @@ describe( '@elementor/locations injections', () => {
165
165
 
166
166
  it( 'should overwrite the injection priority', () => {
167
167
  // Arrange.
168
+ const { inject, getInjections } = createLocation();
169
+
168
170
  inject( {
169
- name: 'test-1',
170
- location: 'test',
171
+ id: 'test-1',
171
172
  filler: () => <div />,
172
173
  options: { priority: 5 },
173
174
  } );
174
175
 
175
176
  inject( {
176
- name: 'test-1',
177
- location: 'test',
177
+ id: 'test-1',
178
178
  filler: () => <div />,
179
179
  options: { overwrite: true },
180
180
  } );
181
181
 
182
182
  // Act + Assert.
183
- expect( getInjectionsOf( 'test' ) ).toHaveLength( 1 );
184
- expect( getInjectionsOf( 'test' )[ 0 ].priority ).toBe( 10 );
183
+ expect( getInjections() ).toHaveLength( 1 );
184
+ expect( getInjections()[ 0 ].priority ).toBe( 10 );
185
185
  } );
186
186
 
187
187
  it( 'should catch filler errors with error boundary', () => {
188
188
  // Arrange.
189
+ const { inject, Slot } = createLocation();
190
+
189
191
  inject( {
190
- name: 'test-1',
191
- location: 'test',
192
+ id: 'test-1',
192
193
  filler: () => <div>Test 1</div>,
193
194
  } );
194
195
 
195
196
  inject( {
196
- name: 'test-2',
197
- location: 'test',
197
+ id: 'test-2',
198
198
  filler: () => {
199
199
  throw new Error( 'Error' );
200
200
  },
201
201
  } );
202
202
 
203
203
  inject( {
204
- name: 'test-3',
205
- location: 'test',
204
+ id: 'test-3',
206
205
  filler: () => <div>Test 3</div>,
207
206
  } );
208
207
 
209
208
  // Act.
210
- const { queryByText } = render( <Slot location="test" /> );
209
+ const { queryByText } = render( <Slot /> );
211
210
 
212
211
  // Assert.
213
212
  expect( queryByText( 'Test 1' ) ).toBeTruthy();
214
213
  expect( queryByText( 'Test 3' ) ).toBeTruthy();
215
214
  expect( console ).toHaveErrored();
216
215
  } );
216
+
217
+ it( 'should pass the props from Slot to a Filler', () => {
218
+ // Arrange.
219
+ const { inject, Slot } = createLocation<{ text: string, number: number }>();
220
+
221
+ inject( {
222
+ id: 'test-1',
223
+ filler: ( { text, number } ) => <div>{ text }: { number }</div>,
224
+ } );
225
+
226
+ // Act.
227
+ const { queryByText } = render( <Slot text="The number is" number={ 1 } /> );
228
+
229
+ // Assert.
230
+ expect( queryByText( 'The number is: 1' ) ).toBeTruthy();
231
+ } );
217
232
  } );
package/src/index.ts CHANGED
@@ -1,4 +1,2 @@
1
1
  export * from './types';
2
- export { inject, getInjectionsOf, flushInjections, createInjectorFor } from './injections';
3
- export { default as useInjectionsOf } from './hooks/use-injections-of';
4
- export { default as Slot } from './components/slot';
2
+ export { createLocation, flushAllInjections } from './locations';
@@ -0,0 +1,84 @@
1
+ import * as React from 'react';
2
+ import { Filler, Injection, InjectArgs, EmptyObject, Location, Id } from './types';
3
+ import FillerWrapper from './components/filler-wrapper';
4
+ import { useMemo } from 'react';
5
+
6
+ type InjectionsMap<TProps extends object = EmptyObject> = Map<Id, Injection<TProps>>
7
+
8
+ const DEFAULT_PRIORITY = 10;
9
+
10
+ // Allow flushing all injections at once, for testing purposes.
11
+ const flushInjectionsFns: ( () => void )[] = [];
12
+
13
+ export function createLocation<TProps extends object = EmptyObject>(): Location<TProps> {
14
+ const injections: InjectionsMap<TProps> = new Map();
15
+
16
+ const getInjections = createGetInjections( injections );
17
+ const useInjections = createUseInjections( getInjections );
18
+ const Slot = createSlot( useInjections );
19
+ const inject = createInject( injections );
20
+
21
+ // Push the clear function to the flushInjectionsFns array, so we can flush all injections at once.
22
+ flushInjectionsFns.push( () => injections.clear() );
23
+
24
+ return {
25
+ inject,
26
+ getInjections,
27
+ useInjections,
28
+ Slot,
29
+ };
30
+ }
31
+
32
+ export function flushAllInjections() {
33
+ flushInjectionsFns.forEach( ( flush ) => flush() );
34
+ }
35
+
36
+ function wrapFiller<TProps extends object = EmptyObject>( FillerComponent: Filler<TProps> ) {
37
+ return ( props: TProps ) => (
38
+ <FillerWrapper>
39
+ <FillerComponent { ...props } />
40
+ </FillerWrapper>
41
+ );
42
+ }
43
+
44
+ function createSlot<TProps extends object = EmptyObject>( useInjections: Location<TProps>[ 'useInjections' ] ) {
45
+ return ( props: TProps ) => {
46
+ const injections = useInjections();
47
+
48
+ return (
49
+ <>
50
+ { injections.map( ( { id, filler: Component } ) => (
51
+ <Component { ...props } key={ id } />
52
+ ) ) }
53
+ </>
54
+ );
55
+ };
56
+ }
57
+
58
+ function createGetInjections<TProps extends object = EmptyObject>( injections: InjectionsMap<TProps> ) {
59
+ return () => [ ...injections.values() ]
60
+ .sort( ( a, b ) => a.priority - b.priority );
61
+ }
62
+
63
+ function createInject<TProps extends object = EmptyObject>( injections: InjectionsMap<TProps> ) {
64
+ return ( { filler, id, options = {} }: InjectArgs<TProps> ) => {
65
+ if ( injections.has( id ) && ! options?.overwrite ) {
66
+ // eslint-disable-next-line no-console
67
+ console.error(
68
+ `An injection with the id "${ id }" already exists. Did you mean to use "options.overwrite"?`,
69
+ );
70
+
71
+ return;
72
+ }
73
+
74
+ injections.set( id, {
75
+ id,
76
+ filler: wrapFiller( filler ),
77
+ priority: options.priority ?? DEFAULT_PRIORITY,
78
+ } );
79
+ };
80
+ }
81
+
82
+ function createUseInjections<TProps extends object = EmptyObject>( getInjections: Location<TProps>[ 'getInjections' ] ) {
83
+ return () => useMemo( () => getInjections(), [] );
84
+ }
package/src/types.ts CHANGED
@@ -1,19 +1,29 @@
1
1
  import { ComponentType } from 'react';
2
2
 
3
- export type Location = string;
4
- export type Filler = ComponentType;
5
- export type Name = string;
3
+ export type EmptyObject = Record<string, never>;
4
+
5
+ export type Filler<TProps extends object = EmptyObject> = ComponentType<TProps>;
6
6
  export type Id = string;
7
7
  export type Priority = number;
8
8
 
9
- export type InjectionOptions = {
10
- priority?: Priority;
11
- overwrite?: boolean;
9
+ export type Injection<TProps extends object = EmptyObject> = {
10
+ id: Id;
11
+ filler: Filler<TProps>;
12
+ priority: Priority;
12
13
  }
13
14
 
14
- export type Injection = {
15
- location: Location;
16
- filler: Filler;
17
- priority: Priority;
15
+ export type InjectArgs<TProps extends object = EmptyObject> = {
18
16
  id: Id;
17
+ filler: Filler<TProps>;
18
+ options?: {
19
+ priority?: Priority;
20
+ overwrite?: boolean;
21
+ };
22
+ }
23
+
24
+ export type Location<TProps extends object = EmptyObject> = {
25
+ inject: ( args: InjectArgs<TProps> ) => void;
26
+ getInjections: () => Injection<TProps>[];
27
+ useInjections: () => Injection<TProps>[];
28
+ Slot: ComponentType<TProps>;
19
29
  }
@@ -1,19 +0,0 @@
1
- import * as React from 'react';
2
- import useInjectionsOf from '../hooks/use-injections-of';
3
- import { Location } from '../types';
4
-
5
- type Props = {
6
- location: Location
7
- }
8
-
9
- export default function Slot( { location }: Props ) {
10
- const injections = useInjectionsOf( location );
11
-
12
- return (
13
- <>
14
- { injections.map( ( { id, filler: Filler } ) => (
15
- <Filler key={ id } />
16
- ) ) }
17
- </>
18
- );
19
- }
@@ -1,18 +0,0 @@
1
- import { useMemo } from 'react';
2
- import { getInjectionsOf } from '../injections';
3
- import { Injection } from '../types';
4
-
5
- export default function useInjectionsOf( locations: string[] ): Injection[][];
6
- export default function useInjectionsOf( location: string ): Injection[];
7
- export default function useInjectionsOf( locations: string | string[] ) {
8
- return useMemo(
9
- () => {
10
- if ( Array.isArray( locations ) ) {
11
- return locations.map( ( location ) => getInjectionsOf( location ) );
12
- }
13
-
14
- return getInjectionsOf( locations );
15
- },
16
- [ locations ]
17
- );
18
- }
@@ -1,64 +0,0 @@
1
- import * as React from 'react';
2
- import { Location, Filler, Injection, InjectionOptions, Name, Id } from './types';
3
- import FillerWrapper from './components/filler-wrapper';
4
-
5
- const DEFAULT_PRIORITY = 10;
6
-
7
- const injections: Map<Id, Injection> = new Map();
8
-
9
- type InjectArgs = {
10
- location: Location;
11
- filler: Filler;
12
- name: Name;
13
- options?: InjectionOptions;
14
- }
15
-
16
- export function inject( { location, filler, name, options = {} }: InjectArgs ) {
17
- const id = generateId( location, name );
18
-
19
- if ( injections.has( id ) && ! options?.overwrite ) {
20
- // eslint-disable-next-line no-console
21
- console.error(
22
- `An injection named "${ name }" under location "${ location }" already exists. Did you mean to use "options.overwrite"?`
23
- );
24
-
25
- return;
26
- }
27
-
28
- const injection = {
29
- id,
30
- location,
31
- filler: wrapFiller( filler ),
32
- priority: options.priority ?? DEFAULT_PRIORITY,
33
- };
34
-
35
- injections.set( id, injection );
36
- }
37
-
38
- export function createInjectorFor( location: Location ) {
39
- return ( { filler, name, options }: Omit<InjectArgs, 'location'> ) => {
40
- return inject( { location, name, filler, options } );
41
- };
42
- }
43
-
44
- export function getInjectionsOf( location: string ) {
45
- return [ ...injections.values() ]
46
- .filter( ( injection ) => injection.location === location )
47
- .sort( ( a, b ) => a.priority - b.priority );
48
- }
49
-
50
- export function flushInjections() {
51
- injections.clear();
52
- }
53
-
54
- function wrapFiller( FillerComponent: Filler ) {
55
- return ( props: object ) => (
56
- <FillerWrapper>
57
- <FillerComponent { ...props } />
58
- </FillerWrapper>
59
- );
60
- }
61
-
62
- function generateId( location: Location, name: Name ) {
63
- return `${ location }::${ name }`;
64
- }