@elementor/locations 0.2.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 +15 -0
- package/dist/index.d.ts +20 -28
- package/dist/index.js +46 -60
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +44 -54
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/index.test.tsx +67 -52
- package/src/index.ts +1 -3
- package/src/locations.tsx +84 -0
- package/src/types.ts +20 -10
- package/src/components/slot.tsx +0 -19
- package/src/hooks/use-injections-of.ts +0 -18
- package/src/injections.tsx +0 -64
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
|
|
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
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
34
|
-
|
|
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,
|
|
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
|
-
|
|
34
|
-
|
|
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/
|
|
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/
|
|
67
|
+
// src/locations.tsx
|
|
68
|
+
var import_react3 = require("react");
|
|
72
69
|
var DEFAULT_PRIORITY = 10;
|
|
73
|
-
var
|
|
74
|
-
function
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
96
|
-
|
|
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
|
|
105
|
-
return
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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/
|
|
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/
|
|
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/
|
|
30
|
+
// src/locations.tsx
|
|
31
|
+
import { useMemo } from "react";
|
|
31
32
|
var DEFAULT_PRIORITY = 10;
|
|
32
|
-
var
|
|
33
|
-
function
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
55
|
-
|
|
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
|
|
64
|
-
return
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
flushInjections,
|
|
91
|
-
getInjectionsOf,
|
|
92
|
-
inject,
|
|
93
|
-
useInjectionsOf
|
|
82
|
+
createLocation,
|
|
83
|
+
flushAllInjections
|
|
94
84
|
};
|
|
95
85
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
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.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Elementor Team",
|
|
6
6
|
"homepage": "https://elementor.com/",
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"react": "17.x"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
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 {
|
|
5
|
-
import Slot from '../components/slot';
|
|
4
|
+
import { createLocation } from '../locations';
|
|
6
5
|
|
|
7
|
-
describe( '@elementor/locations
|
|
6
|
+
describe( '@elementor/locations', () => {
|
|
8
7
|
it( 'should render components based on the location name', () => {
|
|
9
8
|
// Arrange.
|
|
10
|
-
inject(
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
location: 'test',
|
|
17
|
+
injectIntoTest( {
|
|
18
|
+
id: 'test-2',
|
|
19
19
|
filler: () => <div data-testid="element">Second div</div>,
|
|
20
20
|
} );
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
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( <
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
76
|
-
const {
|
|
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
|
-
|
|
86
|
-
location: 'test',
|
|
88
|
+
id: 'test-1',
|
|
87
89
|
filler: () => <div>First div</div>,
|
|
88
90
|
} );
|
|
89
91
|
|
|
90
92
|
inject( {
|
|
91
|
-
|
|
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
|
|
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
|
-
|
|
116
|
-
location: 'test',
|
|
118
|
+
id: 'test',
|
|
117
119
|
filler: () => <div>First div</div>,
|
|
118
120
|
} );
|
|
119
121
|
|
|
120
122
|
inject( {
|
|
121
|
-
|
|
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
|
|
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
|
-
|
|
139
|
-
location: 'test',
|
|
141
|
+
id: 'test',
|
|
140
142
|
filler: () => <div>First div</div>,
|
|
141
143
|
} );
|
|
142
144
|
|
|
143
145
|
inject( {
|
|
144
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
170
|
-
location: 'test',
|
|
171
|
+
id: 'test-1',
|
|
171
172
|
filler: () => <div />,
|
|
172
173
|
options: { priority: 5 },
|
|
173
174
|
} );
|
|
174
175
|
|
|
175
176
|
inject( {
|
|
176
|
-
|
|
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(
|
|
184
|
-
expect(
|
|
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
|
-
|
|
191
|
-
location: 'test',
|
|
192
|
+
id: 'test-1',
|
|
192
193
|
filler: () => <div>Test 1</div>,
|
|
193
194
|
} );
|
|
194
195
|
|
|
195
196
|
inject( {
|
|
196
|
-
|
|
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
|
-
|
|
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
|
|
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 {
|
|
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
|
|
4
|
-
|
|
5
|
-
export type
|
|
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
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
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
|
}
|
package/src/components/slot.tsx
DELETED
|
@@ -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
|
-
}
|
package/src/injections.tsx
DELETED
|
@@ -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
|
-
}
|