Skip to content

Commit 2f09944

Browse files
committed
refactor: Removed hardcoded CSS variables and default column count
Moved the save/load layout logic in a separate module.
1 parent 3c23f11 commit 2f09944

File tree

5 files changed

+170
-82
lines changed

5 files changed

+170
-82
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
import { registerComponent } from '../common/definitions/register.js';
99
import type { Constructor } from '../common/mixins/constructor.js';
1010
import { EventEmitterMixin } from '../common/mixins/event-emitter.js';
11+
import IgcIconComponent from '../icon/icon.js';
1112
import {
1213
type ResizeCallbackParams,
1314
type ResizeMode,
@@ -32,7 +33,7 @@ export default class IgcResizeComponent extends EventEmitterMixin<
3233

3334
/* blazorSuppress */
3435
public static register() {
35-
registerComponent(IgcResizeComponent);
36+
registerComponent(IgcResizeComponent, IgcIconComponent);
3637
}
3738

3839
private _resizeController: ReturnType<typeof addResizeController>;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import type IgcTileManagerComponent from './tile-manager.js';
2+
3+
export interface SerializedTile {
4+
colSpan: number;
5+
colStart: number | null;
6+
disableDrag: boolean;
7+
disableResize: boolean;
8+
gridColumn: string;
9+
gridRow: string;
10+
maximized: boolean;
11+
position: number;
12+
rowSpan: number;
13+
rowStart: number | null;
14+
tileId: string | null;
15+
}
16+
17+
class TileManagerSerializer {
18+
public tileManager: IgcTileManagerComponent;
19+
20+
constructor(tileManager: IgcTileManagerComponent) {
21+
this.tileManager = tileManager;
22+
}
23+
24+
public save(): SerializedTile[] {
25+
return this.tileManager.tiles.map((tile) => {
26+
const { gridColumn, gridRow } = getComputedStyle(tile);
27+
28+
return {
29+
colSpan: tile.colSpan,
30+
colStart: tile.colStart,
31+
disableDrag: tile.disableDrag,
32+
disableResize: tile.disableResize,
33+
gridColumn,
34+
gridRow,
35+
maximized: tile.maximized,
36+
position: tile.position,
37+
rowSpan: tile.rowSpan,
38+
rowStart: tile.rowStart,
39+
tileId: tile.tileId,
40+
};
41+
});
42+
}
43+
44+
public saveAsJSON(): string {
45+
return JSON.stringify(this.save());
46+
}
47+
48+
public load(tiles: SerializedTile[]): void {
49+
const mapped = new Map(tiles.map((tile) => [tile.tileId, tile]));
50+
51+
for (const tile of this.tileManager.tiles) {
52+
if (!mapped.has(tile.tileId)) {
53+
continue;
54+
}
55+
56+
const serialized = mapped.get(tile.tileId)!;
57+
const properties = omit(serialized, 'gridColumn', 'gridRow');
58+
const styles = pick(serialized, 'gridColumn', 'gridRow');
59+
60+
Object.assign(tile, properties);
61+
Object.assign(tile.style, styles);
62+
}
63+
}
64+
65+
public loadFromJSON(data: string): void {
66+
this.load(JSON.parse(data));
67+
}
68+
}
69+
70+
export function createSerializer(host: IgcTileManagerComponent) {
71+
return new TileManagerSerializer(host);
72+
}
73+
74+
function pick<T extends object>(entry: T, ...props: Array<keyof T>) {
75+
return Object.fromEntries(
76+
Object.entries(entry).filter(([key, _]) => props.includes(key as keyof T))
77+
);
78+
}
79+
80+
function omit<T extends object>(entry: T, ...props: Array<keyof T>) {
81+
return Object.fromEntries(
82+
Object.entries(entry).filter(([key, _]) => !props.includes(key as keyof T))
83+
);
84+
}

src/components/tile-manager/themes/tile-manager.base.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
:host {
55
display: block;
6+
7+
--ig-min-col-width: 200px;
8+
--ig-min-row-height: 200px;
69
}
710

811
[part='base'] {

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

Lines changed: 58 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { LitElement, html } from 'lit';
22
import { property, query } from 'lit/decorators.js';
3-
import { styleMap } from 'lit/directives/style-map.js';
3+
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
44
import { themes } from '../../theming/theming-decorator.js';
55
import {
66
type MutationControllerParams,
@@ -9,7 +9,9 @@ import {
99
import { registerComponent } from '../common/definitions/register.js';
1010
import type { Constructor } from '../common/mixins/constructor.js';
1111
import { EventEmitterMixin } from '../common/mixins/event-emitter.js';
12+
import { asNumber } from '../common/util.js';
1213
import { addFullscreenController } from './controllers/fullscreen.js';
14+
import { createSerializer } from './serializer.js';
1315
import { all } from './themes/container.js';
1416
import { styles as shared } from './themes/shared/tile-manager.common.css.js';
1517
import { styles } from './themes/tile-manager.base.css.js';
@@ -45,8 +47,13 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
4547
registerComponent(IgcTileManagerComponent, IgcTileComponent);
4648
}
4749

50+
private _internalStyles: StyleInfo = {};
51+
4852
private positionedTiles: IgcTileComponent[] = [];
49-
private _columnCount = 10;
53+
private _columnCount = 0;
54+
private _minColWidth?: string;
55+
private _minRowHeight?: string;
56+
private _serializer = createSerializer(this);
5057

5158
/** @private @hidden @internal */
5259
public draggedItem: IgcTileComponent | null = null;
@@ -106,35 +113,61 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
106113

107114
/**
108115
* Determines whether the tiles slide or swap on drop.
109-
* @attr
116+
* @attr drag-mode
110117
*/
111-
@property()
118+
@property({ attribute: 'drag-mode' })
112119
public dragMode: 'slide' | 'swap' = 'slide';
113120

114-
@property({ type: Number })
121+
/**
122+
* Sets the number of columns for the tile manager.
123+
* Setting value <= than zero will trigger a responsive layout.
124+
*
125+
* @attr column-count
126+
* @default 0
127+
*/
128+
@property({ type: Number, attribute: 'column-count' })
115129
public set columnCount(value: number) {
116-
const oldValue = this._columnCount;
117-
118-
if (value <= 0 || value === undefined) {
119-
this._columnCount = 10;
120-
} else {
121-
this._columnCount = value;
122-
}
123-
124-
if (oldValue !== this._columnCount) {
125-
this.requestUpdate('columnCount', oldValue);
126-
}
130+
this._columnCount = Math.max(0, asNumber(value));
131+
Object.assign(this._internalStyles, {
132+
'--ig-column-count': this._columnCount || undefined,
133+
});
127134
}
128135

129136
public get columnCount(): number {
130137
return this._columnCount;
131138
}
132139

133-
@property({ type: Number })
134-
public minColumnWidth = 200;
140+
/**
141+
* Sets the minimum width for a column unit in the tile manager.
142+
* @attr min-column-width
143+
*/
144+
@property({ attribute: 'min-column-width' })
145+
public set minColumnWidth(value: string | undefined) {
146+
this._minColWidth = value ?? undefined;
147+
Object.assign(this._internalStyles, {
148+
'--ig-min-col-width': this._minColWidth,
149+
});
150+
}
151+
152+
public get minColumnWidth(): string | undefined {
153+
return this._minColWidth;
154+
}
135155

136-
@property({ type: Number })
137-
public minRowHeight = 40;
156+
/**
157+
* Sets the minimum height for a row unit in the tile manager.
158+
* @attr min-row-height
159+
*/
160+
@property({ attribute: 'min-row-height' })
161+
public set minRowHeight(value: string | undefined) {
162+
this._minRowHeight = value ?? undefined;
163+
Object.assign(this._internalStyles, {
164+
'--ig-min-row-height': this._minRowHeight,
165+
});
166+
}
167+
168+
public get minRowHeight(): string | undefined {
169+
return this._minRowHeight;
170+
}
138171

139172
/**
140173
* Gets the tiles sorted by their position in the layout.
@@ -226,65 +259,18 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
226259
}
227260
}
228261

229-
public saveLayout() {
230-
// TODO: serialize fullscreen when added
231-
const tilesData = this.tiles.map((tile) => {
232-
const tileStyles = window.getComputedStyle(tile);
233-
234-
return {
235-
colSpan: tile.colSpan,
236-
colStart: tile.colStart,
237-
disableDrag: tile.disableDrag,
238-
disableResize: tile.disableResize,
239-
// TODO: Review. We are saving gridColumn and gridRow as they keep the size of the resized tiles.
240-
gridColumn: tileStyles.gridColumn,
241-
gridRow: tileStyles.gridRow,
242-
maximized: tile.maximized,
243-
position: tile.position,
244-
rowSpan: tile.rowSpan,
245-
rowStart: tile.rowStart,
246-
tileId: tile.tileId,
247-
};
248-
});
249-
250-
return JSON.stringify(tilesData);
262+
public saveLayout(): string {
263+
return this._serializer.saveAsJSON();
251264
}
252265

253-
public loadLayout(data: string) {
254-
const tilesData = JSON.parse(data);
255-
256-
tilesData.forEach((tileInfo: any) => {
257-
const existingTile = this._tiles.find(
258-
(tile) => tile.tileId === tileInfo.tileId
259-
);
260-
261-
if (existingTile) {
262-
existingTile.colSpan = tileInfo.colSpan;
263-
existingTile.colStart = tileInfo.colStart;
264-
existingTile.disableDrag = tileInfo.disableDrag;
265-
existingTile.disableResize = tileInfo.disableResize;
266-
existingTile.maximized = tileInfo.maximized;
267-
existingTile.position = tileInfo.position;
268-
existingTile.rowSpan = tileInfo.rowSpan;
269-
existingTile.rowStart = tileInfo.rowStart;
270-
271-
// TODO: Review. We are saving gridColumn and gridRow as they keep the size of the resized tiles.
272-
existingTile.style.gridColumn = tileInfo.gridColumn;
273-
existingTile.style.gridRow = tileInfo.gridRow;
274-
}
275-
});
266+
public loadLayout(data: string): void {
267+
this._serializer.loadFromJSON(data);
276268
}
277269

278270
protected override render() {
279-
const styles = {
280-
'--ig-column-count': `${this.columnCount}`,
281-
'--ig-min-col-width': `${this.minColumnWidth}px`,
282-
'--ig-min-row-height': `${this.minRowHeight}px`,
283-
};
284-
285271
return html`
286272
<div
287-
style=${styleMap(styles)}
273+
style=${styleMap(this._internalStyles)}
288274
part="base"
289275
@tileDragStart=${this.handleTileDragStart}
290276
@tileDragEnd=${this.handleTileDragEnd}

stories/tile-manager.stories.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,29 +80,43 @@ const metadata: Meta<IgcTileManagerComponent> = {
8080
control: { type: 'inline-radio' },
8181
table: { defaultValue: { summary: 'slide' } },
8282
},
83-
columnCount: { type: 'number', control: 'number' },
84-
minColumnWidth: {
83+
columnCount: {
8584
type: 'number',
85+
description:
86+
'Sets the number of columns for the tile manager.\nSetting value <= than zero will trigger a responsive layout.',
8687
control: 'number',
87-
table: { defaultValue: { summary: '200' } },
88+
table: { defaultValue: { summary: '0' } },
89+
},
90+
minColumnWidth: {
91+
type: 'string',
92+
description:
93+
'Sets the minimum width for a column unit in the tile manager.',
94+
control: 'text',
8895
},
8996
minRowHeight: {
90-
type: 'number',
91-
control: 'number',
92-
table: { defaultValue: { summary: '40' } },
97+
type: 'string',
98+
description:
99+
'Sets the minimum height for a row unit in the tile manager.',
100+
control: 'text',
93101
},
94102
},
95-
args: { dragMode: 'slide', minColumnWidth: 200, minRowHeight: 40 },
103+
args: { dragMode: 'slide', columnCount: 0 },
96104
};
97105

98106
export default metadata;
99107

100108
interface IgcTileManagerArgs {
101109
/** Determines whether the tiles slide or swap on drop. */
102110
dragMode: 'slide' | 'swap';
111+
/**
112+
* Sets the number of columns for the tile manager.
113+
* Setting value <= than zero will trigger a responsive layout.
114+
*/
103115
columnCount: number;
104-
minColumnWidth: number;
105-
minRowHeight: number;
116+
/** Sets the minimum width for a column unit in the tile manager. */
117+
minColumnWidth: string;
118+
/** Sets the minimum height for a row unit in the tile manager. */
119+
minRowHeight: string;
106120
}
107121
type Story = StoryObj<IgcTileManagerArgs>;
108122

0 commit comments

Comments
 (0)