bitandblack-rows
Version:
A small and simple CSS gutter to create rows and cells using the flexbox model.
252 lines (213 loc) • 7.5 kB
text/typescript
/**
* Bit&Black Rows
*
* @copyright Copyright (c) Bit&Black
* @author Tobias Köngeter <hello@bitandblack.com>
* @link https://www.bitandblack.com
*/
/**
* Receiving information about the cell positions in the grid.
*/
class Position {
private _resizeObserver: ResizeObserver = null;
private _addCssClassesForOutsideCells: boolean = true;
private _addDataAttributesAboutCellPositions: boolean = false;
private _cssClassFirstOfRow: string = "row__cell--first-of-row";
private _cssClassLastOfRow: string = "row__cell--last-of-row";
private _cssClassFirstOfColumn: string = "row__cell--first-of-column";
private _cssClassLastOfColumn: string = "row__cell--last-of-column";
public constructor() {
this.resizeObserverCallback = this.resizeObserverCallback.bind(this);
this._resizeObserver = new ResizeObserver(
this.resizeObserverCallback
);
}
/**
* Add a rows-container element.
*
* @param element {HTMLElement}
* @return self
*/
public add(element: HTMLElement): this {
this._resizeObserver.observe(element);
return this;
}
/**
* Removes a rows-container element from observing.
*
* @param element {HTMLElement}
* @return self
*/
public remove(element: HTMLElement): this {
this._resizeObserver.unobserve(element);
this.removeElement(element);
return this;
}
/**
* Tells if adding CSS classes to outside laying cells has been enabled or disabled.
*/
public addCssClassesForOutsideCells(): boolean {
return this._addCssClassesForOutsideCells;
}
/**
* Enable or disable adding CSS classes to outside laying cells.
*
* @param addCssClassesForOutsideCells {boolean}
*/
public setAddCssClassesForOutsideCells(addCssClassesForOutsideCells: boolean): this {
this._addCssClassesForOutsideCells = addCssClassesForOutsideCells;
return this;
}
/**
* Tells if adding data attributes to each cell with information about its position has been enabled or disabled.
*/
public addDataAttributesAboutCellPositions(): boolean {
return this._addDataAttributesAboutCellPositions;
}
/**
* Enable or disable adding data attributes to each cell with information about its position.
*
* @param addDataAttributesAboutCellPositions {boolean}
*/
public setAddDataAttributesAboutCellPositions(addDataAttributesAboutCellPositions: boolean): this {
this._addDataAttributesAboutCellPositions = addDataAttributesAboutCellPositions;
return this;
}
/**
* @return {string}
*/
public getCssClassFirstOfRow(): string {
return this._cssClassFirstOfRow;
}
/**
* @param cssClassFirstOfRow {string}
* @return self
*/
public setCssClassFirstOfRow(cssClassFirstOfRow: string): this {
this._cssClassFirstOfRow = cssClassFirstOfRow;
return this;
}
/**
* @return {string}
*/
public getCssClassLastOfRow(): string {
return this._cssClassLastOfRow;
}
/**
* @param cssClassLastOfRow {string}
* @return self
*/
public setCssClassLastOfRow(cssClassLastOfRow: string): this {
this._cssClassLastOfRow = cssClassLastOfRow;
return this;
}
/**
* @return {string}
*/
public getCssClassFirstOfColumn(): string {
return this._cssClassFirstOfColumn;
}
/**
* @param cssClassFirstOfColumn {string}
* @return self
*/
public setCssClassFirstOfColumn(cssClassFirstOfColumn: string): this {
this._cssClassFirstOfColumn = cssClassFirstOfColumn;
return this;
}
/**
* @return {string}
*/
public getCssClassLastOfColumn(): string {
return this._cssClassLastOfColumn;
}
/**
* @param cssClassLastOfColumn {string}
* @return self
*/
public setCssClassLastOfColumn(cssClassLastOfColumn: string): this {
this._cssClassLastOfColumn = cssClassLastOfColumn;
return this;
}
/**
* @param resizeObserverEntries {ResizeObserverEntry[]}
* @private
*/
private resizeObserverCallback(resizeObserverEntries: ResizeObserverEntry[]): void {
for (const resizeObserverEntry of resizeObserverEntries) {
this.handleEntry(resizeObserverEntry);
}
};
/**
* @param resizeObserverEntry {ResizeObserverEntry}
* @private
*/
private handleEntry(resizeObserverEntry: ResizeObserverEntry): void {
const rowElement: HTMLElement = resizeObserverEntry.target as HTMLElement;
const rowCells: HTMLElement[] = Array.from(rowElement.children) as HTMLElement[];
let columnCounter = 0;
let rowCounter = 0;
for (const [index, rowCell] of Object.entries(rowCells)) {
const rowCellIndex = Number.parseInt(index);
const rowCellFirst: HTMLElement = rowCells[0];
const rowCellPrevious: HTMLElement = rowCells[rowCellIndex - 1];
const rowCellNext: HTMLElement = rowCells[rowCellIndex + 1];
const rowCellLast: HTMLElement = rowCells[rowCells.length - 1];
const isFirstOfRow = !rowCellPrevious || rowCell.offsetTop !== rowCellPrevious.offsetTop;
const isLastOfRow = !rowCellNext || rowCell.offsetTop !== rowCellNext.offsetTop;
const isFirstOfColumn = rowCell.offsetTop === rowCellFirst.offsetTop;
const isLastOfColumn = rowCell.offsetTop === rowCellLast.offsetTop;
if (true === this.addCssClassesForOutsideCells()) {
rowCell.classList.toggle(
this.getCssClassFirstOfRow(),
isFirstOfRow
);
rowCell.classList.toggle(
this.getCssClassLastOfRow(),
isLastOfRow
);
rowCell.classList.toggle(
this.getCssClassFirstOfColumn(),
isFirstOfColumn
);
rowCell.classList.toggle(
this.getCssClassLastOfColumn(),
isLastOfColumn
);
}
if (true === this.addDataAttributesAboutCellPositions()) {
rowCell.dataset.nthOfColumn = columnCounter.toString();
rowCell.dataset.nthOfRow = rowCounter.toString();
}
++columnCounter;
if (isLastOfRow) {
columnCounter = 0;
++rowCounter;
}
}
}
/**
* @param rowElement {HTMLElement}
* @private
*/
private removeElement(rowElement: HTMLElement): void {
const rowCells: HTMLElement[] = Array.from(rowElement.children) as HTMLElement[];
for (const [, rowCell] of Object.entries(rowCells)) {
rowCell.classList.remove(
this.getCssClassFirstOfRow(),
);
rowCell.classList.remove(
this.getCssClassLastOfRow(),
);
rowCell.classList.remove(
this.getCssClassFirstOfColumn(),
);
rowCell.classList.remove(
this.getCssClassLastOfColumn(),
);
delete rowCell.dataset.nthOfColumn;
delete rowCell.dataset.nthOfRow;
}
}
}
export { Position };