Skip to content

Commit

Permalink
improve component type and component inheritence
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathantneal committed Aug 4, 2021
1 parent 9a9953d commit 3f2e42c
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 85 deletions.
8 changes: 8 additions & 0 deletions packages/core/src/features/css.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ export type InitComposer = {
}
} & Styling

/** Composer as it has been processed. */
export type Internals = {
/** Component type. */
type: any
/** Component composers. */
composers: Set<Composer>
}

/** Composer as it has been processed. */
export type Composer = [
/** Composer base class name. */
Expand Down
72 changes: 30 additions & 42 deletions packages/core/src/features/css.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { $$composers } from '../utility/composers.js'
import { internals } from '../utility/internals.js'
import { createMemo } from '../utility/createMemo.js'
import { define } from '../utility/define.js'
import { hasNames } from '../utility/hasNames.js'
Expand All @@ -8,6 +8,7 @@ import { toCssRules } from '../convert/toCssRules.js'
import { toHash } from '../convert/toHash.js'
import { toTailDashed } from '../convert/toTailDashed.js'

/** @typedef {import('./css').Internals} Internals */
/** @typedef {import('./css').Composer} Composer */
/** @typedef {import('./css').Config} Config */
/** @typedef {import('./css').InitComposer} InitComposer */
Expand All @@ -26,53 +27,41 @@ const createComponentFunctionMap = createMemo()
/** Returns a function that applies component styles. */
export const createComponentFunction = (/** @type {Config} */ config, /** @type {SheetGroup} */ sheet) =>
createComponentFunctionMap(config, () => (...args) => {
/** @type {string | Function} Component type, which may be a function or a string. */
let componentType = null

/** @type {Set<Composer>} Composers. */
const composers = new Set()
/** @type {Internals} */
let internal = {
type: null,
composers: new Set(),
}

for (const arg of args) {
// skip any void argument
if (arg == null) continue

switch (typeof arg) {
case 'function':
// allow a composer-less function to be the component type
if (componentType == null && !arg[$$composers]) {
componentType = arg

break
}

case 'object':
// allow a type property to be this component type
if (componentType == null && arg.type != null) componentType = arg.type

// copy all composers into this component
if ($$composers in arg)
for (const composer of arg[$$composers]) {
composers.add(composer)
}
// otherwise, add a new composer to this component
else if (!('$$typeof' in arg)) {
const composer = createComposer(arg, config)
// conditionally extend the component
if (arg[internals]) {
internal.type = arg[internals].type

composers.add(composer)
}
for (const composer of arg[internals].composers) {
internal.composers.add(composer)
}
}

break
// otherwise, conditionally define the component type
else if (arg.constructor !== Object || arg.$$typeof) {
internal.type = arg
}

case 'string':
componentType = arg
// otherwise, add a new composer to this component
else {
internal.composers.add(createComposer(arg, config))
}
}

// set the component type if none was set
if (componentType == null) componentType = 'span'
if (!composers.size) composers.add(['PJLV', {}, [], [], {}, []])
if (internal.type == null) internal.type = 'span'
if (!internal.composers.size) internal.composers.add(['PJLV', {}, [], [], {}, []])

return createRenderer(config, componentType, composers, sheet)
return createRenderer(config, internal, sheet)
})

/** Creates a composer from a configuration object. */
Expand Down Expand Up @@ -142,16 +131,15 @@ const createComposer = (/** @type {InitComposer} */ { variants: initSingularVari

const createRenderer = (
/** @type {Config} */ config,
/** @type {string | Function} */ type,
/** @type {Set<Composer>} */ composers,
/** @type {Internals} */ internal,
/** @type {import('../sheet').SheetGroup} */ sheet
) => {
const [
baseClassName,
baseClassNames,
prefilledVariants,
undefinedVariants
] = getPreparedDataFromComposers(composers)
] = getPreparedDataFromComposers(internal.composers)

const selector = `.${baseClassName}${baseClassNames.length > 1 ? `:where(.${baseClassNames.slice(1).join('.')})` : ``}`

Expand Down Expand Up @@ -202,7 +190,7 @@ const createRenderer = (
// 2.2.1. orders regular variants before responsive variants
// 2.3. iterate their compound variants, add their compound variant classes

for (const [composerBaseClass, composerBaseStyle, singularVariants, compoundVariants] of composers) {
for (const [composerBaseClass, composerBaseStyle, singularVariants, compoundVariants] of internal.composers) {
if (!sheet.rules.styled.cache.has(composerBaseClass)) {
sheet.rules.styled.cache.add(composerBaseClass)

Expand Down Expand Up @@ -276,7 +264,7 @@ const createRenderer = (
const renderedToString = () => renderedClassName

return {
type,
type: internal.type,
className: renderedClassName,
selector,
props: forwardProps,
Expand All @@ -286,14 +274,14 @@ const createRenderer = (

const toString = () => {
if (!sheet.rules.styled.cache.has(baseClassName)) render()

return baseClassName
}

return define(render, {
type,
className: baseClassName,
selector,
[$$composers]: composers,
[internals]: internal,
toString,
})
} // prettier-ignore
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/utility/composers.js

This file was deleted.

1 change: 1 addition & 0 deletions packages/core/src/utility/internals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const internals = Symbol.for('stitches.internal')
9 changes: 3 additions & 6 deletions packages/react/src/features/styled.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React from 'react'

import { $$composers } from '../../../core/src/utility/composers.js'
import { internals } from '../../../core/src/utility/internals.js'
import { createMemo } from '../../../core/src/utility/createMemo.js'
import { getComponentType } from '../utility/getComponentType.js'

import { createComponentFunction } from '../../../core/src/features/css.js'

Expand All @@ -17,9 +16,8 @@ export const createStyledFunction = ({ /** @type {Config} */ config, /** @type {
const css = createComponentFunction(config, sheet)

const styled = (...args) => {
const DefaultType = getComponentType(args[0])

const cssComponent = css(...args)
const DefaultType = cssComponent[internals].type

const styledComponent = React.forwardRef((props, ref) => {
const Type = props && props.as || DefaultType
Expand All @@ -38,9 +36,8 @@ export const createStyledFunction = ({ /** @type {Config} */ config, /** @type {
styledComponent.className = cssComponent.className
styledComponent.displayName = `Styled.${DefaultType.displayName || DefaultType.name || DefaultType}`
styledComponent.selector = cssComponent.selector
styledComponent.type = DefaultType
styledComponent.toString = toString
styledComponent[$$composers] = cssComponent[$$composers]
styledComponent[internals] = cssComponent[internals]

return styledComponent
}
Expand Down
14 changes: 0 additions & 14 deletions packages/react/src/utility/getComponentType.js

This file was deleted.

46 changes: 24 additions & 22 deletions packages/react/tests/component.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@
import { createStitches } from '../src/index.js'

const internals = Symbol.for('stitches.internal')

describe('Components', () => {
// test('The `styled` function returns an implicit span component', () => {
// const { styled } = createStitches()
// const component = styled()
test('The `styled` function returns an implicit span component', () => {
const { styled } = createStitches()
const component = styled()

// expect(component.$$typeof).toBe(Symbol.for('react.forward_ref'))
// expect(component.type).toBe('span')
// })
expect(component.$$typeof).toBe(Symbol.for('react.forward_ref'))
expect(component[internals].type).toBe('span')
})

// test('The `styled` function can return an explicit div component', () => {
// const { styled } = createStitches()
// const component = styled('div')
test('The `styled` function can return an explicit div component', () => {
const { styled } = createStitches()
const component = styled('div')

// expect(component.$$typeof).toBe(Symbol.for('react.forward_ref'))
// expect(component.type).toBe('div')
// })
expect(component.$$typeof).toBe(Symbol.for('react.forward_ref'))
expect(component[internals].type).toBe('div')
})

// test('The `styled` function can return an explicit React component', () => {
// function TextComponent() {
// return 'text'
// }
test('The `styled` function can return an explicit React component', () => {
function TextComponent() {
return 'text'
}

// const { styled } = createStitches()
// const component = styled(TextComponent)
const { styled } = createStitches()
const component = styled(TextComponent)

// expect(component.$$typeof).toBe(Symbol.for('react.forward_ref'))
// expect(component.type).toBe(TextComponent)
// })
expect(component.$$typeof).toBe(Symbol.for('react.forward_ref'))
expect(component[internals].type).toBe(TextComponent)
})

test('The `styled` function can return an explicit forwarded React component', () => {
const ForwardedComponent = {
Expand All @@ -39,6 +41,6 @@ describe('Components', () => {
const component = styled(ForwardedComponent)

expect(component.$$typeof).toBe(Symbol.for('react.forward_ref'))
expect(component.type).toBe(ForwardedComponent)
expect(component[internals].type).toBe(ForwardedComponent)
})
})

0 comments on commit 3f2e42c

Please sign in to comment.