@vitus-labs/coolgrid 2.0.0-alpha.9 → 2.0.0-beta.0
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 +7 -3
- package/lib/index.js +25 -21
- package/lib/vitus-labs-coolgrid.native.js +51 -39
- package/package.json +21 -11
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @vitus-labs/coolgrid
|
|
2
2
|
|
|
3
|
-
Responsive grid system for React
|
|
3
|
+
Responsive grid system for React.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@vitus-labs/coolgrid)
|
|
6
6
|
[](https://github.com/vitus-labs/ui-system/blob/main/LICENSE)
|
|
@@ -20,7 +20,7 @@ Bootstrap-inspired Container / Row / Col grid with context-cascading configurati
|
|
|
20
20
|
## Installation
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
npm install @vitus-labs/coolgrid @vitus-labs/core @vitus-labs/unistyle
|
|
23
|
+
npm install @vitus-labs/coolgrid @vitus-labs/core @vitus-labs/unistyle
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
## Quick Start
|
|
@@ -213,6 +213,10 @@ All numeric props support three formats:
|
|
|
213
213
|
<Col size={{ xs: 12, md: 6, lg: 4 }} />
|
|
214
214
|
```
|
|
215
215
|
|
|
216
|
+
## React Native
|
|
217
|
+
|
|
218
|
+
Coolgrid includes native components (`Col.native.tsx`, `Row.native.tsx`) that use `onLayout` measurement instead of CSS `calc()`. Works automatically when initialized with `@vitus-labs/connector-native`.
|
|
219
|
+
|
|
216
220
|
## Peer Dependencies
|
|
217
221
|
|
|
218
222
|
| Package | Version |
|
|
@@ -220,7 +224,7 @@ All numeric props support three formats:
|
|
|
220
224
|
| react | >= 19 |
|
|
221
225
|
| @vitus-labs/core | * |
|
|
222
226
|
| @vitus-labs/unistyle | * |
|
|
223
|
-
|
|
|
227
|
+
| react-native | >= 0.76 (optional) |
|
|
224
228
|
|
|
225
229
|
## License
|
|
226
230
|
|
package/lib/index.js
CHANGED
|
@@ -84,12 +84,14 @@ const widthStyles = ({ size, columns, gap, RNparentWidth }, { rootSize }) => {
|
|
|
84
84
|
const c = columns;
|
|
85
85
|
const g = gap;
|
|
86
86
|
const width = s / c * 100;
|
|
87
|
-
const
|
|
87
|
+
const v = value(hasValue(gap) ? `calc(${width}% - ${g}px)` : `${width}%`, rootSize);
|
|
88
88
|
return css$2`
|
|
89
89
|
flex-grow: 0;
|
|
90
90
|
flex-shrink: 0;
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
${css$2`
|
|
92
|
+
max-width: ${v};
|
|
93
|
+
flex-basis: ${v};
|
|
94
|
+
`}
|
|
93
95
|
`;
|
|
94
96
|
};
|
|
95
97
|
/** Applies half of the given value as either margin or padding (used for gap and padding distribution). */
|
|
@@ -107,7 +109,7 @@ const spacingStyles$1 = (type, param, rootSize) => {
|
|
|
107
109
|
const styles$2 = ({ theme, css, rootSize }) => {
|
|
108
110
|
const { size, columns, gap, padding, extraStyles, RNparentWidth } = theme;
|
|
109
111
|
if (isVisible(size)) return css`
|
|
110
|
-
left: initial;
|
|
112
|
+
${"left: initial;"}
|
|
111
113
|
position: relative;
|
|
112
114
|
${widthStyles({
|
|
113
115
|
size,
|
|
@@ -121,7 +123,7 @@ const styles$2 = ({ theme, css, rootSize }) => {
|
|
|
121
123
|
`;
|
|
122
124
|
return css`
|
|
123
125
|
left: -9999px;
|
|
124
|
-
position: fixed;
|
|
126
|
+
position: ${"fixed"};
|
|
125
127
|
margin: 0;
|
|
126
128
|
padding: 0;
|
|
127
129
|
`;
|
|
@@ -129,14 +131,14 @@ const styles$2 = ({ theme, css, rootSize }) => {
|
|
|
129
131
|
var styled_default$2 = styled$2(component$2)`
|
|
130
132
|
${css$2`
|
|
131
133
|
box-sizing: border-box;
|
|
132
|
-
|
|
134
|
+
justify-content: stretch;
|
|
135
|
+
`}
|
|
133
136
|
|
|
134
137
|
position: relative;
|
|
135
138
|
display: flex;
|
|
136
139
|
flex-basis: 0;
|
|
137
140
|
flex-grow: 1;
|
|
138
141
|
flex-direction: column;
|
|
139
|
-
justify-content: stretch;
|
|
140
142
|
|
|
141
143
|
${makeItResponsive({
|
|
142
144
|
key: "$coolgrid",
|
|
@@ -194,21 +196,23 @@ var Col_default = Component$2;
|
|
|
194
196
|
//#region src/Container/styled.ts
|
|
195
197
|
const { styled: styled$1, css: css$1, component: component$1 } = config;
|
|
196
198
|
/** Responsive styles that apply the container's max-width and any extra CSS at each breakpoint. */
|
|
197
|
-
const styles$1 = ({ theme: t, css, rootSize }) =>
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
const styles$1 = ({ theme: t, css, rootSize }) => {
|
|
200
|
+
const w = t.width != null && typeof t.width !== "object" ? t.width : null;
|
|
201
|
+
return css`
|
|
202
|
+
${w != null ? `max-width: ${value(w, rootSize)};` : ""};
|
|
203
|
+
${extendCss(t.extraStyles)};
|
|
204
|
+
`;
|
|
205
|
+
};
|
|
201
206
|
/** Styled Container element. Centered via auto margins with responsive max-width. */
|
|
202
207
|
var styled_default$1 = styled$1(component$1)`
|
|
203
|
-
${css$1`
|
|
204
|
-
box-sizing: border-box;
|
|
205
|
-
`};
|
|
206
|
-
|
|
207
208
|
display: flex;
|
|
208
|
-
width: 100%;
|
|
209
209
|
flex-direction: column;
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
${css$1`
|
|
211
|
+
box-sizing: border-box;
|
|
212
|
+
width: 100%;
|
|
213
|
+
margin-right: auto;
|
|
214
|
+
margin-left: auto;
|
|
215
|
+
`}
|
|
212
216
|
|
|
213
217
|
${makeItResponsive({
|
|
214
218
|
key: "$coolgrid",
|
|
@@ -228,7 +232,7 @@ var styled_default$1 = styled$1(component$1)`
|
|
|
228
232
|
*/
|
|
229
233
|
const DEV_PROPS$1 = process.env.NODE_ENV !== "production" ? { "data-coolgrid": "container" } : {};
|
|
230
234
|
const Component$1 = ({ children, component, css, width, ...props }) => {
|
|
231
|
-
const { containerWidth
|
|
235
|
+
const { containerWidth, columns, size, gap, padding, gutter, colCss, colComponent, rowCss, rowComponent, contentAlignX } = useGridContext(props);
|
|
232
236
|
const context = useMemo(() => ({
|
|
233
237
|
columns,
|
|
234
238
|
size,
|
|
@@ -289,8 +293,8 @@ const spacingStyles = ({ gap, gutter }, { rootSize }) => {
|
|
|
289
293
|
const getValue = (param) => value(param, rootSize);
|
|
290
294
|
const spacingX = g / 2 * -1;
|
|
291
295
|
return css`
|
|
292
|
-
|
|
293
|
-
|
|
296
|
+
margin: ${getValue(isNumber(gutter) ? gutter - g / 2 : g / 2)} ${getValue(spacingX)};
|
|
297
|
+
`;
|
|
294
298
|
};
|
|
295
299
|
/** Maps the contentAlignX prop to a CSS justify-content value. */
|
|
296
300
|
const contentAlign = (align) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ALIGN_CONTENT_MAP_X, Provider, context, extendCss, makeItResponsive, value } from "@vitus-labs/unistyle";
|
|
2
|
-
import { createContext, useContext, useMemo } from "react";
|
|
2
|
+
import { createContext, useCallback, useContext, useMemo, useState } from "react";
|
|
3
3
|
import { config, get, omit, pick } from "@vitus-labs/core";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
|
|
@@ -83,13 +83,15 @@ const widthStyles = ({ size, columns, gap, RNparentWidth }, { rootSize }) => {
|
|
|
83
83
|
const s = size;
|
|
84
84
|
const c = columns;
|
|
85
85
|
const g = gap;
|
|
86
|
+
if (!RNparentWidth) return "";
|
|
86
87
|
const width = RNparentWidth / c * s;
|
|
87
|
-
const val = hasValue(gap) ? width - g : width;
|
|
88
88
|
return css$2`
|
|
89
89
|
flex-grow: 0;
|
|
90
90
|
flex-shrink: 0;
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
${css$2`
|
|
92
|
+
width: ${value(hasValue(gap) ? Math.max(0, width - g) : width, rootSize)};
|
|
93
|
+
flex-basis: auto;
|
|
94
|
+
`}
|
|
93
95
|
`;
|
|
94
96
|
};
|
|
95
97
|
/** Applies half of the given value as either margin or padding (used for gap and padding distribution). */
|
|
@@ -107,7 +109,7 @@ const spacingStyles$1 = (type, param, rootSize) => {
|
|
|
107
109
|
const styles$2 = ({ theme, css, rootSize }) => {
|
|
108
110
|
const { size, columns, gap, padding, extraStyles, RNparentWidth } = theme;
|
|
109
111
|
if (isVisible(size)) return css`
|
|
110
|
-
|
|
112
|
+
${""}
|
|
111
113
|
position: relative;
|
|
112
114
|
${widthStyles({
|
|
113
115
|
size,
|
|
@@ -121,20 +123,19 @@ const styles$2 = ({ theme, css, rootSize }) => {
|
|
|
121
123
|
`;
|
|
122
124
|
return css`
|
|
123
125
|
left: -9999px;
|
|
124
|
-
position:
|
|
126
|
+
position: ${"absolute"};
|
|
125
127
|
margin: 0;
|
|
126
128
|
padding: 0;
|
|
127
129
|
`;
|
|
128
130
|
};
|
|
129
131
|
var styled_default$2 = styled$2(component$2)`
|
|
130
|
-
${false}
|
|
132
|
+
${false}
|
|
131
133
|
|
|
132
134
|
position: relative;
|
|
133
135
|
display: flex;
|
|
134
136
|
flex-basis: 0;
|
|
135
137
|
flex-grow: 1;
|
|
136
138
|
flex-direction: column;
|
|
137
|
-
justify-content: stretch;
|
|
138
139
|
|
|
139
140
|
${makeItResponsive({
|
|
140
141
|
key: "$coolgrid",
|
|
@@ -145,29 +146,32 @@ var styled_default$2 = styled$2(component$2)`
|
|
|
145
146
|
`;
|
|
146
147
|
|
|
147
148
|
//#endregion
|
|
148
|
-
//#region src/Col/component.tsx
|
|
149
|
+
//#region src/Col/component.native.tsx
|
|
149
150
|
/**
|
|
150
|
-
* Col
|
|
151
|
-
*
|
|
152
|
-
*
|
|
151
|
+
* Native Col component that reads RNparentWidth from RowContext
|
|
152
|
+
* and passes it into the styled component so column widths can be
|
|
153
|
+
* computed as absolute pixels (CSS calc() is unavailable on RN).
|
|
153
154
|
*/
|
|
154
|
-
const DEV_PROPS$2 = process.env.NODE_ENV !== "production" ? { "data-coolgrid": "col" } : {};
|
|
155
155
|
const Component$2 = ({ children, component, css, ...props }) => {
|
|
156
|
+
const parentCtx = useContext(RowContext_default);
|
|
156
157
|
const { colCss, colComponent, columns, gap, size, padding } = useGridContext({
|
|
157
|
-
...
|
|
158
|
+
...parentCtx,
|
|
158
159
|
...props
|
|
159
160
|
});
|
|
161
|
+
const RNparentWidth = parentCtx.RNparentWidth ?? 0;
|
|
160
162
|
const finalProps = useMemo(() => ({ $coolgrid: {
|
|
161
163
|
columns,
|
|
162
164
|
gap,
|
|
163
165
|
size,
|
|
164
166
|
padding,
|
|
167
|
+
RNparentWidth,
|
|
165
168
|
extraStyles: css ?? colCss
|
|
166
169
|
} }), [
|
|
167
170
|
columns,
|
|
168
171
|
gap,
|
|
169
172
|
size,
|
|
170
173
|
padding,
|
|
174
|
+
RNparentWidth,
|
|
171
175
|
css,
|
|
172
176
|
colCss
|
|
173
177
|
]);
|
|
@@ -175,7 +179,6 @@ const Component$2 = ({ children, component, css, ...props }) => {
|
|
|
175
179
|
...omitCtxKeys(props),
|
|
176
180
|
as: component ?? colComponent,
|
|
177
181
|
...finalProps,
|
|
178
|
-
...DEV_PROPS$2,
|
|
179
182
|
children
|
|
180
183
|
});
|
|
181
184
|
};
|
|
@@ -192,19 +195,18 @@ var Col_default = Component$2;
|
|
|
192
195
|
//#region src/Container/styled.ts
|
|
193
196
|
const { styled: styled$1, css: css$1, component: component$1 } = config;
|
|
194
197
|
/** Responsive styles that apply the container's max-width and any extra CSS at each breakpoint. */
|
|
195
|
-
const styles$1 = ({ theme: t, css, rootSize }) =>
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
const styles$1 = ({ theme: t, css, rootSize }) => {
|
|
199
|
+
const w = t.width != null && typeof t.width !== "object" ? t.width : null;
|
|
200
|
+
return css`
|
|
201
|
+
${w != null ? `max-width: ${value(w, rootSize)};` : ""};
|
|
202
|
+
${extendCss(t.extraStyles)};
|
|
203
|
+
`;
|
|
204
|
+
};
|
|
199
205
|
/** Styled Container element. Centered via auto margins with responsive max-width. */
|
|
200
206
|
var styled_default$1 = styled$1(component$1)`
|
|
201
|
-
${false};
|
|
202
|
-
|
|
203
207
|
display: flex;
|
|
204
|
-
width: 100%;
|
|
205
208
|
flex-direction: column;
|
|
206
|
-
|
|
207
|
-
margin-left: auto;
|
|
209
|
+
${false}
|
|
208
210
|
|
|
209
211
|
${makeItResponsive({
|
|
210
212
|
key: "$coolgrid",
|
|
@@ -222,9 +224,9 @@ var styled_default$1 = styled$1(component$1)`
|
|
|
222
224
|
* components via ContainerContext, and renders a styled wrapper with
|
|
223
225
|
* responsive max-width.
|
|
224
226
|
*/
|
|
225
|
-
const DEV_PROPS
|
|
227
|
+
const DEV_PROPS = process.env.NODE_ENV !== "production" ? { "data-coolgrid": "container" } : {};
|
|
226
228
|
const Component$1 = ({ children, component, css, width, ...props }) => {
|
|
227
|
-
const { containerWidth
|
|
229
|
+
const { containerWidth, columns, size, gap, padding, gutter, colCss, colComponent, rowCss, rowComponent, contentAlignX } = useGridContext(props);
|
|
228
230
|
const context = useMemo(() => ({
|
|
229
231
|
columns,
|
|
230
232
|
size,
|
|
@@ -260,7 +262,7 @@ const Component$1 = ({ children, component, css, width, ...props }) => {
|
|
|
260
262
|
...omitCtxKeys(props),
|
|
261
263
|
as: component,
|
|
262
264
|
...finalProps,
|
|
263
|
-
...DEV_PROPS
|
|
265
|
+
...DEV_PROPS,
|
|
264
266
|
children: /* @__PURE__ */ jsx(ContainerContext_default.Provider, {
|
|
265
267
|
value: context,
|
|
266
268
|
children
|
|
@@ -284,9 +286,13 @@ const spacingStyles = ({ gap, gutter }, { rootSize }) => {
|
|
|
284
286
|
const g = gap;
|
|
285
287
|
const getValue = (param) => value(param, rootSize);
|
|
286
288
|
const spacingX = g / 2 * -1;
|
|
289
|
+
const spacingY = isNumber(gutter) ? gutter - g / 2 : g / 2;
|
|
287
290
|
return css`
|
|
288
|
-
|
|
289
|
-
|
|
291
|
+
margin-top: ${getValue(spacingY)};
|
|
292
|
+
margin-bottom: ${getValue(spacingY)};
|
|
293
|
+
margin-left: ${getValue(spacingX)};
|
|
294
|
+
margin-right: ${getValue(spacingX)};
|
|
295
|
+
`;
|
|
290
296
|
};
|
|
291
297
|
/** Maps the contentAlignX prop to a CSS justify-content value. */
|
|
292
298
|
const contentAlign = (align) => {
|
|
@@ -324,17 +330,17 @@ var styled_default = styled(component)`
|
|
|
324
330
|
`;
|
|
325
331
|
|
|
326
332
|
//#endregion
|
|
327
|
-
//#region src/Row/component.tsx
|
|
333
|
+
//#region src/Row/component.native.tsx
|
|
328
334
|
/**
|
|
329
|
-
* Row component that
|
|
330
|
-
* it
|
|
331
|
-
*
|
|
332
|
-
* with negative margins to offset column gutters.
|
|
335
|
+
* Native Row component that measures its own width via onLayout and passes
|
|
336
|
+
* it as RNparentWidth through RowContext so Col children can compute
|
|
337
|
+
* absolute pixel widths (CSS calc() is unavailable on React Native).
|
|
333
338
|
*/
|
|
334
|
-
const DEV_PROPS = process.env.NODE_ENV !== "production" ? { "data-coolgrid": "row" } : {};
|
|
335
339
|
const Component = ({ children, component, css, contentAlignX: rowAlignX, ...props }) => {
|
|
340
|
+
const parentCtx = useContext(ContainerContext_default);
|
|
341
|
+
const [parentWidth, setParentWidth] = useState(0);
|
|
336
342
|
const { columns, gap, gutter, rowComponent, rowCss, contentAlignX, containerWidth, size, padding, colCss, colComponent } = useGridContext({
|
|
337
|
-
...
|
|
343
|
+
...parentCtx,
|
|
338
344
|
...props
|
|
339
345
|
});
|
|
340
346
|
const context = useMemo(() => ({
|
|
@@ -345,7 +351,8 @@ const Component = ({ children, component, css, contentAlignX: rowAlignX, ...prop
|
|
|
345
351
|
colComponent,
|
|
346
352
|
columns,
|
|
347
353
|
gap,
|
|
348
|
-
gutter
|
|
354
|
+
gutter,
|
|
355
|
+
RNparentWidth: parentWidth
|
|
349
356
|
}), [
|
|
350
357
|
containerWidth,
|
|
351
358
|
size,
|
|
@@ -354,7 +361,8 @@ const Component = ({ children, component, css, contentAlignX: rowAlignX, ...prop
|
|
|
354
361
|
colComponent,
|
|
355
362
|
columns,
|
|
356
363
|
gap,
|
|
357
|
-
gutter
|
|
364
|
+
gutter,
|
|
365
|
+
parentWidth
|
|
358
366
|
]);
|
|
359
367
|
const finalProps = useMemo(() => ({ $coolgrid: {
|
|
360
368
|
contentAlignX: rowAlignX || contentAlignX,
|
|
@@ -371,11 +379,15 @@ const Component = ({ children, component, css, contentAlignX: rowAlignX, ...prop
|
|
|
371
379
|
css,
|
|
372
380
|
rowCss
|
|
373
381
|
]);
|
|
382
|
+
const onLayout = useCallback((e) => {
|
|
383
|
+
const newWidth = e?.nativeEvent?.layout?.width;
|
|
384
|
+
if (newWidth != null) setParentWidth((prev) => prev === newWidth ? prev : newWidth);
|
|
385
|
+
}, []);
|
|
374
386
|
return /* @__PURE__ */ jsx(styled_default, {
|
|
375
387
|
...omitCtxKeys(props),
|
|
376
388
|
as: component || rowComponent,
|
|
377
389
|
...finalProps,
|
|
378
|
-
|
|
390
|
+
onLayout,
|
|
379
391
|
children: /* @__PURE__ */ jsx(RowContext_default.Provider, {
|
|
380
392
|
value: context,
|
|
381
393
|
children
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitus-labs/coolgrid",
|
|
3
|
-
"version": "2.0.0-
|
|
3
|
+
"version": "2.0.0-beta.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Vit Bokisch <vit@bokisch.cz>",
|
|
6
6
|
"maintainers": [
|
|
@@ -10,11 +10,13 @@
|
|
|
10
10
|
"sideEffects": false,
|
|
11
11
|
"exports": {
|
|
12
12
|
"source": "./src/index.ts",
|
|
13
|
+
"react-native": "./lib/vitus-labs-coolgrid.native.js",
|
|
13
14
|
"import": "./lib/index.js",
|
|
14
15
|
"types": "./lib/index.d.ts"
|
|
15
16
|
},
|
|
16
17
|
"types": "./lib/index.d.ts",
|
|
17
18
|
"react-native": "./lib/vitus-labs-coolgrid.native.js",
|
|
19
|
+
"main": "./lib/index.js",
|
|
18
20
|
"files": [
|
|
19
21
|
"lib",
|
|
20
22
|
"!lib/**/*.map",
|
|
@@ -56,19 +58,27 @@
|
|
|
56
58
|
"test:coverage": "vitest run --coverage",
|
|
57
59
|
"test:watch": "vitest",
|
|
58
60
|
"cover": "coveralls < .coverage/lcov.info",
|
|
59
|
-
"typecheck": "tsc --noEmit"
|
|
61
|
+
"typecheck": "tsc --noEmit",
|
|
62
|
+
"version": "node ../../scripts/sync-peer-deps.mjs"
|
|
60
63
|
},
|
|
61
64
|
"peerDependencies": {
|
|
62
|
-
"@vitus-labs/core": "
|
|
63
|
-
"@vitus-labs/unistyle": "
|
|
64
|
-
"react": ">= 19"
|
|
65
|
+
"@vitus-labs/core": "2.0.0-alpha.27",
|
|
66
|
+
"@vitus-labs/unistyle": "2.0.0-alpha.27",
|
|
67
|
+
"react": ">= 19",
|
|
68
|
+
"react-native": ">= 0.76"
|
|
69
|
+
},
|
|
70
|
+
"peerDependenciesMeta": {
|
|
71
|
+
"react-native": {
|
|
72
|
+
"optional": true
|
|
73
|
+
}
|
|
65
74
|
},
|
|
66
75
|
"devDependencies": {
|
|
67
|
-
"@vitus-labs/core": "2.0.0-
|
|
68
|
-
"@vitus-labs/tools-rolldown": "
|
|
69
|
-
"@vitus-labs/tools-storybook": "
|
|
70
|
-
"@vitus-labs/tools-typescript": "
|
|
71
|
-
"@vitus-labs/unistyle": "2.0.0-
|
|
76
|
+
"@vitus-labs/core": "2.0.0-beta.0",
|
|
77
|
+
"@vitus-labs/tools-rolldown": "1.10.0",
|
|
78
|
+
"@vitus-labs/tools-storybook": "1.10.0",
|
|
79
|
+
"@vitus-labs/tools-typescript": "1.10.0",
|
|
80
|
+
"@vitus-labs/unistyle": "2.0.0-beta.0",
|
|
81
|
+
"react-native": ">=0.76.0"
|
|
72
82
|
},
|
|
73
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "dd8b9f356086ecd8bfb69c87fcad1e8bfa9ab1f4"
|
|
74
84
|
}
|