gucci
This commit is contained in:
parent
097dae4f2a
commit
966a0ce4b0
37
README.md
37
README.md
|
|
@ -6,16 +6,15 @@ Example:
|
|||
import { define } from "forge"
|
||||
|
||||
export const Button = define("button", {
|
||||
layout: {
|
||||
base: "button",
|
||||
|
||||
padding: 20,
|
||||
},
|
||||
look: {
|
||||
background: "blue",
|
||||
},
|
||||
|
||||
variants: {
|
||||
kind: {
|
||||
danger: { look: { background: "red" } },
|
||||
warning: { look: { background: "yellow" } },
|
||||
danger: { background: "red" },
|
||||
warning: { background: "yellow" },
|
||||
}
|
||||
},
|
||||
})
|
||||
|
|
@ -26,35 +25,31 @@ export const Button = define("button", {
|
|||
<Button kind="warning">Click me?</Button>
|
||||
|
||||
export const Profile = define("div", {
|
||||
layout: {
|
||||
padding: 50,
|
||||
},
|
||||
look: {
|
||||
background: "red",
|
||||
},
|
||||
|
||||
parts: {
|
||||
header: { layout: { display: "flex" } },
|
||||
avatar: { layout: { width: 50 } },
|
||||
bio: { look: { color: "gray" } },
|
||||
Header: { display: "flex" },
|
||||
Avatar: { base: 'img', width: 50 },
|
||||
Bio: { color: "gray" },
|
||||
},
|
||||
|
||||
variants: {
|
||||
size: {
|
||||
small: {
|
||||
parts: { avatar: { layout: { width: 20 }}}
|
||||
parts: { Avatar: { width: 20 }}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render({ props, parts: { root, header, avatar, bio } }) {
|
||||
render({ props, parts: { Root, Header, Avatar, Bio } }) {
|
||||
return (
|
||||
<root>
|
||||
<header>
|
||||
<avatar src={props.pic} />
|
||||
<bio>{props.bio}</bio>
|
||||
</header>
|
||||
</root>
|
||||
<Root>
|
||||
<Header>
|
||||
<Avatar src={props.pic} />
|
||||
<Bio>{props.bio}</Bio>
|
||||
</Header>
|
||||
</Root>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -4,15 +4,11 @@ import { Layout, ExampleSection } from './helpers'
|
|||
const Button = define('Button', {
|
||||
base: 'button',
|
||||
|
||||
layout: {
|
||||
padding: "12px 24px",
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 8,
|
||||
},
|
||||
|
||||
look: {
|
||||
background: "#3b82f6",
|
||||
color: "white",
|
||||
border: "none",
|
||||
|
|
@ -24,89 +20,64 @@ const Button = define('Button', {
|
|||
userSelect: "none",
|
||||
boxShadow: "0 4px 6px rgba(59, 130, 246, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1)",
|
||||
transform: "translateY(0)",
|
||||
},
|
||||
|
||||
states: {
|
||||
":not(:disabled):hover": {
|
||||
look: {
|
||||
transform: 'translateY(-2px) !important',
|
||||
filter: 'brightness(1.05)'
|
||||
}
|
||||
},
|
||||
":not(:disabled):active": {
|
||||
look: {
|
||||
transform: 'translateY(1px) !important',
|
||||
boxShadow: '0 2px 3px rgba(0, 0, 0, 0.2) !important'
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
variants: {
|
||||
intent: {
|
||||
primary: {
|
||||
look: {
|
||||
background: "#3b82f6",
|
||||
color: "white",
|
||||
boxShadow: "0 4px 6px rgba(59, 130, 246, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1)",
|
||||
},
|
||||
},
|
||||
secondary: {
|
||||
look: {
|
||||
background: "#f3f4f6",
|
||||
color: "#374151",
|
||||
boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06)",
|
||||
},
|
||||
},
|
||||
danger: {
|
||||
look: {
|
||||
background: "#ef4444",
|
||||
color: "white",
|
||||
boxShadow: "0 4px 6px rgba(239, 68, 68, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1)",
|
||||
},
|
||||
},
|
||||
ghost: {
|
||||
look: {
|
||||
background: "transparent",
|
||||
color: "#aaa",
|
||||
boxShadow: "0 4px 6px rgba(0, 0, 0, 0.2), 0 2px 4px rgba(0, 0, 0, 0.1)",
|
||||
border: "1px solid #eee",
|
||||
},
|
||||
},
|
||||
},
|
||||
size: {
|
||||
small: {
|
||||
layout: {
|
||||
padding: "8px 16px",
|
||||
},
|
||||
look: {
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
large: {
|
||||
layout: {
|
||||
padding: "16px 32px",
|
||||
},
|
||||
look: {
|
||||
fontSize: 18,
|
||||
},
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
look: {
|
||||
opacity: 0.5,
|
||||
cursor: "not-allowed",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const ButtonRow = define('ButtonRow', {
|
||||
layout: {
|
||||
display: 'flex',
|
||||
gap: 16,
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
}
|
||||
})
|
||||
|
||||
export const ButtonExamplesPage = () => (
|
||||
|
|
|
|||
|
|
@ -2,49 +2,36 @@ import { define, Styles } from '../src'
|
|||
|
||||
export const Body = define('Body', {
|
||||
base: 'body',
|
||||
layout: {
|
||||
|
||||
margin: 0,
|
||||
padding: '40px 20px',
|
||||
|
||||
},
|
||||
look: {
|
||||
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
||||
background: '#f3f4f6',
|
||||
}
|
||||
})
|
||||
|
||||
const Container = define('Container', {
|
||||
layout: {
|
||||
maxWidth: 1200,
|
||||
margin: '0 auto'
|
||||
}
|
||||
})
|
||||
|
||||
export const Header = define('Header', {
|
||||
base: 'h1',
|
||||
layout: {
|
||||
|
||||
marginBottom: 40,
|
||||
},
|
||||
look: {
|
||||
color: '#111827'
|
||||
}
|
||||
})
|
||||
|
||||
export const ExampleSection = define('ExampleSection', {
|
||||
layout: {
|
||||
marginBottom: 40
|
||||
},
|
||||
marginBottom: 40,
|
||||
|
||||
parts: {
|
||||
Header: {
|
||||
base: 'h2',
|
||||
layout: {
|
||||
marginBottom: 16
|
||||
},
|
||||
look: {
|
||||
|
||||
marginBottom: 16,
|
||||
color: '#374151',
|
||||
fontSize: 18
|
||||
}
|
||||
}
|
||||
},
|
||||
render({ props, parts: { Root, Header } }) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -2,23 +2,17 @@ import { define } from '../src'
|
|||
import { Layout, ExampleSection } from './helpers'
|
||||
|
||||
const Tabs = define('Tabs', {
|
||||
layout: {
|
||||
display: 'flex',
|
||||
gap: 0,
|
||||
},
|
||||
look: {
|
||||
borderBottom: '2px solid #e5e7eb',
|
||||
},
|
||||
|
||||
parts: {
|
||||
Tab: {
|
||||
base: 'button',
|
||||
layout: {
|
||||
|
||||
padding: '12px 24px',
|
||||
position: 'relative',
|
||||
marginBottom: -2,
|
||||
},
|
||||
look: {
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
borderBottom: '2px solid transparent',
|
||||
|
|
@ -27,25 +21,21 @@ const Tabs = define('Tabs', {
|
|||
fontWeight: 500,
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s ease',
|
||||
},
|
||||
|
||||
states: {
|
||||
':hover': {
|
||||
look: {
|
||||
color: '#111827',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
variants: {
|
||||
active: {
|
||||
parts: {
|
||||
Tab: {
|
||||
look: {
|
||||
color: '#3b82f6',
|
||||
borderBottom: '2px solid #3b82f6',
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -69,19 +59,15 @@ const Tabs = define('Tabs', {
|
|||
})
|
||||
|
||||
const Pills = define('Pills', {
|
||||
layout: {
|
||||
display: 'flex',
|
||||
gap: 8,
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
|
||||
parts: {
|
||||
Pill: {
|
||||
base: 'button',
|
||||
layout: {
|
||||
|
||||
padding: '8px 16px',
|
||||
},
|
||||
look: {
|
||||
background: '#f3f4f6',
|
||||
border: 'none',
|
||||
borderRadius: 20,
|
||||
|
|
@ -90,29 +76,24 @@ const Pills = define('Pills', {
|
|||
fontWeight: 500,
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s ease',
|
||||
},
|
||||
|
||||
states: {
|
||||
':hover': {
|
||||
look: {
|
||||
background: '#e5e7eb',
|
||||
color: '#111827',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
variants: {
|
||||
active: {
|
||||
parts: {
|
||||
Pill: {
|
||||
look: {
|
||||
background: '#3b82f6',
|
||||
color: 'white',
|
||||
},
|
||||
states: {
|
||||
':hover': {
|
||||
look: {
|
||||
background: '#2563eb',
|
||||
color: 'white',
|
||||
}
|
||||
|
|
@ -120,7 +101,6 @@ const Pills = define('Pills', {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render({ props, parts: { Root, Pill } }) {
|
||||
|
|
@ -141,23 +121,20 @@ const Pills = define('Pills', {
|
|||
})
|
||||
|
||||
const VerticalNav = define('VerticalNav', {
|
||||
layout: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 4,
|
||||
width: 240,
|
||||
},
|
||||
|
||||
parts: {
|
||||
NavItem: {
|
||||
base: 'a',
|
||||
layout: {
|
||||
|
||||
padding: '12px 16px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
},
|
||||
look: {
|
||||
|
||||
background: 'transparent',
|
||||
borderRadius: 8,
|
||||
color: '#6b7280',
|
||||
|
|
@ -166,41 +143,32 @@ const VerticalNav = define('VerticalNav', {
|
|||
textDecoration: 'none',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s ease',
|
||||
},
|
||||
|
||||
states: {
|
||||
':hover': {
|
||||
look: {
|
||||
background: '#f3f4f6',
|
||||
color: '#111827',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Icon: {
|
||||
layout: {
|
||||
width: 20,
|
||||
height: 20,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
look: {
|
||||
fontSize: 18,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
variants: {
|
||||
active: {
|
||||
parts: {
|
||||
NavItem: {
|
||||
look: {
|
||||
background: '#eff6ff',
|
||||
color: '#3b82f6',
|
||||
},
|
||||
states: {
|
||||
':hover': {
|
||||
look: {
|
||||
background: '#dbeafe',
|
||||
color: '#2563eb',
|
||||
}
|
||||
|
|
@ -208,7 +176,6 @@ const VerticalNav = define('VerticalNav', {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render({ props, parts: { Root, NavItem, Icon } }) {
|
||||
|
|
@ -230,44 +197,36 @@ const VerticalNav = define('VerticalNav', {
|
|||
})
|
||||
|
||||
const Breadcrumbs = define('Breadcrumbs', {
|
||||
layout: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
|
||||
parts: {
|
||||
Item: {
|
||||
base: 'a',
|
||||
look: {
|
||||
|
||||
color: '#6b7280',
|
||||
fontSize: 14,
|
||||
textDecoration: 'none',
|
||||
transition: 'color 0.2s ease',
|
||||
},
|
||||
|
||||
states: {
|
||||
':hover': {
|
||||
look: {
|
||||
color: '#3b82f6',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Separator: {
|
||||
look: {
|
||||
color: '#d1d5db',
|
||||
fontSize: 14,
|
||||
userSelect: 'none',
|
||||
}
|
||||
},
|
||||
Current: {
|
||||
look: {
|
||||
color: '#111827',
|
||||
fontSize: 14,
|
||||
fontWeight: 500,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render({ props, parts: { Root, Item, Separator, Current } }) {
|
||||
|
|
|
|||
|
|
@ -4,167 +4,129 @@ import { Layout, ExampleSection } from './helpers'
|
|||
const UserProfile = define('UserProfile', {
|
||||
base: 'div',
|
||||
|
||||
layout: {
|
||||
padding: 24,
|
||||
maxWidth: 600,
|
||||
margin: "0 auto",
|
||||
},
|
||||
|
||||
look: {
|
||||
background: "white",
|
||||
borderRadius: 12,
|
||||
boxShadow: "0 2px 8px rgba(0,0,0,0.1)",
|
||||
},
|
||||
|
||||
parts: {
|
||||
Header: {
|
||||
layout: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 16,
|
||||
marginBottom: 16,
|
||||
},
|
||||
},
|
||||
Avatar: {
|
||||
base: 'img',
|
||||
layout: {
|
||||
width: 64,
|
||||
height: 64,
|
||||
},
|
||||
look: {
|
||||
borderRadius: "50%",
|
||||
objectFit: "cover",
|
||||
border: "3px solid #e5e7eb",
|
||||
},
|
||||
},
|
||||
Info: {
|
||||
layout: {
|
||||
flex: 1,
|
||||
},
|
||||
},
|
||||
Name: {
|
||||
layout: {
|
||||
marginBottom: 4,
|
||||
},
|
||||
look: {
|
||||
fontSize: 20,
|
||||
fontWeight: 600,
|
||||
color: "#111827",
|
||||
},
|
||||
},
|
||||
Handle: {
|
||||
look: {
|
||||
fontSize: 14,
|
||||
color: "#6b7280",
|
||||
},
|
||||
},
|
||||
Bio: {
|
||||
layout: {
|
||||
marginBottom: 16,
|
||||
width: "100%",
|
||||
},
|
||||
look: {
|
||||
fontSize: 14,
|
||||
lineHeight: 1.6,
|
||||
color: "#374151",
|
||||
wordWrap: "break-word",
|
||||
},
|
||||
},
|
||||
Stats: {
|
||||
layout: {
|
||||
display: "flex",
|
||||
gap: 24,
|
||||
paddingTop: 16,
|
||||
},
|
||||
look: {
|
||||
borderTop: "1px solid #e5e7eb",
|
||||
},
|
||||
},
|
||||
Stat: {
|
||||
layout: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 4,
|
||||
},
|
||||
},
|
||||
StatValue: {
|
||||
look: {
|
||||
fontSize: 18,
|
||||
fontWeight: 600,
|
||||
color: "#111827",
|
||||
},
|
||||
},
|
||||
StatLabel: {
|
||||
look: {
|
||||
fontSize: 12,
|
||||
color: "#6b7280",
|
||||
textTransform: "uppercase",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
variants: {
|
||||
size: {
|
||||
compact: {
|
||||
layout: { padding: 16, maxWidth: 300 },
|
||||
padding: 16,
|
||||
maxWidth: 300,
|
||||
parts: {
|
||||
Avatar: { layout: { width: 48, height: 48 } },
|
||||
Name: { look: { fontSize: 16 } },
|
||||
Bio: { look: { fontSize: 13 } },
|
||||
Avatar: { width: 48, height: 48 },
|
||||
Name: { fontSize: 16 },
|
||||
Bio: { fontSize: 13 },
|
||||
},
|
||||
},
|
||||
skinny: {
|
||||
layout: { padding: 20, maxWidth: 125 },
|
||||
padding: 20,
|
||||
maxWidth: 125,
|
||||
parts: {
|
||||
Header: {
|
||||
layout: {
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: 12,
|
||||
}
|
||||
},
|
||||
Avatar: { layout: { width: 80, height: 80 } },
|
||||
Info: { layout: { flex: 0, width: "100%" } },
|
||||
Name: { look: { textAlign: "center", fontSize: 18 } },
|
||||
Handle: { look: { textAlign: "center" } },
|
||||
Bio: { look: { textAlign: "center", fontSize: 13 } },
|
||||
Avatar: { width: 80, height: 80 },
|
||||
Info: { flex: 0, width: "100%" },
|
||||
Name: { textAlign: "center", fontSize: 18 },
|
||||
Handle: { textAlign: "center" },
|
||||
Bio: { textAlign: "center", fontSize: 13 },
|
||||
Stats: {
|
||||
layout: {
|
||||
flexDirection: "column",
|
||||
gap: 16,
|
||||
}
|
||||
},
|
||||
Stat: { layout: { alignItems: "center" } },
|
||||
Stat: { alignItems: "center" },
|
||||
},
|
||||
},
|
||||
large: {
|
||||
layout: { padding: 32, maxWidth: 800 },
|
||||
padding: 32,
|
||||
maxWidth: 800,
|
||||
parts: {
|
||||
Avatar: { layout: { width: 96, height: 96 } },
|
||||
Name: { look: { fontSize: 24 } },
|
||||
Avatar: { width: 96, height: 96 },
|
||||
Name: { fontSize: 24 },
|
||||
},
|
||||
},
|
||||
},
|
||||
verified: {
|
||||
parts: {
|
||||
Avatar: {
|
||||
look: {
|
||||
border: "3px solid #3b82f6",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
theme: {
|
||||
dark: {
|
||||
look: {
|
||||
background: "#1f2937",
|
||||
},
|
||||
parts: {
|
||||
Name: { look: { color: "#f9fafb" } },
|
||||
Handle: { look: { color: "#9ca3af" } },
|
||||
Bio: { look: { color: "#d1d5db" } },
|
||||
Stats: { look: { borderTop: "1px solid #374151" } },
|
||||
StatValue: { look: { color: "#f9fafb" } },
|
||||
Name: { color: "#f9fafb" },
|
||||
Handle: { color: "#9ca3af" },
|
||||
Bio: { color: "#d1d5db" },
|
||||
Stats: { borderTop: "1px solid #374151" },
|
||||
StatValue: { color: "#f9fafb" },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { JSX } from 'hono/jsx'
|
||||
import { type TagDef, UnitlessProps } from './types'
|
||||
import { type TagDef, UnitlessProps, NonStyleKeys } from './types'
|
||||
|
||||
export const styles: Record<string, Record<string, string>> = {}
|
||||
export const Styles = () => <style dangerouslySetInnerHTML={{ __html: stylesToCSS(styles) }} />
|
||||
|
|
@ -45,8 +45,10 @@ function camelToDash(name: string): string {
|
|||
function makeStyle(def: TagDef) {
|
||||
const style: Record<string, string> = {}
|
||||
|
||||
for (const [name, value] of Object.entries(Object.assign({}, def.layout ?? {}, def.look ?? {})))
|
||||
for (const [name, value] of Object.entries(def)) {
|
||||
if (NonStyleKeys.has(name)) continue
|
||||
style[camelToDash(name)] = `${typeof value === 'number' && !UnitlessProps.has(name) ? `${value}px` : value}`
|
||||
}
|
||||
|
||||
return style
|
||||
}
|
||||
|
|
@ -57,28 +59,37 @@ function makeComponent(baseName: string, rootDef: TagDef, rootProps: Record<stri
|
|||
const base = def.base ?? 'div'
|
||||
const Tag = (base) as keyof JSX.IntrinsicElements
|
||||
|
||||
return ({ children, ...props }: { children: any, [key: string]: any }) => {
|
||||
const classNames = [makeClassName(baseName, partName)]
|
||||
|
||||
for (const [key, value] of Object.entries(rootProps)) {
|
||||
const allProps = { ...rootProps, ...props }
|
||||
|
||||
for (const [key, value] of Object.entries(allProps)) {
|
||||
const variantConfig = rootDef.variants?.[key]
|
||||
if (!variantConfig) continue
|
||||
|
||||
// Remove variant prop from being passed to HTML element
|
||||
delete props[key]
|
||||
|
||||
const variantName = key
|
||||
const variantKey = value
|
||||
|
||||
let variantDef: TagDef | undefined
|
||||
if ('parts' in variantConfig || 'layout' in variantConfig || 'look' in variantConfig) {
|
||||
if (value === true) variantDef = variantConfig as TagDef
|
||||
} else {
|
||||
variantDef = (variantConfig as Record<string, TagDef>)[value as string]
|
||||
// Distinguish boolean variants from keyed variants:
|
||||
// - Boolean variants: component({ variant: true }) → variantConfig is a TagDef
|
||||
// - Keyed variants: component({ variant: 'key' }) → variantConfig[key] is a TagDef
|
||||
if (value === true) {
|
||||
variantDef = variantConfig as TagDef
|
||||
} else if (typeof value === 'string') {
|
||||
variantDef = (variantConfig as Record<string, TagDef>)[value]
|
||||
}
|
||||
if (!variantDef) continue
|
||||
|
||||
classNames.push(variantKey === true ? variantName : `${variantName}-${variantKey}`)
|
||||
}
|
||||
|
||||
return ({ children, ...props }: { children: any, [key: string]: any }) =>
|
||||
<Tag class={classNames.join(' ')} {...props}>{children}</Tag>
|
||||
return <Tag class={classNames.join(' ')} {...props}>{children}</Tag>
|
||||
}
|
||||
}
|
||||
|
||||
// adds CSS styles for tag definition
|
||||
|
|
@ -98,13 +109,17 @@ function registerStyles(name: string, def: TagDef) {
|
|||
}
|
||||
|
||||
for (const [variantName, variantConfig] of Object.entries(def.variants ?? {})) {
|
||||
// Check if it's a boolean variant (has layout/look/parts directly) or a keyed variant
|
||||
if ('parts' in variantConfig || 'layout' in variantConfig || 'look' in variantConfig) {
|
||||
// Boolean variant - treat variantConfig as TagDef
|
||||
// Detect boolean vs keyed variants by checking if config has structural keys or looks like a TagDef
|
||||
const isBooleanVariant = 'parts' in variantConfig || 'styles' in variantConfig || 'states' in variantConfig ||
|
||||
// If first key is camelCase or contains CSS-like properties, treat as boolean variant
|
||||
Object.keys(variantConfig).some(k => k !== k.toLowerCase() || typeof (variantConfig as any)[k] !== 'object')
|
||||
|
||||
if (isBooleanVariant) {
|
||||
// Boolean variant - variantConfig is a TagDef
|
||||
const variantDef = variantConfig as TagDef
|
||||
const baseClassName = makeClassName(name)
|
||||
const className = `${baseClassName}.${variantName}`
|
||||
styles[className] ??= makeStyle({ layout: variantDef.layout, look: variantDef.look })
|
||||
styles[className] ??= makeStyle(variantDef)
|
||||
for (const [state, style] of Object.entries(variantDef.states ?? {}))
|
||||
styles[`${className}${state}`] = makeStyle(style)
|
||||
|
||||
|
|
@ -119,7 +134,7 @@ function registerStyles(name: string, def: TagDef) {
|
|||
// Keyed variant - iterate over the keys
|
||||
for (const [variantKey, variantDef] of Object.entries(variantConfig as Record<string, TagDef>)) {
|
||||
const className = makeClassName(name, undefined, variantName, variantKey)
|
||||
styles[className] ??= makeStyle({ layout: variantDef.layout, look: variantDef.look })
|
||||
styles[className] ??= makeStyle(variantDef)
|
||||
for (const [state, style] of Object.entries(variantDef.states ?? {}))
|
||||
styles[`${className}${state}`] = makeStyle(style)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { renderToString, getStylesCSS, parseCSS } from './test_helpers'
|
|||
describe('define - basic functionality', () => {
|
||||
test('creates a component function', () => {
|
||||
const Component = define({
|
||||
layout: { display: 'flex' }
|
||||
display: 'flex'
|
||||
})
|
||||
|
||||
expect(typeof Component).toBe('function')
|
||||
|
|
@ -13,7 +13,7 @@ describe('define - basic functionality', () => {
|
|||
|
||||
test('component returns a JSX element', () => {
|
||||
const Component = define({
|
||||
layout: { display: 'flex' }
|
||||
display: 'flex'
|
||||
})
|
||||
|
||||
const result = Component({})
|
||||
|
|
@ -23,7 +23,7 @@ describe('define - basic functionality', () => {
|
|||
|
||||
test('applies className to rendered element', () => {
|
||||
const Component = define('MyComponent', {
|
||||
layout: { display: 'flex' }
|
||||
display: 'flex'
|
||||
})
|
||||
|
||||
const html = renderToString(Component({}))
|
||||
|
|
@ -31,8 +31,8 @@ describe('define - basic functionality', () => {
|
|||
})
|
||||
|
||||
test('generates unique anonymous component names', () => {
|
||||
const Component1 = define({ layout: { display: 'flex' } })
|
||||
const Component2 = define({ layout: { display: 'block' } })
|
||||
const Component1 = define({ display: 'flex' })
|
||||
const Component2 = define({ display: 'block' })
|
||||
|
||||
const html1 = renderToString(Component1({}))
|
||||
const html2 = renderToString(Component2({}))
|
||||
|
|
@ -45,7 +45,7 @@ describe('define - basic functionality', () => {
|
|||
|
||||
test('renders default div element', () => {
|
||||
const Component = define('DivTest', {
|
||||
layout: { display: 'flex' }
|
||||
display: 'flex'
|
||||
})
|
||||
|
||||
const html = renderToString(Component({}))
|
||||
|
|
@ -56,7 +56,7 @@ describe('define - basic functionality', () => {
|
|||
test('respects custom base element', () => {
|
||||
const Component = define('ButtonTest', {
|
||||
base: 'button',
|
||||
look: { color: 'blue' }
|
||||
color: 'blue'
|
||||
})
|
||||
|
||||
const html = renderToString(Component({}))
|
||||
|
|
@ -83,11 +83,9 @@ describe('define - basic functionality', () => {
|
|||
describe('CSS generation - camelCase to kebab-case', () => {
|
||||
test('converts camelCase properties to kebab-case', () => {
|
||||
define('CamelTest', {
|
||||
layout: {
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between'
|
||||
}
|
||||
})
|
||||
|
||||
const css = getStylesCSS()
|
||||
|
|
@ -98,10 +96,8 @@ describe('CSS generation - camelCase to kebab-case', () => {
|
|||
|
||||
test('handles consecutive capital letters', () => {
|
||||
define('ConsecutiveTest', {
|
||||
look: {
|
||||
backgroundColor: 'red',
|
||||
borderRadius: 5
|
||||
}
|
||||
})
|
||||
|
||||
const css = getStylesCSS()
|
||||
|
|
@ -113,12 +109,10 @@ describe('CSS generation - camelCase to kebab-case', () => {
|
|||
describe('CSS generation - numeric values and units', () => {
|
||||
test('adds px unit to numeric layout values', () => {
|
||||
define('NumericTest', {
|
||||
layout: {
|
||||
width: 100,
|
||||
height: 200,
|
||||
padding: 16,
|
||||
margin: 8
|
||||
}
|
||||
})
|
||||
|
||||
const styles = parseCSS(getStylesCSS())
|
||||
|
|
@ -130,12 +124,10 @@ describe('CSS generation - numeric values and units', () => {
|
|||
|
||||
test('preserves string values without adding px', () => {
|
||||
define('StringTest', {
|
||||
layout: {
|
||||
width: '100%',
|
||||
height: 'auto',
|
||||
margin: '0 auto',
|
||||
padding: '1rem'
|
||||
}
|
||||
})
|
||||
|
||||
const styles = parseCSS(getStylesCSS())
|
||||
|
|
@ -147,18 +139,14 @@ describe('CSS generation - numeric values and units', () => {
|
|||
|
||||
test('does not add px to unitless properties', () => {
|
||||
define('UnitlessTest', {
|
||||
layout: {
|
||||
flex: 1,
|
||||
flexGrow: 2,
|
||||
flexShrink: 1,
|
||||
zIndex: 10,
|
||||
order: 3
|
||||
},
|
||||
look: {
|
||||
order: 3,
|
||||
opacity: 0.5,
|
||||
fontWeight: 700,
|
||||
lineHeight: 1.5
|
||||
}
|
||||
})
|
||||
|
||||
const styles = parseCSS(getStylesCSS())
|
||||
|
|
@ -174,14 +162,10 @@ describe('CSS generation - numeric values and units', () => {
|
|||
|
||||
test('handles numeric zero values correctly', () => {
|
||||
define('ZeroTest', {
|
||||
layout: {
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
zIndex: 0
|
||||
},
|
||||
look: {
|
||||
zIndex: 0,
|
||||
opacity: 0
|
||||
}
|
||||
})
|
||||
|
||||
const styles = parseCSS(getStylesCSS())
|
||||
|
|
@ -195,12 +179,10 @@ describe('CSS generation - numeric values and units', () => {
|
|||
describe('CSS generation - layout and look', () => {
|
||||
test('generates CSS for layout properties', () => {
|
||||
define('LayoutTest', {
|
||||
layout: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 16,
|
||||
padding: 20
|
||||
}
|
||||
})
|
||||
|
||||
const css = getStylesCSS()
|
||||
|
|
@ -213,12 +195,10 @@ describe('CSS generation - layout and look', () => {
|
|||
|
||||
test('generates CSS for look properties', () => {
|
||||
define('LookTest', {
|
||||
look: {
|
||||
color: 'blue',
|
||||
backgroundColor: 'white',
|
||||
fontSize: 16,
|
||||
fontWeight: 600
|
||||
}
|
||||
})
|
||||
|
||||
const css = getStylesCSS()
|
||||
|
|
@ -231,14 +211,10 @@ describe('CSS generation - layout and look', () => {
|
|||
|
||||
test('combines layout and look properties', () => {
|
||||
define('CombinedTest', {
|
||||
layout: {
|
||||
display: 'flex',
|
||||
padding: 16
|
||||
},
|
||||
look: {
|
||||
padding: 16,
|
||||
color: 'blue',
|
||||
backgroundColor: 'white'
|
||||
}
|
||||
})
|
||||
|
||||
const styles = parseCSS(getStylesCSS())
|
||||
|
|
@ -253,11 +229,11 @@ describe('CSS generation - layout and look', () => {
|
|||
describe('CSS generation - parts', () => {
|
||||
test('generates separate CSS for each part', () => {
|
||||
define('PartTest', {
|
||||
layout: { display: 'flex' },
|
||||
display: 'flex',
|
||||
parts: {
|
||||
Header: { base: 'header', look: { color: 'red', fontSize: 20 } },
|
||||
Body: { base: 'main', layout: { padding: 20 } },
|
||||
Footer: { base: 'footer', look: { fontSize: 12 } }
|
||||
Header: { base: 'header', color: 'red', fontSize: 20 },
|
||||
Body: { base: 'main', padding: 20 },
|
||||
Footer: { base: 'footer', fontSize: 12 }
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -271,7 +247,7 @@ describe('CSS generation - parts', () => {
|
|||
test('part className format is ComponentName_PartName', () => {
|
||||
define('ComponentWithParts', {
|
||||
parts: {
|
||||
MyPart: { look: { color: 'green' } }
|
||||
MyPart: { color: 'green' }
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -345,10 +321,10 @@ describe('components with parts', () => {
|
|||
describe('variants - boolean variants', () => {
|
||||
test('applies boolean variant class when true', () => {
|
||||
const Component = define('BoolVariant', {
|
||||
layout: { display: 'flex' },
|
||||
display: 'flex',
|
||||
variants: {
|
||||
primary: {
|
||||
look: { color: 'blue' }
|
||||
color: 'blue'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -362,7 +338,7 @@ describe('variants - boolean variants', () => {
|
|||
const Component = define('BoolVariantFalse', {
|
||||
variants: {
|
||||
active: {
|
||||
look: { backgroundColor: 'green' }
|
||||
backgroundColor: 'green'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -379,10 +355,10 @@ describe('variants - boolean variants', () => {
|
|||
|
||||
test('generates CSS for component with boolean variant', () => {
|
||||
define('BoolVariantCSS', {
|
||||
layout: { display: 'block' },
|
||||
display: 'block',
|
||||
variants: {
|
||||
active: {
|
||||
look: { backgroundColor: 'green' }
|
||||
backgroundColor: 'green'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -398,9 +374,9 @@ describe('variants - string/enum variants', () => {
|
|||
const Component = define('StringVariant', {
|
||||
variants: {
|
||||
size: {
|
||||
small: { layout: { padding: 8 } },
|
||||
medium: { layout: { padding: 16 } },
|
||||
large: { layout: { padding: 24 } }
|
||||
small: { padding: 8 },
|
||||
medium: { padding: 16 },
|
||||
large: { padding: 24 }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -416,9 +392,9 @@ describe('variants - string/enum variants', () => {
|
|||
define('ColorVariant', {
|
||||
variants: {
|
||||
color: {
|
||||
red: { look: { color: 'red', backgroundColor: '#ffeeee' } },
|
||||
blue: { look: { color: 'blue', backgroundColor: '#eeeeff' } },
|
||||
green: { look: { color: 'green', backgroundColor: '#eeffee' } }
|
||||
red: { color: 'red', backgroundColor: '#ffeeee' },
|
||||
blue: { color: 'blue', backgroundColor: '#eeeeff' },
|
||||
green: { color: 'green', backgroundColor: '#eeffee' }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -433,12 +409,12 @@ describe('variants - string/enum variants', () => {
|
|||
const Component = define('MultiVariant', {
|
||||
variants: {
|
||||
size: {
|
||||
small: { layout: { padding: 8 } },
|
||||
large: { layout: { padding: 24 } }
|
||||
small: { padding: 8 },
|
||||
large: { padding: 24 }
|
||||
},
|
||||
color: {
|
||||
red: { look: { color: 'red' } },
|
||||
blue: { look: { color: 'blue' } }
|
||||
red: { color: 'red' },
|
||||
blue: { color: 'blue' }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -452,7 +428,7 @@ describe('variants - string/enum variants', () => {
|
|||
const Component = define('UndefinedVariant', {
|
||||
variants: {
|
||||
size: {
|
||||
small: { layout: { padding: 8 } }
|
||||
small: { padding: 8 }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -466,7 +442,7 @@ describe('variants - string/enum variants', () => {
|
|||
const Component = define('NonVariantProps', {
|
||||
variants: {
|
||||
size: {
|
||||
small: { layout: { padding: 8 } }
|
||||
small: { padding: 8 }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -488,12 +464,12 @@ describe('variants with parts', () => {
|
|||
theme: {
|
||||
dark: {
|
||||
parts: {
|
||||
Header: { look: { color: 'white', backgroundColor: 'black' } }
|
||||
Header: { color: 'white', backgroundColor: 'black' }
|
||||
}
|
||||
},
|
||||
light: {
|
||||
parts: {
|
||||
Header: { look: { color: 'black', backgroundColor: 'white' } }
|
||||
Header: { color: 'black', backgroundColor: 'white' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -514,7 +490,7 @@ describe('variants with parts', () => {
|
|||
},
|
||||
variants: {
|
||||
size: {
|
||||
large: { layout: { padding: 20 } }
|
||||
large: { padding: 20 }
|
||||
}
|
||||
},
|
||||
render: ({ props, parts }) => {
|
||||
|
|
@ -531,7 +507,7 @@ describe('variants with parts', () => {
|
|||
describe('custom render function', () => {
|
||||
test('uses custom render when provided', () => {
|
||||
const Component = define('CustomRender', {
|
||||
layout: { display: 'flex' },
|
||||
display: 'flex',
|
||||
render: ({ props, parts }) => {
|
||||
return <div class="custom-wrapper">{props.children}</div>
|
||||
}
|
||||
|
|
@ -596,7 +572,7 @@ describe('Styles component', () => {
|
|||
})
|
||||
|
||||
test('Styles renders to HTML with CSS', () => {
|
||||
define('StylesComp1', { layout: { width: 100 } })
|
||||
define('StylesComp1', { width: 100 })
|
||||
|
||||
const html = renderToString(define.Styles())
|
||||
expect(html).toContain('<style>')
|
||||
|
|
@ -606,8 +582,8 @@ describe('Styles component', () => {
|
|||
})
|
||||
|
||||
test('Styles includes CSS for all registered components', () => {
|
||||
define('StylesComp2', { layout: { width: 100 } })
|
||||
define('StylesComp3', { layout: { height: 200 } })
|
||||
define('StylesComp2', { width: 100 })
|
||||
define('StylesComp3', { height: 200 })
|
||||
|
||||
const css = getStylesCSS()
|
||||
expect(css).toContain('.StylesComp2')
|
||||
|
|
@ -620,8 +596,8 @@ describe('Styles component', () => {
|
|||
define('StylesVariant', {
|
||||
variants: {
|
||||
size: {
|
||||
small: { layout: { padding: 8 } },
|
||||
large: { layout: { padding: 24 } }
|
||||
small: { padding: 8 },
|
||||
large: { padding: 24 }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -636,8 +612,8 @@ describe('Styles component', () => {
|
|||
test('Styles includes part CSS', () => {
|
||||
define('StylesPart', {
|
||||
parts: {
|
||||
Header: { look: { color: 'red' } },
|
||||
Body: { look: { color: 'blue' } }
|
||||
Header: { color: 'red' },
|
||||
Body: { color: 'blue' }
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -649,6 +625,125 @@ describe('Styles component', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('variants on parts via props', () => {
|
||||
test('applies boolean variant to part when passed as prop', () => {
|
||||
const Component = define('PartVariantBool', {
|
||||
parts: {
|
||||
Tab: { base: 'button', color: 'gray' }
|
||||
},
|
||||
variants: {
|
||||
active: {
|
||||
parts: {
|
||||
Tab: { color: 'blue' }
|
||||
}
|
||||
}
|
||||
},
|
||||
render: ({ props, parts }) => {
|
||||
return (
|
||||
<parts.Root>
|
||||
<parts.Tab active={true}>Active Tab</parts.Tab>
|
||||
<parts.Tab active={false}>Inactive Tab</parts.Tab>
|
||||
</parts.Root>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const html = renderToString(Component({}))
|
||||
// Should have one tab with active class, one without
|
||||
expect(html).toContain('PartVariantBool_Tab active')
|
||||
// Count occurrences - should have 2 tabs total
|
||||
const tabCount = (html.match(/PartVariantBool_Tab/g) || []).length
|
||||
expect(tabCount).toBe(2)
|
||||
})
|
||||
|
||||
test('applies string variant to part when passed as prop', () => {
|
||||
const Component = define('PartVariantString', {
|
||||
parts: {
|
||||
Pill: { base: 'button' }
|
||||
},
|
||||
variants: {
|
||||
size: {
|
||||
small: { parts: { Pill: { padding: 8 } } },
|
||||
large: { parts: { Pill: { padding: 24 } } }
|
||||
}
|
||||
},
|
||||
render: ({ props, parts }) => {
|
||||
return (
|
||||
<parts.Root>
|
||||
<parts.Pill size="small">Small</parts.Pill>
|
||||
<parts.Pill size="large">Large</parts.Pill>
|
||||
</parts.Root>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const html = renderToString(Component({}))
|
||||
expect(html).toContain('PartVariantString_Pill size-small')
|
||||
expect(html).toContain('PartVariantString_Pill size-large')
|
||||
})
|
||||
|
||||
test('does not pass variant props through to HTML', () => {
|
||||
const Component = define('NoVariantLeakage', {
|
||||
parts: {
|
||||
Item: { base: 'div' }
|
||||
},
|
||||
variants: {
|
||||
active: {
|
||||
parts: { Item: { color: 'blue' } }
|
||||
}
|
||||
},
|
||||
render: ({ props, parts }) => {
|
||||
return (
|
||||
<parts.Root>
|
||||
<parts.Item active={true}>Item</parts.Item>
|
||||
</parts.Root>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const html = renderToString(Component({}))
|
||||
// Should have the class, but not the attribute
|
||||
expect(html).toContain('class="NoVariantLeakage_Item active"')
|
||||
expect(html).not.toContain('active="true"')
|
||||
expect(html).not.toContain('active="false"')
|
||||
})
|
||||
|
||||
test('combines root and part level variants', () => {
|
||||
const Component = define('CombinedVariants', {
|
||||
parts: {
|
||||
NavItem: { base: 'a' }
|
||||
},
|
||||
variants: {
|
||||
theme: {
|
||||
dark: {
|
||||
backgroundColor: 'black',
|
||||
parts: { NavItem: { color: 'white' } }
|
||||
}
|
||||
},
|
||||
active: {
|
||||
parts: { NavItem: { fontWeight: 700 } }
|
||||
}
|
||||
},
|
||||
render: ({ props, parts }) => {
|
||||
return (
|
||||
<parts.Root>
|
||||
<parts.NavItem active={true}>Active Link</parts.NavItem>
|
||||
<parts.NavItem active={false}>Inactive Link</parts.NavItem>
|
||||
</parts.Root>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const html = renderToString(Component({ theme: 'dark' }))
|
||||
// Root should have theme variant
|
||||
expect(html).toContain('CombinedVariants theme-dark')
|
||||
// Active NavItem should have both theme and active classes
|
||||
expect(html).toContain('CombinedVariants_NavItem theme-dark active')
|
||||
// Inactive NavItem should have only theme class
|
||||
expect(html).toMatch(/CombinedVariants_NavItem theme-dark"[^>]*>Inactive/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('edge cases', () => {
|
||||
test('handles empty definition', () => {
|
||||
const Component = define({})
|
||||
|
|
@ -673,7 +768,7 @@ describe('edge cases', () => {
|
|||
const Component = define({
|
||||
variants: {
|
||||
size: {
|
||||
small: { layout: { padding: 8 } }
|
||||
small: { padding: 8 }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -683,8 +778,8 @@ describe('edge cases', () => {
|
|||
})
|
||||
|
||||
test('does not duplicate styles when registered multiple times', () => {
|
||||
define('NoDuplicate', { layout: { width: 100 } })
|
||||
define('NoDuplicate', { layout: { width: 200 } })
|
||||
define('NoDuplicate', { width: 100 })
|
||||
define('NoDuplicate', { width: 200 })
|
||||
|
||||
const styles = parseCSS(getStylesCSS())
|
||||
// Should keep first value (??= operator)
|
||||
|
|
@ -693,18 +788,18 @@ describe('edge cases', () => {
|
|||
|
||||
test('handles complex nested structures', () => {
|
||||
define('ComplexNested', {
|
||||
layout: { display: 'grid' },
|
||||
display: 'grid',
|
||||
parts: {
|
||||
Container: { layout: { padding: 16 } },
|
||||
Item: { look: { fontSize: 14 } }
|
||||
Container: { padding: 16 },
|
||||
Item: { fontSize: 14 }
|
||||
},
|
||||
variants: {
|
||||
theme: {
|
||||
dark: {
|
||||
look: { backgroundColor: 'black' },
|
||||
backgroundColor: 'black',
|
||||
parts: {
|
||||
Container: { look: { backgroundColor: '#222' } },
|
||||
Item: { look: { color: 'white' } }
|
||||
Container: { backgroundColor: '#222' },
|
||||
Item: { color: 'white' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
129
src/types.ts
129
src/types.ts
|
|
@ -1,37 +1,39 @@
|
|||
export type TagDef = {
|
||||
className?: string
|
||||
base?: string
|
||||
layout?: Layout
|
||||
look?: Look
|
||||
states?: Record<string, TagDef>,
|
||||
parts?: Record<string, TagDef>
|
||||
variants?: Record<string, TagDef | Record<string, TagDef>>
|
||||
render?: (obj: any) => any
|
||||
}
|
||||
|
||||
type Layout = {
|
||||
alignContent?: string
|
||||
alignItems?: string
|
||||
alignSelf?: string
|
||||
// layout-related
|
||||
alignContent?: 'normal' | 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly' | 'stretch' | 'start' | 'end' | 'baseline' | string
|
||||
alignItems?: 'normal' | 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline' | 'start' | 'end' | 'self-start' | 'self-end' | string
|
||||
alignSelf?: 'auto' | 'normal' | 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline' | 'start' | 'end' | 'self-start' | 'self-end' | string
|
||||
aspectRatio?: number | string
|
||||
bottom?: number | string
|
||||
display?: string
|
||||
boxSizing?: 'content-box' | 'border-box'
|
||||
columnGap?: number | string
|
||||
contain?: 'none' | 'strict' | 'content' | 'size' | 'layout' | 'style' | 'paint' | string
|
||||
display?: 'block' | 'inline' | 'inline-block' | 'flex' | 'inline-flex' | 'grid' | 'inline-grid' | 'flow-root' | 'none' | 'contents' | 'table' | 'table-row' | 'table-cell' | string
|
||||
flex?: number | string
|
||||
flexBasis?: number | string
|
||||
flexDirection?: string
|
||||
flexDirection?: 'row' | 'row-reverse' | 'column' | 'column-reverse'
|
||||
flexGrow?: number
|
||||
flexShrink?: number
|
||||
flexWrap?: string
|
||||
flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse'
|
||||
gap?: number | string
|
||||
gridAutoFlow?: string
|
||||
gridAutoFlow?: 'row' | 'column' | 'dense' | 'row dense' | 'column dense'
|
||||
gridColumn?: string
|
||||
gridGap?: number | string
|
||||
gridRow?: string
|
||||
gridTemplateColumns?: string
|
||||
gridTemplateRows?: string
|
||||
height?: number | string
|
||||
justifyContent?: string
|
||||
justifyItems?: string
|
||||
justifySelf?: string
|
||||
inset?: number | string
|
||||
justifyContent?: 'normal' | 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly' | 'start' | 'end' | 'left' | 'right' | 'stretch' | string
|
||||
justifyItems?: 'normal' | 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline' | 'start' | 'end' | 'self-start' | 'self-end' | 'left' | 'right' | string
|
||||
justifySelf?: 'auto' | 'normal' | 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline' | 'start' | 'end' | 'self-start' | 'self-end' | 'left' | 'right' | string
|
||||
left?: number | string
|
||||
margin?: number | string
|
||||
marginBottom?: number | string
|
||||
|
|
@ -43,70 +45,125 @@ type Layout = {
|
|||
minHeight?: number | string
|
||||
minWidth?: number | string
|
||||
order?: number
|
||||
overflow?: string
|
||||
overflowX?: string
|
||||
overflowY?: string
|
||||
overflow?: 'visible' | 'hidden' | 'scroll' | 'auto' | 'clip'
|
||||
overflowX?: 'visible' | 'hidden' | 'scroll' | 'auto' | 'clip'
|
||||
overflowY?: 'visible' | 'hidden' | 'scroll' | 'auto' | 'clip'
|
||||
padding?: number | string
|
||||
paddingBottom?: number | string
|
||||
paddingLeft?: number | string
|
||||
paddingRight?: number | string
|
||||
paddingTop?: number | string
|
||||
position?: string
|
||||
placeContent?: string
|
||||
placeItems?: string
|
||||
placeSelf?: string
|
||||
position?: 'static' | 'relative' | 'absolute' | 'fixed' | 'sticky'
|
||||
right?: number | string
|
||||
rowGap?: number | string
|
||||
top?: number | string
|
||||
verticalAlign?: 'baseline' | 'top' | 'middle' | 'bottom' | 'text-top' | 'text-bottom' | 'sub' | 'super' | string
|
||||
width?: number | string
|
||||
zIndex?: number
|
||||
}
|
||||
|
||||
type Look = {
|
||||
// visual/theme-related
|
||||
animation?: string
|
||||
appearance?: 'none' | 'auto' | 'button' | 'textfield' | 'searchfield' | 'textarea' | 'checkbox' | 'radio' | string
|
||||
backdropFilter?: string
|
||||
background?: string
|
||||
backgroundAttachment?: 'scroll' | 'fixed' | 'local'
|
||||
backgroundClip?: 'border-box' | 'padding-box' | 'content-box' | 'text'
|
||||
backgroundColor?: string
|
||||
backgroundImage?: string
|
||||
backgroundPosition?: string
|
||||
backgroundRepeat?: 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat' | 'space' | 'round'
|
||||
backgroundSize?: 'auto' | 'cover' | 'contain' | string
|
||||
border?: string
|
||||
borderBottom?: string
|
||||
borderBottomColor?: string
|
||||
borderBottomLeftRadius?: number | string
|
||||
borderBottomRightRadius?: number | string
|
||||
borderBottomStyle?: 'none' | 'solid' | 'dashed' | 'dotted' | 'double' | 'groove' | 'ridge' | 'inset' | 'outset' | 'hidden'
|
||||
borderBottomWidth?: number | string
|
||||
borderColor?: string
|
||||
borderLeft?: string
|
||||
borderLeftColor?: string
|
||||
borderLeftStyle?: 'none' | 'solid' | 'dashed' | 'dotted' | 'double' | 'groove' | 'ridge' | 'inset' | 'outset' | 'hidden'
|
||||
borderLeftWidth?: number | string
|
||||
borderRadius?: number | string
|
||||
borderRight?: string
|
||||
borderStyle?: string
|
||||
borderRightColor?: string
|
||||
borderRightStyle?: 'none' | 'solid' | 'dashed' | 'dotted' | 'double' | 'groove' | 'ridge' | 'inset' | 'outset' | 'hidden'
|
||||
borderRightWidth?: number | string
|
||||
borderStyle?: 'none' | 'solid' | 'dashed' | 'dotted' | 'double' | 'groove' | 'ridge' | 'inset' | 'outset' | 'hidden'
|
||||
borderTop?: string
|
||||
borderTopColor?: string
|
||||
borderTopLeftRadius?: number | string
|
||||
borderTopRightRadius?: number | string
|
||||
borderTopStyle?: 'none' | 'solid' | 'dashed' | 'dotted' | 'double' | 'groove' | 'ridge' | 'inset' | 'outset' | 'hidden'
|
||||
borderTopWidth?: number | string
|
||||
borderWidth?: number | string
|
||||
boxShadow?: string
|
||||
clipPath?: string
|
||||
color?: string
|
||||
cursor?: string
|
||||
content?: string
|
||||
cursor?: 'auto' | 'default' | 'none' | 'context-menu' | 'help' | 'pointer' | 'progress' | 'wait' | 'cell' | 'crosshair' | 'text' | 'vertical-text' | 'alias' | 'copy' | 'move' | 'no-drop' | 'not-allowed' | 'grab' | 'grabbing' | 'e-resize' | 'n-resize' | 'ne-resize' | 'nw-resize' | 's-resize' | 'se-resize' | 'sw-resize' | 'w-resize' | 'ew-resize' | 'ns-resize' | 'nesw-resize' | 'nwse-resize' | 'col-resize' | 'row-resize' | 'all-scroll' | 'zoom-in' | 'zoom-out' | string
|
||||
filter?: string
|
||||
fontFamily?: string
|
||||
fontSize?: number | string
|
||||
fontStyle?: string
|
||||
fontWeight?: number | string
|
||||
fontStyle?: 'normal' | 'italic' | 'oblique'
|
||||
fontWeight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 'normal' | 'bold' | 'bolder' | 'lighter' | number
|
||||
isolation?: 'auto' | 'isolate'
|
||||
letterSpacing?: number | string
|
||||
lineHeight?: number | string
|
||||
mixBlendMode?: string
|
||||
objectFit?: string
|
||||
listStyle?: string
|
||||
listStyleImage?: string
|
||||
listStylePosition?: 'inside' | 'outside'
|
||||
listStyleType?: 'none' | 'disc' | 'circle' | 'square' | 'decimal' | 'decimal-leading-zero' | 'lower-roman' | 'upper-roman' | 'lower-alpha' | 'upper-alpha' | 'lower-greek' | 'lower-latin' | 'upper-latin' | string
|
||||
mixBlendMode?: 'normal' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten' | 'color-dodge' | 'color-burn' | 'hard-light' | 'soft-light' | 'difference' | 'exclusion' | 'hue' | 'saturation' | 'color' | 'luminosity'
|
||||
objectFit?: 'fill' | 'contain' | 'cover' | 'none' | 'scale-down'
|
||||
opacity?: number
|
||||
outline?: string
|
||||
outlineColor?: string
|
||||
outlineOffset?: number | string
|
||||
outlineStyle?: string
|
||||
outlineStyle?: 'none' | 'solid' | 'dashed' | 'dotted' | 'double' | 'groove' | 'ridge' | 'inset' | 'outset'
|
||||
outlineWidth?: number | string
|
||||
pointerEvents?: string
|
||||
textAlign?: string
|
||||
pointerEvents?: 'auto' | 'none' | 'visiblePainted' | 'visibleFill' | 'visibleStroke' | 'visible' | 'painted' | 'fill' | 'stroke' | 'all'
|
||||
resize?: 'none' | 'both' | 'horizontal' | 'vertical' | 'block' | 'inline'
|
||||
scrollBehavior?: 'auto' | 'smooth'
|
||||
textAlign?: 'left' | 'right' | 'center' | 'justify' | 'start' | 'end'
|
||||
textDecoration?: string
|
||||
textOverflow?: string
|
||||
textDecorationColor?: string
|
||||
textDecorationLine?: 'none' | 'underline' | 'overline' | 'line-through' | 'blink' | string
|
||||
textDecorationStyle?: 'solid' | 'double' | 'dotted' | 'dashed' | 'wavy'
|
||||
textDecorationThickness?: number | string
|
||||
textIndent?: number | string
|
||||
textOverflow?: 'clip' | 'ellipsis' | string
|
||||
textShadow?: string
|
||||
textTransform?: string
|
||||
textTransform?: 'none' | 'capitalize' | 'uppercase' | 'lowercase' | 'full-width' | 'full-size-kana'
|
||||
transform?: string
|
||||
transition?: string
|
||||
userSelect?: string
|
||||
visibility?: string
|
||||
whiteSpace?: string
|
||||
wordWrap?: string
|
||||
overflowWrap?: string
|
||||
userSelect?: 'auto' | 'none' | 'text' | 'contain' | 'all'
|
||||
visibility?: 'visible' | 'hidden' | 'collapse'
|
||||
whiteSpace?: 'normal' | 'nowrap' | 'pre' | 'pre-wrap' | 'pre-line' | 'break-spaces'
|
||||
willChange?: 'auto' | 'scroll-position' | 'contents' | string
|
||||
wordBreak?: 'normal' | 'break-all' | 'keep-all' | 'break-word'
|
||||
wordSpacing?: number | string
|
||||
wordWrap?: 'normal' | 'break-word' | 'anywhere'
|
||||
overflowWrap?: 'normal' | 'break-word' | 'anywhere'
|
||||
}
|
||||
|
||||
export const NonStyleKeys = new Set([
|
||||
'className',
|
||||
'base',
|
||||
'states',
|
||||
'parts',
|
||||
'variants',
|
||||
'render',
|
||||
'styles',
|
||||
])
|
||||
|
||||
export const UnitlessProps = new Set([
|
||||
'animationIterationCount',
|
||||
'aspectRatio',
|
||||
'columnCount',
|
||||
'flex',
|
||||
'flexGrow',
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user