Skip to content

Commit

Permalink
chore: theme declaration merging & ThemeValues helper
Browse files Browse the repository at this point in the history
  • Loading branch information
mleralec committed Jul 6, 2022
1 parent 10b2aaa commit 8227b64
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 72 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
node_modules
dist
example

*.tgz
*.log
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ return (

## ✨ Typescript

Thanks to [csstype](https://github.com/frenic/csstype), **jsx-to-styled** is fully typed. You will have autocomplete for all styled props with your theme values if provided or/and possible css values.
Thanks to [csstype](https://github.com/frenic/csstype), **jsx-to-styled** is fully typed. You will have autocomplete for all possible css values.

```tsx
import system, { System } from 'jsx-to-styled'
Expand All @@ -87,6 +87,20 @@ import system, { System } from 'jsx-to-styled'
const Box = styled.div<System>(system)
```

If you want to access to your theme values, you have to redefine "Theme" interface with your custom theme like that:

```ts
// theme.d.ts
import 'jsx-to-styled'
import { theme } from './theme'

type MyTheme = typeof theme

declare module 'jsx-to-styled' {
export interface Theme extends MyTheme {}
}
```

## 📕 Props

> System is composed by all of props below
Expand Down
33 changes: 16 additions & 17 deletions src/config/border.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,35 @@
import type * as CSS from 'csstype'
import type { CSSObject } from 'styled-components'

import type { Props, ThemeProp } from '../types'
import type { Theme } from '../theme'
import type { Props, ThemeProp, ThemeValues } from '../types'
import { get } from '../utils'

export type BorderProps = Props<{
border: CSS.Property.Border
borderWidth: keyof Theme['borderWidths'] | CSS.Property.BorderWidth
borderWidth: ThemeValues<'borderWidths'> | CSS.Property.BorderWidth
borderStyle: CSS.Property.BorderStyle
borderColor: keyof Theme['colors'] | CSS.Property.BorderColor
borderRadius: keyof Theme['radii'] | CSS.Property.BorderRadius
borderColor: ThemeValues<'colors'> | CSS.Property.BorderColor
borderRadius: ThemeValues<'radii'> | CSS.Property.BorderRadius
borderTop: CSS.Property.BorderTop
borderTopWidth: keyof Theme['borderWidths'] | CSS.Property.BorderTopWidth
borderTopWidth: ThemeValues<'borderWidths'> | CSS.Property.BorderTopWidth
borderTopStyle: CSS.Property.BorderTopStyle
borderTopColor: keyof Theme['colors'] | CSS.Property.BorderTopColor
borderTopLeftRadius: keyof Theme['radii'] | CSS.Property.BorderTopLeftRadius
borderTopRightRadius: keyof Theme['radii'] | CSS.Property.BorderTopRightRadius
borderTopColor: ThemeValues<'colors'> | CSS.Property.BorderTopColor
borderTopLeftRadius: ThemeValues<'radii'> | CSS.Property.BorderTopLeftRadius
borderTopRightRadius: ThemeValues<'radii'> | CSS.Property.BorderTopRightRadius
borderRight: CSS.Property.BorderRight
borderRightWidth: keyof Theme['borderWidths'] | CSS.Property.BorderRightWidth
borderRightWidth: ThemeValues<'borderWidths'> | CSS.Property.BorderRightWidth
borderRightStyle: CSS.Property.BorderRightStyle
borderRightColor: keyof Theme['colors'] | CSS.Property.BorderRightColor
borderRightColor: ThemeValues<'colors'> | CSS.Property.BorderRightColor
borderBottom: CSS.Property.BorderBottom
borderBottomWidth: keyof Theme['borderWidths'] | CSS.Property.BorderBottomWidth
borderBottomWidth: ThemeValues<'borderWidths'> | CSS.Property.BorderBottomWidth
borderBottomStyle: CSS.Property.BorderBottomStyle
borderBottomColor: keyof Theme['colors'] | CSS.Property.BorderBottomColor
borderBottomLeftRadius: keyof Theme['radii'] | CSS.Property.BorderBottomLeftRadius
borderBottomRightRadius: keyof Theme['radii'] | CSS.Property.BorderBottomRightRadius
borderBottomColor: ThemeValues<'colors'> | CSS.Property.BorderBottomColor
borderBottomLeftRadius: ThemeValues<'radii'> | CSS.Property.BorderBottomLeftRadius
borderBottomRightRadius: ThemeValues<'radii'> | CSS.Property.BorderBottomRightRadius
borderLeft: CSS.Property.BorderLeft
borderLeftWidth: keyof Theme['borderWidths'] | CSS.Property.BorderLeftWidth
borderLeftWidth: ThemeValues<'borderWidths'> | CSS.Property.BorderLeftWidth
borderLeftStyle: CSS.Property.BorderLeftStyle
borderLeftColor: keyof Theme['colors'] | CSS.Property.BorderLeftColor
borderLeftColor: ThemeValues<'colors'> | CSS.Property.BorderLeftColor
}>

export const border = (props: BorderProps & ThemeProp): CSSObject => {
Expand Down
7 changes: 3 additions & 4 deletions src/config/color.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import type * as CSS from 'csstype'
import type { CSSObject } from 'styled-components'

import type { Props, ThemeProp } from '../types'
import type { Theme } from '../theme'
import type { Props, ThemeProp, ThemeValues } from '../types'
import { get } from '../utils'

export type ColorProps = Props<{
color: keyof Theme['colors'] | CSS.Property.Color
backgroundColor: keyof Theme['colors'] | CSS.Property.BackgroundColor
color: ThemeValues<'colors'> | CSS.Property.Color
backgroundColor: ThemeValues<'colors'> | CSS.Property.BackgroundColor
opacity: CSS.Property.Opacity
}>

Expand Down
9 changes: 4 additions & 5 deletions src/config/grid.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import type * as CSS from 'csstype'
import type { CSSObject } from 'styled-components'

import type { Props, ThemeProp } from '../types'
import type { Theme } from '../theme'
import type { Props, ThemeProp, ThemeValues } from '../types'
import { get } from '../utils'

export type GridProps = Props<{
gridGap: keyof Theme['spaces'] | CSS.Property.GridGap
gridRowGap: keyof Theme['spaces'] | CSS.Property.GridRowGap
gridColumnGap: keyof Theme['spaces'] | CSS.Property.GridColumnGap
gridGap: ThemeValues<'spaces'> | CSS.Property.GridGap
gridRowGap: ThemeValues<'spaces'> | CSS.Property.GridRowGap
gridColumnGap: ThemeValues<'spaces'> | CSS.Property.GridColumnGap
gridColumn: CSS.Property.GridColumn
gridRow: CSS.Property.GridRow
gridArea: CSS.Property.GridArea
Expand Down
15 changes: 7 additions & 8 deletions src/config/layout.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import type * as CSS from 'csstype'
import type { CSSObject } from 'styled-components'

import type { Props, ThemeProp } from '../types'
import type { Theme } from '../theme'
import type { Props, ThemeProp, ThemeValues } from '../types'
import { get } from '../utils'

export type LayoutProps = Props<{
w: keyof Theme['sizes'] | CSS.Property.Width
h: keyof Theme['sizes'] | CSS.Property.Height
minW: keyof Theme['sizes'] | CSS.Property.MinWidth
maxW: keyof Theme['sizes'] | CSS.Property.MaxWidth
minH: keyof Theme['sizes'] | CSS.Property.MinHeight
maxH: keyof Theme['sizes'] | CSS.Property.MaxHeight
w: ThemeValues<'sizes'> | CSS.Property.Width
h: ThemeValues<'sizes'> | CSS.Property.Height
minW: ThemeValues<'sizes'> | CSS.Property.MinWidth
maxW: ThemeValues<'sizes'> | CSS.Property.MaxWidth
minH: ThemeValues<'sizes'> | CSS.Property.MinHeight
maxH: ThemeValues<'sizes'> | CSS.Property.MaxHeight
display: CSS.Property.Display
verticalAlign: CSS.Property.VerticalAlign
overflow: CSS.Property.Overflow
Expand Down
11 changes: 5 additions & 6 deletions src/config/position.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import type * as CSS from 'csstype'
import type { CSSObject } from 'styled-components'

import type { Props, ThemeProp } from '../types'
import type { Theme } from '../theme'
import type { Props, ThemeProp, ThemeValues } from '../types'
import { get } from '../utils'

export type PositionProps = Props<{
position: CSS.Property.Position
zIndex: CSS.Property.ZIndex
top: keyof Theme['spaces'] | CSS.Property.Top
right: keyof Theme['spaces'] | CSS.Property.Right
bottom: keyof Theme['spaces'] | CSS.Property.Bottom
left: keyof Theme['spaces'] | CSS.Property.Left
top: ThemeValues<'spaces'> | CSS.Property.Top
right: ThemeValues<'spaces'> | CSS.Property.Right
bottom: ThemeValues<'spaces'> | CSS.Property.Bottom
left: ThemeValues<'spaces'> | CSS.Property.Left
}>

export const position = (props: PositionProps & ThemeProp): CSSObject => {
Expand Down
31 changes: 15 additions & 16 deletions src/config/space.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import type * as CSS from 'csstype'
import type { CSSObject } from 'styled-components'

import type { Props, ThemeProp } from '../types'
import type { Theme } from '../theme'
import type { Props, ThemeProp, ThemeValues } from '../types'
import { get } from '../utils'

export type SpaceProps = Props<{
m: keyof Theme['spaces'] | CSS.Property.Margin
mt: keyof Theme['spaces'] | CSS.Property.MarginTop
mr: keyof Theme['spaces'] | CSS.Property.MarginRight
mb: keyof Theme['spaces'] | CSS.Property.MarginBottom
ml: keyof Theme['spaces'] | CSS.Property.MarginLeft
my: keyof Theme['spaces'] | CSS.Property.Margin
mx: keyof Theme['spaces'] | CSS.Property.Margin
p: keyof Theme['spaces'] | CSS.Property.Padding
pt: keyof Theme['spaces'] | CSS.Property.PaddingTop
pr: keyof Theme['spaces'] | CSS.Property.PaddingRight
pb: keyof Theme['spaces'] | CSS.Property.PaddingBottom
pl: keyof Theme['spaces'] | CSS.Property.PaddingLeft
py: keyof Theme['spaces'] | CSS.Property.Padding
px: keyof Theme['spaces'] | CSS.Property.Padding
m: ThemeValues<'spaces'> | CSS.Property.Margin
mt: ThemeValues<'spaces'> | CSS.Property.MarginTop
mr: ThemeValues<'spaces'> | CSS.Property.MarginRight
mb: ThemeValues<'spaces'> | CSS.Property.MarginBottom
ml: ThemeValues<'spaces'> | CSS.Property.MarginLeft
my: ThemeValues<'spaces'> | CSS.Property.Margin
mx: ThemeValues<'spaces'> | CSS.Property.Margin
p: ThemeValues<'spaces'> | CSS.Property.Padding
pt: ThemeValues<'spaces'> | CSS.Property.PaddingTop
pr: ThemeValues<'spaces'> | CSS.Property.PaddingRight
pb: ThemeValues<'spaces'> | CSS.Property.PaddingBottom
pl: ThemeValues<'spaces'> | CSS.Property.PaddingLeft
py: ThemeValues<'spaces'> | CSS.Property.Padding
px: ThemeValues<'spaces'> | CSS.Property.Padding
}>

export const space = (props: SpaceProps & ThemeProp): CSSObject => {
Expand Down
13 changes: 6 additions & 7 deletions src/config/typography.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import type * as CSS from 'csstype'
import type { CSSObject } from 'styled-components'

import type { Props, ThemeProp } from '../types'
import type { Theme } from '../theme'
import type { Props, ThemeProp, ThemeValues } from '../types'
import { get } from '../utils'

export type TypographyProps = Props<{
fontFamily: keyof Theme['fonts'] | CSS.Property.FontFamily
fontSize: keyof Theme['fontSizes'] | CSS.Property.FontSize
fontWeight: keyof Theme['fontWeights'] | CSS.Property.FontWeight
lineHeight: keyof Theme['lineHeights'] | CSS.Property.LineHeight
letterSpacing: keyof Theme['letterSpacings'] | CSS.Property.LetterSpacing
fontFamily: ThemeValues<'fonts'> | CSS.Property.FontFamily
fontSize: ThemeValues<'fontSizes'> | CSS.Property.FontSize
fontWeight: ThemeValues<'fontWeights'> | CSS.Property.FontWeight
lineHeight: ThemeValues<'lineHeights'> | CSS.Property.LineHeight
letterSpacing: ThemeValues<'letterSpacings'> | CSS.Property.LetterSpacing
textAlign: CSS.Property.TextAlign
fontStyle: CSS.Property.FontStyle
textDecoration: CSS.Property.TextDecoration
Expand Down
18 changes: 16 additions & 2 deletions src/theme.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

export type ThemeKeys =
| 'colors'
| 'spaces'
Expand All @@ -9,6 +11,18 @@ export type ThemeKeys =
| 'letterSpacings'
| 'borderWidths'
| 'radii'
| (string & Record<never, never>)

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface Theme extends Partial<Record<ThemeKeys, Record<string, string>>> {}
/**
* import 'jsx-to-styled'
* import { theme } from './index'
*
* type MyTheme = typeof theme
*
* declare module 'jsx-to-styled' {
* export interface Theme extends MyTheme {}
* }
*/
export interface Theme {
[key: string]: any
}
6 changes: 4 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { Theme } from './theme'
import type { Theme, ThemeKeys } from './theme'

type ExcludeNumbers<T> = {
[Key in keyof T]: Exclude<T[Key], number>
[Key in keyof T]: Exclude<T[Key], number | symbol>
}

export type Props<P> = Partial<ExcludeNumbers<P>>

export type ThemeProp = Partial<{ theme: Theme }>

export type ThemeValues<Key extends ThemeKeys> = keyof Theme[Key]
6 changes: 3 additions & 3 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Theme, ThemeKeys } from '../theme'
import type { Theme } from '../theme'

export const get = (key: string, theme: Theme, scope: ThemeKeys): string => {
return theme?.[scope]?.[key as string] || key
export const get = <T extends Theme>(key: string, theme: T, scope: keyof T): string => {
return theme?.[scope]?.[key] || key
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"compilerOptions": {
"outDir": "dist",
"noImplicitAny": true,
"module": "es6",
"module": "esnext",
"target": "es5",
"jsx": "react",
"allowJs": true,
Expand Down

0 comments on commit 8227b64

Please sign in to comment.