@cloudscape-design/board-components 3.0.0 → 3.0.2

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,4 +1,4 @@
1
1
  export var PACKAGE_SOURCE = "board-components";
2
- export var PACKAGE_VERSION = "3.0.0 (f7bedc98)";
2
+ export var PACKAGE_VERSION = "3.0.0 (2398ed65)";
3
3
  export var THEME = "default";
4
4
  export var ALWAYS_VISUAL_REFRESH = false;
@@ -1,5 +1,11 @@
1
1
  import { ItemId } from "../interfaces";
2
- import { LayoutEngineGrid } from "./grid";
2
+ import { LayoutEngineGrid, ReadonlyLayoutEngineGrid } from "./grid";
3
3
  import { CommittedMove } from "./interfaces";
4
- export declare function resolveOverlaps(userMove: CommittedMove, grid: LayoutEngineGrid, moves: CommittedMove[], conflicts: Set<ItemId>): void;
5
- export declare function refloatGrid(grid: LayoutEngineGrid, moves: CommittedMove[], conflicts: Set<ItemId>): void;
4
+ export declare class LayoutEngineStepState {
5
+ grid: ReadonlyLayoutEngineGrid;
6
+ moves: readonly CommittedMove[];
7
+ conflicts: ReadonlySet<ItemId>;
8
+ constructor(grid: LayoutEngineGrid, moves?: CommittedMove[], conflicts?: Set<string>);
9
+ }
10
+ export declare function resolveOverlaps(userMove: CommittedMove, state: LayoutEngineStepState): LayoutEngineStepState;
11
+ export declare function refloatGrid(state: LayoutEngineStepState): LayoutEngineStepState;
@@ -1,19 +1,30 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { StackSet } from "../utils/stack-set";
4
- export function resolveOverlaps(userMove, grid, moves, conflicts) {
5
- new LayoutEngineStep(grid, moves, conflicts).resolveOverlaps(userMove);
4
+ import { LayoutEngineGrid } from "./grid";
5
+ export class LayoutEngineStepState {
6
+ constructor(grid, moves = new Array(), conflicts = new Set()) {
7
+ this.grid = grid;
8
+ this.moves = moves;
9
+ this.conflicts = conflicts;
10
+ }
6
11
  }
7
- export function refloatGrid(grid, moves, conflicts) {
8
- new LayoutEngineStep(grid, moves, conflicts).refloatGrid();
12
+ export function resolveOverlaps(userMove, state) {
13
+ return new LayoutEngineStep(state).resolveOverlaps(userMove).getState();
14
+ }
15
+ export function refloatGrid(state) {
16
+ return new LayoutEngineStep(state).refloatGrid().getState();
9
17
  }
10
18
  class LayoutEngineStep {
11
- constructor(grid, moves, conflicts) {
19
+ constructor(state) {
12
20
  this.moves = [];
13
21
  this.conflicts = new Set();
14
- this.grid = grid;
15
- this.moves = moves;
16
- this.conflicts = conflicts;
22
+ this.grid = LayoutEngineGrid.clone(state.grid);
23
+ this.moves = [...state.moves];
24
+ this.conflicts = new Set([...state.conflicts]);
25
+ }
26
+ getState() {
27
+ return { grid: this.grid, moves: this.moves, conflicts: this.conflicts };
17
28
  }
18
29
  // Issue moves on overlapping items trying to resolve all of them.
19
30
  resolveOverlaps(userMove) {
@@ -27,6 +38,7 @@ class LayoutEngineStep {
27
38
  overlaps.push(itemId);
28
39
  }
29
40
  };
41
+ this.conflicts = this.findConflicts(userMove);
30
42
  this.makeMove(userMove, addOverlap, priorities);
31
43
  const tryVacantMoves = () => {
32
44
  // Try vacant moves on all overlaps.
@@ -56,11 +68,12 @@ class LayoutEngineStep {
56
68
  };
57
69
  tryVacantMoves();
58
70
  this.refloatGrid(activeId);
71
+ return this;
59
72
  }
60
73
  // Find items that can "float" to the top and apply the necessary moves.
61
74
  refloatGrid(activeId) {
62
75
  if (this.conflicts.size > 0) {
63
- return;
76
+ return this;
64
77
  }
65
78
  let needAnotherRefloat = false;
66
79
  for (const item of this.grid.items) {
@@ -90,6 +103,7 @@ class LayoutEngineStep {
90
103
  if (needAnotherRefloat) {
91
104
  this.refloatGrid(activeId);
92
105
  }
106
+ return this;
93
107
  }
94
108
  makeMove(move, addOverlap, priorities) {
95
109
  switch (move.type) {
@@ -220,7 +234,8 @@ class LayoutEngineStep {
220
234
  height: overlapItem.height,
221
235
  type: "ESCAPE",
222
236
  };
223
- for (move.y; move.y < 100; move.y++) {
237
+ // If can't find the escape move after 999 steps down it is likely a bug in the validation.
238
+ for (move.y; move.y < 999; move.y++) {
224
239
  if (this.validatePriorityMove(move, priorities, activeId, false)) {
225
240
  return move;
226
241
  }
@@ -304,4 +319,58 @@ class LayoutEngineStep {
304
319
  }
305
320
  }
306
321
  }
322
+ // Find items that the active item cannot be moved over with the current move.
323
+ findConflicts(move) {
324
+ if (move.type !== "MOVE") {
325
+ return new Set();
326
+ }
327
+ const conflicts = new Set();
328
+ const moveTarget = this.grid.getItem(move.itemId);
329
+ const direction = `${move.x - moveTarget.x}:${move.y - moveTarget.y}`;
330
+ switch (direction) {
331
+ case "-1:0": {
332
+ const left = Math.max(0, moveTarget.left - 1);
333
+ for (let y = moveTarget.y; y < moveTarget.y + moveTarget.height; y++) {
334
+ const block = this.grid.getCellOverlap(left, y, moveTarget.id);
335
+ if (block && block.x < left) {
336
+ conflicts.add(block.id);
337
+ }
338
+ }
339
+ break;
340
+ }
341
+ case "1:0": {
342
+ const right = Math.min(this.grid.width - 1, moveTarget.right + 1);
343
+ for (let y = moveTarget.y; y < moveTarget.y + moveTarget.height; y++) {
344
+ const block = this.grid.getCellOverlap(right, y, moveTarget.id);
345
+ if (block && block.x + block.width - 1 > right) {
346
+ conflicts.add(block.id);
347
+ }
348
+ }
349
+ break;
350
+ }
351
+ case "0:-1": {
352
+ const top = Math.max(0, moveTarget.top - 1);
353
+ for (let x = moveTarget.x; x < moveTarget.x + moveTarget.width; x++) {
354
+ const block = this.grid.getCellOverlap(x, top, moveTarget.id);
355
+ if (block && block.y < top) {
356
+ conflicts.add(block.id);
357
+ }
358
+ }
359
+ break;
360
+ }
361
+ case "0:1": {
362
+ const bottom = moveTarget.bottom + 1;
363
+ for (let x = moveTarget.x; x < moveTarget.x + moveTarget.width; x++) {
364
+ const block = this.grid.getCellOverlap(x, bottom, moveTarget.id);
365
+ if (block && block.y + block.height - 1 > bottom) {
366
+ conflicts.add(block.id);
367
+ }
368
+ }
369
+ break;
370
+ }
371
+ default:
372
+ throw new Error(`Invariant violation: unexpected direction ${direction}.`);
373
+ }
374
+ return conflicts;
375
+ }
307
376
  }
@@ -2,9 +2,7 @@ import { GridLayout, ItemId } from "../interfaces";
2
2
  import { InsertCommand, LayoutShift, MoveCommand, ResizeCommand } from "./interfaces";
3
3
  export declare class LayoutEngine {
4
4
  private current;
5
- private grid;
6
- private moves;
7
- private conflicts;
5
+ private step;
8
6
  private chained;
9
7
  constructor(args: GridLayout | LayoutEngine);
10
8
  move(moveCommand: MoveCommand): LayoutEngine;
@@ -1,25 +1,20 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { Position } from "../utils/position";
4
- import { findConflicts } from "./engine-conflicts";
5
- import { refloatGrid, resolveOverlaps } from "./engine-step";
4
+ import { LayoutEngineStepState, refloatGrid, resolveOverlaps } from "./engine-step";
6
5
  import { LayoutEngineGrid } from "./grid";
7
6
  import { normalizeMovePath, normalizeResizePath, sortGridItems } from "./utils";
8
7
  export class LayoutEngine {
9
8
  constructor(args) {
10
- this.moves = [];
11
- this.conflicts = new Set();
12
9
  this.chained = false;
13
10
  if (args instanceof LayoutEngine) {
14
11
  this.current = args.current;
15
- this.grid = args.grid;
16
- this.moves = args.moves;
17
- this.conflicts = args.conflicts;
12
+ this.step = args.step;
18
13
  this.chained = true;
19
14
  }
20
15
  else {
21
16
  this.current = args;
22
- this.grid = new LayoutEngineGrid(args.items, args.columns);
17
+ this.step = new LayoutEngineStepState(new LayoutEngineGrid(args.items, args.columns));
23
18
  }
24
19
  }
25
20
  move(moveCommand) {
@@ -27,76 +22,73 @@ export class LayoutEngine {
27
22
  const { itemId, path } = this.validateMoveCommand(moveCommand);
28
23
  for (let stepIndex = 0; stepIndex < path.length; stepIndex++) {
29
24
  const step = path[stepIndex];
30
- const { width, height } = this.grid.getItem(itemId);
25
+ const { width, height } = this.step.grid.getItem(itemId);
31
26
  const move = { itemId, x: step.x, y: step.y, width, height, type: "MOVE" };
32
- this.conflicts = findConflicts(move, this.grid);
33
- resolveOverlaps(move, this.grid, this.moves, this.conflicts);
27
+ this.step = resolveOverlaps(move, this.step);
34
28
  }
35
29
  return new LayoutEngine(this);
36
30
  }
37
31
  resize(resize) {
38
32
  this.cleanup();
39
33
  const { itemId, path } = this.validateResizeCommand(resize);
40
- const resizeTarget = this.grid.getItem(itemId);
34
+ const resizeTarget = this.step.grid.getItem(itemId);
41
35
  for (let stepIndex = 0; stepIndex < path.length; stepIndex++) {
42
36
  const width = path[stepIndex].x - resizeTarget.x;
43
37
  const height = path[stepIndex].y - resizeTarget.y;
44
- resolveOverlaps({ itemId, x: resizeTarget.x, y: resizeTarget.y, width, height, type: "RESIZE" }, this.grid, this.moves, this.conflicts);
38
+ this.step = resolveOverlaps({ itemId, x: resizeTarget.x, y: resizeTarget.y, width, height, type: "RESIZE" }, this.step);
45
39
  }
46
40
  return new LayoutEngine(this);
47
41
  }
48
42
  insert({ itemId, width, height, path: [position, ...path] }) {
49
43
  this.cleanup();
50
- resolveOverlaps({ itemId, ...position, width, height, type: "INSERT" }, this.grid, this.moves, this.conflicts);
44
+ this.step = resolveOverlaps({ itemId, ...position, width, height, type: "INSERT" }, this.step);
51
45
  return new LayoutEngine(this).move({ itemId, path });
52
46
  }
53
47
  remove(itemId) {
54
48
  this.cleanup();
55
- const { x, y, width, height } = this.grid.getItem(itemId);
56
- resolveOverlaps({ itemId, x, y, width, height, type: "REMOVE" }, this.grid, this.moves, this.conflicts);
49
+ const { x, y, width, height } = this.step.grid.getItem(itemId);
50
+ this.step = resolveOverlaps({ itemId, x, y, width, height, type: "REMOVE" }, this.step);
57
51
  return new LayoutEngine(this);
58
52
  }
59
53
  refloat() {
60
- refloatGrid(this.grid, this.moves, this.conflicts);
54
+ this.step = refloatGrid(this.step);
61
55
  return new LayoutEngine(this);
62
56
  }
63
57
  getLayoutShift() {
64
58
  return {
65
59
  current: this.current,
66
60
  next: {
67
- items: sortGridItems(this.grid.items.map((item) => ({ ...item }))),
68
- columns: this.grid.width,
69
- rows: this.grid.height,
61
+ items: sortGridItems(this.step.grid.items.map((item) => ({ ...item }))),
62
+ columns: this.step.grid.width,
63
+ rows: this.step.grid.height,
70
64
  },
71
- moves: [...this.moves],
72
- conflicts: [...this.conflicts],
65
+ moves: [...this.step.moves],
66
+ conflicts: [...this.step.conflicts],
73
67
  };
74
68
  }
75
69
  cleanup() {
76
70
  if (!this.chained) {
77
- this.grid = new LayoutEngineGrid(this.current.items, this.current.columns);
78
- this.moves = [];
79
- this.conflicts = new Set();
71
+ this.step = new LayoutEngineStepState(new LayoutEngineGrid(this.current.items, this.current.columns));
80
72
  }
81
73
  }
82
74
  validateMoveCommand({ itemId, path }) {
83
- const moveTarget = this.grid.getItem(itemId);
75
+ const moveTarget = this.step.grid.getItem(itemId);
84
76
  for (const step of path) {
85
- if (step.x < 0 || step.y < 0 || step.x + moveTarget.width > this.grid.width) {
77
+ if (step.x < 0 || step.y < 0 || step.x + moveTarget.width > this.step.grid.width) {
86
78
  throw new Error("Invalid move: outside grid.");
87
79
  }
88
80
  }
89
81
  return { itemId, path: normalizeMovePath(new Position({ x: moveTarget.x, y: moveTarget.y }), path) };
90
82
  }
91
83
  validateResizeCommand({ itemId, path }) {
92
- const resizeTarget = this.grid.getItem(itemId);
84
+ const resizeTarget = this.step.grid.getItem(itemId);
93
85
  const x = resizeTarget.x + resizeTarget.width;
94
86
  const y = resizeTarget.y + resizeTarget.height;
95
87
  for (const step of path) {
96
88
  if (step.x < 1 || step.y < 1) {
97
89
  throw new Error("Invalid resize: can't resize to 0.");
98
90
  }
99
- if (step.x > this.grid.width) {
91
+ if (step.x > this.step.grid.width) {
100
92
  throw new Error("Invalid resize: outside grid.");
101
93
  }
102
94
  }
@@ -6,11 +6,12 @@ export interface LayoutEngineItem extends GridLayoutItem, Rect {
6
6
  originalWidth: number;
7
7
  originalHeight: number;
8
8
  }
9
- export declare class LayoutEngineGrid {
10
- private _width;
11
- private _height;
12
- private _items;
13
- private layout;
9
+ export declare class ReadonlyLayoutEngineGrid {
10
+ protected _width: number;
11
+ protected _height: number;
12
+ protected _items: Map<string, LayoutEngineItem>;
13
+ protected _layout: Set<ItemId>[][];
14
+ static clone(grid: ReadonlyLayoutEngineGrid): LayoutEngineGrid;
14
15
  constructor(items: readonly GridLayoutItem[], columns: number);
15
16
  get width(): number;
16
17
  get height(): number;
@@ -18,11 +19,13 @@ export declare class LayoutEngineGrid {
18
19
  getItem(itemId: ItemId): LayoutEngineItem;
19
20
  getCell(x: number, y: number): LayoutEngineItem[];
20
21
  getCellOverlap(x: number, y: number, itemId: ItemId): null | LayoutEngineItem;
22
+ protected makeNewRow(): void;
23
+ }
24
+ export declare class LayoutEngineGrid extends ReadonlyLayoutEngineGrid {
21
25
  move(itemId: ItemId, x: number, y: number, onOverlap?: (overlapId: ItemId) => void): void;
22
26
  resize(itemId: ItemId, width: number, height: number, onOverlap?: (overlapId: ItemId) => void): void;
23
27
  insert(item: GridLayoutItem, onOverlap: (overlapId: ItemId) => void): void;
24
28
  remove(itemId: ItemId): void;
25
29
  private removeLayoutItem;
26
30
  private insertLayoutItem;
27
- private makeNewRow;
28
31
  }
@@ -1,10 +1,18 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { getItemRect } from "./utils";
4
- export class LayoutEngineGrid {
4
+ export class ReadonlyLayoutEngineGrid {
5
+ static clone(grid) {
6
+ const clone = new LayoutEngineGrid([], 0);
7
+ clone._width = grid._width;
8
+ clone._height = grid._height;
9
+ clone._items = new Map([...grid._items].map(([k, v]) => [k, { ...v }]));
10
+ clone._layout = grid._layout.map((row) => row.map((set) => new Set([...set])));
11
+ return clone;
12
+ }
5
13
  constructor(items, columns) {
6
14
  this._items = new Map();
7
- this.layout = [];
15
+ this._layout = [];
8
16
  this._width = columns;
9
17
  this._height = 0;
10
18
  for (const item of items) {
@@ -23,12 +31,12 @@ export class LayoutEngineGrid {
23
31
  throw new Error("Invalid grid: items of invalid size.");
24
32
  }
25
33
  for (let y = item.y; y < item.y + item.height; y++) {
26
- while (this.layout.length <= y) {
34
+ while (this._layout.length <= y) {
27
35
  this.makeNewRow();
28
36
  }
29
37
  for (let x = item.x; x < item.x + item.width; x++) {
30
- this.layout[y][x].add(item.id);
31
- if (this.layout[y][x].size > 1) {
38
+ this._layout[y][x].add(item.id);
39
+ if (this._layout[y][x].size > 1) {
32
40
  throw new Error("Invalid grid: items overlap.");
33
41
  }
34
42
  }
@@ -52,10 +60,10 @@ export class LayoutEngineGrid {
52
60
  return item;
53
61
  }
54
62
  getCell(x, y) {
55
- if (!this.layout[y] || !this.layout[y][x]) {
63
+ if (!this._layout[y] || !this._layout[y][x]) {
56
64
  return [];
57
65
  }
58
- return [...this.layout[y][x]].map((itemId) => this.getItem(itemId));
66
+ return [...this._layout[y][x]].map((itemId) => this.getItem(itemId));
59
67
  }
60
68
  getCellOverlap(x, y, itemId) {
61
69
  for (const item of this.getCell(x, y)) {
@@ -65,6 +73,12 @@ export class LayoutEngineGrid {
65
73
  }
66
74
  return null;
67
75
  }
76
+ makeNewRow() {
77
+ this._layout.push([...Array(this._width)].map(() => new Set()));
78
+ this._height = this._layout.length;
79
+ }
80
+ }
81
+ export class LayoutEngineGrid extends ReadonlyLayoutEngineGrid {
68
82
  move(itemId, x, y, onOverlap) {
69
83
  const moveTarget = this.getItem(itemId);
70
84
  this.removeLayoutItem(moveTarget);
@@ -97,14 +111,14 @@ export class LayoutEngineGrid {
97
111
  throw new Error("Inserting item has invalid size.");
98
112
  }
99
113
  for (let y = item.y; y < item.y + item.height; y++) {
100
- while (this.layout.length <= y) {
114
+ while (this._layout.length <= y) {
101
115
  this.makeNewRow();
102
116
  }
103
117
  for (let x = item.x; x < item.x + item.width; x++) {
104
- for (const overlapId of this.layout[y][x]) {
118
+ for (const overlapId of this._layout[y][x]) {
105
119
  onOverlap(overlapId);
106
120
  }
107
- this.layout[y][x].add(item.id);
121
+ this._layout[y][x].add(item.id);
108
122
  }
109
123
  }
110
124
  }
@@ -116,25 +130,21 @@ export class LayoutEngineGrid {
116
130
  removeLayoutItem(item) {
117
131
  for (let y = item.y; y < item.y + item.height; y++) {
118
132
  for (let x = item.x; x < item.x + item.width; x++) {
119
- this.layout[y][x].delete(item.id);
133
+ this._layout[y][x].delete(item.id);
120
134
  }
121
135
  }
122
136
  }
123
137
  insertLayoutItem(item, onOverlap) {
124
138
  for (let y = item.y; y < item.y + item.height; y++) {
125
139
  for (let x = item.x; x < item.x + item.width; x++) {
126
- while (!this.layout[y]) {
140
+ while (!this._layout[y]) {
127
141
  this.makeNewRow();
128
142
  }
129
- for (const overlapId of this.layout[y][x]) {
143
+ for (const overlapId of this._layout[y][x]) {
130
144
  onOverlap === null || onOverlap === void 0 ? void 0 : onOverlap(overlapId);
131
145
  }
132
- this.layout[y][x].add(item.id);
146
+ this._layout[y][x].add(item.id);
133
147
  }
134
148
  }
135
149
  }
136
- makeNewRow() {
137
- this.layout.push([...Array(this._width)].map(() => new Set()));
138
- this._height = this.layout.length;
139
- }
140
150
  }
@@ -1,3 +1,3 @@
1
1
  {
2
- "commit": "f7bedc987918e00ec55f49dfcf8109c1f03b2163"
2
+ "commit": "2398ed656ef60e2aa7e40000ab70f5dd6fb1783f"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudscape-design/board-components",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/cloudscape-design/board-components.git"
@@ -40,4 +40,4 @@
40
40
  ]
41
41
  },
42
42
  "license": "Apache-2.0"
43
- }
43
+ }
@@ -1,4 +0,0 @@
1
- import { ItemId } from "../interfaces";
2
- import { LayoutEngineGrid } from "./grid";
3
- import { CommittedMove } from "./interfaces";
4
- export declare function findConflicts(move: CommittedMove, grid: LayoutEngineGrid): Set<ItemId>;
@@ -1,53 +0,0 @@
1
- // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
- // SPDX-License-Identifier: Apache-2.0
3
- // Find items that the active item cannot be moved over with the current move.
4
- export function findConflicts(move, grid) {
5
- const conflicts = new Set();
6
- const moveTarget = grid.getItem(move.itemId);
7
- const direction = `${move.x - moveTarget.x}:${move.y - moveTarget.y}`;
8
- switch (direction) {
9
- case "-1:0": {
10
- const left = Math.max(0, moveTarget.left - 1);
11
- for (let y = moveTarget.y; y < moveTarget.y + moveTarget.height; y++) {
12
- const block = grid.getCellOverlap(left, y, moveTarget.id);
13
- if (block && block.x < left) {
14
- conflicts.add(block.id);
15
- }
16
- }
17
- break;
18
- }
19
- case "1:0": {
20
- const right = Math.min(grid.width - 1, moveTarget.right + 1);
21
- for (let y = moveTarget.y; y < moveTarget.y + moveTarget.height; y++) {
22
- const block = grid.getCellOverlap(right, y, moveTarget.id);
23
- if (block && block.x + block.width - 1 > right) {
24
- conflicts.add(block.id);
25
- }
26
- }
27
- break;
28
- }
29
- case "0:-1": {
30
- const top = Math.max(0, moveTarget.top - 1);
31
- for (let x = moveTarget.x; x < moveTarget.x + moveTarget.width; x++) {
32
- const block = grid.getCellOverlap(x, top, moveTarget.id);
33
- if (block && block.y < top) {
34
- conflicts.add(block.id);
35
- }
36
- }
37
- break;
38
- }
39
- case "0:1": {
40
- const bottom = moveTarget.bottom + 1;
41
- for (let x = moveTarget.x; x < moveTarget.x + moveTarget.width; x++) {
42
- const block = grid.getCellOverlap(x, bottom, moveTarget.id);
43
- if (block && block.y + block.height - 1 > bottom) {
44
- conflicts.add(block.id);
45
- }
46
- }
47
- break;
48
- }
49
- default:
50
- throw new Error(`Invariant violation: unexpected direction ${direction}.`);
51
- }
52
- return conflicts;
53
- }