diff --git a/src/styleSheet.ts b/src/styleSheet.ts deleted file mode 100644 index b012ed3..0000000 --- a/src/styleSheet.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { SerializedStyles, css } from '@emotion/react'; -import type { CSSInterpolation } from '@emotion/serialize'; - -type Styles = Record; - -export function styleSheet(styles: S): { [K in keyof S]: SerializedStyles } { - const stylesNames = Object.getOwnPropertyNames(styles) as (keyof S)[]; - const serializedStyles = {} as { [K in keyof S]: SerializedStyles }; - - for (const styleName of stylesNames) { - serializedStyles[styleName] = css(styles[styleName]); - } - - return serializedStyles; -} diff --git a/src/styleSheet/EnhancedCSSProperties/CSSProperties.ts b/src/styleSheet/EnhancedCSSProperties/CSSProperties.ts new file mode 100644 index 0000000..7741249 --- /dev/null +++ b/src/styleSheet/EnhancedCSSProperties/CSSProperties.ts @@ -0,0 +1,3 @@ +import * as CSS from 'csstype'; + +export type CSSProperties = CSS.PropertiesFallback; diff --git a/src/styleSheet/EnhancedCSSProperties/EnhancedCSSObject.ts b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSObject.ts new file mode 100644 index 0000000..04ac0ae --- /dev/null +++ b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSObject.ts @@ -0,0 +1,31 @@ +import * as CSS from 'csstype'; +import { CSSProperties } from './CSSProperties'; +import { toCSSProperty, EnhancedCSSProperties } from './EnhancedCSSProperties'; + +type CSSPseudos

= { [K in CSS.Pseudos]?: CSSObject

}; + +interface CSSOthersObject

{ + [propertiesName: string]: CSSObject

; +} + +export type CSSObject

= (P & CSSPseudos

) | CSSOthersObject

; + +export type EnhancedCSSObject = CSSObject; + +export function toCSSObject( + cssObject: EnhancedCSSObject, +): CSSObject { + const result: any = {}; + + Object.entries(cssObject).forEach(([key, value]) => { + let cleanValue = toCSSProperty(key, value); + + if (typeof cleanValue === 'object') { + cleanValue = toCSSObject(cleanValue); + } + + result[key] = cleanValue; + }); + + return result; +} diff --git a/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/EnhancedCSSProperties.ts b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/EnhancedCSSProperties.ts new file mode 100644 index 0000000..a9124ea --- /dev/null +++ b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/EnhancedCSSProperties.ts @@ -0,0 +1,16 @@ +import { CSSProperties } from '../CSSProperties'; +import { enhancers, Enhancers } from './enhancers'; + +export type EnhancedCSSProperties = { + [K in keyof CSSProperties]: K extends keyof Enhancers + ? Parameters[0] + : CSSProperties[K]; +}; + +export function toCSSProperty(property: string, value: any) { + if (property in enhancers) { + return enhancers[property as keyof Enhancers](value); + } + + return value; +} diff --git a/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/area.ts b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/area.ts new file mode 100644 index 0000000..17df858 --- /dev/null +++ b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/area.ts @@ -0,0 +1,42 @@ +type AreaValue = number | string | 'auto'; + +type AreaOptions = + | { vertical: AreaValue; horizontal: AreaValue } + | { top: AreaValue; horizontal: AreaValue; bottom: AreaValue } + | { top: AreaValue; right: AreaValue; bottom: AreaValue; left: AreaValue } + | [top: AreaValue, right: AreaValue, bottom: AreaValue, left: AreaValue] + | [top: AreaValue, horizontal: AreaValue, bottom: AreaValue] + | [vertical: AreaValue, horizontal: AreaValue] + | AreaValue; + +function getAreaFromValue(value: AreaOptions): AreaValue[] { + if (Array.isArray(value)) { + return value; + } + + if (typeof value === 'number' || typeof value === 'string') { + return [value]; + } + + if ('left' in value) { + return [value.top, value.right, value.bottom, value.left]; + } + + if ('bottom' in value) { + return [value.top, value.horizontal, value.bottom]; + } + + return [value.vertical, value.horizontal]; +} + +export function area(value: AreaOptions): string { + return getAreaFromValue(value) + .map((area) => { + if (typeof area === 'number') { + return `${area}px`; + } + + return area; + }) + .join(' '); +} diff --git a/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/background.ts b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/background.ts new file mode 100644 index 0000000..b9bbe90 --- /dev/null +++ b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/background.ts @@ -0,0 +1,93 @@ +import { GlobalValues } from '../../../../types'; + +type BackgroundRepeatValues = + | 'repeat-x' + | 'repeat-y' + | 'repeat' + | 'space' + | 'round' + | 'no-repeat'; + +type BackgroundAttachment = 'scroll' | 'fixed' | 'local' | GlobalValues; +type BackgroundClip = + | 'border-box' + | 'padding-box' + | 'content-box' + | 'text' + | GlobalValues; +type BackgroundColor = string | GlobalValues; +type BackgroundImage = string; +type BackgroundOrigin = + | 'border-box' + | 'padding-box' + | 'content-box' + | GlobalValues; +type BackgroundPosition = string | GlobalValues; +type BackgroundRepeat = + | BackgroundRepeatValues + | [BackgroundRepeatValues, BackgroundRepeatValues] + | GlobalValues; +type BackgroundSize = string | GlobalValues; + +interface BackgroundWithoutSize { + attachment?: BackgroundAttachment; + clip?: BackgroundClip; + color?: BackgroundColor; + image?: BackgroundImage; + origin?: BackgroundOrigin; + position?: BackgroundSize; + repeat?: BackgroundRepeat; +} + +type BackgroundWithSize = Omit & { + position: BackgroundPosition; + size?: BackgroundSize; +}; + +type BackgroundOptions = BackgroundWithoutSize | BackgroundWithSize; + +function isBackgroundWithSize( + background: BackgroundOptions, +): background is BackgroundWithSize { + return 'size' in background; +} + +function backgroundOptionsToString(options: BackgroundOptions): string { + let position; + if (isBackgroundWithSize(options) && options.size) { + position = `${options.position}/${options.size}`; + } else { + position = options.position; + } + + return [ + options.image, + position, + options.repeat, + options.origin, + options.clip, + options.attachment, + options.color, + ] + .filter(Boolean) + .join(' '); +} + +/** + * Note: Color can only be defined on the last background, + * https://developer.mozilla.org/en-US/docs/Web/CSS/background + * TODO: Implement this check by types in Typescript 4.2 (by [...options, lastOptions]) + */ +export function background( + value: 'none' | BackgroundOptions | BackgroundOptions[], +): string { + if (value === 'none') { + return value; + } + + if (Array.isArray(value)) { + return value.map(backgroundOptionsToString).join(', '); + } + + return backgroundOptionsToString(value); +} diff --git a/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/border.ts b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/border.ts new file mode 100644 index 0000000..6744346 --- /dev/null +++ b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/border.ts @@ -0,0 +1,15 @@ +type BorderOptions = { + width: number; + type?: string; + color: string; +}; + +export function border(value: 'none' | BorderOptions): string { + if (value === 'none') { + return value; + } + + const { width, type = 'solid', color } = value; + + return `${width}px ${type} ${color}`; +} diff --git a/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/boxShadow.ts b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/boxShadow.ts new file mode 100644 index 0000000..89154ee --- /dev/null +++ b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/boxShadow.ts @@ -0,0 +1,66 @@ +interface BoxShadow { + inset?: boolean; + x: number; + y: number; + color: string; + blur?: never; + spread?: never; +} +interface BoxShadowWithBlur { + inset?: boolean; + x: number; + y: number; + color: string; + blur: number; + spread?: never; +} +interface BoxShadowWithSpread { + inset?: boolean; + x: number; + y: number; + color: string; + blur: number; + spread: number; +} + +type BoxShadowOptions = BoxShadow | BoxShadowWithBlur | BoxShadowWithSpread; + +function isBoxShadowWithBlur(shadowOption: BoxShadowOptions): shadowOption is BoxShadowWithSpread { + return 'blur' in shadowOption; +} + +function isBoxShadowWithSpread( + shadowOption: BoxShadowOptions, +): shadowOption is BoxShadowWithSpread { + return 'blur' in shadowOption && 'spread' in shadowOption; +} + +function boxShadowOptionsToString(options: BoxShadowOptions): string { + const { inset, x, y, color } = options; + + let blur: number | undefined; + let spread: number | undefined; + + if (isBoxShadowWithSpread(options)) { + ({ blur, spread } = options); + } else if (isBoxShadowWithBlur(options)) { + ({ blur } = options); + } + + return [inset && 'inset', `${x}px`, `${y}px`, blur && `${blur}px`, spread && `${spread}px`, color] + .filter(Boolean) + .join(' '); +} + +export function boxShadow(value: 'none' | BoxShadowOptions | BoxShadowOptions[]): string { + if (value === 'none') { + return value; + } + + + if (Array.isArray(value)) { + return value.map(boxShadowOptionsToString).join(', '); + } + + return boxShadowOptionsToString(value); +} diff --git a/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/enhancers.ts b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/enhancers.ts new file mode 100644 index 0000000..6194e64 --- /dev/null +++ b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/enhancers.ts @@ -0,0 +1,16 @@ +import { area } from './area'; +import { background } from './background'; +import { border } from './border'; +import { boxShadow } from './boxShadow'; +import { transition } from './transition'; + +export const enhancers = { + background, + border, + boxShadow, + margin: area, + padding: area, + transition, +}; + +export type Enhancers = typeof enhancers; diff --git a/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/index.ts b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/index.ts new file mode 100644 index 0000000..ae1107b --- /dev/null +++ b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/index.ts @@ -0,0 +1 @@ +export * from './enhancers'; diff --git a/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/transition.ts b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/transition.ts new file mode 100644 index 0000000..b3e09c0 --- /dev/null +++ b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/enhancers/transition.ts @@ -0,0 +1,26 @@ +interface TransitionOptions { + property: string; + duration: string; + timingFunction?: string; + delay?: string; +} + +function transitionOptionsToString(options: TransitionOptions) { + const { property, duration, timingFunction, delay } = options; + + return [property, duration, timingFunction, delay].filter(Boolean).join(' '); +} + +export function transition( + value: 'none' | TransitionOptions | TransitionOptions[], +): string { + if (value === 'none') { + return value; + } + + if (Array.isArray(value)) { + return value.map(options => transitionOptionsToString(options)).join(', '); + } + + return transitionOptionsToString(value); +} diff --git a/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/index.ts b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/index.ts new file mode 100644 index 0000000..330b325 --- /dev/null +++ b/src/styleSheet/EnhancedCSSProperties/EnhancedCSSProperties/index.ts @@ -0,0 +1 @@ +export * from './EnhancedCSSProperties'; diff --git a/src/styleSheet/EnhancedCSSProperties/index.ts b/src/styleSheet/EnhancedCSSProperties/index.ts new file mode 100644 index 0000000..1582e36 --- /dev/null +++ b/src/styleSheet/EnhancedCSSProperties/index.ts @@ -0,0 +1 @@ +export * from './EnhancedCSSObject'; diff --git a/src/styleSheet/index.ts b/src/styleSheet/index.ts new file mode 100644 index 0000000..e861208 --- /dev/null +++ b/src/styleSheet/index.ts @@ -0,0 +1 @@ +export * from './styleSheet'; diff --git a/src/styleSheet/styleSheet.ts b/src/styleSheet/styleSheet.ts new file mode 100644 index 0000000..4a5b6a1 --- /dev/null +++ b/src/styleSheet/styleSheet.ts @@ -0,0 +1,22 @@ +import { SerializedStyles, css } from '@emotion/react'; +import { + CSSObject, + toCSSObject, + EnhancedCSSObject, +} from './EnhancedCSSProperties'; + +type Styles = Record>; + +type Result = { [K in keyof S]: SerializedStyles }; + +export function styleSheet(styles: S): Result { + const result = {} as Result; + + Object.entries(styles).forEach(([key, value]) => { + const cssObject: any = toCSSObject(value); + + result[key as keyof Result] = css(cssObject); + }); + + return result; +}