Skip to content

Commit 4de16cd

Browse files
committed
feat: elevation plugin can customize tokens, modify prefix, and exclude tokens
1 parent 8c8699c commit 4de16cd

File tree

3 files changed

+186
-30
lines changed

3 files changed

+186
-30
lines changed

src/tokens/internal/elevation.ts

Lines changed: 184 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,198 @@
11
import plugin from "tailwindcss/plugin";
2-
import type { IProvider } from "./provider.interface";
2+
import type { IProvider } from "../../declaration/provider.interface";
3+
import { Strings } from "../../utils/strings";
4+
import { Validates } from "../../utils/validates";
35

46
export type TElevationProviderConstructorParams = {
5-
readonly shadowToken?: string
7+
8+
/**
9+
* @description
10+
* The shadows are divided into three layers.
11+
* Normally, keep the default.
12+
*
13+
* @example
14+
* ```typescript
15+
* const color = provideElevation({
16+
* shadowColorCssVariable: {
17+
* layer1: 'red',
18+
* layer2: 'green',
19+
* layer3: 'yellow'
20+
* }
21+
* })
22+
* ```
23+
*/
24+
readonly shadowColorCssVariable: Partial<{
25+
layer1: string
26+
layer2: string
27+
layer3: string
28+
}>
29+
30+
/**
31+
* @description
32+
* Variables used to control the internal use of shadow.
33+
* Note that this option is usually used together with [prefix].
34+
*
35+
* @default 'md-sys-color'
36+
*
37+
* @example If the value of prefix is 'your-prefix'.
38+
* ```css
39+
* .elevation-1 {
40+
* box-shadow: var(--md-sys-elevation-level-1, var(--your-prefix-shadow, rgba(0, 0, 0, 0.2)) 0px 2px 1px -1px, var(--your-prefix-shadow, rgba(0, 0, 0, 0.14)) 0px 1px 1px 0px, var(--your-prefix-shadow, rgba(0, 0, 0, 0.12)) 0px 1px 3px 0px);
41+
* }
42+
* ```
43+
*/
44+
readonly shadowColorTokenPrefix: string
45+
46+
/**
47+
* @description
48+
* For managed or public CSS variables.
49+
* Note that this option is usually used together with [shadowColorTokenPrefix].
50+
*
51+
* @default 'md-sys-elevation'
52+
*
53+
* @example If the value of prefix is 'your-prefix'.
54+
* ```css
55+
* .elevation-1 {
56+
* box-shadow: var(--your-prefix-level-1, var(--md-sys-color-shadow, rgba(0, 0, 0, 0.2)) 0px 2px 1px -1px, var(--md-sys-color-shadow, rgba(0, 0, 0, 0.14)) 0px 1px 1px 0px, var(--md-sys-color-shadow, rgba(0, 0, 0, 0.12)) 0px 1px 3px 0px);
57+
* }
58+
* ```
59+
*/
60+
readonly prefix: string
61+
62+
/**
63+
* @description
64+
* This option can customize the CSS value corresponding to certain tokens.
65+
* Note that this option ignores [hardcodeDefaultValue] and [prefix].
66+
*
67+
* @default []
68+
*
69+
* @example
70+
* ```typescript
71+
* const color = provideElevation({
72+
* customTokens: {
73+
* // .elevation-1 { box-shadow: red 0px 5px 10px 10px; }
74+
* 'level1': 'red 0px 5px 10px 10px'
75+
* },
76+
* })
77+
* ```
78+
*/
79+
readonly customTokens: IElevation | Record<string, string>
80+
81+
/**
82+
* @description
83+
* When set to true, each token contains a default value.
84+
*
85+
* @default true
86+
*
87+
* @example
88+
* ```typescript
89+
* const color = provideElevation({
90+
* // .elevation-1 { box-shadow: var(--md-sys-elevation-level-1, var(--md-sys-color-shadow, rgba(0, 0, 0, 0.2)) 0px 2px 1px -1px, var(--md-sys-color-shadow, rgba(0, 0, 0, 0.14)) 0px 1px 1px 0px, var(--md-sys-color-shadow, rgba(0, 0, 0, 0.12)) 0px 1px 3px 0px); }
91+
* hardcodeDefaultValue: true,
92+
*
93+
* // .elevation-1 { box-shadow: var(--md-sys-elevation-level-1, ); }
94+
* hardcodeDefaultValue: false,
95+
* })
96+
* ```
97+
*/
98+
readonly hardcodeDefaultValue: boolean
99+
100+
/**
101+
* @description
102+
* Exclude some unnecessary tokens.
103+
*
104+
* @default []
105+
*
106+
* @example
107+
* ```typescript
108+
* const color = provideElevation({
109+
* excludedTokens: [
110+
* // remove .elevation-5
111+
* 'level5',
112+
* ],
113+
* })
114+
* ```
115+
*/
116+
readonly excludedTokens: Array<(keyof IElevation) | {}>
6117
}
7118

8-
export class ElevationProvider implements IProvider {
119+
export interface IElevation {
120+
levelNone: string
121+
level1: string
122+
level2: string
123+
level3: string
124+
level4: string
125+
level5: string
126+
}
127+
128+
class DefaultElevationTokens implements IElevation {
129+
levelNone = 'none'
130+
level1
131+
level2
132+
level3
133+
level4
134+
level5
9135

10-
public shadowToken
136+
constructor(shadowColorCssVariableLayer1: string, shadowColorCssVariableLayer2: string, shadowColorCssVariableLayer3: string) {
137+
this.level1 = `${shadowColorCssVariableLayer1} 0px 2px 1px -1px, ${shadowColorCssVariableLayer2} 0px 1px 1px 0px, ${shadowColorCssVariableLayer3} 0px 1px 3px 0px`
138+
this.level2 = `${shadowColorCssVariableLayer1} 0px 3px 3px -2px, ${shadowColorCssVariableLayer2} 0px 3px 4px 0px, ${shadowColorCssVariableLayer3} 0px 1px 8px 0px`
139+
this.level3 = `${shadowColorCssVariableLayer1} 0px 3px 5px -1px, ${shadowColorCssVariableLayer2} 0px 6px 10px 0px, ${shadowColorCssVariableLayer3} 0px 1px 18px 0px`
140+
this.level4 = `${shadowColorCssVariableLayer1} 0px 5px 5px -3px, ${shadowColorCssVariableLayer2} 0px 8px 10px 1px, ${shadowColorCssVariableLayer3} 0px 3px 14px 2px`
141+
this.level5 = `${shadowColorCssVariableLayer1} 0px 7px 8px -4px, ${shadowColorCssVariableLayer2} 0px 12px 17px 2px, ${shadowColorCssVariableLayer3} 0px 5px 22px 4px`
142+
}
11143

12-
constructor(params: TElevationProviderConstructorParams) {
13-
this.shadowToken = params.shadowToken
144+
public get defaultTokenRecord() {
145+
return {
146+
levelNone: this.levelNone,
147+
level1: this.level1,
148+
level2: this.level2,
149+
level3: this.level3,
150+
level4: this.level4,
151+
level5: this.level5,
152+
}
153+
}
154+
}
155+
156+
export class ElevationProvider extends DefaultElevationTokens implements IProvider {
157+
public prefix
158+
public hardcodeDefaultValue
159+
public excludedTokens
160+
public tokens
161+
public customTokens
162+
163+
constructor(params: Partial<TElevationProviderConstructorParams>) {
164+
const shadowColorCssVariableLayer1 = params.shadowColorCssVariable?.layer1 ?? `var(--${params.shadowColorTokenPrefix ?? 'md-sys-color'}-shadow, rgba(0, 0, 0, 0.2))`
165+
const shadowColorCssVariableLayer2 = params.shadowColorCssVariable?.layer2 ?? `var(--${params.shadowColorTokenPrefix ?? 'md-sys-color'}-shadow, rgba(0, 0, 0, 0.14))`
166+
const shadowColorCssVariableLayer3 = params.shadowColorCssVariable?.layer3 ?? `var(--${params.shadowColorTokenPrefix ?? 'md-sys-color'}-shadow, rgba(0, 0, 0, 0.12))`
167+
super(shadowColorCssVariableLayer1, shadowColorCssVariableLayer2, shadowColorCssVariableLayer3)
168+
169+
this.prefix = params.prefix ?? 'md-sys-elevation'
170+
this.hardcodeDefaultValue = params.hardcodeDefaultValue ?? true
171+
this.excludedTokens = params.excludedTokens ?? []
172+
this.customTokens = params.customTokens ?? {}
173+
this.tokens = this.validate([this.customTokens as Record<string, string>, this.defaultTokenRecord, this.excludedTokens])
174+
}
175+
176+
protected validate(params: Parameters<typeof Validates.validate>) {
177+
return Validates.validate(...params)
178+
}
179+
180+
protected transformTokensToCssRuleObject(prefix: string, tokens: Record<string, string>, hardcodeDefaultValue: boolean) {
181+
const cssPropertyComputed = (name: string, value: string) => !Object.hasOwn(this.customTokens, name) ? `var(--${prefix}-${Strings.toKebabCase(name)}, ${hardcodeDefaultValue ? value : ''})` : `${value}`
182+
183+
return Validates.transformTokenRecordToCssRuleObject(
184+
tokens,
185+
(name) => `.${Strings.toKebabCase(name).replace('level', 'elevation')}`,
186+
(name, value) => ({
187+
'box-shadow': cssPropertyComputed(name, value)
188+
})
189+
)
14190
}
15191

16192
public getPlugin() {
193+
const tokens = this.transformTokensToCssRuleObject(this.prefix, this.tokens, this.hardcodeDefaultValue)
17194
return plugin(({ addUtilities }) => {
18-
addUtilities({
19-
'.elevation-none': {
20-
'box-shadow': `none`,
21-
},
22-
'.elevation-1': {
23-
'box-shadow': `rgba(0, 0, 0, 0.2) 0px 2px 1px -1px, var(--${this.shadowToken}-shadow, rgba(0, 0, 0, 0.14)) 0px 1px 1px 0px, var(--${this.shadowToken}-shadow, rgba(0, 0, 0, 0.12)) 0px 1px 3px 0px`,
24-
},
25-
'.elevation-2': {
26-
'box-shadow': `rgba(0, 0, 0, 0.2) 0px 3px 3px -2px, var(--${this.shadowToken}-shadow, rgba(0, 0, 0, 0.14)) 0px 3px 4px 0px, var(--${this.shadowToken}-shadow, rgba(0, 0, 0, 0.12)) 0px 1px 8px 0px`,
27-
},
28-
'.elevation-3': {
29-
'box-shadow': `rgba(0, 0, 0, 0.2) 0px 3px 5px -1px, var(--${this.shadowToken}-shadow, rgba(0, 0, 0, 0.14)) 0px 6px 10px 0px, var(--${this.shadowToken}-shadow, rgba(0, 0, 0, 0.12)) 0px 1px 18px 0px`,
30-
},
31-
'.elevation-4': {
32-
'box-shadow': `rgba(0, 0, 0, 0.2) 0px 5px 5px -3px, var(--${this.shadowToken}-shadow, rgba(0, 0, 0, 0.14)) 0px 8px 10px 1px, var(--${this.shadowToken}-shadow, rgba(0, 0, 0, 0.12)) 0px 3px 14px 2px`,
33-
},
34-
'.elevation-5': {
35-
'box-shadow': `rgba(0, 0, 0, 0.2) 0px 7px 8px -4px, var(--${this.shadowToken}-shadow, rgba(0, 0, 0, 0.14)) 0px 12px 17px 2px, var(--${this.shadowToken}-shadow, rgba(0, 0, 0, 0.12)) 0px 5px 22px 4px`,
36-
},
37-
})
195+
addUtilities(tokens)
38196
})
39197
}
40198
}

src/tokens/provide-all.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { provideTypography } from "./provide-typography";
1111

1212
export function provideAll(params: {
1313
color?: Partial<TColorProviderConstructorParams>,
14-
elevation?: TElevationProviderConstructorParams,
14+
elevation?: Partial<TElevationProviderConstructorParams>,
1515
motion?: TMotionProviderConstructorParams,
1616
shape?: TShapeProviderConstructorParams,
1717
typography?: TTypographyProviderConstructorParams,

src/tokens/provide-elevation.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,4 @@ import { ElevationProvider, type TElevationProviderConstructorParams } from "./i
1212
* }
1313
* ```
1414
*/
15-
export const provideElevation = (params: TElevationProviderConstructorParams = {
16-
shadowToken: 'md-sys-color-shadow'
17-
}) => new ElevationProvider(params)
15+
export const provideElevation = (params?: Partial<TElevationProviderConstructorParams>) => new ElevationProvider(params ?? {})

0 commit comments

Comments
 (0)