Skip to content

Commit

Permalink
feat: add contentAlign prop (#102)
Browse files Browse the repository at this point in the history
  • Loading branch information
daybrush authored Nov 24, 2023
1 parent cee6ec4 commit bfa75f3
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 5 deletions.
51 changes: 46 additions & 5 deletions src/grids/MasonryGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
import Grid from "../Grid";
import { PROPERTY_TYPE, UPDATE_STATE } from "../consts";
import { GridOptions, Properties, GridOutlines, GridAlign } from "../types";
import { GridOptions, Properties, GridOutlines, GridAlign, MasonryGridVerticalAlign } from "../types";
import { range, GetterSetter } from "../utils";
import { GridItem } from "../GridItem";

Expand All @@ -19,12 +19,19 @@ function getColumnPoint(
return Math[pointCaculationName](...outline.slice(columnIndex, columnIndex + columnCount));
}

function getColumnIndex(outline: number[], columnCount: number, nearestCalculationName: "max" | "min") {
function getColumnIndex(
outline: number[],
columnCount: number,
nearestCalculationName: "max" | "min",
startPos: number,
) {
const length = outline.length - columnCount + 1;
const pointCaculationName = nearestCalculationName === "max" ? "min" : "max";
const indexCaculationName = nearestCalculationName === "max" ? "lastIndexOf" : "indexOf";
const points = range(length).map((index) => {
return getColumnPoint(outline, index, columnCount, pointCaculationName);
const point = getColumnPoint(outline, index, columnCount, pointCaculationName);

return Math[pointCaculationName](startPos, point);
});

return points[indexCaculationName](Math[nearestCalculationName](...points));
Expand Down Expand Up @@ -60,6 +67,13 @@ export interface MasonryGridOptions extends GridOptions {
* @default "justify"
*/
align?: GridAlign;
/**
* Content direction alignment of items. “Masonry” is sorted in the form of masonry. Others are applied as content direction alignment, similar to vertical-align of inline-block.
* If you set multiple columns (`data-grid-column`), the screen may look strange.
* <ko>아이템들의 Content 방향의 정렬. "masonry"는 masonry 형태로 정렬이 된다. 그 외는 inline-block의 vertical-align과 유사하게 content 방향 정렬로 적용이 된다.칼럼(`data-grid-column` )을 여러개 설정하면 화면이 이상하게 보일 수 있다. </ko>
* @default "masonry"
*/
contentAlign?: MasonryGridVerticalAlign;
/**
* Difference Threshold for Counting Columns. Since offsetSize is calculated by rounding, the number of columns may not be accurate.
* <ko>칼럼 개수를 계산하기 위한 차이 임계값. offset 사이즈는 반올림으로 게산하기 때문에 정확하지 않을 수 있다.</ko>
Expand Down Expand Up @@ -91,6 +105,7 @@ export class MasonryGrid extends Grid<MasonryGridOptions> {
align: PROPERTY_TYPE.RENDER_PROPERTY,
columnCalculationThreshold: PROPERTY_TYPE.RENDER_PROPERTY,
maxStretchColumnSize: PROPERTY_TYPE.RENDER_PROPERTY,
contentAlign: PROPERTY_TYPE.RENDER_PROPERTY,
};
public static defaultOptions: Required<MasonryGridOptions> = {
...Grid.defaultOptions,
Expand All @@ -100,6 +115,7 @@ export class MasonryGrid extends Grid<MasonryGridOptions> {
columnSizeRatio: 0,
columnCalculationThreshold: 0.5,
maxStretchColumnSize: Infinity,
contentAlign: "masonry",
};

public applyGrid(items: GridItem[], direction: "start" | "end", outline: number[]): GridOutlines {
Expand All @@ -114,6 +130,7 @@ export class MasonryGrid extends Grid<MasonryGridOptions> {
align,
observeChildren,
columnSizeRatio,
contentAlign,
} = this.options;
const outlineLength = outline.length;
const itemsLength = items.length;
Expand All @@ -130,9 +147,19 @@ export class MasonryGrid extends Grid<MasonryGridOptions> {

startOutline = range(column).map(() => point);
}
const endOutline = startOutline.slice();
let endOutline = startOutline.slice();
const columnDist = column > 1 ? alignPoses[1] - alignPoses[0] : 0;
const isStretch = align === "stretch";
const isStartContentAlign = isEndDirection && contentAlign === "start";


let startPos = isEndDirection ? -Infinity : Infinity;


if (isStartContentAlign) {
// support only end direction
startPos = Math.min(...endOutline);
}

for (let i = 0; i < itemsLength; ++i) {
const item = items[isEndDirection ? i : itemsLength - 1 - i];
Expand All @@ -144,9 +171,16 @@ export class MasonryGrid extends Grid<MasonryGridOptions> {
columnAttribute || Math.max(1, Math.ceil((item.inlineSize + gap) / columnDist)),
);
const maxColumnCount = Math.min(column, Math.max(columnCount, maxColumnAttribute));
let columnIndex = getColumnIndex(endOutline, columnCount, nearestCalculationName);
let columnIndex = getColumnIndex(endOutline, columnCount, nearestCalculationName, startPos);
let contentPos = getColumnPoint(endOutline, columnIndex, columnCount, pointCalculationName);

if (isStartContentAlign && startPos !== contentPos) {
startPos = Math.max(...endOutline);
endOutline = endOutline.map(() => startPos);
contentPos = startPos;
columnIndex = 0;
}

while (columnCount < maxColumnCount) {
const nextEndColumnIndex = columnIndex + columnCount;
const nextColumnIndex = columnIndex - 1;
Expand Down Expand Up @@ -191,6 +225,13 @@ export class MasonryGrid extends Grid<MasonryGridOptions> {
});
}

// Finally, check whether startPos and min of the outline match.
// If different, endOutline is updated.
if (isStartContentAlign && startPos !== Math.min(...endOutline)) {
startPos = Math.max(...endOutline);
endOutline = endOutline.map(() => startPos);
}

// if end items, startOutline is low, endOutline is high
// if start items, startOutline is high, endOutline is low
return {
Expand Down
7 changes: 7 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,13 @@ export interface OnContentError {
*/
export type GridAlign = "start" | "center" | "end" | "justify" | "stretch";

/**
* @typedef
* @memberof Grid
*/
export type MasonryGridVerticalAlign = "masonry" | "start";


export type GridEvents = {
renderComplete: OnRenderComplete;
contentError: OnContentError;
Expand Down
148 changes: 148 additions & 0 deletions test/manual/contentAlign.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<style>
html, body {
position: relative;
height: 100%;
padding: 0!important;
margin: 0!important;
}

.container {
overflow: hidden;
}

.item {
position: absolute;
width: 200px;
color: white;
text-align: center;
}

.item:nth-child(6n + 1) {
background: #f55;
height: 200px;
}

.item:nth-child(6n + 2) {
background: #7e7;
height: 300px;
}

.item:nth-child(6n + 3) {
background: #66e;
height: 200px;
}

.item:nth-child(6n + 4) {
background: #4af;
height: 100px;
}

.item:nth-child(6n + 5) {
background: #ed5;
height: 150px;
}

.item:nth-child(6n + 6) {
background: #d5e;
height: 130px;
}

.result {
text-align: center;
padding: 10px;
font-weight: bold;
box-sizing: border-box;
font-size: 14px;
}

.button {
position: relative;
display: block;
margin: 10px auto;
padding: 10px 20px;
background: white;
border: 1px solid #ccc;
appearance: none;
font-weight: bold;
width: 150px;
text-align: center;
box-sizing: border-box;
font-size: 14px;
}

.image {
position: relative;
width: 200px;
color: black;
}

.image img {
width: 100%;
}

.title {
height: 40px;
line-height: 40px;
text-align: center;
font-weight: bold;
font-size: 14px;
}

</style>
<div class="container">
<div class="image" data-grid-content-offset="40">
<img src="https://naver.github.io/egjs-infinitegrid/assets/image/1.jpg" alt="image1" data-grid-lazy="true"/>
<div class="title">Item 1</div>
</div>
<div class="image" data-grid-content-offset="40">
<img src="https://naver.github.io/egjs-infinitegrid/assets/image/2.jpg" alt="image2" data-grid-lazy="true"/>
<div class="title">Item 2</div>
</div>
<div class="image" data-grid-content-offset="40">
<img src="https://naver.github.io/egjs-infinitegrid/assets/image/3.jpg" alt="image3" data-grid-lazy="true"/>
<div class="title">Item 3</div>
</div>
<div class="image" data-grid-content-offset="40">
<img src="https://naver.github.io/egjs-infinitegrid/assets/image/4.jpg" alt="image4" data-grid-lazy="true"/>
<div class="title">Item 4</div>
</div>
<div class="image" data-grid-content-offset="40">
<img src="https://naver.github.io/egjs-infinitegrid/assets/image/5.jpg" alt="image5" data-grid-lazy="true"/>
<div class="title">Item 5</div>
</div>
<div class="image" data-grid-content-offset="40">
<img src="https://naver.github.io/egjs-infinitegrid/assets/image/6.jpg" alt="image6" data-grid-lazy="true"/>
<div class="title">Item 6</div>
</div>
<div class="image" data-grid-content-offset="40">
<img src="https://naver.github.io/egjs-infinitegrid/assets/image/7.jpg" alt="image7" data-grid-lazy="true"/>
<div class="title">Item 7</div>
</div>
<div class="image" data-grid-content-offset="40">
<img src="https://naver.github.io/egjs-infinitegrid/assets/image/8.jpg" alt="image8" data-grid-lazy="true"/>
<div class="title">Item 8</div>
</div>
<div class="image" data-grid-content-offset="40">
<img src="https://naver.github.io/egjs-infinitegrid/assets/image/9.jpg" alt="image9" data-grid-lazy="true"/>
<div class="title">Item 9</div>
</div>
<div class="image" data-grid-content-offset="40">
<img src="https://naver.github.io/egjs-infinitegrid/assets/image/10.jpg" alt="image10" data-grid-lazy="true"/>
<div class="title">Item 10</div>
</div>
</div>
<script src="../../dist/grid.js"></script>
<script>

const grid = new Grid.MasonryGrid(".container", {
maxStretchSize: 400,
useResizeObserver: true,
observeChildren: true,
gap: 5,
autoResize: true,
contentAlign: "start",
}).on("renderComplete", () => {
console.log("?");
});
grid.renderItems();
</script>
36 changes: 36 additions & 0 deletions test/unit/MasonryGrid.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -798,5 +798,41 @@ describe("test MasonryGrid", () => {
expect(startOutline).to.be.deep.equals([0, 0]);
expect(startOutline2).to.be.deep.equals([0]);
});

it(`should check if it is aligned at the top If contentAlign is "start".`, async () => {
// Given
container!.style.cssText = "width: 600px; height: 600px;";
grid = new MasonryGrid(container!, {
contentAlign: "start",
});

grid.setItems([
new GridItem(false, {
rect: { width: 300, height: 150, top: 0, left: 0 },
}),
new GridItem(false, {
rect: { width: 300, height: 100, top: 0, left: 0 },
}),
new GridItem(false, {
rect: { width: 300, height: 150, top: 0, left: 0 },
}),
new GridItem(false, {
rect: { width: 300, height: 100, top: 0, left: 0 },
}),
]);

// When
grid.renderItems();

await waitEvent(grid, "renderComplete");


// Then
// [0, 0]
// [150, 150]
expect(grid.getItems()[2].cssRect.top).to.be.deep.equals(150);
expect(grid.getItems()[3].cssRect.top).to.be.deep.equals(150);
expect(grid.getOutlines().end).to.be.deep.equals([300, 300]);
});
});

0 comments on commit bfa75f3

Please sign in to comment.