Skip to content

Commit c7a691e

Browse files
committed
refactor: modify position assignment to keep originally assigned positions
1 parent 08c84ec commit c7a691e

File tree

3 files changed

+147
-41
lines changed

3 files changed

+147
-41
lines changed

src/components/tile-manager/tile-manager.ts

Lines changed: 121 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
4242
}
4343

4444
private draggedItem: IgcTileComponent | null = null;
45+
private finalOrder: IgcTileComponent[] = [];
46+
private positionedTiles: IgcTileComponent[] = [];
4547

4648
// @query('[part="base"]', true)
4749
// private _baseWrapper!: HTMLDivElement;
@@ -58,20 +60,11 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
5860
}
5961

6062
private _observerCallback({
61-
changes: { added },
63+
changes: { added, removed },
6264
}: MutationControllerParams<IgcTileComponent>) {
63-
// FIX: currently the newly added tile has the specified position+1 in the layout
64-
added.forEach((newTile) => {
65-
const specifiedPosition = newTile.position;
66-
67-
if (specifiedPosition !== undefined) {
68-
this.tiles.splice(specifiedPosition - 1, 0, newTile);
69-
} else {
70-
this.tiles.push(newTile);
71-
}
72-
});
65+
removed.forEach(this.handleTileRemoval.bind(this));
66+
added.forEach(this.handleTileAddition.bind(this));
7367

74-
this.assignPositions();
7568
this.updateSlotAssignment();
7669
}
7770

@@ -90,9 +83,7 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
9083
* @attr
9184
*/
9285
public get tiles() {
93-
return Array.from(this._tiles).sort(
94-
(a, b) => (a.position ?? 0) - (b.position ?? 0)
95-
);
86+
return Array.from(this._tiles).sort((a, b) => a.position - b.position);
9687
}
9788

9889
@watch('columnCount', { waitUntilFirstUpdate: true })
@@ -126,13 +117,113 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
126117
}
127118

128119
private assignPositions() {
129-
this.tiles.forEach((tile, index) => {
130-
tile.position = index;
120+
const finalOrder: IgcTileComponent[] = [];
121+
const unpositionedTiles = this._tiles.filter(
122+
(tile) => tile.position === -1
123+
);
124+
125+
this.positionedTiles = this._tiles.filter((tile) => tile.position !== -1);
126+
this.positionedTiles.sort((a, b) => a.position - b.position);
127+
128+
let nextFreePosition = 0;
129+
130+
this.positionedTiles.forEach((tile) => {
131+
// Fill any unassigned slots before the next assigned tile's position
132+
while (nextFreePosition < tile.position && unpositionedTiles.length > 0) {
133+
const unpositionedTile = unpositionedTiles.shift()!;
134+
unpositionedTile.position = nextFreePosition++;
135+
finalOrder.push(unpositionedTile);
136+
}
137+
tile.position = finalOrder.length;
138+
finalOrder.push(tile);
139+
nextFreePosition = tile.position + 1;
140+
});
141+
142+
unpositionedTiles.forEach((tile) => {
143+
tile.position = nextFreePosition++;
144+
finalOrder.push(tile);
131145
});
146+
147+
this.finalOrder = finalOrder;
148+
}
149+
150+
private handleTileRemoval(removedTile: IgcTileComponent) {
151+
const removedPosition = removedTile.position;
152+
153+
this.finalOrder = this.finalOrder.filter((tile) => tile !== removedTile);
154+
this.positionedTiles = this.positionedTiles.filter(
155+
(tile) => tile !== removedTile
156+
);
157+
158+
// Shift only non-positioned tiles
159+
this.finalOrder.forEach((tile) => {
160+
if (
161+
tile.position > removedPosition &&
162+
!this.positionedTiles.includes(tile)
163+
) {
164+
tile.position -= 1;
165+
}
166+
});
167+
168+
this.reassignUnpositionedTiles();
169+
}
170+
171+
private handleTileAddition(newTile: IgcTileComponent) {
172+
let specifiedPosition = newTile.position;
173+
174+
if (specifiedPosition !== -1) {
175+
// Place at specified position, shift non-positioned tiles only
176+
this.finalOrder.forEach((tile) => {
177+
if (
178+
tile.position >= specifiedPosition &&
179+
!this.positionedTiles.includes(tile)
180+
) {
181+
tile.position += 1;
182+
}
183+
});
184+
185+
this.finalOrder.splice(specifiedPosition, 0, newTile);
186+
this.positionedTiles.push(newTile);
187+
} else {
188+
// Find next available position in DOM for unpositioned tile
189+
specifiedPosition = this.getAvailablePosition(newTile);
190+
newTile.position = specifiedPosition;
191+
this.finalOrder.push(newTile);
192+
}
193+
194+
this.reassignUnpositionedTiles();
195+
}
196+
197+
private reassignUnpositionedTiles() {
198+
let nextPosition = 0;
199+
200+
this.finalOrder.sort((a, b) => a.position - b.position);
201+
this.finalOrder.forEach((tile) => {
202+
if (!this.positionedTiles.includes(tile)) {
203+
while (
204+
this.finalOrder.some(
205+
(posTile) =>
206+
posTile.position === nextPosition &&
207+
this.positionedTiles.includes(posTile)
208+
)
209+
) {
210+
nextPosition += 1;
211+
}
212+
tile.position = nextPosition++;
213+
}
214+
});
215+
}
216+
217+
private getAvailablePosition(newTile: IgcTileComponent): number {
218+
let domIndex = Array.from(this.children).indexOf(newTile);
219+
while (this.positionedTiles.some((tile) => tile.position === domIndex)) {
220+
domIndex += 1;
221+
}
222+
return domIndex;
132223
}
133224

134225
private updateSlotAssignment() {
135-
this.slotElement.assign(...this.tiles);
226+
this.slotElement.assign(...this._tiles);
136227
}
137228

138229
private handleTileDragStart(e: CustomEvent) {
@@ -169,20 +260,18 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
169260
const targetPos = target.position ?? 0;
170261

171262
this.tiles.forEach((tile) => {
172-
if (tile.position) {
173-
if (
174-
draggedPos < targetPos &&
175-
tile.position > draggedPos &&
176-
tile.position <= targetPos
177-
) {
178-
tile.position -= 1;
179-
} else if (
180-
draggedPos > targetPos &&
181-
tile.position >= targetPos &&
182-
tile.position < draggedPos
183-
) {
184-
tile.position += 1;
185-
}
263+
if (
264+
draggedPos < targetPos &&
265+
tile.position > draggedPos &&
266+
tile.position <= targetPos
267+
) {
268+
tile.position -= 1;
269+
} else if (
270+
draggedPos > targetPos &&
271+
tile.position >= targetPos &&
272+
tile.position < draggedPos
273+
) {
274+
tile.position += 1;
186275
}
187276
});
188277

src/components/tile-manager/tile.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export default class IgcTileComponent extends EventEmitterMixin<
5959
private static readonly increment = createCounter();
6060
private ghostElement!: HTMLElement | null;
6161
private _dragController?: TileDragAndDropController;
62+
private _position = -1;
6263
private _resizeController?: TileResizeController;
6364
private _resizeHandleRef: Ref<HTMLDivElement> = createRef();
6465

@@ -113,16 +114,18 @@ export default class IgcTileComponent extends EventEmitterMixin<
113114
public disableResize = false;
114115

115116
/**
116-
* Corresponds to the CSS order property
117-
* and indicates the visual position the tile has in the layout.
117+
* Gets/sets the tile's visual position in the layout.
118+
* Corresponds to the CSS order property.
118119
* @attr
119120
*/
120-
@property({ attribute: 'position', type: Number, reflect: true })
121-
public position: number | undefined = undefined;
121+
@property({ type: Number })
122+
public set position(value: number) {
123+
this._position = Number(value);
124+
this.style.order = `${this._position}`;
125+
}
122126

123-
@watch('position')
124-
protected updateOrder() {
125-
this.style.order = `${this.position}`;
127+
public get position() {
128+
return this._position;
126129
}
127130

128131
@watch('maximized')

stories/tile-manager.stories.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,14 @@ export const Maximized: Story = {
167167
function addTile() {
168168
const tileManager =
169169
document.querySelector<IgcTileManagerComponent>('#tile-manager1')!;
170+
const tiles = tileManager.querySelectorAll('igc-tile');
170171
const newTile = document.createElement('igc-tile');
171172
const content = document.createElement('h2');
172173
content.textContent = `Tile ${tileManager.tiles.length + 1}`;
173-
newTile.position = 1;
174+
newTile.position = 0;
174175
newTile.append(content);
175-
tileManager.appendChild(newTile);
176+
// tileManager.appendChild(newTile);
177+
tileManager.insertBefore(newTile, tiles[3]);
176178
}
177179

178180
function removeTile() {
@@ -196,6 +198,18 @@ export const DynamicTiles: Story = {
196198
<igc-tile id="tile2">
197199
<h2>Tile2</h2>
198200
</igc-tile>
201+
<igc-tile id="tile3">
202+
<h2>Tile3</h2>
203+
</igc-tile>
204+
<igc-tile id="tile4">
205+
<h2>Tile4</h2>
206+
</igc-tile>
207+
<igc-tile id="tile5">
208+
<h2>Tile5</h2>
209+
</igc-tile>
210+
<igc-tile id="tile6" position="2">
211+
<h2>Tile6</h2>
212+
</igc-tile>
199213
</igc-tile-manager>
200214
`,
201215
};

0 commit comments

Comments
 (0)