Skip to content

Instantly share code, notes, and snippets.

@bbg
Last active July 17, 2022 12:19
Show Gist options
  • Save bbg/5224d4e10034f3e7eb1f3be9f788cd32 to your computer and use it in GitHub Desktop.
Save bbg/5224d4e10034f3e7eb1f3be9f788cd32 to your computer and use it in GitHub Desktop.
generic-component-styled
/// <reference types="styled-jsx" />
import clsx from 'clsx';
import React, { Fragment } from 'react';
type Property = {
display:
| 'block'
| 'inline-block'
| 'inline'
| 'flex'
| 'inline-flex'
| 'table'
| 'inline-table'
| 'table-caption'
| 'table-cell'
| 'table-column'
| 'table-column-group'
| 'table-footer-group'
| 'table-header-group'
| 'table-row-group'
| 'table-row'
| 'flow-root'
| 'grid'
| 'inline-grid'
| 'contents'
| 'list-item'
| 'none';
flexDirection: 'row' | 'column' | 'row-reverse' | 'column-reverse' | string;
flexWrap: 'wrap' | 'nowrap' | 'wrap-reverse' | string;
flexBasis: number | string;
flexGrow: '1' | '0' | string;
flexShrink: '1' | '0' | string;
justifyContent:
| 'flex-start'
| 'flex-end'
| 'center'
| 'space-between'
| 'space-around'
| string;
alignItems:
| 'flex-start'
| 'flex-end'
| 'center'
| 'baseline'
| 'stretch'
| string;
alignContent:
| 'flex-start'
| 'flex-end'
| 'center'
| 'space-between'
| 'space-around'
| 'stretch'
| string;
order:
| '0'
| '1'
| '2'
| '3'
| '4'
| '5'
| '6'
| '7'
| '8'
| '9'
| '10'
| '11'
| '12'
| '-9999'
| '9999'
| 'auto'
| string;
flex:
| '1 1 0%'
| '0 1 auto'
| '1 1 auto'
| 'initial'
| 'inherit'
| 'unset'
| 'none'
| string;
alignSelf:
| 'auto'
| 'flex-start'
| 'flex-end'
| 'center'
| 'baseline'
| 'stretch'
| string;
justifySelf:
| 'auto'
| 'flex-start'
| 'flex-end'
| 'center'
| 'space-between'
| 'space-around'
| string;
shrink: number;
placeContent:
| 'center'
| 'start'
| 'end'
| 'space-between'
| 'space-around'
| 'space-evenly'
| 'stretch'
| string;
placeItems: 'center' | 'start' | 'end' | 'stretch' | string;
placeSelf: 'center' | 'start' | 'end' | 'stretch' | 'auto' | string;
gridTemplateColumns: string;
gridColumn: string;
gridColumnStart:
| '1'
| '2'
| '3'
| '4'
| '5'
| '6'
| '7'
| '8'
| '9'
| '10'
| '11'
| '12'
| '13'
| 'auto'
| string;
gridColumnEnd:
| '1'
| '2'
| '3'
| '4'
| '5'
| '6'
| '7'
| '8'
| '9'
| '10'
| '11'
| '12'
| '13'
| 'auto'
| string;
gridTemplateRows: 'none' | string;
gridRow: string;
gridRowStart: '1' | '2' | '3' | '4' | '5' | '6' | '7' | 'auto' | string;
gridRowEnd: '1' | '2' | '3' | '4' | '5' | '6' | '7' | 'auto' | string;
gridAutoFlow:
| 'row'
| 'column'
| 'dense'
| 'row dense'
| 'column dense'
| string;
gridAutoColumns:
| 'auto'
| 'min-content'
| 'max-content'
| 'minmax(0, 1fr)'
| string;
gridAutoRows:
| 'auto'
| 'min-content'
| 'max-content'
| 'minmax(0, 1fr)'
| string;
p: number | string;
m: number | string;
w: number | string;
h: number | string;
minw: number | string;
maxw: number | string;
minh: number | string;
maxh: number | string;
ygap: number | string;
xgap: number | string;
radius: number | string;
lineHeight: number | string;
color: string;
bgColor: string;
fontSize: string;
fontWeight: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
textAlign: 'left' | 'right' | 'center' | 'justify' | string;
textColor: string;
textDecoration:
| 'underline'
| 'line-through'
| 'none'
| 'overline'
| 'no-underline'
| string;
textDecorationColor: string;
textDecorationStyle:
| 'solid'
| 'double'
| 'dotted'
| 'dashed'
| 'wavy'
| string;
textDecorationThickness: number | string;
textUnderlineOffset: number | string;
textTransform: string;
textOverflow: string;
textIndent: number | string;
border: string;
borderWidth: number;
borderStyle: string;
borderColor: string;
borderTop: string;
borderRight: string;
borderBottom: string;
borderLeft: string;
borderTopLeftRadius: string;
borderTopRightRadius: string;
borderBottomLeftRadius: string;
borderBottomRightRadius: string;
borderImage: string;
borderImageSlice: string;
borderImageWidth: string;
borderImageOutset: string;
borderCollapse: 'separate' | 'collapse' | string;
borderSpacing: number | string;
tableLayout: 'auto' | 'fixed' | string;
outline: string;
outlineWidth: number | string;
outlineColor: string;
outlineStyle:
| 'solid'
| 'double'
| 'dotted'
| 'dashed'
| 'wavy'
| 'hidden'
| string;
outlineOffset: number | string;
outlineRadius: number | string;
backgroundAttachment: 'fixed' | 'scroll' | 'local' | string;
backgroundClip:
| 'border-box'
| 'padding-box'
| 'content-box'
| 'text'
| string;
backgroundColor: string;
backgroundOrigin: 'border-box' | 'padding-box' | 'content-box' | string;
backgroundPosition:
| 'left'
| 'center'
| 'right'
| 'bottom'
| 'center'
| 'left bottom'
| 'left top'
| 'right bottom'
| 'right top'
| 'top'
| string;
backgroundRepeat:
| 'repeat'
| 'repeat-x'
| 'repeat-y'
| 'no-repeat'
| 'round'
| 'space'
| string;
backgroundSize: 'auto' | 'cover' | 'contain' | string;
backgroundImage: string;
boxShadow: string;
opacity: number | string;
zIndex: number | string;
mixBlendMode:
| 'normal'
| 'multiply'
| 'screen'
| 'overlay'
| 'darken'
| 'lighten'
| 'color-dodge'
| 'color-burn'
| 'hard-light'
| 'soft-light'
| 'difference'
| 'exclusion'
| 'hue'
| 'saturation'
| 'color'
| 'luminosity'
| 'normal'
| 'inherit'
| 'plus-lighter'
| string;
backgroundBlendMode:
| 'multiply'
| 'screen'
| 'overlay'
| 'darken'
| 'lighten'
| 'color-dodge'
| 'color-burn'
| 'hard-light'
| 'soft-light'
| 'difference'
| 'exclusion'
| 'hue'
| 'saturation'
| 'color'
| 'luminosity'
| 'normal'
| 'inherit'
| string;
filter: string;
backdropFilter: string;
transform: string;
transformOrigin: string;
transition: string;
transitionProperty: string;
transitionDuration: string;
transitionTimingFunction: string;
transitionDelay: string;
accentColor: string;
appearance: 'none';
cursor: 'auto' | 'default' | 'pointer' | 'text' | 'wait' | string;
caretColor: string;
pointerEvents: 'auto' | 'none' | 'all' | string;
resize: 'none' | 'both' | 'horizontal' | 'vertical' | string;
float: 'left' | 'right' | 'none' | string;
clear: 'left' | 'right' | 'both' | 'none' | string;
isolation: 'isolate' | 'auto' | 'inherit' | 'unset' | string;
objectFit: 'fill' | 'contain' | 'cover' | 'none' | 'scale-down' | string;
objectPosition:
| 'bottom'
| 'center'
| 'left'
| 'left bottom'
| 'left top'
| 'right'
| 'right bottom'
| 'right top'
| 'top'
| string;
overflow: 'auto' | 'hidden' | 'clip' | 'auto' | 'visible' | 'scroll' | string;
overflowX:
| 'auto'
| 'hidden'
| 'clip'
| 'auto'
| 'visible'
| 'scroll'
| string;
overflowY:
| 'auto'
| 'hidden'
| 'clip'
| 'auto'
| 'visible'
| 'scroll'
| string;
overscroll: 'auto' | 'contain' | 'none' | string;
overscrollX: 'auto' | 'contain' | 'none' | string;
overscrollY: 'auto' | 'contain' | 'none' | string;
verticalAlign:
| 'baseline'
| 'top'
| 'middle'
| 'bottom'
| 'text-top'
| 'text-bottom'
| 'sub'
| 'super'
| string;
whiteSpace: 'normal' | 'nowrap' | 'pre' | 'pre-line' | 'pre-wrap' | string;
wordWrap: string;
content: 'none';
scrollBehavior: 'auto' | 'smooth';
scrollMargin: number | string;
scrollMarginLeft: number | string;
scrollMarginRight: number | string;
scrollMarginTop: number | string;
scrollMarginBottom: number | string;
scrollPadding: number | string;
scrollPaddingLeft: number | string;
scrollPaddingRight: number | string;
scrollPaddingTop: number | string;
scrollPaddingBottom: number | string;
scrollSnapAlign: 'none' | 'start' | 'center' | 'end' | 'stretch';
scrollSnapStop: 'always' | 'normal' | 'never';
scrollSnapType: 'none' | 'mandatory' | 'proximity' | string;
touchAction:
| 'auto'
| 'none'
| 'pan-x'
| 'pan-left;'
| 'pan-right;'
| 'pan-y'
| 'pan-up'
| 'pan-down'
| 'pinch-zoom'
| 'manipulation';
userSelect: 'none' | 'text' | 'all' | 'auto' | string;
willChange: 'auto' | 'scroll-position' | 'contents' | 'transform' | string;
fill: string;
stroke: string;
strokeWidth: number | string;
aspectRatio: 'auto' | '1 / 1' | '16 / 9' | number | string;
columns: number | string;
breakAfter:
| 'auto'
| 'avoid'
| 'all'
| 'avoid-page'
| 'page'
| 'left'
| 'right'
| 'column'
| string;
breakBefore:
| 'auto'
| 'avoid'
| 'all'
| 'avoid-page'
| 'page'
| 'left'
| 'right'
| 'column'
| string;
breakInside: 'auto' | 'avoid' | 'avoid-page' | 'avoid-column' | string;
boxDecorationBreak: 'slice' | 'clone' | string;
boxSizing: 'border-box' | 'content-box' | string;
position: 'static' | 'relative' | 'absolute' | 'fixed' | 'sticky' | string;
top: number | string;
left: number | string;
bottom: number | string;
right: number | string;
visibility: 'visible' | 'hidden' | 'collapse' | string;
hover: Record<string, string>;
/* hover?: {
[property: string]: Property;
}; */
};
type GenericOwnProps<E extends React.ElementType = React.ElementType> = Partial<
Property
> & {
children?: JSX.Element | JSX.Element[] | string;
as?: E;
cls?: string | Record<string, string> | string[];
};
export type GenericProps<E extends React.ElementType> = GenericOwnProps<E> &
Omit<React.ComponentProps<E>, keyof GenericOwnProps>;
const __DEFAULT_ELEMENT__ = 'div';
const shortName = [
{
shortname: 'p',
longname: 'padding',
},
{
shortname: 'm',
longname: 'margin',
},
{
shortname: 'w',
longname: 'width',
},
{
shortname: 'h',
longname: 'height',
},
{
shortname: 'minw',
longname: 'minWidth',
},
{
shortname: 'maxw',
longname: 'maxWidth',
},
{
shortname: 'minh',
longname: 'minHeight',
},
{
shortname: 'maxh',
longname: 'maxHeight',
},
{
shortname: 'ygap',
longname: 'rowGap',
},
{
shortname: 'xgap',
longname: 'columnGap',
},
{
shortname: 'bgColor',
longname: 'backgroundColor',
},
{
shortname: 'radius',
longname: 'borderRadius',
},
{
shortname: 'overscroll',
longname: 'overscrollBehavior',
},
{
shortname: 'overscrollX',
longname: 'overscrollBehaviorX',
},
{
shortname: 'overscrollY',
longname: 'overscrollBehaviorY',
},
];
function longName(value: string): string {
const object = shortName.find(data => data?.shortname === value);
return object ? object.longname : value;
}
function converter(value: string | number): string {
return typeof value === 'number' ? `${value}rem` : value;
}
function className(value: string): string {
const property = longName(value);
return property.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
}
function css(props: Record<string, string>): string {
return Object.entries(props)
.map(([key, value]) => `${className(key)}: ${converter(value)};`)
.join(';');
}
function Title<E extends React.ElementType = typeof __DEFAULT_ELEMENT__>({
children,
as,
cls,
hover,
...props
}: GenericProps<E>) {
const Component = as || __DEFAULT_ELEMENT__;
return (
<Fragment>
<Component className={clsx('timboo', cls)} {...props}>
{children}
</Component>
<style jsx>{`
.timboo {
${css(props)}
}
.timboo:hover {
${css(hover ?? {})}
}
`}</style>
</Fragment>
);
}
export default Title;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment