Skip to content

Commit 2cf4e2e

Browse files
authored
Merge pull request #41 from buildo/3154377-port_flex_reverse_sticky
2 parents c344d7c + c21c557 commit 2cf4e2e

11 files changed

+193
-37
lines changed

src/Form/createFormLayoutComponents.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { FunctionComponent } from "react";
2-
import { ActionsProps } from "src";
2+
import { ActionsProps } from "../";
33
import { createForm, FormConfig } from "./Form";
44
import { createFormRow, FormRowConfig } from "./FormRow";
55
import { createFormSection, FormSectionConfig } from "./FormSection";

src/Layout/Column.css.ts

+30-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
import { StyleRule, styleVariants } from "@vanilla-extract/css";
2+
import { bentoSprinkles } from "../internal/sprinkles.css";
3+
import { Breakpoint, breakpoints } from "../util/breakpoints";
24

35
const styleForScale = (scale: number): StyleRule => ({
46
flex: `0 0 ${scale * 100}%`,
7+
width: "100%",
58
});
69

7-
export const width = styleVariants({
10+
export const fullWidth = bentoSprinkles({
11+
width: "full",
12+
});
13+
14+
const widths = {
15+
content: { flexShrink: 0 },
16+
full: styleForScale(1),
817
"1/2": styleForScale(1 / 2),
918
"1/3": styleForScale(1 / 3),
1019
"2/3": styleForScale(2 / 3),
@@ -14,4 +23,23 @@ export const width = styleVariants({
1423
"2/5": styleForScale(2 / 5),
1524
"3/5": styleForScale(3 / 5),
1625
"4/5": styleForScale(4 / 5),
17-
});
26+
};
27+
28+
const makeWidthVariants = (breakpoint: Breakpoint) =>
29+
styleVariants(widths, (widthStyle) => {
30+
switch (breakpoint) {
31+
case "desktop":
32+
return widthStyle;
33+
case "tablet":
34+
case "mobile":
35+
return {
36+
"@media": {
37+
[breakpoints[breakpoint]["@media"]]: widthStyle,
38+
},
39+
};
40+
}
41+
});
42+
43+
export const desktopWidths = makeWidthVariants("desktop");
44+
export const tabletWidths = makeWidthVariants("tablet");
45+
export const mobileWidths = makeWidthVariants("mobile");

src/Layout/createColumns.tsx

+30-16
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,49 @@
11
import { ReactChild, ReactElement } from "react";
22
import flattenChildren from "react-keyed-flatten-children";
33
import { BoxType, BoxProps } from "../Box/createBentoBox";
4-
import { bentoSprinkles } from "../internal/sprinkles.css";
4+
import {
5+
bentoSprinkles,
6+
normalizeResponsiveValue,
7+
OptionalResponsiveValue,
8+
} from "../internal/sprinkles.css";
59
import { childKey } from "../util/childKey";
610
import { Children } from "../util/Children";
711
import {
812
CollapsibleAlignmentProps,
913
responsiveCollapsibleAlignmentProps,
1014
} from "../util/collapsible";
11-
import * as columnStyles from "./Column.css";
15+
import { desktopWidths, tabletWidths, mobileWidths, fullWidth } from "./Column.css";
1216

1317
export function createColumns<AtomsFn extends typeof bentoSprinkles>(Box: BoxType<AtomsFn>) {
1418
type ResponsiveSpace = BoxProps<AtomsFn>["gap"];
1519

1620
type ColumnProps = {
1721
children: Children;
18-
width?: keyof typeof columnStyles.width | "content";
22+
width?: OptionalResponsiveValue<keyof typeof desktopWidths>;
23+
sticky?: {
24+
top: BoxProps<AtomsFn>["top"];
25+
};
1926
};
2027

21-
function Column(props: ColumnProps) {
28+
function Column({ children, width, sticky }: ColumnProps) {
29+
const { desktop, tablet, mobile } = width
30+
? normalizeResponsiveValue(width)
31+
: { desktop: undefined, tablet: undefined, mobile: undefined };
32+
33+
const className =
34+
width == null
35+
? fullWidth
36+
: [
37+
desktop && desktopWidths[desktop],
38+
tablet && tabletWidths[tablet],
39+
mobile && mobileWidths[mobile],
40+
];
41+
42+
const stickyProps = sticky ? ({ position: "sticky", top: sticky.top } as const) : {};
43+
2244
return (
23-
<Box
24-
className={
25-
props.width != null && props.width !== "content"
26-
? columnStyles.width[props.width]
27-
: undefined
28-
}
29-
width={props.width !== "content" ? "full" : undefined}
30-
flexShrink={props.width === "content" ? 0 : undefined}
31-
>
32-
{props.children}
45+
<Box className={className} {...stickyProps}>
46+
{children}
3347
</Box>
3448
);
3549
}
@@ -39,12 +53,12 @@ export function createColumns<AtomsFn extends typeof bentoSprinkles>(Box: BoxTyp
3953
children: Children;
4054
} & CollapsibleAlignmentProps;
4155

42-
function Columns({ space, children, align, alignY, collapseBelow }: Props) {
56+
function Columns({ space, children, align, alignY, collapseBelow, reverse }: Props) {
4357
return (
4458
<Box
4559
display="flex"
4660
gap={space}
47-
{...responsiveCollapsibleAlignmentProps({ align, alignY, collapseBelow })}
61+
{...responsiveCollapsibleAlignmentProps({ align, alignY, collapseBelow, reverse })}
4862
>
4963
{flattenChildren(children).map((child, index) => {
5064
if (isColumn(child)) {

src/Layout/createInline.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export function createInline<AtomsFn extends typeof bentoSprinkles>(Box: BoxType
2222
align,
2323
alignY,
2424
collapseBelow,
25+
reverse,
2526
...boxProps
2627
}: InlineProps) {
2728
return (
@@ -34,6 +35,7 @@ export function createInline<AtomsFn extends typeof bentoSprinkles>(Box: BoxType
3435
align,
3536
alignY,
3637
collapseBelow,
38+
reverse,
3739
})}
3840
>
3941
{flattenChildren(children)}

src/Placeholder/Placeholder.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Inset } from "../internal";
1+
import { BentoSprinkles, Inset } from "../internal";
22
import { Box } from "../internal/Box/Box";
33
import { vars } from "../vars.css";
44
import { unsafeLocalizedString } from "..";
@@ -7,16 +7,18 @@ type Props = {
77
height?: string | number;
88
width?: string | number;
99
label?: string;
10+
background?: BentoSprinkles["background"];
1011
};
1112

12-
export function Placeholder({ label, height = 120, width = "auto" }: Props) {
13+
export function Placeholder({ label, height = 120, width = "auto", background }: Props) {
1314
return (
1415
<Box
1516
position="relative"
1617
display="flex"
1718
style={{ height, width, border: `2px solid ${vars.outlineColor.outlineDecorative}` }}
1819
justifyContent="center"
1920
alignItems="center"
21+
background={background}
2022
>
2123
{label ? (
2224
<Inset space={8}>{unsafeLocalizedString(label)}</Inset>

src/internal/sprinkles.css.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { statusConditions } from "../util/conditions";
1313
const unconditionalAtomicProperties = defineProperties({
1414
properties: unconditionalProperties,
1515
shorthands: {
16-
inset: ["top", "right", "bottom", "left"],
1716
borderTopRadius: ["borderTopLeftRadius", "borderTopRightRadius"],
1817
borderBottomRadius: ["borderBottomLeftRadius", "borderBottomRightRadius"],
1918
},
@@ -24,6 +23,7 @@ const responsiveAtomicProperties = defineProperties({
2423
defaultCondition: "desktop",
2524
properties: responsiveProperties,
2625
shorthands: {
26+
inset: ["top", "right", "bottom", "left"],
2727
padding: ["paddingTop", "paddingBottom", "paddingLeft", "paddingRight"],
2828
paddingX: ["paddingLeft", "paddingRight"],
2929
paddingY: ["paddingTop", "paddingBottom"],

src/util/atoms.ts

+15-10
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,11 @@ export const unconditionalProperties = {
1919
fontSize: vars.fontSize,
2020
lineHeight: vars.lineHeight,
2121
letterSpacing: vars.letterSpacing,
22-
width: {
23-
...vars.space,
24-
full: "100%",
25-
},
2622
height: {
2723
...vars.space,
2824
full: "100%",
2925
},
30-
top: vars.space,
31-
bottom: vars.space,
32-
left: vars.space,
33-
right: vars.space,
34-
position: ["relative", "absolute", "fixed"],
26+
position: ["relative", "absolute", "fixed", "sticky"],
3527
overflow: ["hidden", "visible", "auto"],
3628
overflowX: ["hidden", "visible", "auto"],
3729
overflowY: ["hidden", "visible", "auto"],
@@ -40,7 +32,12 @@ export const unconditionalProperties = {
4032

4133
export const responsiveProperties = {
4234
display: ["flex", "none", "block", "grid", "inline-block"],
43-
flexDirection: ["row", "column"],
35+
flexDirection: {
36+
row: "row",
37+
column: "column",
38+
rowReverse: "row-reverse",
39+
columnReverse: "column-reverse",
40+
},
4441
alignItems: {
4542
flexStart: "flex-start",
4643
center: "center",
@@ -58,6 +55,10 @@ export const responsiveProperties = {
5855
wrapReverse: "wrap-reverse",
5956
},
6057
flexShrink: [0],
58+
width: {
59+
...vars.space,
60+
full: "100%",
61+
},
6162
paddingTop: vars.space,
6263
paddingBottom: vars.space,
6364
paddingLeft: vars.space,
@@ -68,6 +69,10 @@ export const responsiveProperties = {
6869
700: "700px",
6970
1440: "1440px",
7071
},
72+
top: vars.space,
73+
bottom: vars.space,
74+
left: vars.space,
75+
right: vars.space,
7176
} as const;
7277

7378
const color = {

src/util/collapsible.ts

+47-4
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ export type CollapsibleAlignmentProps = {
66
align?: ResponsiveAlign;
77
alignY?: ResponsiveAlignY;
88
collapseBelow?: Exclude<Breakpoint, "mobile">;
9+
reverse?: boolean | Partial<Record<Breakpoint, boolean>>;
910
};
1011

1112
export function responsiveCollapsibleAlignmentProps({
1213
align,
1314
alignY,
1415
collapseBelow,
16+
reverse,
1517
}: CollapsibleAlignmentProps): Pick<
1618
BentoSprinkles,
1719
"flexDirection" | "justifyContent" | "alignItems"
@@ -27,6 +29,19 @@ export function responsiveCollapsibleAlignmentProps({
2729
}
2830
})();
2931

32+
const normalizedReverse = (() => {
33+
if (typeof reverse === "boolean") {
34+
return { desktop: reverse, tablet: reverse, mobile: reverse };
35+
}
36+
return reverse || {};
37+
})();
38+
39+
const {
40+
desktop: reverseDesktop,
41+
tablet: reverseTablet = reverseDesktop,
42+
mobile: reverseMobile = reverseTablet,
43+
} = normalizedReverse;
44+
3045
const normalizedAlign = normalizeResponsiveValue(alignToFlexAlign(align) || "flexStart");
3146
const {
3247
desktop: justifyContentDesktop,
@@ -43,22 +58,38 @@ export function responsiveCollapsibleAlignmentProps({
4358

4459
return {
4560
flexDirection: {
46-
mobile: collapseMobile ? "column" : "row",
47-
tablet: collapseTablet ? "column" : "row",
48-
desktop: "row",
61+
mobile: collapseMobile
62+
? reverseMobile
63+
? "columnReverse"
64+
: "column"
65+
: reverseMobile
66+
? "rowReverse"
67+
: "row",
68+
tablet: collapseTablet
69+
? reverseTablet
70+
? "columnReverse"
71+
: "column"
72+
: reverseTablet
73+
? "rowReverse"
74+
: "row",
75+
desktop: reverseDesktop ? "rowReverse" : "row",
4976
},
5077
justifyContent: {
5178
mobile: collapseMobile
5279
? alignItemsMobile === "stretch"
5380
? undefined
5481
: alignItemsMobile
82+
: reverseMobile
83+
? invertAlignment(justifyContentMobile)
5584
: justifyContentMobile,
5685
tablet: collapseTablet
5786
? alignItemsTablet === "stretch"
5887
? undefined
5988
: alignItemsTablet
89+
: reverseTablet
90+
? invertAlignment(justifyContentTablet)
6091
: justifyContentTablet,
61-
desktop: justifyContentDesktop,
92+
desktop: reverseDesktop ? invertAlignment(justifyContentDesktop) : justifyContentDesktop,
6293
},
6394
alignItems: {
6495
mobile: collapseMobile ? justifyContentMobile : alignItemsMobile,
@@ -67,3 +98,15 @@ export function responsiveCollapsibleAlignmentProps({
6798
},
6899
};
69100
}
101+
102+
function invertAlignment<Alignment extends string>(alignment: Alignment | undefined) {
103+
if (alignment === "flexStart") {
104+
return "flexEnd";
105+
}
106+
107+
if (alignment === "flexEnd") {
108+
return "flexStart";
109+
}
110+
111+
return alignment;
112+
}

stories/Layout/Columns.stories.tsx

+39
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,42 @@ export const alignYStretch = createStory({
8282
alignY: "stretch",
8383
children: [<Placeholder height={100} />, <Placeholder height="100%" />],
8484
});
85+
86+
export const reverse = createStory({
87+
children: [<Placeholder label="1" />, <Placeholder label="2" />, <Placeholder label="3" />],
88+
reverse: true,
89+
});
90+
91+
export const responsiveReverse = createStory(
92+
{
93+
children: [
94+
<Placeholder background="brandPrimary" />,
95+
<Column width={{ desktop: "content", tablet: "full", mobile: "full" }}>
96+
<Placeholder label="sidebar" background="brandSecondary" />
97+
</Column>,
98+
],
99+
collapseBelow: "desktop",
100+
reverse: {
101+
tablet: true,
102+
mobile: false,
103+
},
104+
},
105+
{ viewport: { defaultViewport: "tablet" } }
106+
);
107+
108+
export const sticky = createStory(
109+
{
110+
collapseBelow: "desktop",
111+
reverse: { tablet: true },
112+
children: [
113+
<Placeholder background="brandPrimary" height={600} />,
114+
<Column
115+
width={{ desktop: "content", tablet: "full" }}
116+
sticky={{ top: { desktop: 40, tablet: 16 } }}
117+
>
118+
<Placeholder label="sidebar" background="brandSecondary" />
119+
</Column>,
120+
],
121+
},
122+
{ viewport: { defaultViewport: "mobile1" } }
123+
);

0 commit comments

Comments
 (0)