Skip to content

Instantly share code, notes, and snippets.

@smashercosmo
Last active Jun 11, 2021
Embed
What would you like to do?
Base components
.root {
--pl: initial;
--pr: initial;
--pt: initial;
--pb: initial;
--ml: initial;
--mr: initial;
--mt: initial;
--mb: initial;
--w: initial;
--h: initial;
--max-w: initial;
--min-w: initial;
--max-h: initial;
--min-h: initial;
--d: initial;
padding: var(--pt, 0) var(--pr, 0) var(--pb, 0) var(--pl, 0);
margin: var(--mt, 0) var(--mr, 0) var(--mb, 0) var(--ml, 0);
width: var(--w);
height: var(--h);
min-width: var(--min-w);
min-height: var(--min-h);
max-width: var(--max-w);
max-height: var(--max-h);
display: var(--d);
}
div.box {
display: var(--d, block);
}
div.grid {
display: var(--d, grid);
}
div.flex {
display: var(--d, flex);
}
import React from 'react'
import clsx from 'clsx'
import { getPadding } from './getPadding'
import { getMargin } from './getMargin'
import { getDimensions } from './getDimensions'
import './Base.css'
type Padding = string | number
type Margin = string | number
type Dimensions = string | number
type Display =
| 'flex'
| 'grid'
| 'inline'
| 'block'
| 'inline-block'
| 'none'
type BaseProps = {
p?: Padding
px?: Padding
py?: Padding
pl?: Padding
pr?: Padding
pt?: Padding
pb?: Padding
m?: Margin
mx?: Margin
my?: Margin
ml?: Margin
mr?: Margin
mt?: Margin
mb?: Margin
width?: Dimensions
height?: Dimensions
minHeight?: Dimensions
minWidth?: Dimensions
maxHeight?: Dimensions
maxWidth?: Dimensions
display?: Display
__dangerousClassName?: string
__dangerousStyle?: React.CSSProperties
}
function useProps<P>({
props,
componentClassName,
}: {
props: BaseProps & P
componentClassName?: string
}) {
const {
p,
px,
py,
pl,
pr,
pt,
pb,
m,
mx,
my,
ml,
mr,
mt,
mb,
width,
height,
minWidth,
minHeight,
maxWidth,
maxHeight,
display,
__dangerousStyle,
__dangerousClassName,
...rest
} = props
const className = clsx('root', componentClassName, __dangerousClassName)
const style = {
...getPadding({ p, px, py, pl, pr, pt, pb }),
...getMargin({ m, mx, my, ml, mr, mt, mb }),
...getDimensions({
width,
height,
maxWidth,
maxHeight,
minWidth,
minHeight,
}),
...(display !== undefined && { ['--d']: display }),
...__dangerousStyle,
}
return {
style,
className,
props: rest,
}
}
type HtmlPropsWithoutClassNameAndStyle<
T extends keyof JSX.IntrinsicElements | HTMLElement,
> = Omit<
T extends keyof JSX.IntrinsicElements
? JSX.IntrinsicElements[T]
: React.DetailedHTMLProps<React.HTMLAttributes<T>, T>,
'className' | 'style'
>
const [Box, Grid, Flex] = (
[
{ displayName: 'Box', tag: 'div', className: 'box' },
{ displayName: 'Grid', tag: 'div', className: 'grid' },
{ displayName: 'Flex', tag: 'div', className: 'flex' },
] as const
).map(function createBoxElements({
displayName,
tag: Tag,
className: componentClassName,
}) {
function Component(
props: BaseProps & HtmlPropsWithoutClassNameAndStyle<'div'>,
) {
const { children } = props
const {
style,
className,
props: rest,
} = useProps<HtmlPropsWithoutClassNameAndStyle<'div'>>({
props,
componentClassName,
})
return (
<Tag className={className} style={style} {...rest}>
{children}
</Tag>
)
}
Component.displayName = displayName
return Component
})
export { Box }
export { Grid }
export { Flex }
import { getProperty } from './getProperty'
import type { Dimensions } from './Base'
function getDimensions(props: {
width?: Dimensions
height?: Dimensions
minWidth?: Dimensions
maxWidth?: Dimensions
minHeight?: Dimensions
maxHeight?: Dimensions
}) {
const { width, height, minWidth, minHeight, maxWidth, maxHeight } = props
const names = {
width: 'w',
height: 'h',
minWidth: 'min-w',
minHeight: 'min-h',
maxWidth: 'max-w',
maxHeight: 'max-h',
}
return {
...getProperty({ name: names.width, value: width }),
...getProperty({ name: names.height, value: height }),
...getProperty({ name: names.minWidth, value: minWidth }),
...getProperty({ name: names.minHeight, value: minHeight }),
...getProperty({ name: names.maxWidth, value: maxWidth }),
...getProperty({ name: names.maxHeight, value: maxHeight }),
}
}
export { getDimensions }
import { getProperty } from './getProperty'
import type { Margin } from './Base'
function getMargin(props: {
m?: Margin
mx?: Margin
my?: Margin
ml?: Margin
mr?: Margin
mt?: Margin
mb?: Margin
}) {
const { m, mx, my, ml: _ml, mr: _mr, mt: _mt, mb: _mb } = props
const names = {
ml: 'ml',
mr: 'mr',
mt: 'mt',
mb: 'mb',
}
const values = {
ml: _ml ?? mx ?? m,
mr: _mr ?? mx ?? m,
mt: _mt ?? my ?? m,
mb: _mb ?? my ?? m,
}
const ml = getProperty({
name: names.ml,
value: values.ml,
})
const mr = getProperty({
name: names.mr,
value: values.mr,
})
const mt = getProperty({
name: names.mt,
value: values.mt,
})
const mb = getProperty({
name: names.mb,
value: values.mb,
})
return {
...ml,
...mr,
...mt,
...mb,
}
}
export { getMargin }
import { getProperty } from './getProperty'
import type { Padding } from './Base'
function getPadding(props: {
p?: Padding
px?: Padding
py?: Padding
pl?: Padding
pr?: Padding
pt?: Padding
pb?: Padding
}) {
const { p, px, py, pl: _pl, pr: _pr, pt: _pt, pb: _pb } = props
const names = {
pl: 'pl',
pr: 'pr',
pt: 'pt',
pb: 'pb',
}
const values = {
pl: _pl ?? px ?? p,
pr: _pr ?? px ?? p,
pt: _pt ?? py ?? p,
pb: _pb ?? py ?? p,
}
const pl = getProperty({
name: names.pl,
value: values.pl,
})
const pr = getProperty({
name: names.pr,
value: values.pr,
})
const pt = getProperty({
name: names.pt,
value: values.pt,
})
const pb = getProperty({
name: names.pb,
value: values.pb,
})
return {
...pl,
...pr,
...pt,
...pb,
}
}
export { getPadding }
export function getProperty({
name,
value: v,
}: {
name: string
value?: number | string
}) {
if (v === null || v === undefined) return undefined
const value = typeof v === 'number' ? `${v}px` : v
return {
[`--${name}`]: value,
}
}
import React from 'react'
import { Box, Flex } from '../Base/Base'
export function SomeComponent(props: TilesProps) {
const { children, gap, width, mode } = props
return (
<Box display="inline-block" width={300} p={20} m={20}>
<Flex px={10}>
<Box>Hello</Box>
<Box>World</Box>
</Flex>
</Box>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment