@cleanweb/oore 2.0.0-alpha.12 → 2.0.0-alpha.14

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.
@@ -1,10 +1,11 @@
1
- import type { ReactElement, ReactNode } from 'react';
1
+ import type { ReactElement, ReactNode, ReactPortal } from 'react';
2
2
  import type { IUseSlots, PotentialSlotComponent } from './types';
3
3
  export declare const isElementChild: (child: ReactNode) => child is ReactElement<any, any>;
4
4
  interface IGetSlotName {
5
5
  (TargetComponent: PotentialSlotComponent, child?: ReactElement): string | undefined;
6
6
  }
7
7
  export declare const getComponentSlotName: IGetSlotName;
8
+ export declare const isPortalChild: (child: ReactNode) => child is ReactPortal;
8
9
  /**
9
10
  * Groups `children` prop into predefined slots.
10
11
  *
@@ -16,4 +17,4 @@ export declare const getComponentSlotName: IGetSlotName;
16
17
  * @see {@link SlotComponent} for more on how to use the returned slot nodes.
17
18
  */
18
19
  export declare const useSlots: IUseSlots;
19
- export type { SlotNamedComponent, Slotted as SlottedComponent, TSlotsRecord, PotentialSlotComponent, } from './types';
20
+ export type { SlottedComponent, TSlotsRecord, SlotComponent, PotentialSlotComponent, } from './types';
@@ -32,7 +32,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
32
32
  return to.concat(ar || Array.prototype.slice.call(from));
33
33
  };
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.useSlots = exports.getComponentSlotName = exports.isElementChild = void 0;
35
+ exports.useSlots = exports.isPortalChild = exports.getComponentSlotName = exports.isElementChild = void 0;
36
36
  var errors_1 = require("../helpers/errors");
37
37
  var react_1 = __importStar(require("react"));
38
38
  var isElementChild = function (child) {
@@ -47,9 +47,6 @@ var getComponentSlotName = function (TargetComponent, child) {
47
47
  var keyTypes = ['string', 'number', 'symbol'];
48
48
  var slotName = child.props['data-slot-name'];
49
49
  if (keyTypes.includes(typeof slotName)) {
50
- if (typeof child.type === 'string') {
51
- child.props.tagName = child.type;
52
- }
53
50
  return slotName;
54
51
  }
55
52
  }
@@ -65,6 +62,12 @@ var getComponentSlotName = function (TargetComponent, child) {
65
62
  return undefined;
66
63
  };
67
64
  exports.getComponentSlotName = getComponentSlotName;
65
+ var isPortalChild = function (child) {
66
+ return (!!child
67
+ && typeof child === 'object'
68
+ && 'children' in child);
69
+ };
70
+ exports.isPortalChild = isPortalChild;
68
71
  /**
69
72
  * Groups `children` prop into predefined slots.
70
73
  *
@@ -77,7 +80,7 @@ exports.getComponentSlotName = getComponentSlotName;
77
80
  */
78
81
  var useSlots = function (children, Caller) {
79
82
  var slotsAliasLookup = (0, react_1.useMemo)(function () {
80
- var entries = Object.entries(Caller.slots);
83
+ var entries = Object.entries(Caller.Slots);
81
84
  var aliasLookup = {};
82
85
  entries.forEach(function (_a) {
83
86
  var alias = _a[0], RegisteredSlotComponent = _a[1];
@@ -89,15 +92,17 @@ var useSlots = function (children, Caller) {
89
92
  aliasLookup[slotName] = alias;
90
93
  });
91
94
  return aliasLookup;
92
- }, [Caller.slots]);
95
+ }, [Caller.Slots]);
96
+ // @todo Expose original source order of `children` with respect to slot aliases.
93
97
  var result = (0, react_1.useMemo)(function () {
94
98
  var _a;
95
99
  var slotNodes = {};
96
100
  var unmatchedChildren = [];
97
101
  var invalidChildren = [];
98
102
  var requiredSlotAliases = __spreadArray([], ((_a = Caller.requiredSlotAliases) !== null && _a !== void 0 ? _a : []), true);
99
- react_1.default.Children.forEach(children, function (child) {
103
+ react_1.default.Children.forEach(children, function (_child) {
100
104
  var _a;
105
+ var child = _child;
101
106
  if (!child) {
102
107
  invalidChildren.push(child);
103
108
  return;
@@ -114,15 +119,16 @@ var useSlots = function (children, Caller) {
114
119
  return;
115
120
  }
116
121
  var slotAlias = (function () {
122
+ var _a;
117
123
  var slotName = (0, exports.getComponentSlotName)(child.type, child);
118
- return slotName ? slotsAliasLookup[slotName] : null;
124
+ return slotName ? (_a = slotsAliasLookup[slotName]) !== null && _a !== void 0 ? _a : null : null;
119
125
  })();
120
- if (slotAlias && (typeof Caller.slots[slotAlias] !== 'string')) {
121
- if ((_a = Caller.slots[slotAlias]) === null || _a === void 0 ? void 0 : _a.isRequiredSlot) {
122
- requiredSlotAliases.push(slotAlias);
123
- }
124
- }
125
126
  if (slotAlias) {
127
+ if (typeof Caller.Slots[slotAlias] !== 'string') {
128
+ if ((_a = Caller.Slots[slotAlias]) === null || _a === void 0 ? void 0 : _a.isRequiredSlot) {
129
+ requiredSlotAliases.push(slotAlias);
130
+ }
131
+ }
126
132
  if (slotNodes[slotAlias]) {
127
133
  slotNodes[slotAlias].push(child);
128
134
  }
@@ -1,7 +1,8 @@
1
- import { ComponentMethods } from '../base';
2
- import { ComponentLogic } from '../classy';
3
- import type { ReactElement, ReactNode, JSXElementConstructor } from 'react';
4
- export type TComponent = JSXElementConstructor<any> | ComponentLogic | ComponentMethods;
1
+ import type { ReactElement, ReactNode, ComponentType, ReactPortal, JSX } from 'react';
2
+ /** @todo ComponentType force children to be ReactNode, but custom components can have any children type. */
3
+ type JSXTagLike = string | keyof JSX.IntrinsicElements | ComponentType<any>;
4
+ /** This fixes overly narrow T type used by React's ComponentProps type. */
5
+ export type ComponentProps<T extends JSXTagLike> = (T extends ComponentType<infer P> ? P : T extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[T] : {});
5
6
  export type TSlotName = keyof any;
6
7
  export type TSlotAlias = keyof any;
7
8
  /**
@@ -11,18 +12,24 @@ export type TSlotAlias = keyof any;
11
12
  * each alias in this record to hold any `ReactNode`s rendered for that slot.
12
13
  */
13
14
  export type TSlotsRecord<TKey extends TSlotAlias = TSlotAlias> = {
14
- [Key in TKey]: string | SlotComponent;
15
+ [Key in TKey]: SlotComponent<string | ComponentType<any>>;
15
16
  };
16
- export type DisplayNamedComponent<TComponentArg extends TComponent = TComponent, TDisplayNameArg extends string = string> = TComponentArg & {
17
- displayName: TDisplayNameArg;
18
- };
19
- export type SlotNamedComponent<TComponentArg extends TComponent = TComponent, TSlotNameArg extends TSlotName = TSlotName> = TComponentArg & {
20
- slotName: TSlotNameArg;
17
+ export type DisplayNamedComponent<TComponent extends ComponentType<any> = ComponentType<any>, TName extends string = string> = TComponent & {
18
+ displayName: TName;
21
19
  };
20
+ interface ISlotConfig<TName> {
21
+ slotName: TName;
22
+ /**
23
+ * @deprecated The SlottedComponent should be responsible for indicating which slots it requires.
24
+ * Individual slot components may be reused by multiple slotted components with varying requirements.
25
+ */
26
+ isRequiredSlot?: boolean;
27
+ }
22
28
  /**
23
29
  * A child component used to insert content into a specific slot in the parent component.
24
30
  * This can either be a string, or a React component with a `slotName` property.
25
- * If `slotName` is missing `displayName` will be used as a fallback.
31
+ *
32
+ * > `displayName` is no longer supported as a fallback for `slotName`.
26
33
  *
27
34
  * ### Strings
28
35
  * For strings, they are treated by React a native tags. This lets you use custom strings
@@ -38,7 +45,7 @@ export type SlotNamedComponent<TComponentArg extends TComponent = TComponent, TS
38
45
  * and you can map that to
39
46
  * ```jsx
40
47
  * <button
41
- * {...slotNodes.Option.props}
48
+ * {...slotNodes.Option[0].props}
42
49
  * type="button"
43
50
  * />
44
51
  * ```
@@ -49,9 +56,9 @@ export type SlotNamedComponent<TComponentArg extends TComponent = TComponent, TS
49
56
  *
50
57
  * So consumers may pass the following as `children`.
51
58
  * ```jsx
52
- * <Parent.slots.Content>
59
+ * <Parent.Slots.Content>
53
60
  * Some awesome content.
54
- * </Parent.slots.Content>
61
+ * </Parent.Slots.Content>
55
62
  * ```
56
63
  * and you can map that to
57
64
  * ```jsx
@@ -62,7 +69,7 @@ export type SlotNamedComponent<TComponentArg extends TComponent = TComponent, TS
62
69
  * </>
63
70
  * ```
64
71
  *
65
- * This means `Parent.slots.Content` must be defined as a proper React component
72
+ * This means `Parent.Slots.Content` must be defined as a proper React component
66
73
  * and is solely responsible for doing something with the `"Some awesome content."`
67
74
  * which was passed into it as children.
68
75
  *
@@ -70,9 +77,7 @@ export type SlotNamedComponent<TComponentArg extends TComponent = TComponent, TS
70
77
  * extracts the props passed to the slot and handles
71
78
  * what the slot actually renders.
72
79
  */
73
- export type SlotComponent<TComponentArg extends TComponent = TComponent> = (SlotNamedComponent<TComponentArg> | DisplayNamedComponent<TComponentArg>) & {
74
- isRequiredSlot?: boolean;
75
- };
80
+ export type SlotComponent<TComponent extends JSXTagLike = ComponentType<any>, TName extends TSlotName = TSlotName> = (TComponent extends string ? TComponent : TComponent & ISlotConfig<TName>);
76
81
  /**
77
82
  * A parent component which accepts content that can be grouped into predefined slots.
78
83
  * By convention, it should have a `slots` property which is a {@link TSlotsRecord}.
@@ -81,18 +86,20 @@ export type SlotComponent<TComponentArg extends TComponent = TComponent> = (Slot
81
86
  * directly from the parent component itself,
82
87
  * through an alias that is easy to remember.
83
88
  */
84
- export type Slotted<TComponentArg extends object = TComponent, TSlotAliasArg extends TSlotAlias = TSlotAlias, TSlotsRecordArg extends TSlotsRecord<TSlotAliasArg> = TSlotsRecord<TSlotAliasArg>> = TComponentArg & {
85
- slots: TSlotsRecordArg;
86
- requiredSlotAliases?: TSlotAliasArg[];
89
+ export type SlottedComponent<TOwner extends object = ComponentType<any>, TSlots extends TSlotsRecord = TSlotsRecord> = TOwner & {
90
+ Slots: TSlots;
91
+ requiredSlotAliases?: Array<keyof TSlots>;
87
92
  };
93
+ export type TypedNode<P, T extends JSXTagLike> = (ReactElement<P, T> | (ReactElement<P, T> & ReactPortal));
94
+ export type TSlotNode<TSlotted extends SlottedComponent, Key extends keyof TSlotted['Slots'] = keyof TSlotted['Slots']> = (TypedNode<ComponentProps<TSlotted['Slots'][Key]>, TSlotted['Slots'][Key]>);
88
95
  /**
89
96
  * A record of slot aliases mapped to the corresponding `ReactNode`(s)
90
97
  * to be rendered for that slot.
91
98
  */
92
- export type TSlotNodes<TSlotAliasArg extends TSlotAlias> = {
93
- [Key in TSlotAliasArg]?: Array<ReactElement<any>>;
99
+ export type TSlotNodes<TSlotted extends SlottedComponent> = {
100
+ [Key in keyof TSlotted['Slots']]?: Array<TSlotNode<TSlotted, Key>>;
94
101
  };
95
- export type TUseSlotsResult<TSlotAliasArg extends TSlotAlias = TSlotAlias> = Readonly<[
102
+ export type TUseSlotsResult<TSlotted extends SlottedComponent> = Readonly<[
96
103
  /**
97
104
  * A record of slot aliases to their corresponding React nodes.
98
105
  * Each alias maps to an array of one or more React nodes that were passed
@@ -100,7 +107,7 @@ export type TUseSlotsResult<TSlotAliasArg extends TSlotAlias = TSlotAlias> = Rea
100
107
  *
101
108
  * If a slot was not rendered in `children`, it's alias will be `undefined` in this object.
102
109
  */
103
- slotNodes: TSlotNodes<TSlotAliasArg>,
110
+ slotNodes: TSlotNodes<TSlotted>,
104
111
  /**
105
112
  * Valid React nodes passed as children which did not match any of the
106
113
  * predefined slots.
@@ -112,12 +119,13 @@ export type TUseSlotsResult<TSlotAliasArg extends TSlotAlias = TSlotAlias> = Rea
112
119
  invalidChildren: any[]
113
120
  ]>;
114
121
  export interface IUseSlots {
115
- <TSlotAliasArg extends TSlotAlias = TSlotAlias>(
122
+ <TSlotted extends SlottedComponent>(
116
123
  /**
117
124
  * Your component's `children` prop.
118
125
  * The nodes it contains will be categorized and
119
126
  * grouped according to the predefined {@link slotComponents}.
120
127
  */
121
- children: ReactNode, Caller: Slotted): TUseSlotsResult<TSlotAliasArg>;
128
+ children: ReactNode, Caller: TSlotted): TUseSlotsResult<TSlotted>;
122
129
  }
123
- export type PotentialSlotComponent = string | SlotComponent | TComponent;
130
+ export type PotentialSlotComponent = string | SlotComponent | ComponentType<any>;
131
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleanweb/oore",
3
- "version": "2.0.0-alpha.12",
3
+ "version": "2.0.0-alpha.14",
4
4
  "description": "A library of helpers for writing cleaner React function components with object-oriented patterns.",
5
5
  "engines": {
6
6
  "node": ">=22"