piral-core 0.14.24-beta.4144 → 0.14.24-beta.4150

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.
@@ -2,17 +2,25 @@ import * as React from 'react';
2
2
  import { isfunc } from 'piral-base';
3
3
  import { useGlobalState } from '../hooks';
4
4
  import { defaultRender, none } from '../utils';
5
+ function defaultOrder(extensions) {
6
+ return extensions;
7
+ }
5
8
  /**
6
9
  * The extension slot component to be used when the available
7
10
  * extensions of a given name should be rendered at a specific
8
11
  * location.
9
12
  */
10
13
  export function ExtensionSlot(props) {
11
- const { name, render = defaultRender, empty, params, children } = props;
14
+ const { name, render = defaultRender, empty, params, children, noEmptyRender = false, order = defaultOrder } = props;
12
15
  const extensions = useGlobalState((s) => s.registry.extensions[name] || none);
13
- return render(extensions.length === 0 && isfunc(empty)
16
+ const isEmpty = extensions.length === 0 && isfunc(empty);
17
+ const content = isEmpty
14
18
  ? [defaultRender(empty(), 'empty')]
15
- : extensions.map(({ component: Component, reference, defaults = {} }, i) => (React.createElement(Component, { key: `${(reference === null || reference === void 0 ? void 0 : reference.displayName) || '_'}${i}`, children: children, params: Object.assign(Object.assign({}, defaults), (params || {})) }))));
19
+ : order(extensions).map(({ component: Component, reference, defaults = {} }, i) => (React.createElement(Component, { key: `${(reference === null || reference === void 0 ? void 0 : reference.displayName) || '_'}${i}`, children: children, params: Object.assign(Object.assign({}, defaults), (params || {})) })));
20
+ if (isEmpty && noEmptyRender) {
21
+ return content[0];
22
+ }
23
+ return render(content);
16
24
  }
17
25
  ExtensionSlot.displayName = `ExtensionSlot`;
18
26
  //# sourceMappingURL=ExtensionSlot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExtensionSlot.js","sourceRoot":"","sources":["../../src/components/ExtensionSlot.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAG/C;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAmB,KAA4B;IAC1E,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IACxE,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;IAC9E,OAAO,MAAM,CACX,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC;QACtC,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;QACnC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CACxE,oBAAC,SAAS,IACR,GAAG,EAAE,GAAG,CAAA,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,WAAW,KAAI,GAAG,GAAG,CAAC,EAAE,EAC3C,QAAQ,EAAE,QAAQ,EAClB,MAAM,kCACD,QAAQ,GACR,CAAC,MAAM,IAAI,EAAE,CAAC,IAEnB,CACH,CAAC,CACP,CAAC;AACJ,CAAC;AAED,aAAa,CAAC,WAAW,GAAG,eAAe,CAAC"}
1
+ {"version":3,"file":"ExtensionSlot.js","sourceRoot":"","sources":["../../src/components/ExtensionSlot.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAG/C,SAAS,YAAY,CAAC,UAAwC;IAC5D,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAmB,KAA4B;IAC1E,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,KAAK,EAAE,KAAK,GAAG,YAAY,EAAE,GAAG,KAAK,CAAC;IACrH,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;IAC9E,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,OAAO;QACrB,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;QACnC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAC/E,oBAAC,SAAS,IACR,GAAG,EAAE,GAAG,CAAA,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,WAAW,KAAI,GAAG,GAAG,CAAC,EAAE,EAC3C,QAAQ,EAAE,QAAQ,EAClB,MAAM,kCACD,QAAQ,GACR,CAAC,MAAM,IAAI,EAAE,CAAC,IAEnB,CACH,CAAC,CAAC;IAEP,IAAI,OAAO,IAAI,aAAa,EAAE;QAC5B,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;KACnB;IAED,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;AACzB,CAAC;AAED,aAAa,CAAC,WAAW,GAAG,eAAe,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { ReactElement } from 'react';
1
+ import type { ReactElement, ReactNode } from 'react';
2
2
  import type { RouteComponentProps } from 'react-router';
3
3
  import type { PiletApi, Pilet, PiletMetadata, EventEmitter, SinglePilet, MultiPilet } from 'piral-base';
4
4
  import type { PiletCustomApi, PiralCustomPageMeta } from './custom';
@@ -33,6 +33,10 @@ export interface ExtensionComponentProps<T> extends BaseComponentProps {
33
33
  * The provided parameters for showing the extension.
34
34
  */
35
35
  params: T extends keyof PiralExtensionSlotMap ? PiralExtensionSlotMap[T] : T extends string ? any : T;
36
+ /**
37
+ * The optional children to receive, if any.
38
+ */
39
+ children?: ReactNode;
36
40
  }
37
41
  /**
38
42
  * The props that every registered page component obtains.
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode, ReactElement } from 'react';
2
2
  import type { PiralCustomExtensionSlotMap } from './custom';
3
+ import type { ExtensionRegistration } from './state';
3
4
  /**
4
5
  * The mapping of the existing (known) extension slots.
5
6
  */
@@ -18,9 +19,27 @@ export interface BaseExtensionSlotProps<TName, TParams> {
18
19
  * for the specified extension.
19
20
  */
20
21
  empty?(): ReactNode;
22
+ /**
23
+ * Determines if the `render` function should be called in case no
24
+ * components are available for the specified extension.
25
+ *
26
+ * If true, `empty` will be called and returned from the slot.
27
+ * If false, `render` will be called with the result of calling `empty`.
28
+ * The result of calling `render` will then be returned from the slot.
29
+ */
30
+ noEmptyRender?: boolean;
31
+ /**
32
+ * Defines the order of the components to render.
33
+ * May be more convient than using `render` w.r.t. ordering extensions
34
+ * by their supplied metadata.
35
+ * @param extensions The registered extensions.
36
+ * @returns The ordered extensions.
37
+ */
38
+ order?(extensions: Array<ExtensionRegistration>): Array<ExtensionRegistration>;
21
39
  /**
22
40
  * Defines how the provided nodes should be rendered.
23
41
  * @param nodes The rendered extension nodes.
42
+ * @returns The rendered nodes, i.e., an ReactElement.
24
43
  */
25
44
  render?(nodes: Array<ReactNode>): ReactElement<any, any> | null;
26
45
  /**
@@ -22,21 +22,39 @@ export declare type WrappedComponent<TProps> = ComponentType<Without<TProps, key
22
22
  * The base type for pilet component registration in the global state context.
23
23
  */
24
24
  export interface BaseRegistration {
25
+ /**
26
+ * The pilet registering the component.
27
+ */
25
28
  pilet: string;
26
29
  }
27
30
  /**
28
31
  * The interface modeling the registration of a pilet page component.
29
32
  */
30
33
  export interface PageRegistration extends BaseRegistration {
34
+ /**
35
+ * The registered page component.
36
+ */
31
37
  component: WrappedComponent<PageComponentProps>;
38
+ /**
39
+ * The page's associated metadata.
40
+ */
32
41
  meta: PiralPageMeta;
33
42
  }
34
43
  /**
35
44
  * The interface modeling the registration of a pilet extension component.
36
45
  */
37
46
  export interface ExtensionRegistration extends BaseRegistration {
47
+ /**
48
+ * The wrapped registered extension component.
49
+ */
38
50
  component: WrappedComponent<ExtensionComponentProps<string>>;
51
+ /**
52
+ * The original extension component that has been registered.
53
+ */
39
54
  reference: any;
55
+ /**
56
+ * The default params (i.e., meta) of the extension.
57
+ */
40
58
  defaults: any;
41
59
  }
42
60
  /**
@@ -5,17 +5,25 @@ const React = require("react");
5
5
  const piral_base_1 = require("piral-base");
6
6
  const hooks_1 = require("../hooks");
7
7
  const utils_1 = require("../utils");
8
+ function defaultOrder(extensions) {
9
+ return extensions;
10
+ }
8
11
  /**
9
12
  * The extension slot component to be used when the available
10
13
  * extensions of a given name should be rendered at a specific
11
14
  * location.
12
15
  */
13
16
  function ExtensionSlot(props) {
14
- const { name, render = utils_1.defaultRender, empty, params, children } = props;
17
+ const { name, render = utils_1.defaultRender, empty, params, children, noEmptyRender = false, order = defaultOrder } = props;
15
18
  const extensions = (0, hooks_1.useGlobalState)((s) => s.registry.extensions[name] || utils_1.none);
16
- return render(extensions.length === 0 && (0, piral_base_1.isfunc)(empty)
19
+ const isEmpty = extensions.length === 0 && (0, piral_base_1.isfunc)(empty);
20
+ const content = isEmpty
17
21
  ? [(0, utils_1.defaultRender)(empty(), 'empty')]
18
- : extensions.map(({ component: Component, reference, defaults = {} }, i) => (React.createElement(Component, { key: `${(reference === null || reference === void 0 ? void 0 : reference.displayName) || '_'}${i}`, children: children, params: Object.assign(Object.assign({}, defaults), (params || {})) }))));
22
+ : order(extensions).map(({ component: Component, reference, defaults = {} }, i) => (React.createElement(Component, { key: `${(reference === null || reference === void 0 ? void 0 : reference.displayName) || '_'}${i}`, children: children, params: Object.assign(Object.assign({}, defaults), (params || {})) })));
23
+ if (isEmpty && noEmptyRender) {
24
+ return content[0];
25
+ }
26
+ return render(content);
19
27
  }
20
28
  exports.ExtensionSlot = ExtensionSlot;
21
29
  ExtensionSlot.displayName = `ExtensionSlot`;
@@ -1 +1 @@
1
- {"version":3,"file":"ExtensionSlot.js","sourceRoot":"","sources":["../../src/components/ExtensionSlot.tsx"],"names":[],"mappings":";;;AAAA,+BAA+B;AAC/B,2CAAoC;AACpC,oCAA0C;AAC1C,oCAA+C;AAG/C;;;;GAIG;AACH,SAAgB,aAAa,CAAmB,KAA4B;IAC1E,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAa,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IACxE,MAAM,UAAU,GAAG,IAAA,sBAAc,EAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,YAAI,CAAC,CAAC;IAC9E,OAAO,MAAM,CACX,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,IAAA,mBAAM,EAAC,KAAK,CAAC;QACtC,CAAC,CAAC,CAAC,IAAA,qBAAa,EAAC,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;QACnC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CACxE,oBAAC,SAAS,IACR,GAAG,EAAE,GAAG,CAAA,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,WAAW,KAAI,GAAG,GAAG,CAAC,EAAE,EAC3C,QAAQ,EAAE,QAAQ,EAClB,MAAM,kCACD,QAAQ,GACR,CAAC,MAAM,IAAI,EAAE,CAAC,IAEnB,CACH,CAAC,CACP,CAAC;AACJ,CAAC;AAjBD,sCAiBC;AAED,aAAa,CAAC,WAAW,GAAG,eAAe,CAAC"}
1
+ {"version":3,"file":"ExtensionSlot.js","sourceRoot":"","sources":["../../src/components/ExtensionSlot.tsx"],"names":[],"mappings":";;;AAAA,+BAA+B;AAC/B,2CAAoC;AACpC,oCAA0C;AAC1C,oCAA+C;AAG/C,SAAS,YAAY,CAAC,UAAwC;IAC5D,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,SAAgB,aAAa,CAAmB,KAA4B;IAC1E,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAa,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,KAAK,EAAE,KAAK,GAAG,YAAY,EAAE,GAAG,KAAK,CAAC;IACrH,MAAM,UAAU,GAAG,IAAA,sBAAc,EAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,YAAI,CAAC,CAAC;IAC9E,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,IAAA,mBAAM,EAAC,KAAK,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,OAAO;QACrB,CAAC,CAAC,CAAC,IAAA,qBAAa,EAAC,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;QACnC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAC/E,oBAAC,SAAS,IACR,GAAG,EAAE,GAAG,CAAA,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,WAAW,KAAI,GAAG,GAAG,CAAC,EAAE,EAC3C,QAAQ,EAAE,QAAQ,EAClB,MAAM,kCACD,QAAQ,GACR,CAAC,MAAM,IAAI,EAAE,CAAC,IAEnB,CACH,CAAC,CAAC;IAEP,IAAI,OAAO,IAAI,aAAa,EAAE;QAC5B,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;KACnB;IAED,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;AACzB,CAAC;AAtBD,sCAsBC;AAED,aAAa,CAAC,WAAW,GAAG,eAAe,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { ReactElement } from 'react';
1
+ import type { ReactElement, ReactNode } from 'react';
2
2
  import type { RouteComponentProps } from 'react-router';
3
3
  import type { PiletApi, Pilet, PiletMetadata, EventEmitter, SinglePilet, MultiPilet } from 'piral-base';
4
4
  import type { PiletCustomApi, PiralCustomPageMeta } from './custom';
@@ -33,6 +33,10 @@ export interface ExtensionComponentProps<T> extends BaseComponentProps {
33
33
  * The provided parameters for showing the extension.
34
34
  */
35
35
  params: T extends keyof PiralExtensionSlotMap ? PiralExtensionSlotMap[T] : T extends string ? any : T;
36
+ /**
37
+ * The optional children to receive, if any.
38
+ */
39
+ children?: ReactNode;
36
40
  }
37
41
  /**
38
42
  * The props that every registered page component obtains.
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode, ReactElement } from 'react';
2
2
  import type { PiralCustomExtensionSlotMap } from './custom';
3
+ import type { ExtensionRegistration } from './state';
3
4
  /**
4
5
  * The mapping of the existing (known) extension slots.
5
6
  */
@@ -18,9 +19,27 @@ export interface BaseExtensionSlotProps<TName, TParams> {
18
19
  * for the specified extension.
19
20
  */
20
21
  empty?(): ReactNode;
22
+ /**
23
+ * Determines if the `render` function should be called in case no
24
+ * components are available for the specified extension.
25
+ *
26
+ * If true, `empty` will be called and returned from the slot.
27
+ * If false, `render` will be called with the result of calling `empty`.
28
+ * The result of calling `render` will then be returned from the slot.
29
+ */
30
+ noEmptyRender?: boolean;
31
+ /**
32
+ * Defines the order of the components to render.
33
+ * May be more convient than using `render` w.r.t. ordering extensions
34
+ * by their supplied metadata.
35
+ * @param extensions The registered extensions.
36
+ * @returns The ordered extensions.
37
+ */
38
+ order?(extensions: Array<ExtensionRegistration>): Array<ExtensionRegistration>;
21
39
  /**
22
40
  * Defines how the provided nodes should be rendered.
23
41
  * @param nodes The rendered extension nodes.
42
+ * @returns The rendered nodes, i.e., an ReactElement.
24
43
  */
25
44
  render?(nodes: Array<ReactNode>): ReactElement<any, any> | null;
26
45
  /**
@@ -22,21 +22,39 @@ export declare type WrappedComponent<TProps> = ComponentType<Without<TProps, key
22
22
  * The base type for pilet component registration in the global state context.
23
23
  */
24
24
  export interface BaseRegistration {
25
+ /**
26
+ * The pilet registering the component.
27
+ */
25
28
  pilet: string;
26
29
  }
27
30
  /**
28
31
  * The interface modeling the registration of a pilet page component.
29
32
  */
30
33
  export interface PageRegistration extends BaseRegistration {
34
+ /**
35
+ * The registered page component.
36
+ */
31
37
  component: WrappedComponent<PageComponentProps>;
38
+ /**
39
+ * The page's associated metadata.
40
+ */
32
41
  meta: PiralPageMeta;
33
42
  }
34
43
  /**
35
44
  * The interface modeling the registration of a pilet extension component.
36
45
  */
37
46
  export interface ExtensionRegistration extends BaseRegistration {
47
+ /**
48
+ * The wrapped registered extension component.
49
+ */
38
50
  component: WrappedComponent<ExtensionComponentProps<string>>;
51
+ /**
52
+ * The original extension component that has been registered.
53
+ */
39
54
  reference: any;
55
+ /**
56
+ * The default params (i.e., meta) of the extension.
57
+ */
40
58
  defaults: any;
41
59
  }
42
60
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "piral-core",
3
- "version": "0.14.24-beta.4144",
3
+ "version": "0.14.24-beta.4150",
4
4
  "description": "The core library for creating a Piral instance.",
5
5
  "keywords": [
6
6
  "portal",
@@ -63,8 +63,8 @@
63
63
  },
64
64
  "dependencies": {
65
65
  "@dbeining/react-atom": "^4.0.0",
66
- "piral-base": "0.14.24-beta.4144",
67
- "piral-debug-utils": "0.14.24-beta.4144"
66
+ "piral-base": "0.14.24-beta.4150",
67
+ "piral-debug-utils": "0.14.24-beta.4150"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "react": ">=16.8.0",
@@ -91,5 +91,5 @@
91
91
  "@libre/atom",
92
92
  "@dbeining/react-atom"
93
93
  ],
94
- "gitHead": "b14c6360d67ae6fc0af29567eaf3011d782fd4b2"
94
+ "gitHead": "3a3b6461ff039a1751494b6481aefa6fffbf22a7"
95
95
  }
@@ -29,6 +29,11 @@ const state = {
29
29
  component: StubComponent2,
30
30
  },
31
31
  ],
32
+ lol: [
33
+ {
34
+ component: StubComponent1,
35
+ },
36
+ ],
32
37
  },
33
38
  },
34
39
  };
@@ -67,7 +72,7 @@ describe('Extension Module', () => {
67
72
  it('overrides the empty renderer on not available extension', () => {
68
73
  const node = mount(<ExtensionSlot name="qxz" empty={() => <StubComponent1 key="empty" />} />);
69
74
  expect(node.find(StubComponent1).length).toBe(1);
70
- expect(node.find(StubComponent1).length).toBe(1);
75
+ expect(node.find(StubComponent2).length).toBe(0);
71
76
  });
72
77
 
73
78
  it('overrides the empty renderer on an available extension', () => {
@@ -87,4 +92,30 @@ describe('Extension Module', () => {
87
92
  expect(node.find(StubComponent1).length).toBe(1);
88
93
  expect(node.find(StubComponent2).length).toBe(1);
89
94
  });
95
+
96
+ it('does not use the render function with empty when noEmptyRender is set', () => {
97
+ const node = mount(
98
+ <ExtensionSlot
99
+ name="foo"
100
+ noEmptyRender
101
+ empty={() => <StubComponent2 key="empty" />}
102
+ render={(nodes) => <StubComponent1 children={nodes} />}
103
+ />,
104
+ );
105
+ expect(node.find(StubComponent1).length).toBe(0);
106
+ expect(node.find(StubComponent2).length).toBe(1);
107
+ });
108
+
109
+ it('does use the render function without empty independent if noEmptyRender is set', () => {
110
+ const node = mount(
111
+ <ExtensionSlot
112
+ name="lol"
113
+ noEmptyRender
114
+ empty={() => <StubComponent2 key="empty" />}
115
+ render={(nodes) => <StubComponent1 children={nodes} />}
116
+ />,
117
+ );
118
+ expect(node.find(StubComponent1).length).toBe(2);
119
+ expect(node.find(StubComponent2).length).toBe(0);
120
+ });
90
121
  });
@@ -2,7 +2,11 @@ import * as React from 'react';
2
2
  import { isfunc } from 'piral-base';
3
3
  import { useGlobalState } from '../hooks';
4
4
  import { defaultRender, none } from '../utils';
5
- import { ExtensionSlotProps } from '../types';
5
+ import { ExtensionRegistration, ExtensionSlotProps } from '../types';
6
+
7
+ function defaultOrder(extensions: Array<ExtensionRegistration>) {
8
+ return extensions;
9
+ }
6
10
 
7
11
  /**
8
12
  * The extension slot component to be used when the available
@@ -10,22 +14,27 @@ import { ExtensionSlotProps } from '../types';
10
14
  * location.
11
15
  */
12
16
  export function ExtensionSlot<T extends string>(props: ExtensionSlotProps<T>) {
13
- const { name, render = defaultRender, empty, params, children } = props;
17
+ const { name, render = defaultRender, empty, params, children, noEmptyRender = false, order = defaultOrder } = props;
14
18
  const extensions = useGlobalState((s) => s.registry.extensions[name] || none);
15
- return render(
16
- extensions.length === 0 && isfunc(empty)
17
- ? [defaultRender(empty(), 'empty')]
18
- : extensions.map(({ component: Component, reference, defaults = {} }, i) => (
19
- <Component
20
- key={`${reference?.displayName || '_'}${i}`}
21
- children={children}
22
- params={{
23
- ...defaults,
24
- ...(params || {}),
25
- }}
26
- />
27
- )),
28
- );
19
+ const isEmpty = extensions.length === 0 && isfunc(empty);
20
+ const content = isEmpty
21
+ ? [defaultRender(empty(), 'empty')]
22
+ : order(extensions).map(({ component: Component, reference, defaults = {} }, i) => (
23
+ <Component
24
+ key={`${reference?.displayName || '_'}${i}`}
25
+ children={children}
26
+ params={{
27
+ ...defaults,
28
+ ...(params || {}),
29
+ }}
30
+ />
31
+ ));
32
+
33
+ if (isEmpty && noEmptyRender) {
34
+ return content[0];
35
+ }
36
+
37
+ return render(content);
29
38
  }
30
39
 
31
40
  ExtensionSlot.displayName = `ExtensionSlot`;
package/src/types/api.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ReactElement } from 'react';
1
+ import type { ReactElement, ReactNode } from 'react';
2
2
  import type { RouteComponentProps } from 'react-router';
3
3
  import type { PiletApi, Pilet, PiletMetadata, EventEmitter, SinglePilet, MultiPilet } from 'piral-base';
4
4
  import type { PiletCustomApi, PiralCustomPageMeta } from './custom';
@@ -37,6 +37,10 @@ export interface ExtensionComponentProps<T> extends BaseComponentProps {
37
37
  * The provided parameters for showing the extension.
38
38
  */
39
39
  params: T extends keyof PiralExtensionSlotMap ? PiralExtensionSlotMap[T] : T extends string ? any : T;
40
+ /**
41
+ * The optional children to receive, if any.
42
+ */
43
+ children?: ReactNode;
40
44
  }
41
45
 
42
46
  /**
@@ -65,10 +69,10 @@ export interface PiralPageMeta extends PiralCustomPageMeta {}
65
69
  * Shorthand for the definition of an extension component.
66
70
  */
67
71
  export type AnyExtensionComponent<TName> = TName extends keyof PiralExtensionSlotMap
68
- ? AnyComponent<ExtensionComponentProps<TName>>
69
- : TName extends string
70
- ? AnyComponent<ExtensionComponentProps<any>>
71
- : AnyComponent<ExtensionComponentProps<TName>>
72
+ ? AnyComponent<ExtensionComponentProps<TName>>
73
+ : TName extends string
74
+ ? AnyComponent<ExtensionComponentProps<any>>
75
+ : AnyComponent<ExtensionComponentProps<TName>>;
72
76
 
73
77
  /**
74
78
  * Defines the Pilet API from piral-core.
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode, ReactElement } from 'react';
2
2
  import type { PiralCustomExtensionSlotMap } from './custom';
3
+ import type { ExtensionRegistration } from './state';
3
4
 
4
5
  /**
5
6
  * The mapping of the existing (known) extension slots.
@@ -19,9 +20,27 @@ export interface BaseExtensionSlotProps<TName, TParams> {
19
20
  * for the specified extension.
20
21
  */
21
22
  empty?(): ReactNode;
23
+ /**
24
+ * Determines if the `render` function should be called in case no
25
+ * components are available for the specified extension.
26
+ *
27
+ * If true, `empty` will be called and returned from the slot.
28
+ * If false, `render` will be called with the result of calling `empty`.
29
+ * The result of calling `render` will then be returned from the slot.
30
+ */
31
+ noEmptyRender?: boolean;
32
+ /**
33
+ * Defines the order of the components to render.
34
+ * May be more convient than using `render` w.r.t. ordering extensions
35
+ * by their supplied metadata.
36
+ * @param extensions The registered extensions.
37
+ * @returns The ordered extensions.
38
+ */
39
+ order?(extensions: Array<ExtensionRegistration>): Array<ExtensionRegistration>;
22
40
  /**
23
41
  * Defines how the provided nodes should be rendered.
24
42
  * @param nodes The rendered extension nodes.
43
+ * @returns The rendered nodes, i.e., an ReactElement.
25
44
  */
26
45
  render?(nodes: Array<ReactNode>): ReactElement<any, any> | null;
27
46
  /**
@@ -48,6 +48,9 @@ export type WrappedComponent<TProps> = ComponentType<Without<TProps, keyof BaseC
48
48
  * The base type for pilet component registration in the global state context.
49
49
  */
50
50
  export interface BaseRegistration {
51
+ /**
52
+ * The pilet registering the component.
53
+ */
51
54
  pilet: string;
52
55
  }
53
56
 
@@ -55,7 +58,13 @@ export interface BaseRegistration {
55
58
  * The interface modeling the registration of a pilet page component.
56
59
  */
57
60
  export interface PageRegistration extends BaseRegistration {
61
+ /**
62
+ * The registered page component.
63
+ */
58
64
  component: WrappedComponent<PageComponentProps>;
65
+ /**
66
+ * The page's associated metadata.
67
+ */
59
68
  meta: PiralPageMeta;
60
69
  }
61
70
 
@@ -63,8 +72,17 @@ export interface PageRegistration extends BaseRegistration {
63
72
  * The interface modeling the registration of a pilet extension component.
64
73
  */
65
74
  export interface ExtensionRegistration extends BaseRegistration {
75
+ /**
76
+ * The wrapped registered extension component.
77
+ */
66
78
  component: WrappedComponent<ExtensionComponentProps<string>>;
79
+ /**
80
+ * The original extension component that has been registered.
81
+ */
67
82
  reference: any;
83
+ /**
84
+ * The default params (i.e., meta) of the extension.
85
+ */
68
86
  defaults: any;
69
87
  }
70
88