Skip to content

Commit

Permalink
feat: add @openlabs/system module (#332)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastiandotdev committed May 31, 2024
1 parent 51cf3b3 commit 3b71d59
Show file tree
Hide file tree
Showing 20 changed files with 438 additions and 212 deletions.
49 changes: 49 additions & 0 deletions packages/system/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "@openlabs/system",
"version": "0.1.0",
"description": "Open UI System primitives",
"publishConfig": {
"access": "public"
},
"author": "Open Labs",
"license": "MIT",
"homepage": "https://github.com/OpenLabs-dev/openui#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/OpenLabs-dev/openui.git"
},
"bugs": {
"url": "https://github.com/OpenLabs-dev/openui/issues"
},
"keywords": [
"ui",
"design system",
"open",
"theme",
"system"
],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./package.json": "./package.json"
},
"scripts": {
"build": "tsup --dts",
"compile": "tsup --dts --watch"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
},
"devDependencies": {
"@openlabs/tsconfig": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tsup": "^8.0.2",
"typescript": "^5.4.5"
}
}
15 changes: 15 additions & 0 deletions packages/system/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export type {
As,
DOMElement,
DOMElements,
CapitalizedDOMElements,
DOMAttributes,
OmitCommonProps,
RightJoinProps,
MergeWithAs,
InternalForwardRefRenderFunction,
PropsOf,
Merge,
HTMLOpenUIProps,
PropGetter,
} from '@system/types'
11 changes: 11 additions & 0 deletions packages/system/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "@openlabs/tsconfig/base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@system/types": ["types/index.d.ts"]
}
},
"include": ["."],
"exclude": ["dist", "build", "node_modules"]
}
10 changes: 10 additions & 0 deletions packages/system/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineConfig } from 'tsup'

export default defineConfig({
name: 'system',
entry: ['src/index.ts'],
clean: true,
target: 'es2019',
format: ['cjs', 'esm'],
banner: { js: '"use client";' },
})
69 changes: 69 additions & 0 deletions packages/system/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
export type As<Props = any> = React.ElementType<Props>
export type DOMElements = keyof JSX.IntrinsicElements
export type CapitalizedDOMElements = Capitalize<DOMElements>

export interface DOMElement extends Element, HTMLOrSVGElement {}

interface DataAttributes {
[dataAttr: string]: any
}

export type DOMAttributes<T = DOMElement> = React.AriaAttributes & React.DOMAttributes<T> & DataAttributes & {
id?: string
role?: React.AriaRole
tabIndex?: number
style?: React.CSSProperties
}

export type OmitCommonProps<Target, OmitAdditionalProps extends keyof any = never> = Omit<Target, 'transition' | 'as' | 'color' | OmitAdditionalProps>

export type RightJoinProps<SourceProps extends object = object, OverrideProps extends object = object> = OmitCommonProps<SourceProps, keyof OverrideProps> & OverrideProps

export type MergeWithAs<
ComponentProps extends object,
AsProps extends object,
AdditionalProps extends object = object,
AsComponent extends As = As,
> = (RightJoinProps<ComponentProps, AdditionalProps> | RightJoinProps<AsProps, AdditionalProps>) & {
as?: AsComponent
}

export interface InternalForwardRefRenderFunction<
Component extends As,
Props extends object = object,
OmitKeys extends keyof any = never,
> {
<AsComponent extends As = Component>(
props: MergeWithAs<
React.ComponentPropsWithoutRef<Component>,
Omit<React.ComponentPropsWithoutRef<AsComponent>, OmitKeys>,
Props,
AsComponent
>,
): React.ReactElement | null
readonly $$typeof: symbol
defaultProps?: Partial<Props> | undefined
propTypes?: React.WeakValidationMap<Props> | undefined
displayName?: string | undefined
}

/**
* Extract the props of a React element or component
*/
export type PropsOf<T extends As> = React.ComponentPropsWithoutRef<T> & {
as?: As
}

export type Merge<M, N> = N extends Record<string, unknown> ? M : Omit<M, keyof N> & N

export type HTMLOpenUIProps<T extends As = 'div', OmitKeys extends keyof any = never> = Omit<
PropsOf<T>,
'ref' | 'color' | 'slot' | 'size' | 'defaultChecked' | 'defaultValue' | OmitKeys
> & {
as?: As
}

export type PropGetter<P = Record<string, unknown>, R = DOMAttributes> = (
props?: Merge<DOMAttributes, P>,
ref?: React.Ref<any>,
) => R & React.RefAttributes<any>
2 changes: 1 addition & 1 deletion packages/theme/src/base.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { LayoutTheme } from './interfaces/theme'
import type { LayoutTheme } from '@theme/types'

export const defatulTheme: LayoutTheme = {
fontSize: {
Expand Down
2 changes: 1 addition & 1 deletion packages/theme/src/colors/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { BaseColors } from '../interfaces/utils'
import type { BaseColors } from '@theme/types'

export const colors: BaseColors = {
light: {
Expand Down
3 changes: 1 addition & 2 deletions packages/theme/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
import Color from 'color'
import kebabCase from 'lodash.kebabcase'
import mapKeys from 'lodash.mapkeys'
import type { ConfigTheme } from './interfaces/theme'
import type { DefaultThemeType, Resolved } from './interfaces/utils'
import type { ConfigTheme, DefaultThemeType, Resolved } from '@theme/types'
import { flattenThemeObject } from './utils/functions'

const parsedColorsCache: Record<string, number[]> = {}
Expand Down
2 changes: 1 addition & 1 deletion packages/theme/src/create-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import plugin from 'tailwindcss/plugin.js'
import type { DefaultThemeType } from './interfaces/utils'
import type { DefaultThemeType } from '@theme/types'
import type { ConfigThemes } from './tailwindcss'
import { config } from './config'
import { animations, baseStyles, tailwind, utilities } from './theme'
Expand Down
31 changes: 15 additions & 16 deletions packages/theme/src/tailwindcss.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type plugin from 'tailwindcss/plugin.js'
import type { ConfigTheme, OpenUIPluginConfig } from '@theme/types'
import deepMerge from 'deepmerge'
import get from 'lodash.get'
import omit from 'lodash.omit'
import forEach from 'lodash.foreach'
import type { OpenUIPluginConfig } from './interfaces/config'
import type { ConfigTheme } from './interfaces/theme'
import {
darkTheme,
defatulTheme,
Expand All @@ -27,44 +26,44 @@ export function openui(config: OpenUIPluginConfig = {}): ReturnType<typeof plugi
prefix: defaultPrefix = DEFAULT_PREFIX,
} = config

const userLightColors = get(themeObject, 'light.colors', {})
const userDarkColors = get(themeObject, 'dark.colors', {})
const customLightColors = get(themeObject, 'light.colors', {})
const customDarkColors = get(themeObject, 'dark.colors', {})

const defaultLayoutObj = userLayout && typeof userLayout === 'object'
const mergedLayout = userLayout && typeof userLayout === 'object'
? deepMerge(defatulTheme, userLayout)
: defatulTheme

const baseLayouts = {
const mergedThemeLayouts = {
light: {
...defaultLayoutObj,
...mergedLayout,
...lightTheme,
},
dark: {
...defaultLayoutObj,
...mergedLayout,
...darkTheme,
},
}

const otherThemes = omit(themeObject, ['light', 'dark']) || {}

forEach(otherThemes, ({ extend, colors, layout }, themeName) => {
const baseTheme = extend && isBaseTheme(extend) ? extend : defaultExtendTheme
const defaultExtensionTheme = extend && isBaseTheme(extend) ? extend : defaultExtendTheme

if (colors && typeof colors === 'object')
otherThemes[themeName].colors = deepMerge(semanticColors[baseTheme], colors)
otherThemes[themeName].colors = deepMerge(semanticColors[defaultExtensionTheme], colors)

if (layout && typeof layout === 'object')
otherThemes[themeName].layout = deepMerge(extend ? baseLayouts[extend] : defaultLayoutObj, layout)
otherThemes[themeName].layout = deepMerge(extend ? mergedThemeLayouts[extend] : mergedLayout, layout)
})

const light: ConfigTheme = {
layout: deepMerge(baseLayouts.light, get(themeObject, 'light.layout', {})),
colors: deepMerge(semanticColors.light, userLightColors),
layout: deepMerge(mergedThemeLayouts.light, get(themeObject, 'light.layout', {})),
colors: deepMerge(semanticColors.light, customLightColors),
}

const dark = {
layout: deepMerge(baseLayouts.dark, get(themeObject, 'dark.layout', {})),
colors: deepMerge(semanticColors.dark, userDarkColors),
const dark: ConfigTheme = {
layout: deepMerge(mergedThemeLayouts.dark, get(themeObject, 'dark.layout'), {}),
colors: deepMerge(semanticColors.dark, customDarkColors),
}

const themes = {
Expand Down
7 changes: 7 additions & 0 deletions packages/theme/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
{
"extends": "@openlabs/tsconfig/base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@theme/types": ["./types/index.d.ts"]
}
},
"include": ["."],
"exclude": ["dist", "build", "node_modules"]
}
2 changes: 1 addition & 1 deletion packages/theme/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { defineConfig } from 'tsup'

export default defineConfig({
name: 'theme',
entryPoints: ['./src/index.ts'],
entry: ['./src/index.ts'],
format: ['cjs', 'esm'],
clean: true,
target: 'es2019',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
import type { ConfigThemes } from '../tailwindcss'
import type { LayoutTheme } from './theme'
import type { DefaultThemeType } from './utils'

export interface OpenUIPluginConfig {
/**
* The prefix for the css variables.
* @default "openui"
*/
prefix?: string
/**
* Common layout definitions. These definitions are applied to all themes.
*/
layout?: LayoutTheme
/**
* The theme definitions.
*/
themes?: ConfigThemes
/**
* The default theme to use is light
* @default "light"
*/
defaultTheme?: DefaultThemeType
/**
* The default theme to extend.
* @default "light"
*/
defaultExtendTheme?: DefaultThemeType
}
import type { LayoutTheme } from './theme'
import type { DefaultThemeType } from './utils'
import type { ConfigThemes } from '@/tailwindcss'

export interface OpenUIPluginConfig {
/**
* The prefix for the css variables.
* @default "openui"
*/
prefix?: string
/**
* Common layout definitions. These definitions are applied to all themes.
*/
layout?: LayoutTheme
/**
* The theme definitions.
*/
themes?: ConfigThemes
/**
* The default theme to use is light
* @default "light"
*/
defaultTheme?: DefaultThemeType
/**
* The default theme to extend.
* @default "light"
*/
defaultExtendTheme?: DefaultThemeType
}
14 changes: 14 additions & 0 deletions packages/theme/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export type {
DefaultThemeType,
Color,
Colors,
BaseColors,
BaseUnit,
OpacityValue,
OpacityColor,
Resolved,
} from './utils'

export type { ConfigTheme, LayoutTheme } from './theme'

export type { OpenUIPluginConfig } from './config'
Loading

0 comments on commit 3b71d59

Please sign in to comment.