@spark-web/stack 1.0.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/CHANGELOG.md +18 -0
- package/README.md +118 -0
- package/dist/declarations/src/Stack.d.ts +21 -0
- package/dist/declarations/src/alignment.d.ts +14 -0
- package/dist/declarations/src/index.d.ts +2 -0
- package/dist/spark-web-stack.cjs.d.ts +1 -0
- package/dist/spark-web-stack.cjs.dev.js +62 -0
- package/dist/spark-web-stack.cjs.js +7 -0
- package/dist/spark-web-stack.cjs.prod.js +62 -0
- package/dist/spark-web-stack.esm.js +54 -0
- package/package.json +22 -0
- package/src/Stack.stories.tsx +27 -0
- package/src/Stack.tsx +59 -0
- package/src/alignment.ts +12 -0
- package/src/index.ts +5 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# @spark-web/stack
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [#27](https://github.com/brighte-labs/spark-web/pull/27)
|
|
8
|
+
[`4c8e398`](https://github.com/brighte-labs/spark-web/commit/4c8e3988f8a59d3dab60a6b67b1128b6ff2a5f2c)
|
|
9
|
+
Thanks [@JedWatson](https://github.com/JedWatson)! - Initial Version
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies
|
|
14
|
+
[[`4c8e398`](https://github.com/brighte-labs/spark-web/commit/4c8e3988f8a59d3dab60a6b67b1128b6ff2a5f2c)]:
|
|
15
|
+
- @spark-web/box@1.0.0
|
|
16
|
+
- @spark-web/divider@1.0.0
|
|
17
|
+
- @spark-web/theme@1.0.0
|
|
18
|
+
- @spark-web/utils@1.0.0
|
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Stack
|
|
3
|
+
storybookPath: page-layout-stack--default
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Used to distribute children vertically, with even spacing between each child.
|
|
7
|
+
|
|
8
|
+
```jsx live
|
|
9
|
+
<Stack gap="large">
|
|
10
|
+
<Placeholder />
|
|
11
|
+
<Placeholder />
|
|
12
|
+
<Placeholder />
|
|
13
|
+
</Stack>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Examples
|
|
17
|
+
|
|
18
|
+
### Gap
|
|
19
|
+
|
|
20
|
+
The spacing between children can be adjusted using the `gap` prop.
|
|
21
|
+
|
|
22
|
+
```jsx live
|
|
23
|
+
<Columns gap="xxlarge">
|
|
24
|
+
<Stack gap="small">
|
|
25
|
+
<Placeholder />
|
|
26
|
+
<Placeholder />
|
|
27
|
+
<Placeholder />
|
|
28
|
+
</Stack>
|
|
29
|
+
<Stack gap="medium">
|
|
30
|
+
<Placeholder />
|
|
31
|
+
<Placeholder />
|
|
32
|
+
<Placeholder />
|
|
33
|
+
</Stack>
|
|
34
|
+
<Stack gap="large">
|
|
35
|
+
<Placeholder />
|
|
36
|
+
<Placeholder />
|
|
37
|
+
<Placeholder />
|
|
38
|
+
</Stack>
|
|
39
|
+
<Stack gap="xlarge">
|
|
40
|
+
<Placeholder />
|
|
41
|
+
<Placeholder />
|
|
42
|
+
<Placeholder />
|
|
43
|
+
</Stack>
|
|
44
|
+
</Columns>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Horizontal alignment
|
|
48
|
+
|
|
49
|
+
Items can be aligned horizontally using the `align` prop.
|
|
50
|
+
|
|
51
|
+
```jsx live
|
|
52
|
+
<Stack gap="medium" dividers>
|
|
53
|
+
<Stack gap="small" align="left">
|
|
54
|
+
<Placeholder />
|
|
55
|
+
<Placeholder label="left" width={128} />
|
|
56
|
+
<Placeholder />
|
|
57
|
+
</Stack>
|
|
58
|
+
<Stack gap="small" align="center">
|
|
59
|
+
<Placeholder />
|
|
60
|
+
<Placeholder label="center" width={128} />
|
|
61
|
+
<Placeholder />
|
|
62
|
+
</Stack>
|
|
63
|
+
<Stack gap="small" align="right">
|
|
64
|
+
<Placeholder />
|
|
65
|
+
<Placeholder label="right" width={128} />
|
|
66
|
+
<Placeholder />
|
|
67
|
+
</Stack>
|
|
68
|
+
</Stack>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Dividers
|
|
72
|
+
|
|
73
|
+
Use the `dividers` property to render a [Divider](/package/divider) between each
|
|
74
|
+
element in the Stack.
|
|
75
|
+
|
|
76
|
+
```jsx live
|
|
77
|
+
<Stack gap="medium" dividers>
|
|
78
|
+
<Text>First item</Text>
|
|
79
|
+
<Text>Second item</Text>
|
|
80
|
+
<Text>Third item</Text>
|
|
81
|
+
</Stack>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Nesting
|
|
85
|
+
|
|
86
|
+
Nest Stack components to create more complex white space rules.
|
|
87
|
+
|
|
88
|
+
```jsx live
|
|
89
|
+
<Stack gap="xlarge">
|
|
90
|
+
<Heading level="4">Heading</Heading>
|
|
91
|
+
<Stack gap="small">
|
|
92
|
+
<Text>Line 1</Text>
|
|
93
|
+
<Text>Line 2</Text>
|
|
94
|
+
<Text>Line 3</Text>
|
|
95
|
+
</Stack>
|
|
96
|
+
<Stack gap="small">
|
|
97
|
+
<Text>Line 1</Text>
|
|
98
|
+
<Text>Line 2</Text>
|
|
99
|
+
<Text>Line 3</Text>
|
|
100
|
+
</Stack>
|
|
101
|
+
</Stack>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Props
|
|
105
|
+
|
|
106
|
+
| Prop | Type | Default | Description |
|
|
107
|
+
| --------- | ------------------------------- | --------- | ----------------------------------------------------- |
|
|
108
|
+
| align? | [ResponsiveProp<Align\>][align] | 'stretch' | Horizontally align items within the container. |
|
|
109
|
+
| dividers? | boolean | | Sets whether to place a divider between each element. |
|
|
110
|
+
|
|
111
|
+
`Stack` props also include [`Box`](/package/box) props and are not listed here
|
|
112
|
+
(excludes `display`, `className`, `alignItems`, `flexDirection`,
|
|
113
|
+
`justifyContent` and `flexWrap`).
|
|
114
|
+
|
|
115
|
+
Extra props are also passed into the underlying [`Box`](/package/box) component.
|
|
116
|
+
|
|
117
|
+
[align]:
|
|
118
|
+
https://bitbucket.org/brighte-energy/energy/src/537c678a81090af545969504776c6b3d2e67743e/spark-web/packages/stack/src/Stack.tsx#spark-web/packages/stack/src/Stack.tsx-21
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BoxProps } from '@spark-web/box';
|
|
2
|
+
import type { ResponsiveProp } from '@spark-web/theme';
|
|
3
|
+
import type { ReactElement } from 'react';
|
|
4
|
+
import type { Align } from './alignment';
|
|
5
|
+
declare type ValidBoxProps = Omit<BoxProps, 'display' | 'className' | 'alignItems' | 'flexDirection' | 'justifyContent' | 'flexWrap'>;
|
|
6
|
+
export declare type StackProps = {
|
|
7
|
+
/** Horizontally align items within the stack. */
|
|
8
|
+
align?: ResponsiveProp<Align>;
|
|
9
|
+
/** Place a divider between each element. */
|
|
10
|
+
dividers?: boolean;
|
|
11
|
+
} & ValidBoxProps;
|
|
12
|
+
export declare const Stack: <Comp extends import("react").ElementType<any> = "div">(props: {
|
|
13
|
+
as?: Comp | undefined;
|
|
14
|
+
ref?: import("react").Ref<Comp extends "symbol" | "clipPath" | "filter" | "mask" | "marker" | "text" | "circle" | "svg" | "animate" | "animateMotion" | "animateTransform" | "defs" | "desc" | "ellipse" | "feBlend" | "feColorMatrix" | "feComponentTransfer" | "feComposite" | "feConvolveMatrix" | "feDiffuseLighting" | "feDisplacementMap" | "feDistantLight" | "feDropShadow" | "feFlood" | "feFuncA" | "feFuncB" | "feFuncG" | "feFuncR" | "feGaussianBlur" | "feImage" | "feMerge" | "feMergeNode" | "feMorphology" | "feOffset" | "fePointLight" | "feSpecularLighting" | "feSpotLight" | "feTile" | "feTurbulence" | "foreignObject" | "g" | "image" | "line" | "linearGradient" | "metadata" | "mpath" | "path" | "pattern" | "polygon" | "polyline" | "radialGradient" | "rect" | "stop" | "switch" | "textPath" | "tspan" | "use" | "view" | keyof HTMLElementTagNameMap | "set" ? (HTMLElementTagNameMap & Pick<SVGElementTagNameMap, "symbol" | "clipPath" | "filter" | "mask" | "marker" | "text" | "circle" | "svg" | "animate" | "animateMotion" | "animateTransform" | "defs" | "desc" | "ellipse" | "feBlend" | "feColorMatrix" | "feComponentTransfer" | "feComposite" | "feConvolveMatrix" | "feDiffuseLighting" | "feDisplacementMap" | "feDistantLight" | "feDropShadow" | "feFlood" | "feFuncA" | "feFuncB" | "feFuncG" | "feFuncR" | "feGaussianBlur" | "feImage" | "feMerge" | "feMergeNode" | "feMorphology" | "feOffset" | "fePointLight" | "feSpecularLighting" | "feSpotLight" | "feTile" | "feTurbulence" | "foreignObject" | "g" | "image" | "line" | "linearGradient" | "metadata" | "mpath" | "path" | "pattern" | "polygon" | "polyline" | "radialGradient" | "rect" | "stop" | "switch" | "textPath" | "tspan" | "use" | "view" | "set">)[Comp] : Comp extends new (...args: any) => any ? InstanceType<Comp> : undefined> | undefined;
|
|
15
|
+
} & Omit<import("react").PropsWithoutRef<import("react").ComponentProps<Comp>>, "as"> & {
|
|
16
|
+
/** Horizontally align items within the stack. */
|
|
17
|
+
align?: ResponsiveProp<"left" | "right" | "stretch" | "center"> | undefined;
|
|
18
|
+
/** Place a divider between each element. */
|
|
19
|
+
dividers?: boolean | undefined;
|
|
20
|
+
} & ValidBoxProps) => ReactElement<any, string | import("react").JSXElementConstructor<any>>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare const alignLookup: {
|
|
2
|
+
readonly left: "start";
|
|
3
|
+
readonly center: "center";
|
|
4
|
+
readonly right: "end";
|
|
5
|
+
readonly stretch: "stretch";
|
|
6
|
+
};
|
|
7
|
+
export declare type Align = keyof typeof alignLookup;
|
|
8
|
+
export declare const alignToAlignItems: (prop?: import("@spark-web/theme").ResponsiveProp<"left" | "right" | "stretch" | "center"> | undefined) => "stretch" | "center" | "end" | "start" | {
|
|
9
|
+
mobile: "stretch" | "center" | "end" | "start" | undefined;
|
|
10
|
+
tablet: "stretch" | "center" | "end" | "start" | undefined;
|
|
11
|
+
desktop: "stretch" | "center" | "end" | "start" | undefined;
|
|
12
|
+
wide: "stretch" | "center" | "end" | "start" | undefined;
|
|
13
|
+
} | undefined;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./declarations/src/index";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _defineProperty = require('@babel/runtime/helpers/esm/defineProperty');
|
|
6
|
+
var _objectWithoutProperties = require('@babel/runtime/helpers/esm/objectWithoutProperties');
|
|
7
|
+
var React = require('react');
|
|
8
|
+
var box = require('@spark-web/box');
|
|
9
|
+
var divider = require('@spark-web/divider');
|
|
10
|
+
var ts = require('@spark-web/utils/ts');
|
|
11
|
+
var theme = require('@spark-web/theme');
|
|
12
|
+
|
|
13
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
14
|
+
|
|
15
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
16
|
+
|
|
17
|
+
var alignLookup = {
|
|
18
|
+
left: 'start',
|
|
19
|
+
center: 'center',
|
|
20
|
+
right: 'end',
|
|
21
|
+
stretch: 'stretch'
|
|
22
|
+
};
|
|
23
|
+
var alignToAlignItems = theme.createResponsiveMapFn(alignLookup);
|
|
24
|
+
|
|
25
|
+
var _excluded = ["align", "children", "dividers"];
|
|
26
|
+
var __jsx = React__default["default"].createElement;
|
|
27
|
+
|
|
28
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
29
|
+
|
|
30
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
31
|
+
var Stack = ts.forwardRefWithAs(function (_ref, forwardedRef) {
|
|
32
|
+
var _ref$align = _ref.align,
|
|
33
|
+
align = _ref$align === void 0 ? 'stretch' : _ref$align,
|
|
34
|
+
children = _ref.children,
|
|
35
|
+
dividers = _ref.dividers,
|
|
36
|
+
props = _objectWithoutProperties(_ref, _excluded);
|
|
37
|
+
|
|
38
|
+
var alignItems = alignToAlignItems(align);
|
|
39
|
+
|
|
40
|
+
var rootProps = _objectSpread({
|
|
41
|
+
ref: forwardedRef,
|
|
42
|
+
alignItems: alignItems,
|
|
43
|
+
display: 'flex',
|
|
44
|
+
flexDirection: 'column'
|
|
45
|
+
}, props); // bail early w/o dividers to avoid unnecessary map
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if (!dividers) {
|
|
49
|
+
return __jsx(box.Box, rootProps, children);
|
|
50
|
+
} // map over children to insert dividers
|
|
51
|
+
// remove falsy values before mapping, keeps the index in sync
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
var childArray = React.Children.toArray(children);
|
|
55
|
+
return __jsx(box.Box, rootProps, childArray.map(function (child, idx) {
|
|
56
|
+
return __jsx(React.Fragment, {
|
|
57
|
+
key: child.key || idx
|
|
58
|
+
}, dividers && idx ? __jsx(divider.Divider, null) : null, child);
|
|
59
|
+
}));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
exports.Stack = Stack;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _defineProperty = require('@babel/runtime/helpers/esm/defineProperty');
|
|
6
|
+
var _objectWithoutProperties = require('@babel/runtime/helpers/esm/objectWithoutProperties');
|
|
7
|
+
var React = require('react');
|
|
8
|
+
var box = require('@spark-web/box');
|
|
9
|
+
var divider = require('@spark-web/divider');
|
|
10
|
+
var ts = require('@spark-web/utils/ts');
|
|
11
|
+
var theme = require('@spark-web/theme');
|
|
12
|
+
|
|
13
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
14
|
+
|
|
15
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
16
|
+
|
|
17
|
+
var alignLookup = {
|
|
18
|
+
left: 'start',
|
|
19
|
+
center: 'center',
|
|
20
|
+
right: 'end',
|
|
21
|
+
stretch: 'stretch'
|
|
22
|
+
};
|
|
23
|
+
var alignToAlignItems = theme.createResponsiveMapFn(alignLookup);
|
|
24
|
+
|
|
25
|
+
var _excluded = ["align", "children", "dividers"];
|
|
26
|
+
var __jsx = React__default["default"].createElement;
|
|
27
|
+
|
|
28
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
29
|
+
|
|
30
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
31
|
+
var Stack = ts.forwardRefWithAs(function (_ref, forwardedRef) {
|
|
32
|
+
var _ref$align = _ref.align,
|
|
33
|
+
align = _ref$align === void 0 ? 'stretch' : _ref$align,
|
|
34
|
+
children = _ref.children,
|
|
35
|
+
dividers = _ref.dividers,
|
|
36
|
+
props = _objectWithoutProperties(_ref, _excluded);
|
|
37
|
+
|
|
38
|
+
var alignItems = alignToAlignItems(align);
|
|
39
|
+
|
|
40
|
+
var rootProps = _objectSpread({
|
|
41
|
+
ref: forwardedRef,
|
|
42
|
+
alignItems: alignItems,
|
|
43
|
+
display: 'flex',
|
|
44
|
+
flexDirection: 'column'
|
|
45
|
+
}, props); // bail early w/o dividers to avoid unnecessary map
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if (!dividers) {
|
|
49
|
+
return __jsx(box.Box, rootProps, children);
|
|
50
|
+
} // map over children to insert dividers
|
|
51
|
+
// remove falsy values before mapping, keeps the index in sync
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
var childArray = React.Children.toArray(children);
|
|
55
|
+
return __jsx(box.Box, rootProps, childArray.map(function (child, idx) {
|
|
56
|
+
return __jsx(React.Fragment, {
|
|
57
|
+
key: child.key || idx
|
|
58
|
+
}, dividers && idx ? __jsx(divider.Divider, null) : null, child);
|
|
59
|
+
}));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
exports.Stack = Stack;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import _defineProperty from '@babel/runtime/helpers/esm/defineProperty';
|
|
2
|
+
import _objectWithoutProperties from '@babel/runtime/helpers/esm/objectWithoutProperties';
|
|
3
|
+
import React, { Children, Fragment } from 'react';
|
|
4
|
+
import { Box } from '@spark-web/box';
|
|
5
|
+
import { Divider } from '@spark-web/divider';
|
|
6
|
+
import { forwardRefWithAs } from '@spark-web/utils/ts';
|
|
7
|
+
import { createResponsiveMapFn } from '@spark-web/theme';
|
|
8
|
+
|
|
9
|
+
var alignLookup = {
|
|
10
|
+
left: 'start',
|
|
11
|
+
center: 'center',
|
|
12
|
+
right: 'end',
|
|
13
|
+
stretch: 'stretch'
|
|
14
|
+
};
|
|
15
|
+
var alignToAlignItems = createResponsiveMapFn(alignLookup);
|
|
16
|
+
|
|
17
|
+
var _excluded = ["align", "children", "dividers"];
|
|
18
|
+
var __jsx = React.createElement;
|
|
19
|
+
|
|
20
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
21
|
+
|
|
22
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
23
|
+
var Stack = forwardRefWithAs(function (_ref, forwardedRef) {
|
|
24
|
+
var _ref$align = _ref.align,
|
|
25
|
+
align = _ref$align === void 0 ? 'stretch' : _ref$align,
|
|
26
|
+
children = _ref.children,
|
|
27
|
+
dividers = _ref.dividers,
|
|
28
|
+
props = _objectWithoutProperties(_ref, _excluded);
|
|
29
|
+
|
|
30
|
+
var alignItems = alignToAlignItems(align);
|
|
31
|
+
|
|
32
|
+
var rootProps = _objectSpread({
|
|
33
|
+
ref: forwardedRef,
|
|
34
|
+
alignItems: alignItems,
|
|
35
|
+
display: 'flex',
|
|
36
|
+
flexDirection: 'column'
|
|
37
|
+
}, props); // bail early w/o dividers to avoid unnecessary map
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if (!dividers) {
|
|
41
|
+
return __jsx(Box, rootProps, children);
|
|
42
|
+
} // map over children to insert dividers
|
|
43
|
+
// remove falsy values before mapping, keeps the index in sync
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
var childArray = Children.toArray(children);
|
|
47
|
+
return __jsx(Box, rootProps, childArray.map(function (child, idx) {
|
|
48
|
+
return __jsx(Fragment, {
|
|
49
|
+
key: child.key || idx
|
|
50
|
+
}, dividers && idx ? __jsx(Divider, null) : null, child);
|
|
51
|
+
}));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export { Stack };
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@spark-web/stack",
|
|
3
|
+
"license": "MIT",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"main": "dist/spark-web-stack.cjs.js",
|
|
6
|
+
"module": "dist/spark-web-stack.esm.js",
|
|
7
|
+
"devDependencies": {
|
|
8
|
+
"@types/react": "^17.0.12"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@babel/runtime": "^7.14.6",
|
|
12
|
+
"@emotion/css": "^11.7.1",
|
|
13
|
+
"@spark-web/box": "^1.0.0",
|
|
14
|
+
"@spark-web/divider": "^1.0.0",
|
|
15
|
+
"@spark-web/theme": "^1.0.0",
|
|
16
|
+
"@spark-web/utils": "^1.0.0",
|
|
17
|
+
"react": "^17.0.2"
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">= 14.13"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ComponentMeta, ComponentStory } from '@storybook/react';
|
|
2
|
+
|
|
3
|
+
import { Placeholder } from '../../../docs/components/example-helpers';
|
|
4
|
+
import type { StackProps } from './Stack';
|
|
5
|
+
import { Stack } from './Stack';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
title: 'Page & Layout / Stack',
|
|
9
|
+
component: Stack,
|
|
10
|
+
} as ComponentMeta<typeof Stack>;
|
|
11
|
+
|
|
12
|
+
const StackStory: ComponentStory<typeof Stack> = (args: StackProps) => (
|
|
13
|
+
<Stack {...args} />
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export const Default = StackStory.bind({});
|
|
17
|
+
|
|
18
|
+
Default.args = {
|
|
19
|
+
gap: 'large',
|
|
20
|
+
children: (
|
|
21
|
+
<>
|
|
22
|
+
<Placeholder height={40} />
|
|
23
|
+
<Placeholder height={40} />
|
|
24
|
+
<Placeholder height={40} />
|
|
25
|
+
</>
|
|
26
|
+
),
|
|
27
|
+
} as StackProps;
|
package/src/Stack.tsx
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { BoxProps } from '@spark-web/box';
|
|
2
|
+
import { Box } from '@spark-web/box';
|
|
3
|
+
import { Divider } from '@spark-web/divider';
|
|
4
|
+
import type { ResponsiveProp } from '@spark-web/theme';
|
|
5
|
+
import { forwardRefWithAs } from '@spark-web/utils/ts';
|
|
6
|
+
import type { ReactElement } from 'react';
|
|
7
|
+
import { Children, Fragment } from 'react';
|
|
8
|
+
|
|
9
|
+
import type { Align } from './alignment';
|
|
10
|
+
import { alignToAlignItems } from './alignment';
|
|
11
|
+
|
|
12
|
+
type ValidBoxProps = Omit<
|
|
13
|
+
BoxProps,
|
|
14
|
+
| 'display'
|
|
15
|
+
| 'className'
|
|
16
|
+
| 'alignItems'
|
|
17
|
+
| 'flexDirection'
|
|
18
|
+
| 'justifyContent'
|
|
19
|
+
| 'flexWrap'
|
|
20
|
+
>;
|
|
21
|
+
|
|
22
|
+
export type StackProps = {
|
|
23
|
+
/** Horizontally align items within the stack. */
|
|
24
|
+
align?: ResponsiveProp<Align>;
|
|
25
|
+
/** Place a divider between each element. */
|
|
26
|
+
dividers?: boolean;
|
|
27
|
+
} & ValidBoxProps;
|
|
28
|
+
|
|
29
|
+
export const Stack = forwardRefWithAs<'div', StackProps>(
|
|
30
|
+
({ align = 'stretch', children, dividers, ...props }, forwardedRef) => {
|
|
31
|
+
const alignItems = alignToAlignItems(align);
|
|
32
|
+
const rootProps = {
|
|
33
|
+
ref: forwardedRef,
|
|
34
|
+
alignItems,
|
|
35
|
+
display: 'flex',
|
|
36
|
+
flexDirection: 'column',
|
|
37
|
+
...props,
|
|
38
|
+
} as const;
|
|
39
|
+
|
|
40
|
+
// bail early w/o dividers to avoid unnecessary map
|
|
41
|
+
if (!dividers) {
|
|
42
|
+
return <Box {...rootProps}>{children}</Box>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// map over children to insert dividers
|
|
46
|
+
// remove falsy values before mapping, keeps the index in sync
|
|
47
|
+
const childArray = Children.toArray(children) as ReactElement[];
|
|
48
|
+
return (
|
|
49
|
+
<Box {...rootProps}>
|
|
50
|
+
{childArray.map((child, idx) => (
|
|
51
|
+
<Fragment key={child.key || idx}>
|
|
52
|
+
{dividers && idx ? <Divider /> : null}
|
|
53
|
+
{child}
|
|
54
|
+
</Fragment>
|
|
55
|
+
))}
|
|
56
|
+
</Box>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
);
|
package/src/alignment.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createResponsiveMapFn } from '@spark-web/theme';
|
|
2
|
+
|
|
3
|
+
const alignLookup = {
|
|
4
|
+
left: 'start',
|
|
5
|
+
center: 'center',
|
|
6
|
+
right: 'end',
|
|
7
|
+
stretch: 'stretch',
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export type Align = keyof typeof alignLookup;
|
|
11
|
+
|
|
12
|
+
export const alignToAlignItems = createResponsiveMapFn(alignLookup);
|