@react-spectrum/card 3.0.0-alpha.0 → 3.0.0-alpha.11

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/src/CardView.tsx CHANGED
@@ -12,17 +12,18 @@
12
12
 
13
13
  import {CardBase} from './CardBase';
14
14
  import {CardViewContext, useCardViewContext} from './CardViewContext';
15
- import cardViewStyles from './cardview.css';
16
15
  import {classNames, useDOMRef, useStyleProps, useUnwrapDOMRef} from '@react-spectrum/utils';
17
16
  import {DOMRef, DOMRefValue, Node} from '@react-types/shared';
18
17
  import {GridCollection, useGridState} from '@react-stately/grid';
19
18
  // @ts-ignore
20
19
  import intlMessages from '../intl/*.json';
20
+ import {mergeProps} from '@react-aria/utils';
21
21
  import {ProgressCircle} from '@react-spectrum/progress';
22
- import React, {ReactElement, useMemo, useRef} from 'react';
22
+ import React, {ReactElement, useCallback, useMemo, useRef} from 'react';
23
23
  import {ReusableView} from '@react-stately/virtualizer';
24
24
  import {SpectrumCardViewProps} from '@react-types/card';
25
- import {useCollator, useLocale, useMessageFormatter} from '@react-aria/i18n';
25
+ import styles from '@adobe/spectrum-css-temp/components/card/vars.css';
26
+ import {useCollator, useLocale, useLocalizedStringFormatter} from '@react-aria/i18n';
26
27
  import {useGrid, useGridCell, useGridRow} from '@react-aria/grid';
27
28
  import {useListState} from '@react-stately/list';
28
29
  import {useProvider} from '@react-spectrum/provider';
@@ -37,22 +38,16 @@ function CardView<T extends object>(props: SpectrumCardViewProps<T>, ref: DOMRef
37
38
  renderEmptyState,
38
39
  layout,
39
40
  loadingState,
40
- onLoadMore
41
+ onLoadMore,
42
+ cardOrientation = 'vertical'
41
43
  } = props;
44
+
42
45
  let collator = useCollator({usage: 'search', sensitivity: 'base'});
43
46
  let isLoading = loadingState === 'loading' || loadingState === 'loadingMore';
44
- let cardViewLayout = useMemo(() => typeof layout === 'function' ? new layout({collator}) : layout, [layout, collator]);
47
+ let cardViewLayout = useMemo(() => typeof layout === 'function' ? new layout({collator, cardOrientation, scale}) : layout, [layout, collator, cardOrientation, scale]);
45
48
  let layoutType = cardViewLayout.layoutType;
46
49
 
47
- if (typeof layout === 'function') {
48
- if (layoutType === 'grid') {
49
- cardViewLayout.itemPadding = scale === 'large' ? 116 : 95;
50
- } else if (layoutType === 'gallery') {
51
- cardViewLayout.itemPadding = scale === 'large' ? 143 : 114;
52
- }
53
- }
54
-
55
- let formatMessage = useMessageFormatter(intlMessages);
50
+ let stringFormatter = useLocalizedStringFormatter(intlMessages);
56
51
  let {direction} = useLocale();
57
52
  let {collection} = useListState(props);
58
53
 
@@ -77,7 +72,9 @@ function CardView<T extends object>(props: SpectrumCardViewProps<T>, ref: DOMRef
77
72
 
78
73
  let state = useGridState({
79
74
  ...props,
80
- collection: gridCollection
75
+ selectionMode: cardOrientation === 'horizontal' && layoutType === 'grid' ? 'none' : props.selectionMode,
76
+ collection: gridCollection,
77
+ focusMode: 'cell'
81
78
  });
82
79
 
83
80
  cardViewLayout.collection = gridCollection;
@@ -94,7 +91,6 @@ function CardView<T extends object>(props: SpectrumCardViewProps<T>, ref: DOMRef
94
91
  type View = ReusableView<Node<T>, unknown>;
95
92
  let renderWrapper = (parent: View, reusableView: View) => (
96
93
  <VirtualizerItem
97
- className={classNames(cardViewStyles, 'react-spectrum-CardView-CardWrapper')}
98
94
  key={reusableView.key}
99
95
  reusableView={reusableView}
100
96
  parent={parent} />
@@ -106,13 +102,22 @@ function CardView<T extends object>(props: SpectrumCardViewProps<T>, ref: DOMRef
106
102
  focusedKey = focusedItem.parentKey;
107
103
  }
108
104
 
105
+ let margin = cardViewLayout.margin || 0;
106
+ let virtualizer = cardViewLayout.virtualizer;
107
+ let scrollToItem = useCallback((key) => {
108
+ virtualizer && virtualizer.scrollToItem(key, {
109
+ duration: 0,
110
+ offsetY: margin
111
+ });
112
+ }, [margin, virtualizer]);
113
+
109
114
  // TODO: does aria-row count and aria-col count need to be modified? Perhaps aria-col count needs to be omitted
110
115
  return (
111
- <CardViewContext.Provider value={{state, isQuiet, layout: cardViewLayout}}>
116
+ <CardViewContext.Provider value={{state, isQuiet, layout: cardViewLayout, cardOrientation}}>
112
117
  <Virtualizer
113
118
  {...gridProps}
114
119
  {...styleProps}
115
- className={classNames(cardViewStyles, 'react-spectrum-CardView')}
120
+ className={classNames(styles, 'spectrum-CardView')}
116
121
  ref={domRef}
117
122
  focusedKey={focusedKey}
118
123
  scrollDirection="vertical"
@@ -120,7 +125,9 @@ function CardView<T extends object>(props: SpectrumCardViewProps<T>, ref: DOMRef
120
125
  collection={gridCollection}
121
126
  isLoading={isLoading}
122
127
  onLoadMore={onLoadMore}
123
- renderWrapper={renderWrapper}>
128
+ renderWrapper={renderWrapper}
129
+ transitionDuration={isLoading ? 160 : 220}
130
+ scrollToItem={scrollToItem}>
124
131
  {(type, item) => {
125
132
  if (type === 'item') {
126
133
  return (
@@ -131,7 +138,7 @@ function CardView<T extends object>(props: SpectrumCardViewProps<T>, ref: DOMRef
131
138
  <CenteredWrapper>
132
139
  <ProgressCircle
133
140
  isIndeterminate
134
- aria-label={state.collection.size > 0 ? formatMessage('loadingMore') : formatMessage('loading')} />
141
+ aria-label={state.collection.size > 0 ? stringFormatter.format('loadingMore') : stringFormatter.format('loading')} />
135
142
  </CenteredWrapper>
136
143
  );
137
144
  } else if (type === 'placeholder') {
@@ -159,7 +166,7 @@ function CenteredWrapper({children}) {
159
166
  <div
160
167
  role="row"
161
168
  aria-rowindex={state.collection.size + 1}
162
- className={classNames(cardViewStyles, 'react-spectrum-CardView-centeredWrapper')}>
169
+ className={classNames(styles, 'spectrum-CardView-centeredWrapper')}>
163
170
  <div role="gridcell">
164
171
  {children}
165
172
  </div>
@@ -179,7 +186,7 @@ function InternalCard(props) {
179
186
  let cellRef = useRef<DOMRefValue<HTMLDivElement>>();
180
187
  let unwrappedRef = useUnwrapDOMRef(cellRef);
181
188
 
182
- let {rowProps} = useGridRow({
189
+ let {rowProps: gridRowProps} = useGridRow({
183
190
  node: item,
184
191
  isVirtualized: true
185
192
  }, state, rowRef);
@@ -189,13 +196,35 @@ function InternalCard(props) {
189
196
  focusMode: 'cell'
190
197
  }, state, unwrappedRef);
191
198
 
199
+ // Prevent space key from scrolling the CardView if triggered on a disabled item or on a Card in a selectionMode="none" CardView.
200
+ let allowsInteraction = state.selectionManager.selectionMode !== 'none';
201
+ let isDisabled = !allowsInteraction || state.disabledKeys.has(item.key);
202
+
203
+ let onKeyDown = (e) => {
204
+ if (e.key === ' ' && isDisabled) {
205
+ e.preventDefault();
206
+ }
207
+ };
208
+
209
+ let rowProps = mergeProps(
210
+ gridRowProps,
211
+ {onKeyDown}
212
+ );
192
213
 
193
214
  if (layoutType === 'grid' || layoutType === 'gallery') {
194
215
  isQuiet = true;
195
216
  }
196
217
 
218
+ if (layoutType !== 'grid') {
219
+ cardOrientation = 'vertical';
220
+ }
221
+
222
+ // We don't want to focus the checkbox (or any other focusable elements) within the Card
223
+ // when pressing the arrow keys so we delete the key down handler here. Arrow key navigation between
224
+ // the cards in the CardView is handled by useGrid => useSelectableCollection instead.
225
+ delete gridCellProps.onKeyDownCapture;
197
226
  return (
198
- <div {...rowProps} ref={rowRef} style={{height: '100%'}}>
227
+ <div {...rowProps} ref={rowRef} className={classNames(styles, 'spectrum-CardView-row')}>
199
228
  <CardBase
200
229
  ref={cellRef}
201
230
  articleProps={gridCellProps}
@@ -30,7 +30,7 @@ export interface GalleryLayoutOptions extends BaseLayoutOptions {
30
30
  itemSpacing?: Size,
31
31
  /**
32
32
  * The vertical padding for an item.
33
- * @default 114
33
+ * @default 78
34
34
  */
35
35
  itemPadding?: number,
36
36
  /**
@@ -43,22 +43,14 @@ export interface GalleryLayoutOptions extends BaseLayoutOptions {
43
43
  * will be targeted.
44
44
  * @type {number}
45
45
  */
46
- threshold?: number,
47
- /**
48
- * The margin around the grid view between the edges and the items.
49
- * @default 24
50
- */
51
- margin?: number // TODO: Perhaps should accept Responsive<DimensionValue>
46
+ threshold?: number
52
47
  }
53
48
 
54
- // TODO: copied from V2, update this with the proper spectrum values
55
- // Should these be affected by Scale as well?
56
49
  const DEFAULT_OPTIONS = {
57
50
  S: {
58
51
  idealRowHeight: 112,
59
52
  minItemSize: new Size(96, 96),
60
53
  itemSpacing: new Size(8, 16),
61
- // TODO: will need to update as well
62
54
  itemPadding: 24,
63
55
  dropSpacing: 50,
64
56
  margin: 8
@@ -67,8 +59,10 @@ const DEFAULT_OPTIONS = {
67
59
  idealRowHeight: 208,
68
60
  minItemSize: new Size(136, 136),
69
61
  itemSpacing: new Size(18, 18),
70
- // TODO: updated to work with new v3 cards (there is additional space required for the descriptions if there is a description)
71
- itemPadding: 114,
62
+ itemPadding: {
63
+ 'medium': 78,
64
+ 'large': 99
65
+ },
72
66
  dropSpacing: 100,
73
67
  margin: 24
74
68
  }
@@ -76,7 +70,6 @@ const DEFAULT_OPTIONS = {
76
70
 
77
71
  export class GalleryLayout<T> extends BaseLayout<T> {
78
72
  protected idealRowHeight: number;
79
- protected margin: number;
80
73
  protected itemSpacing: Size;
81
74
  itemPadding: number;
82
75
  protected minItemSize: Size;
@@ -84,11 +77,10 @@ export class GalleryLayout<T> extends BaseLayout<T> {
84
77
 
85
78
  constructor(options: GalleryLayoutOptions = {}) {
86
79
  super(options);
87
- // TODO: restore cardSize option when we support different size cards
88
80
  let cardSize = 'L';
89
81
  this.idealRowHeight = options.idealRowHeight || DEFAULT_OPTIONS[cardSize].idealRowHeight;
90
82
  this.itemSpacing = options.itemSpacing || DEFAULT_OPTIONS[cardSize].itemSpacing;
91
- this.itemPadding = options.itemPadding != null ? options.itemPadding : DEFAULT_OPTIONS[cardSize].itemPadding;
83
+ this.itemPadding = options.itemPadding != null ? options.itemPadding : DEFAULT_OPTIONS[cardSize].itemPadding[this.scale];
92
84
  this.minItemSize = options.minItemSize || DEFAULT_OPTIONS[cardSize].minItemSize;
93
85
  this.threshold = options.threshold || 1;
94
86
  this.margin = options.margin != null ? options.margin : DEFAULT_OPTIONS[cardSize].margin;
@@ -146,104 +138,107 @@ export class GalleryLayout<T> extends BaseLayout<T> {
146
138
  let y = this.margin;
147
139
  let availableWidth = visibleWidth - this.margin * 2;
148
140
 
149
- // Compute aspect ratios for all of the items, and the total width if all items were on in a single row.
150
- let ratios = [];
151
- let totalWidth = 0;
152
- let minRatio = this.minItemSize.width / this.minItemSize.height;
153
- let maxRatio = availableWidth / this.minItemSize.height;
154
-
155
- for (let node of this.collection) {
156
- let ratio = node.props.width / node.props.height;
157
- if (ratio < minRatio) {
158
- ratio = minRatio;
159
- } else if (ratio > maxRatio && ratio !== minRatio) {
160
- ratio = maxRatio;
141
+ // If avaliable width is not greater than 0, skip node layout calculations
142
+ if (availableWidth > 0) {
143
+ // Compute aspect ratios for all of the items, and the total width if all items were on in a single row.
144
+ let ratios = [];
145
+ let totalWidth = 0;
146
+ let minRatio = this.minItemSize.width / this.minItemSize.height;
147
+ let maxRatio = availableWidth / this.minItemSize.height;
148
+
149
+ for (let node of this.collection) {
150
+ let ratio = node.props.width / node.props.height;
151
+ if (ratio < minRatio) {
152
+ ratio = minRatio;
153
+ } else if (ratio > maxRatio && ratio !== minRatio) {
154
+ ratio = maxRatio;
155
+ }
156
+
157
+ let itemWidth = ratio * this.minItemSize.height;
158
+ ratios.push(ratio);
159
+ totalWidth += itemWidth;
161
160
  }
162
161
 
163
- let itemWidth = ratio * this.minItemSize.height;
164
- ratios.push(ratio);
165
- totalWidth += itemWidth;
166
- }
162
+ totalWidth += this.itemSpacing.width * (this.collection.size - 1);
167
163
 
168
- totalWidth += this.itemSpacing.width * (this.collection.size - 1);
164
+ // Determine how many rows we'll need, and partition the items into rows
165
+ // using the aspect ratios as weights.
166
+ let rows = Math.max(1, Math.ceil(totalWidth / availableWidth));
167
+ // if the available width can't hold two items, then every item will get its own row
168
+ // this leads to a faster run through linear partition and more dependable output for small row widths
169
+ if (availableWidth <= (this.minItemSize.width * 2) + (this.itemPadding * 2)) {
170
+ rows = this.collection.size;
171
+ }
169
172
 
170
- // Determine how many rows we'll need, and partition the items into rows
171
- // using the aspect ratios as weights.
172
- let rows = Math.max(1, Math.ceil(totalWidth / availableWidth));
173
- // if the available width can't hold two items, then every item will get its own row
174
- // this leads to a faster run through linear partition and more dependable output for small row widths
175
- if (availableWidth <= (this.minItemSize.width * 2) + (this.itemPadding * 2)) {
176
- rows = this.collection.size;
177
- }
173
+ let weightedRatios = ratios.map(ratio => ratio < this.threshold ? ratio + (0.5 * (1 / ratio)) : ratio);
174
+ let partition = linearPartition(weightedRatios, rows);
178
175
 
179
- let weightedRatios = ratios.map(ratio => ratio < this.threshold ? ratio + (0.5 * (1 / ratio)) : ratio);
180
- let partition = linearPartition(weightedRatios, rows);
176
+ let index = 0;
177
+ for (let row of partition) {
178
+ // Compute the total weight for this row
179
+ let totalWeight = 0;
180
+ for (let j = index; j < index + row.length; j++) {
181
+ totalWeight += ratios[j];
182
+ }
181
183
 
182
- let index = 0;
183
- for (let row of partition) {
184
- // Compute the total weight for this row
185
- let totalWeight = 0;
186
- for (let j = index; j < index + row.length; j++) {
187
- totalWeight += ratios[j];
188
- }
184
+ // Determine the row height based on the total available width and weight of this row.
185
+ let bestRowHeight = (availableWidth - (row.length - 1) * this.itemSpacing.width) / totalWeight;
189
186
 
190
- // Determine the row height based on the total available width and weight of this row.
191
- let bestRowHeight = (availableWidth - (row.length - 1) * this.itemSpacing.width) / totalWeight;
187
+ // if this is the last row and the row height is >2x the ideal row height, then cap to the ideal height
188
+ // probably doing this because if the last row has one extremely tall image, then the row becomes huge
189
+ // though that can happen anywhere if a row has lots of tall images... so i'm not sure why this one matters
190
+ if (row === partition[partition.length - 1] && bestRowHeight > this.idealRowHeight * 2) {
191
+ bestRowHeight = this.idealRowHeight;
192
+ }
193
+ let itemHeight = Math.round(bestRowHeight) + this.itemPadding;
194
+ let x = this.margin;
195
+
196
+ // if any items are going to end up too small, add a bit of width to them and subtract it from wider objects
197
+ let widths = [];
198
+ for (let j = index; j < index + row.length; j++) {
199
+ let width = Math.round(bestRowHeight * ratios[j]);
200
+ widths.push([j - index, width]);
201
+ }
202
+ this._distributeWidths(widths);
203
+
204
+ // Create items for this row.
205
+ for (let j = index; j < index + row.length; j++) {
206
+ let node = this.collection.rows[j];
207
+ let itemWidth = Math.max(widths[j - index][1], this.minItemSize.width);
208
+ let rect = new Rect(x, y, itemWidth, itemHeight);
209
+ let layoutInfo = new LayoutInfo(node.type, node.key, rect);
210
+ layoutInfo.allowOverflow = true;
211
+ this.layoutInfos.set(node.key, layoutInfo);
212
+ x += itemWidth + this.itemSpacing.width;
213
+ }
192
214
 
193
- // if this is the last row and the row height is >2x the ideal row height, then cap to the ideal height
194
- // probably doing this because if the last row has one extremely tall image, then the row becomes huge
195
- // though that can happen anywhere if a row has lots of tall images... so i'm not sure why this one matters
196
- if (row === partition[partition.length - 1] && bestRowHeight > this.idealRowHeight * 2) {
197
- bestRowHeight = this.idealRowHeight;
198
- }
199
- let itemHeight = Math.round(bestRowHeight) + this.itemPadding;
200
- let x = this.margin;
201
-
202
- // if any items are going to end up too small, add a bit of width to them and subtract it from wider objects
203
- let widths = [];
204
- for (let j = index; j < index + row.length; j++) {
205
- let width = Math.round(bestRowHeight * ratios[j]);
206
- widths.push([j - index, width]);
207
- }
208
- this._distributeWidths(widths);
209
-
210
- // Create items for this row.
211
- for (let j = index; j < index + row.length; j++) {
212
- let node = this.collection.rows[j];
213
- let itemWidth = Math.max(widths[j - index][1], this.minItemSize.width);
214
- let rect = new Rect(x, y, itemWidth, itemHeight);
215
- let layoutInfo = new LayoutInfo(node.type, node.key, rect);
216
- this.layoutInfos.set(node.key, layoutInfo);
217
- x += itemWidth + this.itemSpacing.width;
215
+ y += itemHeight + this.itemSpacing.height;
216
+ index += row.length;
218
217
  }
219
218
 
220
- y += itemHeight + this.itemSpacing.height;
221
- index += row.length;
222
- }
219
+ if (this.isLoading) {
220
+ let loaderY = y;
221
+ let loaderHeight = 60;
222
+ // If there aren't any items, make loader take all avaliable room and remove margin from y calculation
223
+ // so it doesn't scroll
224
+ if (this.collection.size === 0) {
225
+ loaderY = 0;
226
+ loaderHeight = visibleHeight || 60;
227
+ }
223
228
 
224
- if (this.isLoading) {
225
- let loaderY = y;
226
- let loaderHeight = 60;
227
- // If there aren't any items, make loader take all avaliable room and remove margin from y calculation
228
- // so it doesn't scroll
229
- if (this.collection.size === 0) {
230
- loaderY = 0;
231
- loaderHeight = visibleHeight || 60;
229
+ let rect = new Rect(0, loaderY, visibleWidth, loaderHeight);
230
+ let loader = new LayoutInfo('loader', 'loader', rect);
231
+ this.layoutInfos.set('loader', loader);
232
+ y = loader.rect.maxY;
232
233
  }
233
234
 
234
- let rect = new Rect(0, loaderY, visibleWidth, loaderHeight);
235
- let loader = new LayoutInfo('loader', 'loader', rect);
236
- this.layoutInfos.set('loader', loader);
237
- y = loader.rect.maxY;
238
- }
239
-
240
- if (this.collection.size === 0 && !this.isLoading) {
241
- let rect = new Rect(0, 0, visibleWidth, visibleHeight);
242
- let placeholder = new LayoutInfo('placeholder', 'placeholder', rect);
243
- this.layoutInfos.set('placeholder', placeholder);
244
- y = placeholder.rect.maxY;
235
+ if (this.collection.size === 0 && !this.isLoading) {
236
+ let rect = new Rect(0, 0, visibleWidth, visibleHeight);
237
+ let placeholder = new LayoutInfo('placeholder', 'placeholder', rect);
238
+ this.layoutInfos.set('placeholder', placeholder);
239
+ y = placeholder.rect.maxY;
240
+ }
245
241
  }
246
-
247
242
  this.contentSize = new Size(visibleWidth, y);
248
243
  }
249
244
  }
@@ -13,7 +13,7 @@
13
13
  import {BaseLayout, BaseLayoutOptions} from './BaseLayout';
14
14
  import {Key} from 'react';
15
15
  import {LayoutInfo, Rect, Size} from '@react-stately/virtualizer';
16
- import {Node} from '@react-types/shared';
16
+ import {Node, Orientation} from '@react-types/shared';
17
17
 
18
18
  export interface GridLayoutOptions extends BaseLayoutOptions {
19
19
  // /**
@@ -22,7 +22,7 @@ export interface GridLayoutOptions extends BaseLayoutOptions {
22
22
  // cardSize?: 'S' | 'M' | 'L',
23
23
  /**
24
24
  * The minimum item size.
25
- * @default 208 x 208
25
+ * @default 208 x 208 for horizontal card orientation. 102 x 102 for vertical card orientation.
26
26
  */
27
27
  minItemSize?: Size,
28
28
  /**
@@ -30,11 +30,6 @@ export interface GridLayoutOptions extends BaseLayoutOptions {
30
30
  * @default Infinity
31
31
  */
32
32
  maxItemSize?: Size,
33
- /**
34
- * The margin around the grid view between the edges and the items.
35
- * @default 24
36
- */
37
- margin?: number, // TODO: Perhaps should accept Responsive<DimensionValue>
38
33
  /**
39
34
  * The minimum space required between items.
40
35
  * @default 18 x 18
@@ -46,18 +41,23 @@ export interface GridLayoutOptions extends BaseLayoutOptions {
46
41
  */
47
42
  maxColumns?: number,
48
43
  /**
49
- * The vertical padding for an item.
44
+ * The additional padding along the card's main axis. Affects the sizing of the content area following the card image.
50
45
  * @default 95
51
46
  */
52
- itemPadding?: number
47
+ itemPadding?: number,
48
+ /**
49
+ * The orientation of the cards withn the grid.
50
+ * @default vertical
51
+ */
52
+ cardOrientation?: Orientation
53
53
  }
54
54
 
55
- // TODO: copied from V2, update this with the proper spectrum values
56
- // Should these be affected by Scale as well?
57
55
  const DEFAULT_OPTIONS = {
58
56
  S: {
59
57
  itemPadding: 20,
60
- minItemSize: new Size(96, 96),
58
+ minItemSize: {
59
+ 'vertical': new Size(96, 96)
60
+ },
61
61
  maxItemSize: new Size(Infinity, Infinity),
62
62
  margin: 8,
63
63
  minSpace: new Size(6, 6),
@@ -65,10 +65,20 @@ const DEFAULT_OPTIONS = {
65
65
  dropSpacing: 50
66
66
  },
67
67
  L: {
68
- // TODO: for now bumping this higher since the new cards have more stuff in the content area.
69
- // Will need to ask Spectrum what these values should be. Used to be 52. Do the same for S above
70
- itemPadding: 95,
71
- minItemSize: new Size(208, 208),
68
+ itemPadding: {
69
+ 'vertical': {
70
+ 'medium': 78,
71
+ 'large': 98
72
+ },
73
+ 'horizontal': {
74
+ 'medium': 150,
75
+ 'large': 170
76
+ }
77
+ },
78
+ minItemSize: {
79
+ 'vertical': new Size(208, 208),
80
+ 'horizontal': new Size(102, 102)
81
+ },
72
82
  maxItemSize: new Size(Infinity, Infinity),
73
83
  margin: 24,
74
84
  minSpace: new Size(18, 18),
@@ -80,10 +90,10 @@ const DEFAULT_OPTIONS = {
80
90
  export class GridLayout<T> extends BaseLayout<T> {
81
91
  protected minItemSize: Size;
82
92
  protected maxItemSize: Size;
83
- protected margin: number;
84
93
  protected minSpace: Size;
85
94
  protected maxColumns: number;
86
95
  itemPadding: number;
96
+ cardOrientation: Orientation;
87
97
  protected itemSize: Size;
88
98
  protected numColumns: number;
89
99
  protected numRows: number;
@@ -91,14 +101,14 @@ export class GridLayout<T> extends BaseLayout<T> {
91
101
 
92
102
  constructor(options: GridLayoutOptions = {}) {
93
103
  super(options);
94
- // TODO: restore cardSize option when we support different size cards
95
104
  let cardSize = 'L';
96
- this.minItemSize = options.minItemSize || DEFAULT_OPTIONS[cardSize].minItemSize;
105
+ this.cardOrientation = options.cardOrientation || 'vertical';
106
+ this.minItemSize = options.minItemSize || DEFAULT_OPTIONS[cardSize].minItemSize[this.cardOrientation];
97
107
  this.maxItemSize = options.maxItemSize || DEFAULT_OPTIONS[cardSize].maxItemSize;
98
108
  this.margin = options.margin != null ? options.margin : DEFAULT_OPTIONS[cardSize].margin;
99
109
  this.minSpace = options.minSpace || DEFAULT_OPTIONS[cardSize].minSpace;
100
110
  this.maxColumns = options.maxColumns || DEFAULT_OPTIONS[cardSize].maxColumns;
101
- this.itemPadding = options.itemPadding != null ? options.itemPadding : DEFAULT_OPTIONS[cardSize].itemPadding;
111
+ this.itemPadding = options.itemPadding != null ? options.itemPadding : DEFAULT_OPTIONS[cardSize].itemPadding[this.cardOrientation][this.scale];
102
112
  this.itemSize = null;
103
113
  this.numColumns = 0;
104
114
  this.numRows = 0;
@@ -109,7 +119,6 @@ export class GridLayout<T> extends BaseLayout<T> {
109
119
  return 'grid';
110
120
  }
111
121
 
112
- // TODO: Below functions From V2 Maybe don't need this? Might be a short cut for getting all visible rects since otherwise we'd have to iterate across all nodes
113
122
  getIndexAtPoint(x, y, allowInsertingAtEnd = false) {
114
123
  let itemHeight = this.itemSize.height + this.minSpace.height;
115
124
  let itemWidth = this.itemSize.width + this.horizontalSpacing;
@@ -141,7 +150,7 @@ export class GridLayout<T> extends BaseLayout<T> {
141
150
  for (let index = firstVisibleItem; index <= lastVisibleItem; index++) {
142
151
  let keyFromIndex = this.collection.rows[index].key;
143
152
  let layoutInfo = this.layoutInfos.get(keyFromIndex);
144
- if (this.isVisible(layoutInfo, rect)) {
153
+ if (layoutInfo && this.isVisible(layoutInfo, rect)) {
145
154
  res.push(layoutInfo);
146
155
  }
147
156
  }
@@ -159,10 +168,13 @@ export class GridLayout<T> extends BaseLayout<T> {
159
168
  buildCollection() {
160
169
  let visibleWidth = this.virtualizer.visibleRect.width;
161
170
  let visibleHeight = this.virtualizer.visibleRect.height;
171
+ let horizontalItemPadding = this.cardOrientation === 'horizontal' ? this.itemPadding : 0;
172
+ let verticalItemPadding = this.cardOrientation === 'vertical' ? this.itemPadding : 0;
173
+ let minCardWidth = this.minItemSize.width + horizontalItemPadding;
162
174
 
163
175
  // Compute the number of rows and columns needed to display the content
164
176
  let availableWidth = visibleWidth - this.margin * 2;
165
- let columns = Math.floor((availableWidth + this.minSpace.width) / (this.minItemSize.width + this.minSpace.width));
177
+ let columns = Math.floor((availableWidth + this.minSpace.width) / (minCardWidth + this.minSpace.width));
166
178
  this.numColumns = Math.max(1, Math.min(this.maxColumns, columns));
167
179
  this.numRows = Math.ceil(this.collection.size / this.numColumns);
168
180
 
@@ -171,13 +183,11 @@ export class GridLayout<T> extends BaseLayout<T> {
171
183
 
172
184
  // Compute the item width based on the space available
173
185
  let itemWidth = Math.floor(width / this.numColumns);
174
- itemWidth = Math.max(this.minItemSize.width, Math.min(this.maxItemSize.width, itemWidth));
175
-
186
+ itemWidth = Math.max(minCardWidth, Math.min(this.maxItemSize.width, itemWidth));
176
187
  // Compute the item height, which is proportional to the item width
177
- let t = ((itemWidth - this.minItemSize.width) / this.minItemSize.width);
178
- let itemHeight = this.minItemSize.height + this.minItemSize.height * t;
179
- itemHeight = Math.max(this.minItemSize.height, Math.min(this.maxItemSize.height, itemHeight)) + this.itemPadding;
180
-
188
+ let t = ((itemWidth - minCardWidth) / minCardWidth);
189
+ let itemHeight = Math.floor(this.minItemSize.height + this.minItemSize.height * t);
190
+ itemHeight = Math.max(this.minItemSize.height, Math.min(this.maxItemSize.height, itemHeight)) + verticalItemPadding;
181
191
  this.itemSize = new Size(itemWidth, itemHeight);
182
192
 
183
193
  // Compute the horizontal spacing and content height
@@ -226,6 +236,7 @@ export class GridLayout<T> extends BaseLayout<T> {
226
236
  let rect = new Rect(x, y, this.itemSize.width, this.itemSize.height);
227
237
  // TODO: Perhaps have it so that the child key for each row is stored with the layoutInfo?
228
238
  let layoutInfo = new LayoutInfo(node.type, node.key, rect);
239
+ layoutInfo.allowOverflow = true;
229
240
  this.layoutInfos.set(node.key, layoutInfo);
230
241
  return layoutInfo;
231
242
  }
@@ -26,11 +26,6 @@ export interface WaterfallLayoutOptions extends BaseLayoutOptions {
26
26
  * @default Infinity
27
27
  */
28
28
  maxItemSize?: Size,
29
- /**
30
- * The margin around the grid view between the edges and the items.
31
- * @default 24
32
- */
33
- margin?: number, // TODO: Perhaps should accept Responsive<DimensionValue>
34
29
  /**
35
30
  * The minimum space required between items.
36
31
  * @default 18 x 18
@@ -40,22 +35,15 @@ export interface WaterfallLayoutOptions extends BaseLayoutOptions {
40
35
  * The maximum number of columns.
41
36
  * @default Infinity
42
37
  */
43
- maxColumns?: number,
44
- /**
45
- * The vertical padding for an item.
46
- * @default 56
47
- */
48
- itemPadding?: number
38
+ maxColumns?: number
49
39
  }
50
40
 
51
41
  // TODO: this didn't have any options that varied with card size, should it have?
52
42
  export class WaterfallLayout<T> extends BaseLayout<T> implements KeyboardDelegate {
53
43
  protected minItemSize: Size;
54
44
  protected maxItemSize: Size;
55
- protected margin: number;
56
45
  protected minSpace: Size;
57
46
  protected maxColumns: number;
58
- itemPadding: number;
59
47
  protected numColumns: number;
60
48
  protected itemWidth: number;
61
49
  protected horizontalSpacing: number;
@@ -69,8 +57,6 @@ export class WaterfallLayout<T> extends BaseLayout<T> implements KeyboardDelegat
69
57
  this.margin = options.margin != null ? options.margin : 24;
70
58
  this.minSpace = options.minSpace || new Size(18, 18);
71
59
  this.maxColumns = options.maxColumns || Infinity;
72
- // TODO: not entirely sure what this is for since the layout will automatically shift itself to the correct vertical space for the card
73
- this.itemPadding = options.itemPadding != null ? options.itemPadding : 56;
74
60
 
75
61
  this.itemWidth = 0;
76
62
  this.numColumns = 0;
@@ -119,8 +105,8 @@ export class WaterfallLayout<T> extends BaseLayout<T> implements KeyboardDelegat
119
105
  } else if (node.props.width && node.props.height) {
120
106
  let nodeWidth = node.props.width;
121
107
  let nodeHeight = node.props.height;
122
- let scaledHeight = Math.round(nodeWidth * ((itemWidth) / nodeHeight));
123
- height = Math.max(this.minItemSize.height, Math.min(this.maxItemSize.height, scaledHeight)) + this.itemPadding;
108
+ let scaledHeight = Math.round(nodeHeight * ((itemWidth) / nodeWidth));
109
+ height = Math.max(this.minItemSize.height, Math.min(this.maxItemSize.height, scaledHeight));
124
110
  } else {
125
111
  height = itemWidth;
126
112
  }
@@ -133,6 +119,7 @@ export class WaterfallLayout<T> extends BaseLayout<T> implements KeyboardDelegat
133
119
  let rect = new Rect(x, y, itemWidth, height);
134
120
  let layoutInfo = new LayoutInfo(node.type, key, rect);
135
121
  layoutInfo.estimatedSize = estimatedSize;
122
+ layoutInfo.allowOverflow = true;
136
123
  this.layoutInfos.set(key, layoutInfo);
137
124
 
138
125
  // TODO: From v2 figure out this bit, when does this get called and what to replace this.collectionView._transaction with?
@@ -185,10 +172,9 @@ export class WaterfallLayout<T> extends BaseLayout<T> implements KeyboardDelegat
185
172
  // TODO: also not sure about copying layout info vs mutating it. Listlayout does the below
186
173
  // but I feel that is because it actually maintained a layoutNode map cache which this doesn't have
187
174
  let newLayoutInfo = layoutInfo.copy();
188
- newLayoutInfo.rect.height = size.height;
175
+ newLayoutInfo.rect.height = size.height < 600 ? size.height : 600;
176
+ newLayoutInfo.estimatedSize = false;
189
177
  this.layoutInfos.set(key, newLayoutInfo);
190
- // TODO: v2 had layoutInfo.estimatedSize = view.estimatedSize || false; but we can't do the same here?
191
- layoutInfo.estimatedSize = false;
192
178
  return true;
193
179
  }
194
180