diff --git a/src/tokens/index.ts b/src/tokens/index.ts index d227020..5eefbf5 100644 --- a/src/tokens/index.ts +++ b/src/tokens/index.ts @@ -3,12 +3,14 @@ * provide*() */ export * from './provide-all' +export * from './provide-border' export * from './provide-color' export * from './provide-elevation' export * from './provide-motion' export * from './provide-shape' export * from './provide-typography' +export * from './internal/border' export * from './internal/color' export * from './internal/elevation' export * from './internal/motion' diff --git a/src/tokens/internal/border.ts b/src/tokens/internal/border.ts new file mode 100644 index 0000000..0917141 --- /dev/null +++ b/src/tokens/internal/border.ts @@ -0,0 +1,148 @@ +import plugin from "tailwindcss/plugin" +import type { IProvider } from "../../declaration/provider.interface" +import { Strings } from "../../utils/strings" +import { Validates } from "../../utils/validates" + +export type TBorderProviderConstructorParams = { + + /** + * @description + * For managed or public CSS variables. + * + * @default 'md-sys-color' + * + * @example If the value of prefix is 'your-prefix'. + * ```css + * .border-outline { + * border-color: var(--your-prefix-outline, #727785); + * } + * ``` + */ + readonly prefix: string + + /** + * @description + * This option can customize the CSS value corresponding to certain tokens. + * Note that this option ignores [hardcodeDefaultValue] and [prefix]. + * + * @default [] + * + * @example + * ```typescript + * const color = provideBorder({ + * customTokens: { + * // .border-outline { border-color: red; } + * 'outline': 'red' + * }, + * }) + * ``` + */ + readonly customTokens: IBorderTokens | Record + + /** + * @description + * When set to true, each token contains a default value. + * + * @default true + * + * @example + * ```typescript + * const color = provideBorder({ + * // .border-outline { border-color: var(--md-sys-color-outline, #727785); } + * hardcodeDefaultValue: true, + * + * // .border-outline { border-color: var(--md-sys-color-outline, ); } + * hardcodeDefaultValue: false, + * }) + * ``` + */ + readonly hardcodeDefaultValue: boolean + + /** + * @description + * Exclude some unnecessary tokens. + * + * @default [] + * + * @example + * ```typescript + * const color = provideBorder({ + * excludedTokens: [ + * // remove .border-outline-variant and .outline-outline-variant + * 'outlineVariant', + * ], + * }) + * ``` + */ + readonly excludedTokens: Array<(keyof IBorderTokens) | {}> + +} + +interface IBorderTokens { + outline: string + outlineVariant: string +} + +class DefaultBorderTokens implements IBorderTokens { + /** + * Colors + */ + outline = '#727785' + outlineVariant = '#c2c6d6' + + public get defaultTokenRecord() { + return { + outline: this.outline, + outlineVariant: this.outlineVariant, + } + } +} + +export class BorderProvider extends DefaultBorderTokens implements IProvider { + private prefix + private tokens + private customTokens + private excludedTokens + private hardcodeDefaultValue + + constructor(params: Partial) { + super() + this.prefix = params.prefix ?? 'md-sys-color' + this.hardcodeDefaultValue = params.hardcodeDefaultValue ?? true + this.customTokens = params.customTokens ?? {} + this.excludedTokens = params.excludedTokens ?? [] + this.tokens = this.validate([this.customTokens as Record, this.defaultTokenRecord, this.excludedTokens]) + } + + private validate(params: Parameters) { + return Validates.validate(...params) + } + + protected transformTokensToCssRuleObject(prefix: string, tokens: Record, hardcodeDefaultValue: boolean) { + const cssPropertyComputed = (name: string, value: string) => !Object.hasOwn(this.customTokens, name) ? `var(--${prefix}-${Strings.toKebabCase(name)}, ${hardcodeDefaultValue ? value : ''})` : `${value}` + + const borderTokens = Validates.transformTokenRecordToCssRuleObject( + tokens, + (name) => `.border-${Strings.toKebabCase(name)}`, + (name, value) => ({ + 'border-color': cssPropertyComputed(name, value) + }) + ) + const outlineTokens = Validates.transformTokenRecordToCssRuleObject( + tokens, + (name) => `.outline-${Strings.toKebabCase(name)}`, + (name, value) => ({ + 'outline-color': cssPropertyComputed(name, value) + }) + ) + return Object.assign({}, borderTokens, outlineTokens) + } + + getPlugin() { + const tokens = this.transformTokensToCssRuleObject(this.prefix, this.tokens, this.hardcodeDefaultValue) + return plugin(({ addUtilities }) => { + addUtilities(tokens) + }) + } + +} diff --git a/src/tokens/provide-all.ts b/src/tokens/provide-all.ts index d8060f2..4d29df0 100644 --- a/src/tokens/provide-all.ts +++ b/src/tokens/provide-all.ts @@ -1,8 +1,10 @@ +import type { TBorderProviderConstructorParams } from "./internal/border"; import type { TColorProviderConstructorParams } from "./internal/color"; import type { TElevationProviderConstructorParams } from "./internal/elevation"; import type { TMotionProviderConstructorParams } from "./internal/motion"; import type { TShapeProviderConstructorParams } from "./internal/shape"; import type { TTypographyProviderConstructorParams } from "./internal/typography"; +import { provideBorder } from "./provide-border"; import { provideColor } from "./provide-color"; import { provideElevation } from "./provide-elevation"; import { provideMotion } from "./provide-motion"; @@ -15,6 +17,7 @@ export function provideAll(params: { motion?: TMotionProviderConstructorParams, shape?: TShapeProviderConstructorParams, typography?: TTypographyProviderConstructorParams, + border?: Partial, }) { return ({ color: provideColor(params.color), @@ -22,6 +25,7 @@ export function provideAll(params: { motion: provideMotion(params.motion), shape: provideShape(params.shape), typography: provideTypography(params.typography), + border: provideBorder(params.border), allPlugins() { return ([ this.color.getPlugin(), @@ -29,6 +33,7 @@ export function provideAll(params: { this.motion.getPlugin(), this.shape.getPlugin(), this.typography.getPlugin(), + this.border.getPlugin(), ]) } }) diff --git a/src/tokens/provide-border.ts b/src/tokens/provide-border.ts new file mode 100644 index 0000000..01c3907 --- /dev/null +++ b/src/tokens/provide-border.ts @@ -0,0 +1,15 @@ +import { BorderProvider, type TBorderProviderConstructorParams } from './internal/border' + +/** + * @example + * ```typescript + * const border = provideBorder() + * + * export default { + * plugins: [ + * border.getPlugin() + * ] + * } + * ``` + */ +export const provideBorder = (param?: Partial) => new BorderProvider(param ?? {})