react-justified-layout-ts 2.1.4 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/main.ts +12 -13
- package/.storybook/preview.ts +11 -4
- package/README.md +44 -44
- package/dist/components/layout.css +3 -3
- package/dist/src/components/JustifiedGrid.d.ts +13 -0
- package/dist/src/components/JustifiedGrid.js +59 -0
- package/dist/src/components/TSJustifiedLayout.d.ts +18 -0
- package/dist/src/components/TSJustifiedLayout.js +99 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +7 -0
- package/dist/src/stories/ConfiguredJustifiedLayout.d.ts +3 -0
- package/dist/src/stories/ConfiguredJustifiedLayout.js +12 -0
- package/dist/src/stories/JustifiedLayout.stories.d.ts +13 -0
- package/dist/src/stories/JustifiedLayout.stories.js +202 -0
- package/dist/stories/JustifiedLayout.stories.d.ts +0 -1
- package/dist/stories/JustifiedLayout.stories.js +5 -6
- package/package.json +43 -49
- package/src/components/JustifiedGrid.d.ts +14 -0
- package/src/components/JustifiedGrid.d.ts.map +1 -0
- package/src/components/JustifiedGrid.js +63 -0
- package/src/components/JustifiedGrid.js.map +1 -0
- package/src/components/JustifiedGrid.tsx +79 -79
- package/src/components/TSJustifiedLayout.d.ts +19 -0
- package/src/components/TSJustifiedLayout.d.ts.map +1 -0
- package/src/components/TSJustifiedLayout.js +98 -0
- package/src/components/TSJustifiedLayout.js.map +1 -0
- package/src/components/TSJustifiedLayout.tsx +139 -139
- package/src/components/layout.css +3 -3
- package/src/declarations.d.ts +1 -0
- package/src/index.d.ts +4 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.js +8 -0
- package/src/index.js.map +1 -0
- package/src/index.ts +3 -2
- package/src/stories/Configure.mdx +388 -364
- package/src/stories/ConfiguredJustifiedLayout.d.ts +3 -0
- package/src/stories/ConfiguredJustifiedLayout.d.ts.map +1 -0
- package/src/stories/ConfiguredJustifiedLayout.js +10 -0
- package/src/stories/ConfiguredJustifiedLayout.js.map +1 -0
- package/src/stories/ConfiguredJustifiedLayout.tsx +22 -22
- package/src/stories/JustifiedLayout.stories.d.ts +13 -0
- package/src/stories/JustifiedLayout.stories.d.ts.map +1 -0
- package/src/stories/JustifiedLayout.stories.js +197 -0
- package/src/stories/JustifiedLayout.stories.js.map +1 -0
- package/src/stories/JustifiedLayout.stories.tsx +204 -205
- package/src/stories/assets/accessibility.svg +1 -5
- package/src/stories/assets/discord.svg +1 -15
- package/src/stories/assets/github.svg +1 -3
- package/src/stories/assets/tutorials.svg +1 -12
- package/src/stories/assets/youtube.svg +1 -4
- package/tsconfig.json +44 -15
- package/LICENSE +0 -21
package/package.json
CHANGED
|
@@ -1,49 +1,43 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "react-justified-layout-ts",
|
|
3
|
-
"version": "2.1
|
|
4
|
-
"description": "A component based off Flickr's justified layout that is compatible with Typescript",
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"types": "./dist/index.d.ts",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"test": "echo \"Error: no test specified\" && exit 1",
|
|
9
|
-
"typescript": "tsc && copyfiles -u 1 src/**/*.css dist/",
|
|
10
|
-
"storybook": "storybook dev -p 6006",
|
|
11
|
-
"build-storybook": "storybook build"
|
|
12
|
-
},
|
|
13
|
-
"keywords": [],
|
|
14
|
-
"author": "Alan19",
|
|
15
|
-
"license": "MIT",
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@types/node": "^
|
|
18
|
-
"lodash": "^4.
|
|
19
|
-
"react-use": "^17.6.0",
|
|
20
|
-
"react-use-measure": "^2.1.7"
|
|
21
|
-
},
|
|
22
|
-
"devDependencies": {
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"@storybook/
|
|
30
|
-
"@storybook
|
|
31
|
-
"@storybook/
|
|
32
|
-
"@storybook/
|
|
33
|
-
"@
|
|
34
|
-
"@
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
},
|
|
45
|
-
"repository": {
|
|
46
|
-
"type": "git",
|
|
47
|
-
"url": "git+https://github.com/Alan19/react-justified-layout-ts.git"
|
|
48
|
-
}
|
|
49
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "react-justified-layout-ts",
|
|
3
|
+
"version": "2.2.1",
|
|
4
|
+
"description": "A component based off Flickr's justified layout that is compatible with Typescript",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
9
|
+
"typescript": "tsc && copyfiles -u 1 src/**/*.css dist/",
|
|
10
|
+
"storybook": "storybook dev -p 6006",
|
|
11
|
+
"build-storybook": "storybook build"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [],
|
|
14
|
+
"author": "Alan19",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@types/node": "^25.5.2",
|
|
18
|
+
"lodash": "^4.18.1",
|
|
19
|
+
"react-use": "^17.6.0",
|
|
20
|
+
"react-use-measure": "^2.1.7"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"copyfiles": "^2.4.1",
|
|
24
|
+
"react": "^19.2.4",
|
|
25
|
+
"react-loading-skeleton": "^3.5.0",
|
|
26
|
+
"rimraf": "^6.1.3",
|
|
27
|
+
"typescript": "^6.0.2",
|
|
28
|
+
"storybook": "^10.3.4",
|
|
29
|
+
"@storybook/react-vite": "^10.3.4",
|
|
30
|
+
"@chromatic-com/storybook": "^5.1.1",
|
|
31
|
+
"@storybook/addon-vitest": "^10.3.4",
|
|
32
|
+
"@storybook/addon-a11y": "^10.3.4",
|
|
33
|
+
"@storybook/addon-docs": "^10.3.4",
|
|
34
|
+
"@storybook/addon-onboarding": "^10.3.4",
|
|
35
|
+
"vitest": "4.1.2",
|
|
36
|
+
"playwright": "^1.59.1",
|
|
37
|
+
"@vitest/browser-playwright": "4.1.2"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/Alan19/react-justified-layout-ts.git"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import './layout.css';
|
|
3
|
+
export interface JustifiedGridProps {
|
|
4
|
+
aspectRatioList: number[];
|
|
5
|
+
itemSpacing?: number;
|
|
6
|
+
rowSpacing?: number;
|
|
7
|
+
targetRowHeight?: number;
|
|
8
|
+
targetRowHeightTolerance?: number;
|
|
9
|
+
width: number;
|
|
10
|
+
children: React.JSX.Element[];
|
|
11
|
+
containerStyle?: React.CSSProperties;
|
|
12
|
+
}
|
|
13
|
+
export declare function JustifiedGrid(props: Readonly<JustifiedGridProps>): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
//# sourceMappingURL=JustifiedGrid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JustifiedGrid.d.ts","sourceRoot":"","sources":["JustifiedGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,cAAc,CAAA;AAErB,MAAM,WAAW,kBAAkB;IAC/B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAC9B,cAAc,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CACxC;AAoCD,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,kBAAkB,CAAC,2CA8BhE"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.JustifiedGrid = JustifiedGrid;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
9
|
+
require("./layout.css");
|
|
10
|
+
function getJustifiedGridRowConfiguration(width, targetRowHeightTolerance, aspectRatioList, targetRowHeight, itemSpacing) {
|
|
11
|
+
let buffer = [];
|
|
12
|
+
const rows = [];
|
|
13
|
+
// We assume that the row will have the specified height, which allows us to convert aspect ratios into widths
|
|
14
|
+
const minRowWidth = width * (1 / (1 + targetRowHeightTolerance));
|
|
15
|
+
const maxRowWidth = width * (1 / (1 - targetRowHeightTolerance));
|
|
16
|
+
aspectRatioList.forEach((aspectRatio, index) => {
|
|
17
|
+
let currentRowWidth = (lodash_1.default.sum(buffer) * targetRowHeight + aspectRatio * targetRowHeight + itemSpacing * (buffer.length));
|
|
18
|
+
// If the new item's width would cause it to fall between the tolerances for max widths, finish the row
|
|
19
|
+
// If the new item's width would cause it to exceed the tolerances for the row, add it to a new row
|
|
20
|
+
if (buffer.length === 0 || currentRowWidth < maxRowWidth) {
|
|
21
|
+
buffer.push(aspectRatio);
|
|
22
|
+
if (currentRowWidth > minRowWidth) {
|
|
23
|
+
rows.push(buffer);
|
|
24
|
+
buffer = [];
|
|
25
|
+
}
|
|
26
|
+
// If we're handling the last item, and it doesn't exceed the minimum tolerance for the row width, we add a "dummy" item to make the rest of the row now take up the whole width
|
|
27
|
+
else if (index === aspectRatioList.length - 1) {
|
|
28
|
+
buffer.push((width - lodash_1.default.sum(buffer) * targetRowHeight - itemSpacing * (buffer.length - 1)) / targetRowHeight);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
rows.push(buffer);
|
|
33
|
+
buffer = [aspectRatio];
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
if (buffer.length !== 0) {
|
|
37
|
+
rows.push(buffer);
|
|
38
|
+
}
|
|
39
|
+
return rows;
|
|
40
|
+
}
|
|
41
|
+
function JustifiedGrid(props) {
|
|
42
|
+
const { targetRowHeightTolerance = .25, width, targetRowHeight = 320, itemSpacing = 8, rowSpacing = 8, children, containerStyle, aspectRatioList } = props;
|
|
43
|
+
const rows = getJustifiedGridRowConfiguration(width, targetRowHeightTolerance, aspectRatioList, targetRowHeight, itemSpacing);
|
|
44
|
+
let childNodeCounter = -1;
|
|
45
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: {
|
|
46
|
+
display: 'flex',
|
|
47
|
+
flexDirection: 'column',
|
|
48
|
+
gap: rowSpacing
|
|
49
|
+
}, children: rows.map((value) => {
|
|
50
|
+
return (0, jsx_runtime_1.jsx)("div", { className: 'justified-row', style: {
|
|
51
|
+
display: "flex",
|
|
52
|
+
flexDirection: "row",
|
|
53
|
+
gap: itemSpacing,
|
|
54
|
+
}, children: value.map((aspectRatio) => {
|
|
55
|
+
childNodeCounter++;
|
|
56
|
+
return (0, jsx_runtime_1.jsx)("div", { style: {
|
|
57
|
+
flex: value.length === 1 ? 1 : aspectRatio,
|
|
58
|
+
...containerStyle
|
|
59
|
+
}, children: children[childNodeCounter] });
|
|
60
|
+
}) });
|
|
61
|
+
}) }));
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=JustifiedGrid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JustifiedGrid.js","sourceRoot":"","sources":["JustifiedGrid.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import _ from "lodash";
|
|
3
|
-
import './layout.css'
|
|
4
|
-
|
|
5
|
-
export interface JustifiedGridProps {
|
|
6
|
-
aspectRatioList: number[];
|
|
7
|
-
itemSpacing?: number;
|
|
8
|
-
rowSpacing?: number;
|
|
9
|
-
targetRowHeight?: number;
|
|
10
|
-
targetRowHeightTolerance?: number;
|
|
11
|
-
width: number;
|
|
12
|
-
children: React.JSX.Element[];
|
|
13
|
-
containerStyle?: React.CSSProperties;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function getJustifiedGridRowConfiguration(width: number, targetRowHeightTolerance: number, aspectRatioList: number[], targetRowHeight: number, itemSpacing: number): number[][] {
|
|
17
|
-
let buffer: number[] = []
|
|
18
|
-
const rows: number[][] = []
|
|
19
|
-
|
|
20
|
-
// We assume that the row will have the specified height, which allows us to convert aspect ratios into widths
|
|
21
|
-
const minRowWidth = width * (1 / (1 + targetRowHeightTolerance));
|
|
22
|
-
const maxRowWidth = width * (1 / (1 - targetRowHeightTolerance));
|
|
23
|
-
aspectRatioList.forEach((aspectRatio, index) => {
|
|
24
|
-
let currentRowWidth = (_.sum(buffer) * targetRowHeight + aspectRatio * targetRowHeight + itemSpacing * (buffer.length));
|
|
25
|
-
// If the new item's width would cause it to fall between the tolerances for max widths, finish the row
|
|
26
|
-
// If the new item's width would cause it to exceed the tolerances for the row, add it to a new row
|
|
27
|
-
if (buffer.length === 0 || currentRowWidth < maxRowWidth) {
|
|
28
|
-
buffer.push(aspectRatio);
|
|
29
|
-
if (currentRowWidth > minRowWidth) {
|
|
30
|
-
rows.push(buffer)
|
|
31
|
-
buffer = []
|
|
32
|
-
}
|
|
33
|
-
// If we're handling the last item, and it doesn't exceed the minimum tolerance for the row width, we add a "dummy" item to make the rest of the row now take up the whole width
|
|
34
|
-
else if (index === aspectRatioList.length - 1) {
|
|
35
|
-
buffer.push((width - _.sum(buffer) * targetRowHeight - itemSpacing * (buffer.length - 1)) / targetRowHeight);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
rows.push(buffer)
|
|
40
|
-
buffer = [aspectRatio];
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
if (buffer.length !== 0) {
|
|
45
|
-
rows.push(buffer)
|
|
46
|
-
}
|
|
47
|
-
return rows;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function JustifiedGrid(props: Readonly<JustifiedGridProps>) {
|
|
51
|
-
const {targetRowHeightTolerance = .25, width, targetRowHeight = 320, itemSpacing = 8, rowSpacing = 8, children, containerStyle, aspectRatioList} = props;
|
|
52
|
-
const rows = getJustifiedGridRowConfiguration(width, targetRowHeightTolerance, aspectRatioList, targetRowHeight, itemSpacing);
|
|
53
|
-
|
|
54
|
-
let childNodeCounter = -1;
|
|
55
|
-
return (
|
|
56
|
-
<div style={{
|
|
57
|
-
display: 'flex',
|
|
58
|
-
flexDirection: 'column',
|
|
59
|
-
gap: rowSpacing
|
|
60
|
-
}}>
|
|
61
|
-
{rows.map((value) => {
|
|
62
|
-
return <div className={'justified-row'} style={{
|
|
63
|
-
display: "flex",
|
|
64
|
-
flexDirection: "row",
|
|
65
|
-
gap: itemSpacing,
|
|
66
|
-
}}>
|
|
67
|
-
{value.map((aspectRatio) => {
|
|
68
|
-
childNodeCounter++;
|
|
69
|
-
return <div style={{
|
|
70
|
-
flex: value.length === 1 ? 1 : aspectRatio,
|
|
71
|
-
...containerStyle
|
|
72
|
-
}}>
|
|
73
|
-
{children[childNodeCounter]}
|
|
74
|
-
</div>;
|
|
75
|
-
})}
|
|
76
|
-
</div>
|
|
77
|
-
})}
|
|
78
|
-
</div>
|
|
79
|
-
);
|
|
1
|
+
import React from "react";
|
|
2
|
+
import _ from "lodash";
|
|
3
|
+
import './layout.css'
|
|
4
|
+
|
|
5
|
+
export interface JustifiedGridProps {
|
|
6
|
+
aspectRatioList: number[];
|
|
7
|
+
itemSpacing?: number;
|
|
8
|
+
rowSpacing?: number;
|
|
9
|
+
targetRowHeight?: number;
|
|
10
|
+
targetRowHeightTolerance?: number;
|
|
11
|
+
width: number;
|
|
12
|
+
children: React.JSX.Element[];
|
|
13
|
+
containerStyle?: React.CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getJustifiedGridRowConfiguration(width: number, targetRowHeightTolerance: number, aspectRatioList: number[], targetRowHeight: number, itemSpacing: number): number[][] {
|
|
17
|
+
let buffer: number[] = []
|
|
18
|
+
const rows: number[][] = []
|
|
19
|
+
|
|
20
|
+
// We assume that the row will have the specified height, which allows us to convert aspect ratios into widths
|
|
21
|
+
const minRowWidth = width * (1 / (1 + targetRowHeightTolerance));
|
|
22
|
+
const maxRowWidth = width * (1 / (1 - targetRowHeightTolerance));
|
|
23
|
+
aspectRatioList.forEach((aspectRatio, index) => {
|
|
24
|
+
let currentRowWidth = (_.sum(buffer) * targetRowHeight + aspectRatio * targetRowHeight + itemSpacing * (buffer.length));
|
|
25
|
+
// If the new item's width would cause it to fall between the tolerances for max widths, finish the row
|
|
26
|
+
// If the new item's width would cause it to exceed the tolerances for the row, add it to a new row
|
|
27
|
+
if (buffer.length === 0 || currentRowWidth < maxRowWidth) {
|
|
28
|
+
buffer.push(aspectRatio);
|
|
29
|
+
if (currentRowWidth > minRowWidth) {
|
|
30
|
+
rows.push(buffer)
|
|
31
|
+
buffer = []
|
|
32
|
+
}
|
|
33
|
+
// If we're handling the last item, and it doesn't exceed the minimum tolerance for the row width, we add a "dummy" item to make the rest of the row now take up the whole width
|
|
34
|
+
else if (index === aspectRatioList.length - 1) {
|
|
35
|
+
buffer.push((width - _.sum(buffer) * targetRowHeight - itemSpacing * (buffer.length - 1)) / targetRowHeight);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
rows.push(buffer)
|
|
40
|
+
buffer = [aspectRatio];
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
if (buffer.length !== 0) {
|
|
45
|
+
rows.push(buffer)
|
|
46
|
+
}
|
|
47
|
+
return rows;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function JustifiedGrid(props: Readonly<JustifiedGridProps>) {
|
|
51
|
+
const {targetRowHeightTolerance = .25, width, targetRowHeight = 320, itemSpacing = 8, rowSpacing = 8, children, containerStyle, aspectRatioList} = props;
|
|
52
|
+
const rows = getJustifiedGridRowConfiguration(width, targetRowHeightTolerance, aspectRatioList, targetRowHeight, itemSpacing);
|
|
53
|
+
|
|
54
|
+
let childNodeCounter = -1;
|
|
55
|
+
return (
|
|
56
|
+
<div style={{
|
|
57
|
+
display: 'flex',
|
|
58
|
+
flexDirection: 'column',
|
|
59
|
+
gap: rowSpacing
|
|
60
|
+
}}>
|
|
61
|
+
{rows.map((value) => {
|
|
62
|
+
return <div className={'justified-row'} style={{
|
|
63
|
+
display: "flex",
|
|
64
|
+
flexDirection: "row",
|
|
65
|
+
gap: itemSpacing,
|
|
66
|
+
}}>
|
|
67
|
+
{value.map((aspectRatio) => {
|
|
68
|
+
childNodeCounter++;
|
|
69
|
+
return <div style={{
|
|
70
|
+
flex: value.length === 1 ? 1 : aspectRatio,
|
|
71
|
+
...containerStyle
|
|
72
|
+
}}>
|
|
73
|
+
{children[childNodeCounter]}
|
|
74
|
+
</div>;
|
|
75
|
+
})}
|
|
76
|
+
</div>
|
|
77
|
+
})}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
80
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import './layout.css';
|
|
3
|
+
export interface TSJustifiedLayoutProps {
|
|
4
|
+
aspectRatioList: number[];
|
|
5
|
+
itemSpacing?: number;
|
|
6
|
+
rowSpacing?: number;
|
|
7
|
+
targetRowHeight?: number;
|
|
8
|
+
targetRowHeightTolerance?: number;
|
|
9
|
+
width: number;
|
|
10
|
+
children: React.JSX.Element[];
|
|
11
|
+
showWidows?: boolean;
|
|
12
|
+
containerStyle?: React.CSSProperties;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated Use the new {@link JustifiedGrid} component instead as it has better handling for heights of widow rows and future work will be done there
|
|
16
|
+
*/
|
|
17
|
+
declare function TSJustifiedLayout({ children, aspectRatioList, itemSpacing, rowSpacing, showWidows, targetRowHeight, targetRowHeightTolerance, width, containerStyle }: Readonly<TSJustifiedLayoutProps>): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export { TSJustifiedLayout, TSJustifiedLayout as JustifiedGrid };
|
|
19
|
+
//# sourceMappingURL=TSJustifiedLayout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TSJustifiedLayout.d.ts","sourceRoot":"","sources":["TSJustifiedLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,cAAc,CAAA;AAIrB,MAAM,WAAW,sBAAsB;IACnC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAC9B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAKxC;AAED;;GAEG;AACH,iBAAS,iBAAiB,CAAC,EACI,QAAQ,EACR,eAAe,EACf,WAAgB,EAChB,UAAe,EACf,UAAiB,EACjB,eAAqB,EACrB,wBAA8B,EAC9B,KAAK,EACL,cAAc,EACjB,EAAE,QAAQ,CAAC,sBAAsB,CAAC,2CAuG7D;AAED,OAAO,EAAC,iBAAiB,EAAE,iBAAiB,IAAI,aAAa,EAAC,CAAA"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TSJustifiedLayout = TSJustifiedLayout;
|
|
4
|
+
exports.JustifiedGrid = TSJustifiedLayout;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
require("./layout.css");
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated Use the new {@link JustifiedGrid} component instead as it has better handling for heights of widow rows and future work will be done there
|
|
9
|
+
*/
|
|
10
|
+
function TSJustifiedLayout({ children, aspectRatioList, itemSpacing = 10, rowSpacing = 10, showWidows = true, targetRowHeight = 320, targetRowHeightTolerance = .25, width, containerStyle }) {
|
|
11
|
+
const minAspectRatio = width / targetRowHeight * (1 - targetRowHeightTolerance);
|
|
12
|
+
const maxAspectRatio = width / targetRowHeight * (1 + targetRowHeightTolerance);
|
|
13
|
+
const rows = [];
|
|
14
|
+
let rowBuffer = [];
|
|
15
|
+
/**
|
|
16
|
+
* Attempts to add an item to the current row buffer
|
|
17
|
+
* @param value The new aspect ratio to be checked
|
|
18
|
+
* @return If the buffer can accept the new value
|
|
19
|
+
* */
|
|
20
|
+
function addItem(value) {
|
|
21
|
+
const newItems = rowBuffer.concat(value);
|
|
22
|
+
const newAspectRatio = newItems.reduce((previousValue, currentValue) => previousValue + currentValue, 0);
|
|
23
|
+
const rowWidthMinusSpacing = width - (newItems.length - 1) * itemSpacing;
|
|
24
|
+
const targetAspectRatio = rowWidthMinusSpacing / targetRowHeight;
|
|
25
|
+
// Row still has space
|
|
26
|
+
if (newAspectRatio < minAspectRatio) {
|
|
27
|
+
rowBuffer.push(value);
|
|
28
|
+
}
|
|
29
|
+
// Row ran out of space, and the new item is larger than the max aspect ratio for the row
|
|
30
|
+
else if (newAspectRatio > maxAspectRatio) {
|
|
31
|
+
// Always accept if it's just 1 item
|
|
32
|
+
if (rowBuffer.length === 0) {
|
|
33
|
+
rowBuffer.push(value);
|
|
34
|
+
rows.push({ items: rowBuffer, height: rowWidthMinusSpacing / newAspectRatio });
|
|
35
|
+
rowBuffer = [];
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// Calculate width/aspect ratio for row before adding new item
|
|
39
|
+
const previousRowWidthWithoutSpacing = width - (rowBuffer.length - 1) * itemSpacing;
|
|
40
|
+
const previousAspectRatio = rowBuffer.reduce((previousValue, currentValue) => previousValue + currentValue, 0);
|
|
41
|
+
const previousTargetAspectRatio = previousRowWidthWithoutSpacing / targetRowHeight;
|
|
42
|
+
// If the new aspect ratio is farther from the target after the insert, then push row buffer and insert new item into the next row
|
|
43
|
+
if (Math.abs(newAspectRatio - targetAspectRatio) > Math.abs(previousAspectRatio - previousTargetAspectRatio)) {
|
|
44
|
+
rows.push({ items: rowBuffer, height: previousRowWidthWithoutSpacing / previousAspectRatio });
|
|
45
|
+
rowBuffer = [value];
|
|
46
|
+
}
|
|
47
|
+
// If the new aspect ratio is closer to the target aspect ratio, then insert item and push row buffer
|
|
48
|
+
else {
|
|
49
|
+
rowBuffer.push(value);
|
|
50
|
+
rows.push({ items: rowBuffer, height: rowWidthMinusSpacing / newAspectRatio });
|
|
51
|
+
rowBuffer = [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// New aspect ratio is within aspect ratio tolerance, so we finish off the row
|
|
57
|
+
rowBuffer.push(value);
|
|
58
|
+
rows.push({ items: rowBuffer, height: rowWidthMinusSpacing / newAspectRatio });
|
|
59
|
+
rowBuffer = [];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
aspectRatioList.forEach((value) => {
|
|
63
|
+
addItem(value);
|
|
64
|
+
});
|
|
65
|
+
// Handle leftover content
|
|
66
|
+
if (showWidows && rowBuffer.length !== 0) {
|
|
67
|
+
rows.push({ items: rowBuffer, height: rows.length === 0 ? targetRowHeight : rows.at(-1)?.height });
|
|
68
|
+
}
|
|
69
|
+
let childNodeCounter = -1;
|
|
70
|
+
/**
|
|
71
|
+
* Clone the children element, and inject the height of the element as a prop
|
|
72
|
+
*/
|
|
73
|
+
function renderRowItem(aspectRatio, isSoloRow, lastRowWithinTolerance, fakeElementAspectRatio) {
|
|
74
|
+
childNodeCounter++;
|
|
75
|
+
return (0, jsx_runtime_1.jsxs)("div", { style: {
|
|
76
|
+
flex: isSoloRow ? 1 : aspectRatio,
|
|
77
|
+
...containerStyle
|
|
78
|
+
}, children: [children[childNodeCounter], lastRowWithinTolerance && (0, jsx_runtime_1.jsx)("div", { style: { flex: fakeElementAspectRatio } })] });
|
|
79
|
+
}
|
|
80
|
+
// TODO Figure out how to eliminate the tiny gap between div and actual image height
|
|
81
|
+
// TODO Make bottom row respect length restrictions
|
|
82
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: {
|
|
83
|
+
display: 'flex',
|
|
84
|
+
flexDirection: 'column',
|
|
85
|
+
gap: rowSpacing
|
|
86
|
+
}, children: rows.map((value, index, array) => {
|
|
87
|
+
let isLastRow = index === array.length - 1 && showWidows;
|
|
88
|
+
let rowAspectRatioSum = value.items.reduce((previousValue, currentValue) => previousValue + currentValue, 0);
|
|
89
|
+
const isLastRowWithinTolerance = isLastRow && rowAspectRatioSum * value.height + (value.items.length - 1) * itemSpacing < minAspectRatio * value.height;
|
|
90
|
+
const fakeElementAspectRatio = (width - rowAspectRatioSum * value.height - (value.items.length - 1) * itemSpacing) / value.height;
|
|
91
|
+
return (0, jsx_runtime_1.jsx)("div", { className: 'justified-row', style: {
|
|
92
|
+
display: "flex",
|
|
93
|
+
flexDirection: "row",
|
|
94
|
+
gap: itemSpacing,
|
|
95
|
+
}, children: value.items.map((aspectRatio) => renderRowItem(aspectRatio, value.items.length === 1, isLastRowWithinTolerance, fakeElementAspectRatio)) });
|
|
96
|
+
}) }));
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=TSJustifiedLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TSJustifiedLayout.js","sourceRoot":"","sources":["TSJustifiedLayout.tsx"],"names":[],"mappings":";;AA2IQ,8CAAiB;AAAuB,0CAAa;;AA1I7D,wBAAqB;AAoBrB;;GAEG;AACH,SAAS,iBAAiB,CAAC,EACI,QAAQ,EACR,eAAe,EACf,WAAW,GAAG,EAAE,EAChB,UAAU,GAAG,EAAE,EACf,UAAU,GAAG,IAAI,EACjB,eAAe,GAAG,GAAG,EACrB,wBAAwB,GAAG,GAAG,EAC9B,KAAK,EACL,cAAc,EACiB;IAC1D,MAAM,cAAc,GAAG,KAAK,GAAG,eAAe,GAAG,CAAC,CAAC,GAAG,wBAAwB,CAAC,CAAC;IAChF,MAAM,cAAc,GAAG,KAAK,GAAG,eAAe,GAAG,CAAC,CAAC,GAAG,wBAAwB,CAAC,CAAC;IAEhF,MAAM,IAAI,GAAsD,EAAE,CAAC;IACnE,IAAI,SAAS,GAAwB,EAAE,CAAC;IAExC;;;;SAIK;IACL,SAAS,OAAO,CAAC,KAAwB;QACrC,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACxC,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,YAAY,EAAE,EAAE,CAAC,aAAa,GAAG,YAAY,EAAE,CAAC,CAAC,CAAC;QACzG,MAAM,oBAAoB,GAAG,KAAK,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,WAAW,CAAC;QACzE,MAAM,iBAAiB,GAAG,oBAAoB,GAAG,eAAe,CAAC;QACjE,sBAAsB;QACtB,IAAI,cAAc,GAAG,cAAc,EAAE,CAAC;YAClC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;QACD,yFAAyF;aACpF,IAAI,cAAc,GAAG,cAAc,EAAE,CAAC;YACvC,oCAAoC;YACpC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACtB,IAAI,CAAC,IAAI,CAAC,EAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,oBAAoB,GAAG,cAAc,EAAC,CAAC,CAAC;gBAC7E,SAAS,GAAG,EAAE,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACJ,8DAA8D;gBAC9D,MAAM,8BAA8B,GAAG,KAAK,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,WAAW,CAAC;gBACpF,MAAM,mBAAmB,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,YAAY,EAAE,EAAE,CAAC,aAAa,GAAG,YAAY,EAAE,CAAC,CAAC,CAAC;gBAC/G,MAAM,yBAAyB,GAAG,8BAA8B,GAAG,eAAe,CAAC;gBACnF,kIAAkI;gBAClI,IAAI,IAAI,CAAC,GAAG,CAAC,cAAc,GAAG,iBAAiB,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,mBAAmB,GAAG,yBAAyB,CAAC,EAAE,CAAC;oBAC3G,IAAI,CAAC,IAAI,CAAC,EAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,8BAA8B,GAAG,mBAAmB,EAAC,CAAC,CAAA;oBAC3F,SAAS,GAAG,CAAC,KAAK,CAAC,CAAA;gBACvB,CAAC;gBACD,qGAAqG;qBAChG,CAAC;oBACF,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBACtB,IAAI,CAAC,IAAI,CAAC,EAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,oBAAoB,GAAG,cAAc,EAAC,CAAC,CAAA;oBAC5E,SAAS,GAAG,EAAE,CAAA;gBAClB,CAAC;YACL,CAAC;QACL,CAAC;aAAM,CAAC;YACJ,8EAA8E;YAC9E,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,EAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,oBAAoB,GAAG,cAAc,EAAC,CAAC,CAAA;YAC5E,SAAS,GAAG,EAAE,CAAA;QAClB,CAAC;IACL,CAAC;IAGD,eAAe,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QAC9B,OAAO,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC,CAAC,CAAA;IAEF,0BAA0B;IAC1B,IAAI,UAAU,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,EAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,MAAgB,EAAC,CAAC,CAAA;IAC9G,CAAC;IAED,IAAI,gBAAgB,GAAG,CAAC,CAAC,CAAC;IAE1B;;OAEG;IACH,SAAS,aAAa,CAAC,WAA8B,EAAE,SAAkB,EAAE,sBAA2C,EAAE,sBAA8B;QAClJ,gBAAgB,EAAE,CAAC;QACnB,OAAO,iCAAK,KAAK,EAAE;gBACf,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW;gBACjC,GAAG,cAAc;aACpB,aACI,QAAQ,CAAC,gBAAgB,CAAC,EAE1B,sBAAsB,IAAI,gCAAK,KAAK,EAAE,EAAC,IAAI,EAAE,sBAAsB,EAAC,GAAG,IACtE,CAAA;IACV,CAAC;IAED,oFAAoF;IACpF,mDAAmD;IACnD,OAAO,CACH,gCAAK,KAAK,EAAE;YACR,OAAO,EAAE,MAAM;YACf,aAAa,EAAE,QAAQ;YACvB,GAAG,EAAE,UAAU;SAClB,YACI,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YAC9B,IAAI,SAAS,GAAG,KAAK,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC;YACzD,IAAI,iBAAiB,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,YAAY,EAAE,EAAE,CAAC,aAAa,GAAG,YAAY,EAAE,CAAC,CAAC,CAAC;YAC7G,MAAM,wBAAwB,GAAG,SAAS,IAAI,iBAAiB,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,WAAW,GAAG,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC;YACxJ,MAAM,sBAAsB,GAAG,CAAC,KAAK,GAAG,iBAAiB,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,WAAW,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;YAClI,OAAO,gCAAK,SAAS,EAAE,eAAe,EAAE,KAAK,EAAE;oBAC3C,OAAO,EAAE,MAAM;oBACf,aAAa,EAAE,KAAK;oBACpB,GAAG,EAAE,WAAW;iBACnB,YACI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,wBAAwB,EAAE,sBAAsB,CAAC,CAAC,GACvI,CAAA;QACV,CAAC,CAAC,GACA,CACT,CAAC;AACN,CAAC"}
|