react-justified-layout-ts 2.0.8 → 2.1.4
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/README.md +3 -2
- package/dist/components/JustifiedGrid.d.ts +13 -0
- package/dist/components/JustifiedGrid.js +59 -0
- package/dist/components/TSJustifiedLayout.d.ts +6 -4
- package/dist/components/TSJustifiedLayout.js +15 -12
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/stories/ConfiguredJustifiedLayout.d.ts +1 -1
- package/dist/stories/ConfiguredJustifiedLayout.js +3 -15
- package/dist/stories/JustifiedLayout.stories.d.ts +3 -1
- package/dist/stories/JustifiedLayout.stories.js +39 -94
- package/package.json +49 -46
- package/src/components/JustifiedGrid.tsx +80 -0
- package/src/components/TSJustifiedLayout.tsx +16 -14
- package/src/index.ts +2 -1
- package/src/stories/ConfiguredJustifiedLayout.tsx +11 -13
- package/src/stories/JustifiedLayout.stories.tsx +35 -91
- package/.idea/discord.xml +0 -7
- package/.idea/jsLibraryMappings.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/react-justified-layout-ts.iml +0 -13
- package/.idea/vcs.xml +0 -6
package/README.md
CHANGED
|
@@ -4,7 +4,8 @@ A TypeScript component similar to Flickr's justified layout.
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
Add the `TSJustifiedLayout` component to your code with the following props:
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
- `aspectRatioList: number[];` - An array of aspect ratios for the images you are adding to the grid
|
|
8
9
|
- `itemSpacing?: number;` - The amount of spacing between each image, in pixels. (Default: 10)
|
|
9
10
|
- `rowSpacing?: number;` - The amount of spacing between each row, in pixels. (Default: 10)
|
|
10
11
|
- `targetRowHeight?: number;` - The target row height for a row, in pixels. (Default: 320)
|
|
@@ -18,7 +19,7 @@ Add the `TSJustifiedLayout` component to your code with the following props:
|
|
|
18
19
|
|
|
19
20
|
```typescript jsx
|
|
20
21
|
<TSJustifiedLayout width={width}
|
|
21
|
-
|
|
22
|
+
aspectRatioList={imagesOnPage.map(value => ({
|
|
22
23
|
dimensions: value.aspectRatio || 1
|
|
23
24
|
}))}>
|
|
24
25
|
{imagesOnPage.map(value => <img
|
|
@@ -0,0 +1,13 @@
|
|
|
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>): React.JSX.Element;
|
|
@@ -0,0 +1,59 @@
|
|
|
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 react_1 = __importDefault(require("react"));
|
|
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 (react_1.default.createElement("div", { style: {
|
|
46
|
+
display: 'flex',
|
|
47
|
+
flexDirection: 'column',
|
|
48
|
+
gap: rowSpacing
|
|
49
|
+
} }, rows.map((value) => {
|
|
50
|
+
return react_1.default.createElement("div", { className: 'justified-row', style: {
|
|
51
|
+
display: "flex",
|
|
52
|
+
flexDirection: "row",
|
|
53
|
+
gap: itemSpacing,
|
|
54
|
+
} }, value.map((aspectRatio) => {
|
|
55
|
+
childNodeCounter++;
|
|
56
|
+
return react_1.default.createElement("div", { style: Object.assign({ flex: value.length === 1 ? 1 : aspectRatio }, containerStyle) }, children[childNodeCounter]);
|
|
57
|
+
}));
|
|
58
|
+
})));
|
|
59
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import './layout.css';
|
|
3
|
-
type ElementDimensions = number;
|
|
4
3
|
export interface TSJustifiedLayoutProps {
|
|
5
|
-
|
|
4
|
+
aspectRatioList: number[];
|
|
6
5
|
itemSpacing?: number;
|
|
7
6
|
rowSpacing?: number;
|
|
8
7
|
targetRowHeight?: number;
|
|
@@ -12,5 +11,8 @@ export interface TSJustifiedLayoutProps {
|
|
|
12
11
|
showWidows?: boolean;
|
|
13
12
|
containerStyle?: React.CSSProperties;
|
|
14
13
|
}
|
|
15
|
-
|
|
16
|
-
|
|
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>): React.JSX.Element;
|
|
18
|
+
export { TSJustifiedLayout, TSJustifiedLayout as JustifiedGrid };
|
|
@@ -3,10 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.TSJustifiedLayout =
|
|
6
|
+
exports.TSJustifiedLayout = TSJustifiedLayout;
|
|
7
|
+
exports.JustifiedGrid = TSJustifiedLayout;
|
|
7
8
|
const react_1 = __importDefault(require("react"));
|
|
8
9
|
require("./layout.css");
|
|
9
|
-
|
|
10
|
+
/**
|
|
11
|
+
* @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
|
|
12
|
+
*/
|
|
13
|
+
function TSJustifiedLayout({ children, aspectRatioList, itemSpacing = 10, rowSpacing = 10, showWidows = true, targetRowHeight = 320, targetRowHeightTolerance = .25, width, containerStyle }) {
|
|
10
14
|
const minAspectRatio = width / targetRowHeight * (1 - targetRowHeightTolerance);
|
|
11
15
|
const maxAspectRatio = width / targetRowHeight * (1 + targetRowHeightTolerance);
|
|
12
16
|
const rows = [];
|
|
@@ -58,7 +62,7 @@ function TSJustifiedLayout({ children, layoutItems, itemSpacing = 10, rowSpacing
|
|
|
58
62
|
rowBuffer = [];
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
|
-
|
|
65
|
+
aspectRatioList.forEach((value) => {
|
|
62
66
|
addItem(value);
|
|
63
67
|
});
|
|
64
68
|
// Handle leftover content
|
|
@@ -69,9 +73,11 @@ function TSJustifiedLayout({ children, layoutItems, itemSpacing = 10, rowSpacing
|
|
|
69
73
|
/**
|
|
70
74
|
* Clone the children element, and inject the height of the element as a prop
|
|
71
75
|
*/
|
|
72
|
-
function renderRowItem(aspectRatio, isSoloRow) {
|
|
76
|
+
function renderRowItem(aspectRatio, isSoloRow, lastRowWithinTolerance, fakeElementAspectRatio) {
|
|
73
77
|
childNodeCounter++;
|
|
74
|
-
return react_1.default.createElement("div", { style: Object.assign({
|
|
78
|
+
return react_1.default.createElement("div", { style: Object.assign({ flex: isSoloRow ? 1 : aspectRatio }, containerStyle) },
|
|
79
|
+
children[childNodeCounter],
|
|
80
|
+
lastRowWithinTolerance && react_1.default.createElement("div", { style: { flex: fakeElementAspectRatio } }));
|
|
75
81
|
}
|
|
76
82
|
// TODO Figure out how to eliminate the tiny gap between div and actual image height
|
|
77
83
|
// TODO Make bottom row respect length restrictions
|
|
@@ -81,16 +87,13 @@ function TSJustifiedLayout({ children, layoutItems, itemSpacing = 10, rowSpacing
|
|
|
81
87
|
gap: rowSpacing
|
|
82
88
|
} }, rows.map((value, index, array) => {
|
|
83
89
|
let isLastRow = index === array.length - 1 && showWidows;
|
|
84
|
-
let
|
|
85
|
-
const isLastRowWithinTolerance = isLastRow &&
|
|
86
|
-
const fakeElementAspectRatio = (width -
|
|
90
|
+
let rowAspectRatioSum = value.items.reduce((previousValue, currentValue) => previousValue + currentValue, 0);
|
|
91
|
+
const isLastRowWithinTolerance = isLastRow && rowAspectRatioSum * value.height + (value.items.length - 1) * itemSpacing < minAspectRatio * value.height;
|
|
92
|
+
const fakeElementAspectRatio = (width - rowAspectRatioSum * value.height - (value.items.length - 1) * itemSpacing) / value.height;
|
|
87
93
|
return react_1.default.createElement("div", { className: 'justified-row', style: {
|
|
88
94
|
display: "flex",
|
|
89
95
|
flexDirection: "row",
|
|
90
96
|
gap: itemSpacing,
|
|
91
|
-
} },
|
|
92
|
-
value.items.map((aspectRatio) => renderRowItem(aspectRatio, value.items.length === 1)),
|
|
93
|
-
isLastRowWithinTolerance && react_1.default.createElement("div", { style: { aspectRatio: fakeElementAspectRatio, flex: fakeElementAspectRatio } }));
|
|
97
|
+
} }, value.items.map((aspectRatio) => renderRowItem(aspectRatio, value.items.length === 1, isLastRowWithinTolerance, fakeElementAspectRatio)));
|
|
94
98
|
})));
|
|
95
99
|
}
|
|
96
|
-
exports.TSJustifiedLayout = TSJustifiedLayout;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TSJustifiedLayout = void 0;
|
|
3
|
+
exports.JustifiedGrid = exports.TSJustifiedLayout = void 0;
|
|
4
4
|
var TSJustifiedLayout_1 = require("./components/TSJustifiedLayout");
|
|
5
5
|
Object.defineProperty(exports, "TSJustifiedLayout", { enumerable: true, get: function () { return TSJustifiedLayout_1.TSJustifiedLayout; } });
|
|
6
|
+
var JustifiedGrid_1 = require("./components/JustifiedGrid");
|
|
7
|
+
Object.defineProperty(exports, "JustifiedGrid", { enumerable: true, get: function () { return JustifiedGrid_1.JustifiedGrid; } });
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { TSJustifiedLayoutProps } from "../components/TSJustifiedLayout";
|
|
2
2
|
import React from "react";
|
|
3
|
-
export declare const ConfiguredJustifiedLayout: ({
|
|
3
|
+
export declare const ConfiguredJustifiedLayout: ({ aspectRatioList, rowSpacing, width, itemSpacing, targetRowHeight, targetRowHeightTolerance, children }: TSJustifiedLayoutProps) => React.JSX.Element;
|
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
-
var t = {};
|
|
4
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
-
t[p] = s[p];
|
|
6
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
-
t[p[i]] = s[p[i]];
|
|
10
|
-
}
|
|
11
|
-
return t;
|
|
12
|
-
};
|
|
13
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
4
|
};
|
|
16
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
6
|
exports.ConfiguredJustifiedLayout = void 0;
|
|
18
|
-
const TSJustifiedLayout_1 = require("../components/TSJustifiedLayout");
|
|
19
7
|
const react_1 = __importDefault(require("react"));
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
return react_1.default.createElement(
|
|
8
|
+
const JustifiedGrid_1 = require("../components/JustifiedGrid");
|
|
9
|
+
const ConfiguredJustifiedLayout = ({ aspectRatioList, rowSpacing = 8, width = 1000, itemSpacing = 8, targetRowHeight = 320, targetRowHeightTolerance = 0.10, children }) => {
|
|
10
|
+
return react_1.default.createElement(JustifiedGrid_1.JustifiedGrid, { aspectRatioList: aspectRatioList, width: width, itemSpacing: itemSpacing, targetRowHeight: targetRowHeight, targetRowHeightTolerance: targetRowHeightTolerance, rowSpacing: rowSpacing }, children.map((child) => child));
|
|
23
11
|
};
|
|
24
12
|
exports.ConfiguredJustifiedLayout = ConfiguredJustifiedLayout;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { StoryObj } from "@storybook/react";
|
|
2
2
|
import React from "react";
|
|
3
|
+
import 'react-loading-skeleton/dist/skeleton.css';
|
|
3
4
|
declare const meta: {
|
|
4
5
|
title: string;
|
|
5
|
-
component: ({
|
|
6
|
+
component: ({ aspectRatioList, rowSpacing, width, itemSpacing, targetRowHeight, targetRowHeightTolerance, children }: import("../components/TSJustifiedLayout").TSJustifiedLayoutProps) => React.JSX.Element;
|
|
6
7
|
};
|
|
7
8
|
export default meta;
|
|
8
9
|
type Story = StoryObj<typeof meta>;
|
|
9
10
|
export declare const Primary: Story;
|
|
10
11
|
export declare const Secondary: Story;
|
|
12
|
+
export declare const Divs: Story;
|
|
11
13
|
export declare const Single: Story;
|
|
12
14
|
export declare const SingleWithDiv: Story;
|
|
@@ -3,9 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.SingleWithDiv = exports.Single = exports.Secondary = exports.Primary = void 0;
|
|
6
|
+
exports.SingleWithDiv = exports.Single = exports.Divs = exports.Secondary = exports.Primary = void 0;
|
|
7
7
|
const ConfiguredJustifiedLayout_1 = require("./ConfiguredJustifiedLayout");
|
|
8
8
|
const react_1 = __importDefault(require("react"));
|
|
9
|
+
const react_loading_skeleton_1 = __importDefault(require("react-loading-skeleton"));
|
|
10
|
+
require("react-loading-skeleton/dist/skeleton.css");
|
|
9
11
|
const meta = {
|
|
10
12
|
title: 'JustifiedLayout/Basic',
|
|
11
13
|
component: ConfiguredJustifiedLayout_1.ConfiguredJustifiedLayout,
|
|
@@ -44,86 +46,11 @@ const displayedImages = [
|
|
|
44
46
|
],
|
|
45
47
|
"href": "https://x.com/RSN_07/status/1775664237119217719?s=20",
|
|
46
48
|
"published": "2024-04-03",
|
|
47
|
-
"aspectRatio": 0.
|
|
49
|
+
"aspectRatio": 0.6328125,
|
|
48
50
|
"thumbnailUrl": "https://alcorsiteartbucket.s3.amazonaws.com/600h/quick_draw_stance.webp",
|
|
49
51
|
"webp": "https://alcorsiteartbucket.s3.amazonaws.com/webp/quick_draw_stance.webp",
|
|
50
52
|
"src": "https://alcorsiteartbucket.s3.amazonaws.com/quick_draw_stance.PNG"
|
|
51
53
|
},
|
|
52
|
-
{
|
|
53
|
-
"title": "Glimmer on the Shore",
|
|
54
|
-
"artist": "@SiN_remyheart",
|
|
55
|
-
"tags": [
|
|
56
|
-
"Jupiter Form",
|
|
57
|
-
"Featured"
|
|
58
|
-
],
|
|
59
|
-
"href": "https://x.com/SiN_remyheart/status/1750043825161244678?s=20",
|
|
60
|
-
"published": "2024-01-24",
|
|
61
|
-
"aspectRatio": 1.15625,
|
|
62
|
-
"thumbnailUrl": "https://alcorsiteartbucket.s3.amazonaws.com/600h/glimmer_on_the_shore.webp",
|
|
63
|
-
"webp": "https://alcorsiteartbucket.s3.amazonaws.com/webp/glimmer_on_the_shore.png",
|
|
64
|
-
"src": "https://alcorsiteartbucket.s3.amazonaws.com/glimmer_on_the_shore.png"
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
"title": "Castor Evolved",
|
|
68
|
-
"artist": "@TOOMIRO",
|
|
69
|
-
"tags": [
|
|
70
|
-
"Castor",
|
|
71
|
-
"Featured"
|
|
72
|
-
],
|
|
73
|
-
"href": "https://x.com/FaintAlcor/status/1749579348045111636?s=20",
|
|
74
|
-
"published": "2024-01-22",
|
|
75
|
-
"aspectRatio": 1.48,
|
|
76
|
-
"thumbnailUrl": "https://alcorsiteartbucket.s3.amazonaws.com/600h/castor_evolved.webp",
|
|
77
|
-
"webp": "https://alcorsiteartbucket.s3.amazonaws.com/webp/castor_evolved.png",
|
|
78
|
-
"src": "https://alcorsiteartbucket.s3.amazonaws.com/castor_evolved.png"
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"title": "Happy 24th Birthday!",
|
|
82
|
-
"artist": "@DrawingDDoom",
|
|
83
|
-
"tags": [
|
|
84
|
-
"Aldhibah Form",
|
|
85
|
-
"Techwear",
|
|
86
|
-
"Castor",
|
|
87
|
-
"Featured"
|
|
88
|
-
],
|
|
89
|
-
"href": "https://x.com/DrawingDDoom/status/1715601315492245545?s=20",
|
|
90
|
-
"published": "2023-10-21",
|
|
91
|
-
"thumbnailUrl": "https://alcorsiteartbucket.s3.amazonaws.com/600h/happy_24_th_birthday.webp",
|
|
92
|
-
"src": "https://alcorsiteartbucket.s3.amazonaws.com/happy_24_th_birthday.png",
|
|
93
|
-
"aspectRatio": 0.6097560975609756,
|
|
94
|
-
"webp": "https://alcorsiteartbucket.s3.amazonaws.com/webp/happy_24_th_birthday.webp"
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
"src": "https://alcorsiteartbucket.s3.amazonaws.com/combat_ready_duo.png",
|
|
98
|
-
"title": "Combat Ready Duo",
|
|
99
|
-
"artist": "@illusummer",
|
|
100
|
-
"tags": [
|
|
101
|
-
"Rastaban Form",
|
|
102
|
-
"Techwear",
|
|
103
|
-
"Featured"
|
|
104
|
-
],
|
|
105
|
-
"href": "https://skeb.jp/@arcielsummer/works/33",
|
|
106
|
-
"published": "2023-05-05",
|
|
107
|
-
"thumbnailUrl": "https://alcorsiteartbucket.s3.amazonaws.com/600h/combat_ready_duo.webp",
|
|
108
|
-
"aspectRatio": 1.7777777777777777,
|
|
109
|
-
"webp": "https://alcorsiteartbucket.s3.amazonaws.com/webp/combat_ready_duo.webp"
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
"src": "https://pbs.twimg.com/media/FnHp4nWaUAA-lgD?format=jpg&name=large",
|
|
113
|
-
"title": "Special Archery Training!",
|
|
114
|
-
"href": "https://twitter.com/Yamainu_ken/status/1617331411761115138?s=20",
|
|
115
|
-
"tags": [
|
|
116
|
-
"Superhero",
|
|
117
|
-
"Indra Suit",
|
|
118
|
-
"Bow",
|
|
119
|
-
"Bodysuit",
|
|
120
|
-
"Thuban Form",
|
|
121
|
-
"Featured"
|
|
122
|
-
],
|
|
123
|
-
"artist": "@Yamainu_ken",
|
|
124
|
-
"published": "2023-01-22",
|
|
125
|
-
"aspectRatio": 1.5442260442260443
|
|
126
|
-
},
|
|
127
54
|
{
|
|
128
55
|
"title": "Evening Train",
|
|
129
56
|
"artist": "@greyy_arts",
|
|
@@ -185,18 +112,20 @@ const displayedImages = [
|
|
|
185
112
|
"aspectRatio": 0.85107421875
|
|
186
113
|
},
|
|
187
114
|
{
|
|
188
|
-
"
|
|
189
|
-
"
|
|
190
|
-
"
|
|
191
|
-
"artist": "@Mike202112",
|
|
115
|
+
"src": "https://pbs.twimg.com/media/FnHp4nWaUAA-lgD?format=jpg&name=large",
|
|
116
|
+
"title": "Special Archery Training!",
|
|
117
|
+
"href": "https://twitter.com/Yamainu_ken/status/1617331411761115138?s=20",
|
|
192
118
|
"tags": [
|
|
193
|
-
"
|
|
194
|
-
"
|
|
195
|
-
"
|
|
119
|
+
"Superhero",
|
|
120
|
+
"Indra Suit",
|
|
121
|
+
"Bow",
|
|
122
|
+
"Bodysuit",
|
|
123
|
+
"Thuban Form",
|
|
196
124
|
"Featured"
|
|
197
125
|
],
|
|
198
|
-
"
|
|
199
|
-
"
|
|
126
|
+
"artist": "@Yamainu_ken",
|
|
127
|
+
"published": "2023-01-22",
|
|
128
|
+
"aspectRatio": 1.5442260442260443
|
|
200
129
|
}
|
|
201
130
|
];
|
|
202
131
|
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
|
|
@@ -208,11 +137,14 @@ exports.Primary = {
|
|
|
208
137
|
targetRowHeight: undefined,
|
|
209
138
|
rowSpacing: undefined,
|
|
210
139
|
itemSpacing: undefined,
|
|
211
|
-
|
|
140
|
+
aspectRatioList: displayedImages.map(value => value.aspectRatio),
|
|
212
141
|
targetRowHeightTolerance: undefined,
|
|
213
|
-
children: displayedImages.map(value =>
|
|
214
|
-
|
|
215
|
-
react_1.default.createElement(
|
|
142
|
+
children: displayedImages.map(value => {
|
|
143
|
+
var _a;
|
|
144
|
+
return react_1.default.createElement(react_1.default.Fragment, null,
|
|
145
|
+
react_1.default.createElement("div", { style: { top: 16, left: 16, position: "absolute" } }, "Testing"),
|
|
146
|
+
react_1.default.createElement("img", { src: (_a = value.webp) !== null && _a !== void 0 ? _a : value.src }));
|
|
147
|
+
}),
|
|
216
148
|
containerStyle: { position: 'relative' }
|
|
217
149
|
},
|
|
218
150
|
};
|
|
@@ -224,9 +156,22 @@ exports.Secondary = {
|
|
|
224
156
|
targetRowHeight: undefined,
|
|
225
157
|
rowSpacing: undefined,
|
|
226
158
|
itemSpacing: undefined,
|
|
227
|
-
|
|
159
|
+
aspectRatioList: displayedImages.slice(1, 5).map(value => value.aspectRatio),
|
|
160
|
+
targetRowHeightTolerance: undefined,
|
|
161
|
+
children: displayedImages.slice(1, 5).map(value => { var _a; return react_1.default.createElement("img", { src: (_a = value.webp) !== null && _a !== void 0 ? _a : value.src }); })
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
exports.Divs = {
|
|
165
|
+
name: "Divs Only",
|
|
166
|
+
args: {
|
|
167
|
+
width: 847,
|
|
168
|
+
showWidows: true,
|
|
169
|
+
targetRowHeight: undefined,
|
|
170
|
+
rowSpacing: undefined,
|
|
171
|
+
itemSpacing: undefined,
|
|
172
|
+
aspectRatioList: displayedImages.slice(1, 5).map(value => value.aspectRatio),
|
|
228
173
|
targetRowHeightTolerance: undefined,
|
|
229
|
-
children: displayedImages.slice(1, 5).map(value => react_1.default.createElement(
|
|
174
|
+
children: displayedImages.slice(1, 5).map(value => react_1.default.createElement(react_loading_skeleton_1.default, { style: { aspectRatio: value.aspectRatio } }))
|
|
230
175
|
},
|
|
231
176
|
};
|
|
232
177
|
exports.Single = {
|
|
@@ -239,7 +184,7 @@ exports.Single = {
|
|
|
239
184
|
rowSpacing: undefined,
|
|
240
185
|
itemSpacing: undefined,
|
|
241
186
|
targetRowHeightTolerance: undefined,
|
|
242
|
-
|
|
187
|
+
aspectRatioList: [1],
|
|
243
188
|
}
|
|
244
189
|
};
|
|
245
190
|
exports.SingleWithDiv = {
|
|
@@ -253,6 +198,6 @@ exports.SingleWithDiv = {
|
|
|
253
198
|
rowSpacing: undefined,
|
|
254
199
|
itemSpacing: undefined,
|
|
255
200
|
targetRowHeightTolerance: undefined,
|
|
256
|
-
|
|
201
|
+
aspectRatioList: [1],
|
|
257
202
|
}
|
|
258
203
|
};
|
package/package.json
CHANGED
|
@@ -1,46 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "react-justified-layout-ts",
|
|
3
|
-
"version": "2.
|
|
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
|
-
"
|
|
19
|
-
"react-use
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"@storybook
|
|
24
|
-
"@storybook/addon-
|
|
25
|
-
"@storybook/addon-
|
|
26
|
-
"@storybook/addon-
|
|
27
|
-
"@storybook/addon-
|
|
28
|
-
"@storybook/
|
|
29
|
-
"@storybook/
|
|
30
|
-
"@storybook/react
|
|
31
|
-
"@storybook/
|
|
32
|
-
"@
|
|
33
|
-
"
|
|
34
|
-
"react": "^
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
},
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "react-justified-layout-ts",
|
|
3
|
+
"version": "2.1.4",
|
|
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": "^24.0.3",
|
|
18
|
+
"lodash": "^4.17.21",
|
|
19
|
+
"react-use": "^17.6.0",
|
|
20
|
+
"react-use-measure": "^2.1.7"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@chromatic-com/storybook": "^3.2.7",
|
|
24
|
+
"@storybook/addon-essentials": "^8.6.14",
|
|
25
|
+
"@storybook/addon-interactions": "^8.6.14",
|
|
26
|
+
"@storybook/addon-links": "^8.6.14",
|
|
27
|
+
"@storybook/addon-onboarding": "^8.6.14",
|
|
28
|
+
"@storybook/addon-webpack5-compiler-swc": "^3.0.0",
|
|
29
|
+
"@storybook/blocks": "^8.6.14",
|
|
30
|
+
"@storybook/react": "^8.6.14",
|
|
31
|
+
"@storybook/react-webpack5": "^8.6.14",
|
|
32
|
+
"@storybook/test": "^8.6.14",
|
|
33
|
+
"@types/lodash": "^4.17.18",
|
|
34
|
+
"@types/react": "^19.1.8",
|
|
35
|
+
"copyfiles": "^2.4.1",
|
|
36
|
+
"react": "^19.1.0",
|
|
37
|
+
"react-loading-skeleton": "^3.5.0",
|
|
38
|
+
"rimraf": "^6.0.1",
|
|
39
|
+
"storybook": "^8.6.14",
|
|
40
|
+
"typescript": "^5.8.3"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"react": "^18.2.0"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/Alan19/react-justified-layout-ts.git"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +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
|
+
);
|
|
80
|
+
}
|
|
@@ -4,7 +4,7 @@ import './layout.css'
|
|
|
4
4
|
type ElementDimensions = number;
|
|
5
5
|
|
|
6
6
|
export interface TSJustifiedLayoutProps {
|
|
7
|
-
|
|
7
|
+
aspectRatioList: number[];
|
|
8
8
|
itemSpacing?: number;
|
|
9
9
|
rowSpacing?: number;
|
|
10
10
|
targetRowHeight?: number;
|
|
@@ -19,9 +19,12 @@ export interface TSJustifiedLayoutProps {
|
|
|
19
19
|
// widowLayoutStyle: "left" | "justify" | "center"
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* @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
|
|
24
|
+
*/
|
|
22
25
|
function TSJustifiedLayout({
|
|
23
26
|
children,
|
|
24
|
-
|
|
27
|
+
aspectRatioList,
|
|
25
28
|
itemSpacing = 10,
|
|
26
29
|
rowSpacing = 10,
|
|
27
30
|
showWidows = true,
|
|
@@ -29,7 +32,7 @@ function TSJustifiedLayout({
|
|
|
29
32
|
targetRowHeightTolerance = .25,
|
|
30
33
|
width,
|
|
31
34
|
containerStyle
|
|
32
|
-
}: TSJustifiedLayoutProps) {
|
|
35
|
+
}: Readonly<TSJustifiedLayoutProps>) {
|
|
33
36
|
const minAspectRatio = width / targetRowHeight * (1 - targetRowHeightTolerance);
|
|
34
37
|
const maxAspectRatio = width / targetRowHeight * (1 + targetRowHeightTolerance);
|
|
35
38
|
|
|
@@ -83,7 +86,7 @@ function TSJustifiedLayout({
|
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
|
|
86
|
-
|
|
89
|
+
aspectRatioList.forEach((value) => {
|
|
87
90
|
addItem(value);
|
|
88
91
|
})
|
|
89
92
|
|
|
@@ -97,14 +100,15 @@ function TSJustifiedLayout({
|
|
|
97
100
|
/**
|
|
98
101
|
* Clone the children element, and inject the height of the element as a prop
|
|
99
102
|
*/
|
|
100
|
-
function renderRowItem(aspectRatio: ElementDimensions, isSoloRow: boolean) {
|
|
103
|
+
function renderRowItem(aspectRatio: ElementDimensions, isSoloRow: boolean, lastRowWithinTolerance: undefined | boolean, fakeElementAspectRatio: number) {
|
|
101
104
|
childNodeCounter++;
|
|
102
105
|
return <div style={{
|
|
103
|
-
aspectRatio: aspectRatio,
|
|
104
106
|
flex: isSoloRow ? 1 : aspectRatio,
|
|
105
107
|
...containerStyle
|
|
106
108
|
}}>
|
|
107
109
|
{children[childNodeCounter]}
|
|
110
|
+
{/*Extra element for widowed rows to stop them from getting scaled up*/}
|
|
111
|
+
{lastRowWithinTolerance && <div style={{flex: fakeElementAspectRatio}}/>}
|
|
108
112
|
</div>
|
|
109
113
|
}
|
|
110
114
|
|
|
@@ -118,21 +122,19 @@ function TSJustifiedLayout({
|
|
|
118
122
|
}}>
|
|
119
123
|
{rows.map((value, index, array) => {
|
|
120
124
|
let isLastRow = index === array.length - 1 && showWidows;
|
|
121
|
-
let
|
|
122
|
-
const isLastRowWithinTolerance = isLastRow &&
|
|
123
|
-
const fakeElementAspectRatio = (width -
|
|
125
|
+
let rowAspectRatioSum = value.items.reduce((previousValue, currentValue) => previousValue + currentValue, 0);
|
|
126
|
+
const isLastRowWithinTolerance = isLastRow && rowAspectRatioSum * value.height + (value.items.length - 1) * itemSpacing < minAspectRatio * value.height;
|
|
127
|
+
const fakeElementAspectRatio = (width - rowAspectRatioSum * value.height - (value.items.length - 1) * itemSpacing) / value.height;
|
|
124
128
|
return <div className={'justified-row'} style={{
|
|
125
129
|
display: "flex",
|
|
126
130
|
flexDirection: "row",
|
|
127
131
|
gap: itemSpacing,
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
{value.items.map((aspectRatio) => renderRowItem(aspectRatio, value.items.length === 1))}
|
|
131
|
-
{isLastRowWithinTolerance && <div style={{aspectRatio: fakeElementAspectRatio, flex: fakeElementAspectRatio}}></div>}
|
|
132
|
+
}}>
|
|
133
|
+
{value.items.map((aspectRatio) => renderRowItem(aspectRatio, value.items.length === 1, isLastRowWithinTolerance, fakeElementAspectRatio))}
|
|
132
134
|
</div>
|
|
133
135
|
})}
|
|
134
136
|
</div>
|
|
135
137
|
);
|
|
136
138
|
}
|
|
137
139
|
|
|
138
|
-
export {TSJustifiedLayout}
|
|
140
|
+
export {TSJustifiedLayout, TSJustifiedLayout as JustifiedGrid}
|
package/src/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export {TSJustifiedLayout} from "./components/TSJustifiedLayout"
|
|
1
|
+
export {TSJustifiedLayout} from "./components/TSJustifiedLayout"
|
|
2
|
+
export {JustifiedGridProps, JustifiedGrid} from "./components/JustifiedGrid"
|
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {TSJustifiedLayoutProps} from "../components/TSJustifiedLayout";
|
|
2
2
|
import React from "react";
|
|
3
|
+
import {JustifiedGrid} from "../components/JustifiedGrid";
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
export const ConfiguredJustifiedLayout = ({
|
|
6
|
-
|
|
7
|
+
aspectRatioList,
|
|
7
8
|
rowSpacing = 8,
|
|
8
9
|
width = 1000,
|
|
9
10
|
itemSpacing = 8,
|
|
10
11
|
targetRowHeight = 320,
|
|
11
12
|
targetRowHeightTolerance = 0.10,
|
|
12
|
-
|
|
13
|
-
children,
|
|
14
|
-
...props
|
|
13
|
+
children
|
|
15
14
|
}: TSJustifiedLayoutProps) => {
|
|
16
|
-
return <
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
showWidows={showWidows} {...props}>
|
|
15
|
+
return <JustifiedGrid aspectRatioList={aspectRatioList}
|
|
16
|
+
width={width}
|
|
17
|
+
itemSpacing={itemSpacing}
|
|
18
|
+
targetRowHeight={targetRowHeight}
|
|
19
|
+
targetRowHeightTolerance={targetRowHeightTolerance}
|
|
20
|
+
rowSpacing={rowSpacing}>
|
|
23
21
|
{children.map((child) => child)}
|
|
24
|
-
</
|
|
22
|
+
</JustifiedGrid>;
|
|
25
23
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type {Meta, StoryObj} from "@storybook/react";
|
|
2
2
|
import {ConfiguredJustifiedLayout} from "./ConfiguredJustifiedLayout";
|
|
3
3
|
import React from "react";
|
|
4
|
+
import Skeleton from "react-loading-skeleton";
|
|
5
|
+
import 'react-loading-skeleton/dist/skeleton.css'
|
|
4
6
|
|
|
5
7
|
const meta = {
|
|
6
8
|
title: 'JustifiedLayout/Basic',
|
|
@@ -43,86 +45,11 @@ const displayedImages = [
|
|
|
43
45
|
],
|
|
44
46
|
"href": "https://x.com/RSN_07/status/1775664237119217719?s=20",
|
|
45
47
|
"published": "2024-04-03",
|
|
46
|
-
"aspectRatio": 0.
|
|
48
|
+
"aspectRatio": 0.6328125,
|
|
47
49
|
"thumbnailUrl": "https://alcorsiteartbucket.s3.amazonaws.com/600h/quick_draw_stance.webp",
|
|
48
50
|
"webp": "https://alcorsiteartbucket.s3.amazonaws.com/webp/quick_draw_stance.webp",
|
|
49
51
|
"src": "https://alcorsiteartbucket.s3.amazonaws.com/quick_draw_stance.PNG"
|
|
50
52
|
},
|
|
51
|
-
{
|
|
52
|
-
"title": "Glimmer on the Shore",
|
|
53
|
-
"artist": "@SiN_remyheart",
|
|
54
|
-
"tags": [
|
|
55
|
-
"Jupiter Form",
|
|
56
|
-
"Featured"
|
|
57
|
-
],
|
|
58
|
-
"href": "https://x.com/SiN_remyheart/status/1750043825161244678?s=20",
|
|
59
|
-
"published": "2024-01-24",
|
|
60
|
-
"aspectRatio": 1.15625,
|
|
61
|
-
"thumbnailUrl": "https://alcorsiteartbucket.s3.amazonaws.com/600h/glimmer_on_the_shore.webp",
|
|
62
|
-
"webp": "https://alcorsiteartbucket.s3.amazonaws.com/webp/glimmer_on_the_shore.png",
|
|
63
|
-
"src": "https://alcorsiteartbucket.s3.amazonaws.com/glimmer_on_the_shore.png"
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"title": "Castor Evolved",
|
|
67
|
-
"artist": "@TOOMIRO",
|
|
68
|
-
"tags": [
|
|
69
|
-
"Castor",
|
|
70
|
-
"Featured"
|
|
71
|
-
],
|
|
72
|
-
"href": "https://x.com/FaintAlcor/status/1749579348045111636?s=20",
|
|
73
|
-
"published": "2024-01-22",
|
|
74
|
-
"aspectRatio": 1.48,
|
|
75
|
-
"thumbnailUrl": "https://alcorsiteartbucket.s3.amazonaws.com/600h/castor_evolved.webp",
|
|
76
|
-
"webp": "https://alcorsiteartbucket.s3.amazonaws.com/webp/castor_evolved.png",
|
|
77
|
-
"src": "https://alcorsiteartbucket.s3.amazonaws.com/castor_evolved.png"
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"title": "Happy 24th Birthday!",
|
|
81
|
-
"artist": "@DrawingDDoom",
|
|
82
|
-
"tags": [
|
|
83
|
-
"Aldhibah Form",
|
|
84
|
-
"Techwear",
|
|
85
|
-
"Castor",
|
|
86
|
-
"Featured"
|
|
87
|
-
],
|
|
88
|
-
"href": "https://x.com/DrawingDDoom/status/1715601315492245545?s=20",
|
|
89
|
-
"published": "2023-10-21",
|
|
90
|
-
"thumbnailUrl": "https://alcorsiteartbucket.s3.amazonaws.com/600h/happy_24_th_birthday.webp",
|
|
91
|
-
"src": "https://alcorsiteartbucket.s3.amazonaws.com/happy_24_th_birthday.png",
|
|
92
|
-
"aspectRatio": 0.6097560975609756,
|
|
93
|
-
"webp": "https://alcorsiteartbucket.s3.amazonaws.com/webp/happy_24_th_birthday.webp"
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
"src": "https://alcorsiteartbucket.s3.amazonaws.com/combat_ready_duo.png",
|
|
97
|
-
"title": "Combat Ready Duo",
|
|
98
|
-
"artist": "@illusummer",
|
|
99
|
-
"tags": [
|
|
100
|
-
"Rastaban Form",
|
|
101
|
-
"Techwear",
|
|
102
|
-
"Featured"
|
|
103
|
-
],
|
|
104
|
-
"href": "https://skeb.jp/@arcielsummer/works/33",
|
|
105
|
-
"published": "2023-05-05",
|
|
106
|
-
"thumbnailUrl": "https://alcorsiteartbucket.s3.amazonaws.com/600h/combat_ready_duo.webp",
|
|
107
|
-
"aspectRatio": 1.7777777777777777,
|
|
108
|
-
"webp": "https://alcorsiteartbucket.s3.amazonaws.com/webp/combat_ready_duo.webp"
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
"src": "https://pbs.twimg.com/media/FnHp4nWaUAA-lgD?format=jpg&name=large",
|
|
112
|
-
"title": "Special Archery Training!",
|
|
113
|
-
"href": "https://twitter.com/Yamainu_ken/status/1617331411761115138?s=20",
|
|
114
|
-
"tags": [
|
|
115
|
-
"Superhero",
|
|
116
|
-
"Indra Suit",
|
|
117
|
-
"Bow",
|
|
118
|
-
"Bodysuit",
|
|
119
|
-
"Thuban Form",
|
|
120
|
-
"Featured"
|
|
121
|
-
],
|
|
122
|
-
"artist": "@Yamainu_ken",
|
|
123
|
-
"published": "2023-01-22",
|
|
124
|
-
"aspectRatio": 1.5442260442260443
|
|
125
|
-
},
|
|
126
53
|
{
|
|
127
54
|
"title": "Evening Train",
|
|
128
55
|
"artist": "@greyy_arts",
|
|
@@ -184,18 +111,20 @@ const displayedImages = [
|
|
|
184
111
|
"aspectRatio": 0.85107421875
|
|
185
112
|
},
|
|
186
113
|
{
|
|
187
|
-
"
|
|
188
|
-
"
|
|
189
|
-
"
|
|
190
|
-
"artist": "@Mike202112",
|
|
114
|
+
"src": "https://pbs.twimg.com/media/FnHp4nWaUAA-lgD?format=jpg&name=large",
|
|
115
|
+
"title": "Special Archery Training!",
|
|
116
|
+
"href": "https://twitter.com/Yamainu_ken/status/1617331411761115138?s=20",
|
|
191
117
|
"tags": [
|
|
192
|
-
"
|
|
193
|
-
"
|
|
194
|
-
"
|
|
118
|
+
"Superhero",
|
|
119
|
+
"Indra Suit",
|
|
120
|
+
"Bow",
|
|
121
|
+
"Bodysuit",
|
|
122
|
+
"Thuban Form",
|
|
195
123
|
"Featured"
|
|
196
124
|
],
|
|
197
|
-
"
|
|
198
|
-
"
|
|
125
|
+
"artist": "@Yamainu_ken",
|
|
126
|
+
"published": "2023-01-22",
|
|
127
|
+
"aspectRatio": 1.5442260442260443
|
|
199
128
|
}
|
|
200
129
|
]
|
|
201
130
|
|
|
@@ -208,17 +137,18 @@ export const Primary: Story = {
|
|
|
208
137
|
targetRowHeight: undefined,
|
|
209
138
|
rowSpacing: undefined,
|
|
210
139
|
itemSpacing: undefined,
|
|
211
|
-
|
|
140
|
+
aspectRatioList: displayedImages.map(value => value.aspectRatio),
|
|
212
141
|
targetRowHeightTolerance: undefined,
|
|
213
142
|
children: displayedImages.map(value => <>
|
|
214
143
|
<div style={{top: 16, left: 16, position: "absolute"}}>Testing</div>
|
|
215
|
-
<img src={value.webp
|
|
144
|
+
<img src={value.webp ?? value.src}/>
|
|
216
145
|
</>
|
|
217
146
|
),
|
|
218
147
|
containerStyle: {position: 'relative'}
|
|
219
148
|
},
|
|
220
149
|
};
|
|
221
150
|
|
|
151
|
+
|
|
222
152
|
export const Secondary: Story = {
|
|
223
153
|
name: "Image Tag Elements",
|
|
224
154
|
args: {
|
|
@@ -227,9 +157,23 @@ export const Secondary: Story = {
|
|
|
227
157
|
targetRowHeight: undefined,
|
|
228
158
|
rowSpacing: undefined,
|
|
229
159
|
itemSpacing: undefined,
|
|
230
|
-
|
|
160
|
+
aspectRatioList: displayedImages.slice(1, 5).map(value => value.aspectRatio),
|
|
161
|
+
targetRowHeightTolerance: undefined,
|
|
162
|
+
children: displayedImages.slice(1, 5).map(value => <img src={value.webp ?? value.src}/>)
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export const Divs: Story = {
|
|
167
|
+
name: "Divs Only",
|
|
168
|
+
args: {
|
|
169
|
+
width: 847,
|
|
170
|
+
showWidows: true,
|
|
171
|
+
targetRowHeight: undefined,
|
|
172
|
+
rowSpacing: undefined,
|
|
173
|
+
itemSpacing: undefined,
|
|
174
|
+
aspectRatioList: displayedImages.slice(1, 5).map(value => value.aspectRatio),
|
|
231
175
|
targetRowHeightTolerance: undefined,
|
|
232
|
-
children: displayedImages.slice(1, 5).map(value => <
|
|
176
|
+
children: displayedImages.slice(1, 5).map(value => <Skeleton style={{aspectRatio: value.aspectRatio}}/>)
|
|
233
177
|
},
|
|
234
178
|
}
|
|
235
179
|
|
|
@@ -243,7 +187,7 @@ export const Single: Story = {
|
|
|
243
187
|
rowSpacing: undefined,
|
|
244
188
|
itemSpacing: undefined,
|
|
245
189
|
targetRowHeightTolerance: undefined,
|
|
246
|
-
|
|
190
|
+
aspectRatioList: [1],
|
|
247
191
|
}
|
|
248
192
|
}
|
|
249
193
|
|
|
@@ -257,6 +201,6 @@ export const SingleWithDiv: Story = {
|
|
|
257
201
|
rowSpacing: undefined,
|
|
258
202
|
itemSpacing: undefined,
|
|
259
203
|
targetRowHeightTolerance: undefined,
|
|
260
|
-
|
|
204
|
+
aspectRatioList: [1],
|
|
261
205
|
}
|
|
262
206
|
}
|
package/.idea/discord.xml
DELETED
package/.idea/modules.xml
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project version="4">
|
|
3
|
-
<component name="ProjectModuleManager">
|
|
4
|
-
<modules>
|
|
5
|
-
<module fileurl="file://$PROJECT_DIR$/.idea/react-justified-layout-ts.iml" filepath="$PROJECT_DIR$/.idea/react-justified-layout-ts.iml" />
|
|
6
|
-
</modules>
|
|
7
|
-
</component>
|
|
8
|
-
</project>
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<module type="WEB_MODULE" version="4">
|
|
3
|
-
<component name="NewModuleRootManager">
|
|
4
|
-
<content url="file://$MODULE_DIR$">
|
|
5
|
-
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
|
6
|
-
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
7
|
-
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
8
|
-
<excludeFolder url="file://$MODULE_DIR$/dist" />
|
|
9
|
-
</content>
|
|
10
|
-
<orderEntry type="inheritedJdk" />
|
|
11
|
-
<orderEntry type="sourceFolder" forTests="false" />
|
|
12
|
-
</component>
|
|
13
|
-
</module>
|