Skip to content

Commit

Permalink
feat(kit): Tiles add reorder strategies (#10360)
Browse files Browse the repository at this point in the history
Co-authored-by: d.kulagin <[email protected]>
Co-authored-by: Alex Inkin <[email protected]>
  • Loading branch information
3 people authored Feb 17, 2025
1 parent 189ee07 commit 1277754
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 7 deletions.
8 changes: 7 additions & 1 deletion projects/addon-table/components/reorder/reorder.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {TUI_TABLE_SHOW_HIDE_MESSAGE} from '@taiga-ui/addon-table/tokens';
import type {TuiContext} from '@taiga-ui/cdk/types';
import {TuiButton} from '@taiga-ui/core/components/button';
import {TuiIcon} from '@taiga-ui/core/components/icon';
import {TuiTiles} from '@taiga-ui/kit/components/tiles';
import {TUI_TILE_REORDER, TuiTiles, tuiTileShift} from '@taiga-ui/kit/components/tiles';
import type {PolymorpheusContent} from '@taiga-ui/polymorpheus';
import {PolymorpheusOutlet, PolymorpheusTemplate} from '@taiga-ui/polymorpheus';

Expand All @@ -37,6 +37,12 @@ import {TUI_REORDER_OPTIONS} from './reorder.options';
'(pointerdown.zoneless)': 'onDrag()',
'(document:pointerup.zoneless)': 'onDrop()',
},
providers: [
{
provide: TUI_TILE_REORDER,
useValue: tuiTileShift,
},
],
})
export class TuiReorder<T> {
private dragging = false;
Expand Down
56 changes: 56 additions & 0 deletions projects/kit/components/tiles/helpers/reorder-functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
export type TuiReorderFunction = (
order: Map<number, number>,
currentIndex: number,
newIndex: number,
) => Map<number, number>;

export const tuiTileSwap: TuiReorderFunction = (order, currentIndex, newIndex) => {
if (!order.has(currentIndex) || !order.has(newIndex)) {
return order;
}

const dragged = order.get(currentIndex) ?? currentIndex;
const placement = order.get(newIndex) ?? newIndex;

const newOrder = new Map(order);

newOrder.set(currentIndex, placement);
newOrder.set(newIndex, dragged);

return newOrder;
};

export const tuiTileShift: TuiReorderFunction = (order, currentIndex, newIndex) => {
if (!order.has(currentIndex) || !order.has(newIndex)) {
return order;
}

const dragged = order.get(currentIndex) ?? currentIndex;
const placement = order.get(newIndex) ?? newIndex;

const reversedOrder = new Map(
Array.from(order).map(([elemIndex, orderIndex]) => [orderIndex, elemIndex]),
);

const newOrder = new Map(order);

const direction = (placement - dragged) / Math.abs(placement - dragged);

if (direction > 0) {
for (let i = placement; i > dragged; i--) {
const tmp = reversedOrder.get(i) ?? i;

newOrder.set(tmp, i - 1);
}
} else {
for (let i = placement; i < dragged; i++) {
const tmp = reversedOrder.get(i) ?? i;

newOrder.set(tmp, i + 1);
}
}

newOrder.set(currentIndex, placement);

return newOrder;
};
2 changes: 2 additions & 0 deletions projects/kit/components/tiles/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export * from './helpers/reorder-functions';
export * from './tile.component';
export * from './tile.service';
export * from './tile-handle.directive';
export * from './tile-reorder.provider';
export * from './tiles';
export * from './tiles.component';
181 changes: 181 additions & 0 deletions projects/kit/components/tiles/test/reorder-functions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import {tuiTileShift, tuiTileSwap} from '../helpers/reorder-functions';

describe('reorder functions', () => {
describe('swap', () => {
it('should swap 1 and 3', () => {
const source = new Map([
[0, 0],
[1, 1],
[2, 2],
[3, 3],
]);

/**
* 1 -> 3
* 3 -> 1
*/
const result = tuiTileSwap(source, 1, 3);

expect(result.get(0)).toBe(0);
expect(result.get(1)).toBe(3);
expect(result.get(2)).toBe(2);
expect(result.get(3)).toBe(1);
expect(result.size).toBe(source.size);
});

it('should swap 3 and 1', () => {
const source = new Map([
[0, 0],
[1, 1],
[2, 2],
[3, 3],
]);

/**
* 3 -> 1
* 1 -> 3
*/
const result = tuiTileSwap(source, 3, 1);

expect(result.get(0)).toBe(0);
expect(result.get(1)).toBe(3);
expect(result.get(2)).toBe(2);
expect(result.get(3)).toBe(1);
expect(result.size).toBe(source.size);
});

it('should return source order if currentIndex is out of range', () => {
const source = new Map([
[0, 0],
[1, 1],
[2, 2],
]);

const result = tuiTileSwap(source, 3, 0);

expect(result).toBe(result);
expect(result.size).toBe(source.size);
});

it('should return source order if newIndex is out of range', () => {
const source = new Map([
[0, 0],
[1, 1],
[2, 2],
]);

const result = tuiTileSwap(source, 0, 3);

expect(result).toBe(result);
expect(result.size).toBe(source.size);
});

it('should not change order if current and new indexes are the same', () => {
const source = new Map([
[0, 0],
[1, 1],
[2, 2],
]);

const result = tuiTileSwap(source, 1, 1);

expect(result.get(0)).toBe(0);
expect(result.get(1)).toBe(1);
expect(result.get(2)).toBe(2);
expect(result.size).toBe(source.size);
});
});

describe('shift', () => {
it('should make right-left shift 1 -> 4', () => {
const source = new Map([
[0, 0],
[1, 1],
[2, 2],
[3, 3],
[4, 4],
]);

/**
* 1 -> 4
* 4 -> 3
* 3 -> 2
* 2 -> 1
*/
const result = tuiTileShift(source, 1, 4);

expect(result.get(0)).toBe(0);
expect(result.get(1)).toBe(4);
expect(result.get(2)).toBe(1);
expect(result.get(3)).toBe(2);
expect(result.get(4)).toBe(3);
expect(result.size).toBe(source.size);
});

it('should make left-right shift 4 -> 1', () => {
const source = new Map([
[0, 0],
[1, 1],
[2, 2],
[3, 3],
[4, 4],
]);

/**
* 4 -> 1
* 1 -> 2
* 2 -> 3
* 3 -> 4
*/
const result = tuiTileShift(source, 4, 1);

expect(result.get(0)).toBe(0);
expect(result.get(1)).toBe(2);
expect(result.get(2)).toBe(3);
expect(result.get(3)).toBe(4);
expect(result.get(4)).toBe(1);
expect(result.size).toBe(source.size);
});

it('should return source order if currentIndex is out of range', () => {
const source = new Map([
[0, 0],
[1, 1],
[2, 2],
]);

const result = tuiTileShift(source, 3, 0);

expect(result).toBe(result);
expect(result.size).toBe(source.size);
});

it('should return source order if newIndex is out of range', () => {
const source = new Map([
[0, 0],
[1, 1],
[2, 2],
]);

const result = tuiTileShift(source, 0, 3);

expect(result).toBe(result);
expect(result.size).toBe(source.size);
});

it('should not change order if current and new indexes are the same', () => {
const source = new Map([
[0, 0],
[1, 1],
[2, 2],
]);

const result = tuiTileShift(source, 1, 1);

expect(result.get(0)).toBe(0);
expect(result.get(1)).toBe(1);
expect(result.get(2)).toBe(2);
expect(result.size).toBe(source.size);
});
});
});
6 changes: 6 additions & 0 deletions projects/kit/components/tiles/tile-reorder.provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {tuiCreateToken} from '@taiga-ui/cdk/utils/miscellaneous';

import type {TuiReorderFunction} from './helpers/reorder-functions';
import {tuiTileSwap} from './helpers/reorder-functions';

export const TUI_TILE_REORDER = tuiCreateToken<TuiReorderFunction>(tuiTileSwap);
14 changes: 8 additions & 6 deletions projects/kit/components/tiles/tiles.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
ChangeDetectionStrategy,
Component,
inject,
Input,
Output,
signal,
Expand All @@ -14,6 +15,8 @@ import {ResizeObserverService} from '@ng-web-apis/resize-observer';
import {tuiInjectElement} from '@taiga-ui/cdk/utils/dom';
import {BehaviorSubject, debounce, filter, map, Subject, timer} from 'rxjs';

import {TUI_TILE_REORDER} from './tile-reorder.provider';

@Component({
standalone: true,
selector: 'tui-tiles',
Expand All @@ -38,6 +41,8 @@ export class TuiTilesComponent {
private readonly el = tuiInjectElement();
private readonly el$ = new Subject<Element | undefined>();

private readonly tuiTileReorder = inject(TUI_TILE_REORDER);

@Input()
public debounce = 0;

Expand Down Expand Up @@ -76,14 +81,11 @@ export class TuiTilesComponent {
const order = this.order.size
? new Map(this.order)
: new Map(elements.map((_, index) => [index, index]));
const dragged = order.get(currentIndex) ?? currentIndex;
const placement = order.get(newIndex) ?? newIndex;

order.set(currentIndex, placement);
order.set(newIndex, dragged);
const reordered = this.tuiTileReorder(order, currentIndex, newIndex);

this.order$.next(order);
this.order$.next(reordered);

return order;
return reordered;
}
}

0 comments on commit 1277754

Please sign in to comment.