Skip to content

Commit

Permalink
Merge pull request #41 from buildo/3154377-port_flex_reverse_sticky
Browse files Browse the repository at this point in the history
  • Loading branch information
gabro committed Mar 3, 2022
2 parents c344d7c + c21c557 commit 2cf4e2e
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 37 deletions.
2 changes: 1 addition & 1 deletion src/Form/createFormLayoutComponents.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FunctionComponent } from "react";
import { ActionsProps } from "src";
import { ActionsProps } from "../";
import { createForm, FormConfig } from "./Form";
import { createFormRow, FormRowConfig } from "./FormRow";
import { createFormSection, FormSectionConfig } from "./FormSection";
Expand Down
32 changes: 30 additions & 2 deletions src/Layout/Column.css.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { StyleRule, styleVariants } from "@vanilla-extract/css";
import { bentoSprinkles } from "../internal/sprinkles.css";
import { Breakpoint, breakpoints } from "../util/breakpoints";

const styleForScale = (scale: number): StyleRule => ({
flex: `0 0 ${scale * 100}%`,
width: "100%",
});

export const width = styleVariants({
export const fullWidth = bentoSprinkles({
width: "full",
});

const widths = {
content: { flexShrink: 0 },
full: styleForScale(1),
"1/2": styleForScale(1 / 2),
"1/3": styleForScale(1 / 3),
"2/3": styleForScale(2 / 3),
Expand All @@ -14,4 +23,23 @@ export const width = styleVariants({
"2/5": styleForScale(2 / 5),
"3/5": styleForScale(3 / 5),
"4/5": styleForScale(4 / 5),
});
};

const makeWidthVariants = (breakpoint: Breakpoint) =>
styleVariants(widths, (widthStyle) => {
switch (breakpoint) {
case "desktop":
return widthStyle;
case "tablet":
case "mobile":
return {
"@media": {
[breakpoints[breakpoint]["@media"]]: widthStyle,
},
};
}
});

export const desktopWidths = makeWidthVariants("desktop");
export const tabletWidths = makeWidthVariants("tablet");
export const mobileWidths = makeWidthVariants("mobile");
46 changes: 30 additions & 16 deletions src/Layout/createColumns.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,49 @@
import { ReactChild, ReactElement } from "react";
import flattenChildren from "react-keyed-flatten-children";
import { BoxType, BoxProps } from "../Box/createBentoBox";
import { bentoSprinkles } from "../internal/sprinkles.css";
import {
bentoSprinkles,
normalizeResponsiveValue,
OptionalResponsiveValue,
} from "../internal/sprinkles.css";
import { childKey } from "../util/childKey";
import { Children } from "../util/Children";
import {
CollapsibleAlignmentProps,
responsiveCollapsibleAlignmentProps,
} from "../util/collapsible";
import * as columnStyles from "./Column.css";
import { desktopWidths, tabletWidths, mobileWidths, fullWidth } from "./Column.css";

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

type ColumnProps = {
children: Children;
width?: keyof typeof columnStyles.width | "content";
width?: OptionalResponsiveValue<keyof typeof desktopWidths>;
sticky?: {
top: BoxProps<AtomsFn>["top"];
};
};

function Column(props: ColumnProps) {
function Column({ children, width, sticky }: ColumnProps) {
const { desktop, tablet, mobile } = width
? normalizeResponsiveValue(width)
: { desktop: undefined, tablet: undefined, mobile: undefined };

const className =
width == null
? fullWidth
: [
desktop && desktopWidths[desktop],
tablet && tabletWidths[tablet],
mobile && mobileWidths[mobile],
];

const stickyProps = sticky ? ({ position: "sticky", top: sticky.top } as const) : {};

return (
<Box
className={
props.width != null && props.width !== "content"
? columnStyles.width[props.width]
: undefined
}
width={props.width !== "content" ? "full" : undefined}
flexShrink={props.width === "content" ? 0 : undefined}
>
{props.children}
<Box className={className} {...stickyProps}>
{children}
</Box>
);
}
Expand All @@ -39,12 +53,12 @@ export function createColumns<AtomsFn extends typeof bentoSprinkles>(Box: BoxTyp
children: Children;
} & CollapsibleAlignmentProps;

function Columns({ space, children, align, alignY, collapseBelow }: Props) {
function Columns({ space, children, align, alignY, collapseBelow, reverse }: Props) {
return (
<Box
display="flex"
gap={space}
{...responsiveCollapsibleAlignmentProps({ align, alignY, collapseBelow })}
{...responsiveCollapsibleAlignmentProps({ align, alignY, collapseBelow, reverse })}
>
{flattenChildren(children).map((child, index) => {
if (isColumn(child)) {
Expand Down
2 changes: 2 additions & 0 deletions src/Layout/createInline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export function createInline<AtomsFn extends typeof bentoSprinkles>(Box: BoxType
align,
alignY,
collapseBelow,
reverse,
...boxProps
}: InlineProps) {
return (
Expand All @@ -34,6 +35,7 @@ export function createInline<AtomsFn extends typeof bentoSprinkles>(Box: BoxType
align,
alignY,
collapseBelow,
reverse,
})}
>
{flattenChildren(children)}
Expand Down
6 changes: 4 additions & 2 deletions src/Placeholder/Placeholder.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Inset } from "../internal";
import { BentoSprinkles, Inset } from "../internal";
import { Box } from "../internal/Box/Box";
import { vars } from "../vars.css";
import { unsafeLocalizedString } from "..";
Expand All @@ -7,16 +7,18 @@ type Props = {
height?: string | number;
width?: string | number;
label?: string;
background?: BentoSprinkles["background"];
};

export function Placeholder({ label, height = 120, width = "auto" }: Props) {
export function Placeholder({ label, height = 120, width = "auto", background }: Props) {
return (
<Box
position="relative"
display="flex"
style={{ height, width, border: `2px solid ${vars.outlineColor.outlineDecorative}` }}
justifyContent="center"
alignItems="center"
background={background}
>
{label ? (
<Inset space={8}>{unsafeLocalizedString(label)}</Inset>
Expand Down
2 changes: 1 addition & 1 deletion src/internal/sprinkles.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { statusConditions } from "../util/conditions";
const unconditionalAtomicProperties = defineProperties({
properties: unconditionalProperties,
shorthands: {
inset: ["top", "right", "bottom", "left"],
borderTopRadius: ["borderTopLeftRadius", "borderTopRightRadius"],
borderBottomRadius: ["borderBottomLeftRadius", "borderBottomRightRadius"],
},
Expand All @@ -24,6 +23,7 @@ const responsiveAtomicProperties = defineProperties({
defaultCondition: "desktop",
properties: responsiveProperties,
shorthands: {
inset: ["top", "right", "bottom", "left"],
padding: ["paddingTop", "paddingBottom", "paddingLeft", "paddingRight"],
paddingX: ["paddingLeft", "paddingRight"],
paddingY: ["paddingTop", "paddingBottom"],
Expand Down
25 changes: 15 additions & 10 deletions src/util/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,11 @@ export const unconditionalProperties = {
fontSize: vars.fontSize,
lineHeight: vars.lineHeight,
letterSpacing: vars.letterSpacing,
width: {
...vars.space,
full: "100%",
},
height: {
...vars.space,
full: "100%",
},
top: vars.space,
bottom: vars.space,
left: vars.space,
right: vars.space,
position: ["relative", "absolute", "fixed"],
position: ["relative", "absolute", "fixed", "sticky"],
overflow: ["hidden", "visible", "auto"],
overflowX: ["hidden", "visible", "auto"],
overflowY: ["hidden", "visible", "auto"],
Expand All @@ -40,7 +32,12 @@ export const unconditionalProperties = {

export const responsiveProperties = {
display: ["flex", "none", "block", "grid", "inline-block"],
flexDirection: ["row", "column"],
flexDirection: {
row: "row",
column: "column",
rowReverse: "row-reverse",
columnReverse: "column-reverse",
},
alignItems: {
flexStart: "flex-start",
center: "center",
Expand All @@ -58,6 +55,10 @@ export const responsiveProperties = {
wrapReverse: "wrap-reverse",
},
flexShrink: [0],
width: {
...vars.space,
full: "100%",
},
paddingTop: vars.space,
paddingBottom: vars.space,
paddingLeft: vars.space,
Expand All @@ -68,6 +69,10 @@ export const responsiveProperties = {
700: "700px",
1440: "1440px",
},
top: vars.space,
bottom: vars.space,
left: vars.space,
right: vars.space,
} as const;

const color = {
Expand Down
51 changes: 47 additions & 4 deletions src/util/collapsible.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ export type CollapsibleAlignmentProps = {
align?: ResponsiveAlign;
alignY?: ResponsiveAlignY;
collapseBelow?: Exclude<Breakpoint, "mobile">;
reverse?: boolean | Partial<Record<Breakpoint, boolean>>;
};

export function responsiveCollapsibleAlignmentProps({
align,
alignY,
collapseBelow,
reverse,
}: CollapsibleAlignmentProps): Pick<
BentoSprinkles,
"flexDirection" | "justifyContent" | "alignItems"
Expand All @@ -27,6 +29,19 @@ export function responsiveCollapsibleAlignmentProps({
}
})();

const normalizedReverse = (() => {
if (typeof reverse === "boolean") {
return { desktop: reverse, tablet: reverse, mobile: reverse };
}
return reverse || {};
})();

const {
desktop: reverseDesktop,
tablet: reverseTablet = reverseDesktop,
mobile: reverseMobile = reverseTablet,
} = normalizedReverse;

const normalizedAlign = normalizeResponsiveValue(alignToFlexAlign(align) || "flexStart");
const {
desktop: justifyContentDesktop,
Expand All @@ -43,22 +58,38 @@ export function responsiveCollapsibleAlignmentProps({

return {
flexDirection: {
mobile: collapseMobile ? "column" : "row",
tablet: collapseTablet ? "column" : "row",
desktop: "row",
mobile: collapseMobile
? reverseMobile
? "columnReverse"
: "column"
: reverseMobile
? "rowReverse"
: "row",
tablet: collapseTablet
? reverseTablet
? "columnReverse"
: "column"
: reverseTablet
? "rowReverse"
: "row",
desktop: reverseDesktop ? "rowReverse" : "row",
},
justifyContent: {
mobile: collapseMobile
? alignItemsMobile === "stretch"
? undefined
: alignItemsMobile
: reverseMobile
? invertAlignment(justifyContentMobile)
: justifyContentMobile,
tablet: collapseTablet
? alignItemsTablet === "stretch"
? undefined
: alignItemsTablet
: reverseTablet
? invertAlignment(justifyContentTablet)
: justifyContentTablet,
desktop: justifyContentDesktop,
desktop: reverseDesktop ? invertAlignment(justifyContentDesktop) : justifyContentDesktop,
},
alignItems: {
mobile: collapseMobile ? justifyContentMobile : alignItemsMobile,
Expand All @@ -67,3 +98,15 @@ export function responsiveCollapsibleAlignmentProps({
},
};
}

function invertAlignment<Alignment extends string>(alignment: Alignment | undefined) {
if (alignment === "flexStart") {
return "flexEnd";
}

if (alignment === "flexEnd") {
return "flexStart";
}

return alignment;
}
39 changes: 39 additions & 0 deletions stories/Layout/Columns.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,42 @@ export const alignYStretch = createStory({
alignY: "stretch",
children: [<Placeholder height={100} />, <Placeholder height="100%" />],
});

export const reverse = createStory({
children: [<Placeholder label="1" />, <Placeholder label="2" />, <Placeholder label="3" />],
reverse: true,
});

export const responsiveReverse = createStory(
{
children: [
<Placeholder background="brandPrimary" />,
<Column width={{ desktop: "content", tablet: "full", mobile: "full" }}>
<Placeholder label="sidebar" background="brandSecondary" />
</Column>,
],
collapseBelow: "desktop",
reverse: {
tablet: true,
mobile: false,
},
},
{ viewport: { defaultViewport: "tablet" } }
);

export const sticky = createStory(
{
collapseBelow: "desktop",
reverse: { tablet: true },
children: [
<Placeholder background="brandPrimary" height={600} />,
<Column
width={{ desktop: "content", tablet: "full" }}
sticky={{ top: { desktop: 40, tablet: 16 } }}
>
<Placeholder label="sidebar" background="brandSecondary" />
</Column>,
],
},
{ viewport: { defaultViewport: "mobile1" } }
);
Loading

0 comments on commit 2cf4e2e

Please sign in to comment.