Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add contentAlign prop #102

Merged
merged 1 commit into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]);
});
});

Loading