2022-03-11 03:13:10 +01:00
|
|
|
import { css } from "@linaria/core";
|
2022-03-11 20:18:26 +01:00
|
|
|
import { h, JSX, VNode, ComponentChildren, createContext } from "preact";
|
2022-03-11 03:13:10 +01:00
|
|
|
import { useContext } from "preact/hooks";
|
2022-03-29 04:41:07 +02:00
|
|
|
// eslint-disable-next-line import/extensions
|
2022-03-11 03:13:10 +01:00
|
|
|
import { theme } from "./style";
|
2022-03-09 18:00:02 +01:00
|
|
|
|
2022-03-11 03:13:10 +01:00
|
|
|
type ResponsiveKeys = "xs" | "sm" | "md" | "lg" | "xl";
|
|
|
|
|
|
|
|
export type ResponsiveSize = {
|
|
|
|
xs: number;
|
|
|
|
sm: number;
|
|
|
|
md: number;
|
|
|
|
lg: number;
|
|
|
|
xl: number;
|
|
|
|
};
|
|
|
|
|
|
|
|
const root = css`
|
|
|
|
box-sizing: border-box;
|
|
|
|
`;
|
|
|
|
const containerStyle = css`
|
|
|
|
display: flex;
|
|
|
|
flex-wrap: wrap;
|
|
|
|
width: 100%;
|
|
|
|
`;
|
|
|
|
const itemStyle = css`
|
|
|
|
margin: 0;
|
|
|
|
`;
|
|
|
|
const zeroMinWidthStyle = css`
|
|
|
|
min-width: 0px;
|
|
|
|
`;
|
|
|
|
|
|
|
|
type GridSizes = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
|
|
|
type SpacingSizes = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
|
|
|
|
|
2022-03-11 20:18:26 +01:00
|
|
|
export interface Props extends JSX.HTMLAttributes<HTMLDivElement> {
|
2022-03-11 03:13:10 +01:00
|
|
|
columns?: number | Partial<ResponsiveSize>;
|
2022-03-09 18:00:02 +01:00
|
|
|
container?: boolean;
|
|
|
|
item?: boolean;
|
2022-03-11 03:13:10 +01:00
|
|
|
|
|
|
|
direction?: "column-reverse" | "column" | "row-reverse" | "row";
|
|
|
|
|
|
|
|
lg?: GridSizes | "auto" | "true";
|
|
|
|
md?: GridSizes | "auto" | "true";
|
|
|
|
sm?: GridSizes | "auto" | "true";
|
|
|
|
xl?: GridSizes | "auto" | "true";
|
|
|
|
xs?: GridSizes | "auto" | "true";
|
|
|
|
|
|
|
|
wrap?: "nowrap" | "wrap-reverse" | "wrap";
|
|
|
|
spacing?: SpacingSizes | Partial<ResponsiveSize>;
|
|
|
|
columnSpacing?: SpacingSizes | Partial<ResponsiveSize>;
|
|
|
|
rowSpacing?: SpacingSizes | Partial<ResponsiveSize>;
|
|
|
|
|
|
|
|
alignItems?: "flex-start" | "flex-end" | "center" | "stretch" | "baseline";
|
|
|
|
justifyContent?:
|
|
|
|
| "flex-start"
|
|
|
|
| "flex-end"
|
|
|
|
| "center"
|
|
|
|
| "space-around"
|
|
|
|
| "space-between"
|
|
|
|
| "space-evenly";
|
|
|
|
|
|
|
|
zeroMinWidth?: boolean;
|
2022-03-09 18:00:02 +01:00
|
|
|
children: ComponentChildren;
|
2022-03-11 03:13:10 +01:00
|
|
|
}
|
|
|
|
theme.breakpoints.up;
|
|
|
|
|
2022-03-29 14:58:06 +02:00
|
|
|
function getOffset(val: number | string): string | number {
|
2022-03-11 03:13:10 +01:00
|
|
|
if (typeof val === "number") `${val}px`;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
const columnGapVariant = css`
|
|
|
|
${theme.breakpoints.up("xs")} {
|
|
|
|
width: calc(100% + var(--space-col-xs));
|
|
|
|
margin-left: calc(-1 * var(--space-col-xs));
|
|
|
|
& > div {
|
|
|
|
padding-left: var(--space-col-xs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
${theme.breakpoints.up("sm")} {
|
|
|
|
width: calc(100% + var(--space-col-sm));
|
|
|
|
margin-left: calc(-1 * var(--space-col-sm));
|
|
|
|
& > div {
|
|
|
|
padding-left: var(--space-col-sm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
${theme.breakpoints.up("md")} {
|
|
|
|
width: calc(100% + var(--space-col-md));
|
|
|
|
margin-left: calc(-1 * var(--space-col-md));
|
|
|
|
& > div {
|
|
|
|
padding-left: var(--space-col-md);
|
|
|
|
}
|
|
|
|
}
|
2022-03-11 06:17:40 +01:00
|
|
|
${theme.breakpoints.up("lg")} {
|
|
|
|
width: calc(100% + var(--space-col-lg));
|
|
|
|
margin-left: calc(-1 * var(--space-col-lg));
|
|
|
|
& > div {
|
|
|
|
padding-left: var(--space-col-lg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
${theme.breakpoints.up("xl")} {
|
|
|
|
width: calc(100% + var(--space-col-xl));
|
|
|
|
margin-left: calc(-1 * var(--space-col-xl));
|
|
|
|
& > div {
|
|
|
|
padding-left: var(--space-col-xl);
|
|
|
|
}
|
|
|
|
}
|
2022-03-11 03:13:10 +01:00
|
|
|
`;
|
|
|
|
const rowGapVariant = css`
|
|
|
|
${theme.breakpoints.up("xs")} {
|
|
|
|
margin-top: calc(-1 * var(--space-row-xs));
|
|
|
|
& > div {
|
|
|
|
padding-top: var(--space-row-xs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
${theme.breakpoints.up("sm")} {
|
|
|
|
margin-top: calc(-1 * var(--space-row-sm));
|
|
|
|
& > div {
|
|
|
|
padding-top: var(--space-row-sm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
${theme.breakpoints.up("md")} {
|
|
|
|
margin-top: calc(-1 * var(--space-row-md));
|
|
|
|
& > div {
|
|
|
|
padding-top: var(--space-row-md);
|
|
|
|
}
|
|
|
|
}
|
2022-03-11 06:17:40 +01:00
|
|
|
${theme.breakpoints.up("lg")} {
|
|
|
|
margin-top: calc(-1 * var(--space-row-lg));
|
|
|
|
& > div {
|
|
|
|
padding-top: var(--space-row-lg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
${theme.breakpoints.up("xl")} {
|
|
|
|
margin-top: calc(-1 * var(--space-row-xl));
|
|
|
|
& > div {
|
|
|
|
padding-top: var(--space-row-xl);
|
|
|
|
}
|
|
|
|
}
|
2022-03-11 03:13:10 +01:00
|
|
|
`;
|
|
|
|
|
2022-03-11 06:17:40 +01:00
|
|
|
const sizeVariantXS = css`
|
2022-03-11 03:13:10 +01:00
|
|
|
${theme.breakpoints.up("xs")} {
|
|
|
|
flex-basis: var(--relation-col-vs-xs);
|
|
|
|
flex-grow: 0;
|
|
|
|
max-width: var(--relation-col-vs-xs);
|
|
|
|
}
|
2022-03-11 06:17:40 +01:00
|
|
|
`;
|
|
|
|
const sizeVariantSM = css`
|
2022-03-11 03:13:10 +01:00
|
|
|
${theme.breakpoints.up("sm")} {
|
|
|
|
flex-basis: var(--relation-col-vs-sm);
|
|
|
|
flex-grow: 0;
|
|
|
|
max-width: var(--relation-col-vs-sm);
|
|
|
|
}
|
2022-03-11 06:17:40 +01:00
|
|
|
`;
|
|
|
|
const sizeVariantMD = css`
|
2022-03-11 03:13:10 +01:00
|
|
|
${theme.breakpoints.up("md")} {
|
|
|
|
flex-basis: var(--relation-col-vs-md);
|
|
|
|
flex-grow: 0;
|
|
|
|
max-width: var(--relation-col-vs-md);
|
|
|
|
}
|
|
|
|
`;
|
2022-03-11 06:17:40 +01:00
|
|
|
const sizeVariantLG = css`
|
|
|
|
${theme.breakpoints.up("lg")} {
|
|
|
|
flex-basis: var(--relation-col-vs-lg);
|
|
|
|
flex-grow: 0;
|
|
|
|
max-width: var(--relation-col-vs-lg);
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
const sizeVariantXL = css`
|
|
|
|
${theme.breakpoints.up("xl")} {
|
|
|
|
flex-basis: var(--relation-col-vs-xl);
|
|
|
|
flex-grow: 0;
|
|
|
|
max-width: var(--relation-col-vs-xl);
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
const sizeVariantExpand = css`
|
|
|
|
flex-basis: 0;
|
|
|
|
flex-grow: 1;
|
|
|
|
max-width: 100%;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const sizeVariantAuto = css`
|
|
|
|
flex-basis: auto;
|
|
|
|
flex-grow: 0;
|
|
|
|
flex-shrink: 0;
|
|
|
|
max-width: none;
|
|
|
|
width: auto;
|
|
|
|
`;
|
2022-03-11 03:13:10 +01:00
|
|
|
|
2022-03-11 06:17:40 +01:00
|
|
|
const GridContext = createContext<Partial<ResponsiveSize>>(toResponsive(12));
|
2022-03-11 03:13:10 +01:00
|
|
|
|
2022-03-11 06:17:40 +01:00
|
|
|
function toResponsive(
|
|
|
|
v: number | Partial<ResponsiveSize>,
|
|
|
|
): Partial<ResponsiveSize> {
|
2022-03-11 03:13:10 +01:00
|
|
|
const p = typeof v === "number" ? { xs: v } : v;
|
2022-03-11 06:17:40 +01:00
|
|
|
const xs = p.xs;
|
2022-03-11 03:13:10 +01:00
|
|
|
const sm = p.sm || xs;
|
|
|
|
const md = p.md || sm;
|
|
|
|
const lg = p.lg || md;
|
|
|
|
const xl = p.xl || lg;
|
|
|
|
return {
|
|
|
|
xs,
|
|
|
|
sm,
|
|
|
|
md,
|
|
|
|
lg,
|
|
|
|
xl,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function Grid({
|
|
|
|
columns: cp,
|
|
|
|
container = false,
|
|
|
|
item = false,
|
|
|
|
direction = "row",
|
|
|
|
lg,
|
|
|
|
md,
|
|
|
|
sm,
|
|
|
|
xl,
|
|
|
|
xs,
|
|
|
|
wrap = "wrap",
|
|
|
|
spacing = 0,
|
|
|
|
columnSpacing: csp,
|
|
|
|
rowSpacing: rsp,
|
|
|
|
alignItems,
|
|
|
|
justifyContent,
|
|
|
|
zeroMinWidth = false,
|
|
|
|
children,
|
2022-03-11 20:18:26 +01:00
|
|
|
onClick,
|
|
|
|
...rest
|
2022-03-11 03:13:10 +01:00
|
|
|
}: Props): VNode {
|
|
|
|
const cc = useContext(GridContext);
|
|
|
|
const columns = !cp ? cc : toResponsive(cp);
|
|
|
|
|
|
|
|
const rowSpacing = rsp ? toResponsive(rsp) : toResponsive(spacing);
|
|
|
|
const columnSpacing = csp ? toResponsive(csp) : toResponsive(spacing);
|
|
|
|
|
|
|
|
const ssize = toResponsive({ xs, md, lg, xl, sm } as any);
|
|
|
|
|
|
|
|
const spacingStyles = !container
|
|
|
|
? {}
|
|
|
|
: {
|
|
|
|
"--space-col-xs": getOffset(theme.spacing(columnSpacing.xs)),
|
|
|
|
"--space-col-sm": getOffset(theme.spacing(columnSpacing.sm)),
|
|
|
|
"--space-col-md": getOffset(theme.spacing(columnSpacing.md)),
|
|
|
|
"--space-col-lg": getOffset(theme.spacing(columnSpacing.lg)),
|
|
|
|
"--space-col-xl": getOffset(theme.spacing(columnSpacing.xl)),
|
|
|
|
|
|
|
|
"--space-row-xs": getOffset(theme.spacing(rowSpacing.xs)),
|
|
|
|
"--space-row-sm": getOffset(theme.spacing(rowSpacing.sm)),
|
|
|
|
"--space-row-md": getOffset(theme.spacing(rowSpacing.md)),
|
|
|
|
"--space-row-lg": getOffset(theme.spacing(rowSpacing.lg)),
|
|
|
|
"--space-row-xl": getOffset(theme.spacing(rowSpacing.xl)),
|
|
|
|
};
|
|
|
|
const relationStyles = !item
|
|
|
|
? {}
|
|
|
|
: {
|
2022-03-11 06:17:40 +01:00
|
|
|
"--relation-col-vs-xs": relation(columns, ssize, "xs"),
|
2022-03-11 03:13:10 +01:00
|
|
|
"--relation-col-vs-sm": relation(columns, ssize, "sm"),
|
2022-03-11 06:17:40 +01:00
|
|
|
"--relation-col-vs-md": relation(columns, ssize, "md"),
|
2022-03-11 03:13:10 +01:00
|
|
|
"--relation-col-vs-lg": relation(columns, ssize, "lg"),
|
|
|
|
"--relation-col-vs-xl": relation(columns, ssize, "xl"),
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<GridContext.Provider value={columns}>
|
|
|
|
<div
|
|
|
|
class={[
|
|
|
|
root,
|
|
|
|
container && containerStyle,
|
|
|
|
item && itemStyle,
|
|
|
|
zeroMinWidth && zeroMinWidthStyle,
|
2022-03-11 06:17:40 +01:00
|
|
|
xs &&
|
|
|
|
(xs === "auto"
|
|
|
|
? sizeVariantAuto
|
|
|
|
: xs === "true"
|
|
|
|
? sizeVariantExpand
|
|
|
|
: sizeVariantXS),
|
|
|
|
sm &&
|
|
|
|
(sm === "auto"
|
|
|
|
? sizeVariantAuto
|
|
|
|
: sm === "true"
|
|
|
|
? sizeVariantExpand
|
|
|
|
: sizeVariantSM),
|
|
|
|
md &&
|
|
|
|
(md === "auto"
|
|
|
|
? sizeVariantAuto
|
|
|
|
: md === "true"
|
|
|
|
? sizeVariantExpand
|
|
|
|
: sizeVariantMD),
|
|
|
|
lg &&
|
|
|
|
(lg === "auto"
|
|
|
|
? sizeVariantAuto
|
|
|
|
: lg === "true"
|
|
|
|
? sizeVariantExpand
|
|
|
|
: sizeVariantLG),
|
|
|
|
xl &&
|
|
|
|
(xl === "auto"
|
|
|
|
? sizeVariantAuto
|
|
|
|
: xl === "true"
|
|
|
|
? sizeVariantExpand
|
|
|
|
: sizeVariantXL),
|
2022-03-11 03:13:10 +01:00
|
|
|
container && columnGapVariant,
|
|
|
|
container && rowGapVariant,
|
|
|
|
].join(" ")}
|
|
|
|
style={{
|
|
|
|
...relationStyles,
|
|
|
|
...spacingStyles,
|
|
|
|
justifyContent,
|
|
|
|
alignItems,
|
2022-03-11 06:17:40 +01:00
|
|
|
flexWrap: wrap,
|
2022-03-11 20:18:26 +01:00
|
|
|
cursor: onClick ? "pointer" : "inherit",
|
2022-03-11 03:13:10 +01:00
|
|
|
}}
|
2022-03-11 20:18:26 +01:00
|
|
|
onClick={onClick}
|
|
|
|
{...rest}
|
2022-03-11 03:13:10 +01:00
|
|
|
>
|
|
|
|
{children}
|
|
|
|
</div>
|
|
|
|
</GridContext.Provider>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
function relation(
|
2022-03-11 06:17:40 +01:00
|
|
|
cols: Partial<ResponsiveSize>,
|
|
|
|
values: Partial<ResponsiveSize>,
|
2022-03-11 03:13:10 +01:00
|
|
|
size: ResponsiveKeys,
|
2022-03-29 14:58:06 +02:00
|
|
|
): string {
|
2022-03-11 03:13:10 +01:00
|
|
|
const colsNum = typeof cols === "number" ? cols : cols[size] || 12;
|
|
|
|
return (
|
|
|
|
String(Math.round(((values[size] || 1) / colsNum) * 10e7) / 10e5) + "%"
|
|
|
|
);
|
2022-03-09 18:00:02 +01:00
|
|
|
}
|