import unitless from "@dash-ui/unitless" ; import type { HtmlAttributes as CSSHTMLAttributes , PropertiesFallback as CSSProperties , Pseudos as CSSPseudos , SvgAttributes as CSSSvgAttributes , } from "csstype" ; import type { JsonValue } from "type-fest" ; import { createDash } from "./create-dash" ; import type { Dash } from "./create-dash" ; import { hash as fnv1aHash , noop , safeHash } from "./utils" ; param options export function createStyles < V extends DashTokens = DashTokens , T extends string = DashThemeNames > ( options = { } ) { const dash = options . dash ?? createDash ( ) ; const { key , insert , sheets } = dash ; const themes = { } as Record < T , V > ; const tokens = { } as V ; const hash = safeHash ( key , options . hash ?? fnv1aHash ) ; let label ; if ( typeof process !== "undefined" && process . env . NODE_ENV === "development" ) { label = function ( args ) { return [ ... args ] . reduce ( ( curr , arg ) => { if ( typeof arg === "string" ) { curr += "-" + arg ; } else if ( typeof arg === "object" ) { const keys = Object . keys ( arg ) . filter ( ( k ) => arg [ k ] ) ; if ( keys . length ) { curr += "-" + keys . join ( "-" ) ; } } return curr ; } , "" ) . replace ( /[^\w-]/g , "-" ) ; } ; } const styles = function < N extends string > ( styleMap ) : Style < N , V > { const compiledStyleMap = new Map ( ) ; let styleKey ; for ( styleKey in styleMap ) compiledStyleMap . set ( styleKey , compileStyles ( styleMap [ styleKey ] , tokens ) ) ; function style ( ) { const css_ = css . apply ( null , arguments as any ) ; if ( ! css_ ) return "" ; let name = hash ( css_ ) ; if ( label ) name += label ( arguments as any ) ; const className = key + "-" + name ; insert ( name , "." + className , css_ ) ; return className ; } function css ( ) { const numArgs = arguments . length ; let nextStyles = compiledStyleMap . get ( "default" ) ?? "" ; if ( numArgs === 1 && typeof arguments [ 0 ] === "string" ) { nextStyles += compiledStyleMap . get ( arguments [ 0 ] ) ?? "" ; } else if ( numArgs > 0 ) { let i = 0 ; let arg ; for ( ; i < numArgs ; i ++ ) { arg = arguments [ i ] ; if ( typeof arg === "string" ) { nextStyles += compiledStyleMap . get ( arg ) ?? "" ; } else if ( typeof arg === "object" ) { for ( const key in arg ) if ( arg [ key ] ) nextStyles += compiledStyleMap . get ( key ) ?? "" ; } } } return nextStyles ; } style . styles = styleMap ; style . css = css ; return style ; } ; styles . one = function ( ) { const one = compileStyles < V > ( compileLiterals ( arguments ) , tokens ) ; const name = hash ( one ) ; const className = key + "-" + name ; const callback = function ( createClassName ) { if ( ! createClassName && createClassName !== void 0 ) return "" ; insert ( name , "." + className , one ) ; return className ; } ; callback . css = function ( createCss ) { return ! createCss && createCss !== void 0 ? "" : one ; } ; return callback ; } ; const cls = function ( ) { const css = compileStyles < V > ( compileLiterals ( arguments ) , tokens ) ; const name = hash ( css ) ; const className = key + "-" + name ; insert ( name , "." + className , css ) ; return className ; } ; styles . cls = cls ; styles . lazy = function < Value extends LazyValue > ( lazyFn ) { const cache = new Map < string | Value , string > ( ) ; function css ( value ? ) { if ( value === void 0 ) return "" ; const key = typeof value === "object" ? JSON . stringify ( value ) : value ; let css = cache . get ( key ) ; if ( css === void 0 ) { css = compileStyles < V > ( lazyFn ( value ) , tokens ) ; cache . set ( key , css ) ; } return css ; } const lazyStyle = function ( value ? ) { const css_ = css ( value ) ; if ( ! css_ ) return "" ; const name = hash ( css_ ) ; const className = key + "-" + name ; insert ( name , "." + className , css_ ) ; return className ; } ; lazyStyle . css = css ; return lazyStyle ; } ; styles . join = function ( ) { const css = "" . concat ( ... Array . prototype . slice . call ( arguments ) ) ; const name = hash ( css ) ; const className = key + "-" + name ; insert ( name , "." + className , css ) ; return className ; } ; styles . keyframes = function ( ) { const css = compileStyles < V > ( compileLiterals ( arguments ) , tokens ) ; const name = hash ( css ) ; const animationName = key + "-" + name ; insert ( name , "" , "@keyframes " + animationName + "{" + css + "}" , sheets . add ( name ) ) ; return animationName ; } ; styles . insertGlobal = function ( ) { const css = compileStyles < V > ( compileLiterals ( arguments ) , tokens ) ; if ( ! css ) return noop ; const name = hash ( css ) ; insert ( name , "" , css , sheets . add ( name ) ) ; return function ( ) { ! sheets . delete ( name ) && dash . inserted . delete ( name ) ; } ; } ; styles . insertTokens = function ( nextTokens , selector = ":root" ) { const { css , vars } = serializeTokens ( nextTokens , options . mangleTokens ) ; if ( ! css ) return noop ; mergeTokens < V > ( tokens , vars ) ; return styles . insertGlobal ( selector + "{" + css + "}" ) ; } ; styles . insertThemes = function ( nextThemes ) { const flush = [ ] ; for ( const name in nextThemes ) { flush . push ( styles . insertTokens ( ( themes [ name as Extract < T , string > ] = themes [ name as Extract < T , string > ] === void 0 ? ( nextThemes [ name ] as V ) : mergeTokens < V > ( themes [ name as Extract < T , string > ] , nextThemes [ name ] as V ) ) as any , "." + styles . theme ( name as any ) ) ) ; } return function ( ) { flush . forEach ( ( e ) => e ( ) ) ; } ; } ; styles . theme = function ( theme ) { return key + "-" + theme + "-theme" ; } ; styles . dash = dash ; styles . hash = hash ; styles . tokens = emptyObj ; Object . defineProperty ( styles , "tokens" , { get ( ) { return tokens ; } , configurable : false , } ) ; styles . insertTokens ( options . tokens ?? emptyObj ) ; styles . insertThemes ( options . themes ?? emptyObj ) ; return styles ; } const emptyObj = { } ; export interface CreateStylesOptions < V extends DashTokens = DashTokens , T extends string = DashThemeNames > { default dash ? : Dash ; example readonly tokens ? : V ; example readonly themes ? : { [ Name in T ] : V ; } ; example example readonly mangleTokens ? : boolean | Record < string , boolean > ; /**
     * Use your own hash function for creating selector names. By default
     * Dash uses an fnv1a hashing algorithm.
     */ readonly hash ? : typeof fnv1aHash ; } param styleMap example export interface Styles < V extends DashTokens = DashTokens , T extends string = DashThemeNames > { < N extends string > ( styleMap ) : Style < N , V > ; example one ( literals , ... placeholders ) : StylesOne ; example cls ( literals , ... placeholders ) : string ; example lazy < Value extends LazyValue > ( lazyFn ) : StylesLazy < Value > ; example join ( ... css ) : string ; example keyframes ( literals , ... placeholders ) : string ; param name example theme ( name ) : string ; param tokens param selector example insertTokens ( tokens , selector ? ) : ( ) => void ; param themes example insertThemes ( themes ) : ( ) => void ; example insertGlobal ( literals , ... placeholders ) : ( ) => void ; /**
     * The CSS tokens currently defined in the instance
     */ tokens : V ; param string hash ( string ) : string ; /**
     * The instance of underlying the Dash cache used by this instance. This was
     * automatically created by `createDash()` when `createStyles()` was called.
     * Dash controls the caching, style sheets, auto-prefixing, and DOM insertion
     * that happens in the `styles()` instance.
     */ dash : Dash ; } param args example export type Style < N extends string , V extends DashTokens = DashTokens > = { ( ... args ) : string ; param names example css ( ... names ) : string ; /**
     * The style map that this `style()` instance was instantiated with.
     */ styles : StyleMap < N , V > ; } ; /**
 * A function that inserts styles into the DOM when called without
 * a falsy value. If the first argument is falsy, the styles will
 * not be inserted and a class name will not be returned.
 */ export type StylesOne = { ( createClassName ? ) : string ; /**
     * A method that returns a CSS string if the first argument is not falsy.
     */ css ( createCss ? ) : string ; } ; export type StyleMap < N extends string , V extends DashTokens = DashTokens > = { [ Name in N | "default" ] ? : StyleValue < V > ; } ; type StyleMapMemo < N extends string > = Map < N | "default" , string > ; export type StyleArguments < N extends string > = ( N | { [ Name in N ] ? : boolean | null | undefined | string | number ; } | Falsy ) [ ] ; export type StyleValue < V extends DashTokens = DashTokens > = string | StyleCallback < V > | StyleObject ; type KnownStyles = { [ property in keyof CSSProperties ] ? : CSSProperties [ property ] | ( string & { } ) | ( number & { } ) ; } ; type PseudoStyles = { [ property in CSSPseudos | CSSHTMLAttributes | CSSSvgAttributes ] ? : StyleObject ; } ; type SelectorStyles = { [ property ] : string | number | KnownStyles | PseudoStyles | SelectorStyles ; } ; export type StyleObject = KnownStyles & PseudoStyles & SelectorStyles ; export type StyleCallback < V extends DashTokens = DashTokens > = ( tokens : V ) => StyleObject | string ; type DeepPartial < T > = T extends ( ... args : any [ ] ) => any ? T : T extends Record < string , unknown > ? { [ P in keyof T ] ? : DeepPartial < T [ P ] > ; } : T ; export type LazyValue = JsonValue ; param value export type StylesLazy < Value extends LazyValue > = { ( value ? ) : string ; param value css ( value ? ) : string ; } ; export type Falsy = false | 0 | null | undefined ; param styles param tokens export function compileStyles < V extends DashTokens = DashTokens > ( styles , tokens = { } as V ) { const value = typeof styles === "function" ? styles ( tokens ) : styles ; return typeof value === "object" && value !== null ? stringifyStyleObject ( value ) : ( ( value || "" ) as string ) ; } function stringifyStyleObject ( object ) { let string = "" ; for ( const key in object ) { const value = object [ key ] ; if ( typeof value !== "object" ) { const isCustom = key . charCodeAt ( 1 ) === 45 ; string += ( isCustom ? key : cssCase ( key ) ) + ":" + ( typeof value !== "number" || unitless [ key as keyof typeof unitless ] || value === 0 || isCustom ? value : value + "px" ) + ";" ; } else { string += key + "{" + stringifyStyleObject ( value as StyleObject ) + "}" ; } } return string ; } function compileLiterals ( args ) { const literals = args [ 0 ] ; return Array . isArray ( literals ) ? literals . reduce ( ( curr , next , i ) => curr + next + ( args [ i + 1 ] || "" ) , "" ) : literals ; } const cssCaseRe = /[A-Z]|^ms/g ; const cssDisallowedRe = /[^\w-]/g ; const caseCache = { } ; const cssCase = ( string ) => caseCache [ string ] || ( caseCache [ string ] = string . replace ( cssCaseRe , "-$&" ) . toLowerCase ( ) ) ; function serializeTokens ( tokens , mangle ? , names = [ ] ) { const keys = Object . keys ( tokens ) ; const vars = { } ; let css = "" ; let i = 0 ; for ( ; i < keys . length ; i ++ ) { const key = keys [ i ] ; const value = tokens [ key ] ; if ( typeof value === "object" ) { const result = serializeTokens ( value , mangle , names . concat ( key ) ) ; vars [ key ] = result . vars ; css += result . css ; } else { let name = cssCase ( names . length > 0 ? names . join ( "-" ) + "-" + key : key ) . replace ( cssDisallowedRe , "-" ) ; vars [ key ] = "var(" + ( name = "--" + ( mangle === true || ( mangle && ! mangle [ name ] ) ? mangled ( name ) : name ) ) + ")" ; css += name + ":" + value + ";" ; } } return { vars , css } ; } const mangled = safeHash ( "" , fnv1aHash ) ; type SerializedTokens = { readonly vars : Record < string , Record < string , any > | string | number > ; readonly css : string ; } ; function mergeTokens < V extends DashTokens = DashTokens > ( target , source ) { for ( const key in source ) { const value = source [ key ] ; target [ key ] = typeof value === "object" ? mergeTokens ( target [ key ] ?? { } , value ) : value ; } return target as V ; } export const styles = createStyles ( ) ; example example export interface DashTokens { } example export interface DashThemes { } /**
 * The names of the themes defined in the `DashThemes` type
 */ export type DashThemeNames = Extract < keyof DashThemes , string > ;