@react-stately/collections 3.11.0 → 3.12.1

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.
@@ -15,19 +15,23 @@ import {PartialNode} from './types';
15
15
  import React, {ReactElement} from 'react';
16
16
 
17
17
  interface CollectionBuilderState {
18
- renderer?: (value: any) => ReactElement
18
+ renderer?: (value: any) => ReactElement | null
19
+ }
20
+
21
+ interface CollectReactElement<T> extends ReactElement {
22
+ getCollectionNode(props: any, context: any): Generator<PartialNode<T>, void, Node<T>[]>
19
23
  }
20
24
 
21
25
  export class CollectionBuilder<T extends object> {
22
26
  private context?: unknown;
23
27
  private cache: WeakMap<T, Node<T>> = new WeakMap();
24
28
 
25
- build(props: CollectionBase<T>, context?: unknown) {
29
+ build(props: Partial<CollectionBase<T>>, context?: unknown) {
26
30
  this.context = context;
27
31
  return iterable(() => this.iterateCollection(props));
28
32
  }
29
33
 
30
- private *iterateCollection(props: CollectionBase<T>): Generator<Node<T>> {
34
+ private *iterateCollection(props: Partial<CollectionBase<T>>): Generator<Node<T>> {
31
35
  let {children, items} = props;
32
36
 
33
37
  if (React.isValidElement<{children: CollectionElement<T>}>(children) && children.type === React.Fragment) {
@@ -40,15 +44,20 @@ export class CollectionBuilder<T extends object> {
40
44
  throw new Error('props.children was a function but props.items is missing');
41
45
  }
42
46
 
43
- for (let item of props.items) {
47
+ let index = 0;
48
+ for (let item of items) {
44
49
  yield* this.getFullNode({
45
- value: item
50
+ value: item,
51
+ index
46
52
  }, {renderer: children});
53
+ index++;
47
54
  }
48
55
  } else {
49
56
  let items: CollectionElement<T>[] = [];
50
57
  React.Children.forEach(children, child => {
51
- items.push(child);
58
+ if (child) {
59
+ items.push(child);
60
+ }
52
61
  });
53
62
 
54
63
  let index = 0;
@@ -66,7 +75,7 @@ export class CollectionBuilder<T extends object> {
66
75
  }
67
76
  }
68
77
 
69
- private getKey(item: CollectionElement<T>, partialNode: PartialNode<T>, state: CollectionBuilderState, parentKey?: Key): Key {
78
+ private getKey(item: NonNullable<CollectionElement<T>>, partialNode: PartialNode<T>, state: CollectionBuilderState, parentKey?: Key | null): Key {
70
79
  if (item.key != null) {
71
80
  return item.key;
72
81
  }
@@ -94,7 +103,7 @@ export class CollectionBuilder<T extends object> {
94
103
  };
95
104
  }
96
105
 
97
- private *getFullNode(partialNode: PartialNode<T>, state: CollectionBuilderState, parentKey?: Key, parentNode?: Node<T>): Generator<Node<T>> {
106
+ private *getFullNode(partialNode: PartialNode<T> & {index: number}, state: CollectionBuilderState, parentKey?: Key | null, parentNode?: Node<T>): Generator<Node<T>> {
98
107
  if (React.isValidElement<{children: CollectionElement<T>}>(partialNode.element) && partialNode.element.type === React.Fragment) {
99
108
  let children: CollectionElement<T>[] = [];
100
109
 
@@ -102,7 +111,7 @@ export class CollectionBuilder<T extends object> {
102
111
  children.push(child);
103
112
  });
104
113
 
105
- let index = partialNode.index;
114
+ let index = partialNode.index ?? 0;
106
115
 
107
116
  for (const child of children) {
108
117
  yield* this.getFullNode({
@@ -132,23 +141,23 @@ export class CollectionBuilder<T extends object> {
132
141
  // If there's an element with a getCollectionNode function on its type, then it's a supported component.
133
142
  // Call this function to get a partial node, and recursively build a full node from there.
134
143
  if (React.isValidElement(element)) {
135
- let type = element.type as any;
144
+ let type = element.type as unknown as CollectReactElement<T>;
136
145
  if (typeof type !== 'function' && typeof type.getCollectionNode !== 'function') {
137
- let name = typeof element.type === 'function' ? element.type.name : element.type;
146
+ let name = element.type;
138
147
  throw new Error(`Unknown element <${name}> in collection.`);
139
148
  }
140
149
 
141
150
  let childNodes = type.getCollectionNode(element.props, this.context) as Generator<PartialNode<T>, void, Node<T>[]>;
142
- let index = partialNode.index;
151
+ let index = partialNode.index ?? 0;
143
152
  let result = childNodes.next();
144
153
  while (!result.done && result.value) {
145
154
  let childNode = result.value;
146
155
 
147
156
  partialNode.index = index;
148
157
 
149
- let nodeKey = childNode.key;
150
- if (!nodeKey) {
151
- nodeKey = childNode.element ? null : this.getKey(element as CollectionElement<T>, partialNode, state, parentKey);
158
+ let nodeKey = childNode.key ?? null;
159
+ if (nodeKey == null) {
160
+ nodeKey = childNode.element ? null : this.getKey(element as NonNullable<CollectionElement<T>>, partialNode, state, parentKey);
152
161
  }
153
162
 
154
163
  let nodes = this.getFullNode({
@@ -161,7 +170,7 @@ export class CollectionBuilder<T extends object> {
161
170
  let children = [...nodes];
162
171
  for (let node of children) {
163
172
  // Cache the node based on its value
164
- node.value = childNode.value || partialNode.value;
173
+ node.value = childNode.value ?? partialNode.value ?? null;
165
174
  if (node.value) {
166
175
  this.cache.set(node.value, node);
167
176
  }
@@ -169,7 +178,7 @@ export class CollectionBuilder<T extends object> {
169
178
  // The partial node may have specified a type for the child in order to specify a constraint.
170
179
  // Verify that the full node that was built recursively matches this type.
171
180
  if (partialNode.type && node.type !== partialNode.type) {
172
- throw new Error(`Unsupported type <${capitalize(node.type)}> in <${capitalize(parentNode.type)}>. Only <${capitalize(partialNode.type)}> is supported.`);
181
+ throw new Error(`Unsupported type <${capitalize(node.type)}> in <${capitalize(parentNode?.type ?? 'unknown parent type')}>. Only <${capitalize(partialNode.type)}> is supported.`);
173
182
  }
174
183
 
175
184
  index++;
@@ -183,7 +192,7 @@ export class CollectionBuilder<T extends object> {
183
192
  }
184
193
 
185
194
  // Ignore invalid elements
186
- if (partialNode.key == null) {
195
+ if (partialNode.key == null || partialNode.type == null) {
187
196
  return;
188
197
  }
189
198
 
@@ -194,17 +203,17 @@ export class CollectionBuilder<T extends object> {
194
203
  props: partialNode.props,
195
204
  key: partialNode.key,
196
205
  parentKey: parentNode ? parentNode.key : null,
197
- value: partialNode.value,
206
+ value: partialNode.value ?? null,
198
207
  level: parentNode ? parentNode.level + 1 : 0,
199
208
  index: partialNode.index,
200
209
  rendered: partialNode.rendered,
201
- textValue: partialNode.textValue,
210
+ textValue: partialNode.textValue ?? '',
202
211
  'aria-label': partialNode['aria-label'],
203
212
  wrapper: partialNode.wrapper,
204
213
  shouldInvalidate: partialNode.shouldInvalidate,
205
- hasChildNodes: partialNode.hasChildNodes,
214
+ hasChildNodes: partialNode.hasChildNodes || false,
206
215
  childNodes: iterable(function *() {
207
- if (!partialNode.hasChildNodes) {
216
+ if (!partialNode.hasChildNodes || !partialNode.childNodes) {
208
217
  return;
209
218
  }
210
219
 
@@ -219,8 +228,7 @@ export class CollectionBuilder<T extends object> {
219
228
  child.key = `${node.key}${child.key}`;
220
229
  }
221
230
 
222
- child.index = index;
223
- let nodes = builder.getFullNode(child, builder.getChildState(state, child), node.key, node);
231
+ let nodes = builder.getFullNode({...child, index}, builder.getChildState(state, child), node.key, node);
224
232
  for (let node of nodes) {
225
233
  index++;
226
234
  yield node;
@@ -235,8 +243,8 @@ export class CollectionBuilder<T extends object> {
235
243
 
236
244
  // Wraps an iterator function as an iterable object, and caches the results.
237
245
  function iterable<T>(iterator: () => IterableIterator<Node<T>>): Iterable<Node<T>> {
238
- let cache = [];
239
- let iterable = null;
246
+ let cache: Array<Node<T>> = [];
247
+ let iterable: null | IterableIterator<Node<T>> = null;
240
248
  return {
241
249
  *[Symbol.iterator]() {
242
250
  for (let item of cache) {
@@ -256,7 +264,7 @@ function iterable<T>(iterator: () => IterableIterator<Node<T>>): Iterable<Node<T
256
264
  }
257
265
 
258
266
  type Wrapper = (element: ReactElement) => ReactElement;
259
- function compose(outer: Wrapper | void, inner: Wrapper | void): Wrapper {
267
+ function compose(outer: Wrapper | void, inner: Wrapper | void): Wrapper | undefined {
260
268
  if (outer && inner) {
261
269
  return (element) => outer(inner(element));
262
270
  }
package/src/Item.ts CHANGED
@@ -14,7 +14,7 @@ import {ItemElement, ItemProps} from '@react-types/shared';
14
14
  import {PartialNode} from './types';
15
15
  import React, {JSX, ReactElement} from 'react';
16
16
 
17
- function Item<T>(props: ItemProps<T>): ReactElement { // eslint-disable-line @typescript-eslint/no-unused-vars
17
+ function Item<T>(props: ItemProps<T>): ReactElement | null { // eslint-disable-line @typescript-eslint/no-unused-vars
18
18
  return null;
19
19
  }
20
20
 
package/src/Section.ts CHANGED
@@ -14,7 +14,7 @@ import {PartialNode} from './types';
14
14
  import React, {JSX, ReactElement} from 'react';
15
15
  import {SectionProps} from '@react-types/shared';
16
16
 
17
- function Section<T>(props: SectionProps<T>): ReactElement { // eslint-disable-line @typescript-eslint/no-unused-vars
17
+ function Section<T>(props: SectionProps<T>): ReactElement | null { // eslint-disable-line @typescript-eslint/no-unused-vars
18
18
  return null;
19
19
  }
20
20
 
@@ -42,7 +42,7 @@ export function getNthItem<T>(iterable: Iterable<T>, index: number): T | undefin
42
42
  }
43
43
 
44
44
  export function getLastItem<T>(iterable: Iterable<T>): T | undefined {
45
- let lastItem = undefined;
45
+ let lastItem: T | undefined = undefined;
46
46
  for (let value of iterable) {
47
47
  lastItem = value;
48
48
  }
@@ -81,11 +81,14 @@ export function compareNodeOrder<T>(collection: Collection<Node<T>>, a: Node<T>,
81
81
  }
82
82
 
83
83
  function getAncestors<T>(collection: Collection<Node<T>>, node: Node<T>): Node<T>[] {
84
- let parents = [];
84
+ let parents: Node<T>[] = [];
85
85
 
86
- while (node?.parentKey != null) {
87
- node = collection.getItem(node.parentKey);
88
- parents.unshift(node);
86
+ let currNode: Node<T> | null = node;
87
+ while (currNode?.parentKey != null) {
88
+ currNode = collection.getItem(currNode.parentKey);
89
+ if (currNode) {
90
+ parents.unshift(currNode);
91
+ }
89
92
  }
90
93
 
91
94
  return parents;
@@ -21,18 +21,19 @@ export function getItemCount<T>(collection: Collection<Node<T>>): number {
21
21
  return count;
22
22
  }
23
23
 
24
- count = 0;
24
+ // TS isn't smart enough to know we've ensured count is a number, so use a new variable
25
+ let counter = 0;
25
26
  let countItems = (items: Iterable<Node<T>>) => {
26
27
  for (let item of items) {
27
28
  if (item.type === 'section') {
28
29
  countItems(getChildNodes(item, collection));
29
30
  } else {
30
- count++;
31
+ counter++;
31
32
  }
32
33
  }
33
34
  };
34
35
 
35
36
  countItems(collection);
36
- cache.set(collection, count);
37
- return count;
37
+ cache.set(collection, counter);
38
+ return counter;
38
39
  }
package/src/types.ts CHANGED
@@ -15,17 +15,17 @@ import {ReactElement, ReactNode} from 'react';
15
15
 
16
16
  export interface PartialNode<T> {
17
17
  type?: string,
18
- key?: Key,
18
+ key?: Key | null,
19
19
  value?: T,
20
- element?: ReactElement,
20
+ element?: ReactElement | null,
21
21
  wrapper?: (element: ReactElement) => ReactElement,
22
22
  rendered?: ReactNode,
23
23
  textValue?: string,
24
24
  'aria-label'?: string,
25
25
  index?: number,
26
- renderer?: (item: T) => ReactElement,
26
+ renderer?: (item: T) => ReactElement | null,
27
27
  hasChildNodes?: boolean,
28
28
  childNodes?: () => IterableIterator<PartialNode<T>>,
29
29
  props?: any,
30
- shouldInvalidate?: (context: unknown) => boolean
30
+ shouldInvalidate?: (context: any) => boolean
31
31
  }
@@ -15,7 +15,7 @@ import {CollectionBuilder} from './CollectionBuilder';
15
15
  import {ReactElement, useMemo} from 'react';
16
16
 
17
17
  interface CollectionOptions<T, C extends Collection<Node<T>>> extends Omit<CollectionStateBase<T, C>, 'children'> {
18
- children?: ReactElement<any> | ReactElement<any>[] | ((item: T) => ReactElement<any>)
18
+ children?: ReactElement<any> | null | (ReactElement<any> | null)[] | ((item: T) => ReactElement<any> | null)
19
19
  }
20
20
 
21
21
  type CollectionFactory<T, C extends Collection<Node<T>>> = (node: Iterable<Node<T>>) => C;