Skip to content

Commit 7d649f9

Browse files
committed
feat: style attribute
1 parent 9c95cb4 commit 7d649f9

File tree

6 files changed

+93
-21
lines changed

6 files changed

+93
-21
lines changed

Diff for: packages/ui/src/components/va-button/VaButton.stories.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { defineComponent } from 'vue'
2-
import VaButton from './VaButton.vue'
2+
import { VaButton } from './'
33
import VaButtonDemo from './VaButton.demo.vue'
44

55
export default {
@@ -22,3 +22,12 @@ export const RightShift = () => ({
2222
</div>
2323
`,
2424
})
25+
26+
export const StyleAttributes = () => ({
27+
components: { VaButton },
28+
template: `
29+
<div>
30+
<VaButton style:background="danger">Text</VaButton>
31+
</div>
32+
`,
33+
})

Diff for: packages/ui/src/components/va-button/VaButton.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,8 @@ defineExpose({
223223
}
224224
225225
&::before {
226-
background: v-bind(backgroundColor);
227-
opacity: v-bind(backgroundColorOpacity);
226+
background: var(--va-button-background, v-bind(backgroundColor));
227+
opacity: var(--va-button-background-opacity, v-bind(backgroundColorOpacity));
228228
}
229229
230230
&::after {

Diff for: packages/ui/src/composables/useColors.ts

+21-16
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,12 @@ export const useColors = () => {
6060
return colors
6161
}
6262

63-
/**
63+
/**
6464
* Returns color from config variables by name or return prop if color is a valid hex, hsl, hsla, rgb or rgba color.
6565
* @param prop - should be color name or color in hex, hsl, hsla, rgb or rgba format.
6666
* @param preferVariables - function should return (if possible) CSS variable instead of hex (hex is needed to set opacity).
67-
* @param defaultColor - this color will be used if prop is invalid.
6867
*/
69-
const getColor = (prop?: string, defaultColor?: string, preferVariables?: boolean): CssColor => {
70-
if (!defaultColor) {
71-
/**
72-
* Most default color - fallback when nothing else is found.
73-
*/
74-
defaultColor = colors.primary
75-
}
76-
68+
const getColorStrict = (prop: string, preferVariables?: boolean): CssColor | null => {
7769
if (prop === 'transparent') {
7870
return '#ffffff00'
7971
}
@@ -90,10 +82,6 @@ export const useColors = () => {
9082
}
9183
}
9284

93-
if (!prop) {
94-
prop = getColor(defaultColor)
95-
}
96-
9785
const colorValue = colors[prop] || colors[normalizeColorName(prop)]
9886
if (colorValue) {
9987
return preferVariables ? `var(${cssVariableName(prop)})` : colorValue
@@ -110,7 +98,23 @@ export const useColors = () => {
11098
warn(`'${prop}' is not a proper color! Use HEX or default color themes
11199
names (https://vuestic.dev/en/styles/colors#default-color-themes)`)
112100

113-
return getColor(defaultColor)
101+
return null
102+
}
103+
104+
/**
105+
* Returns color from config variables by name or return prop if color is a valid hex, hsl, hsla, rgb or rgba color.
106+
* @param prop - should be color name or color in hex, hsl, hsla, rgb or rgba format.
107+
* @param preferVariables - function should return (if possible) CSS variable instead of hex (hex is needed to set opacity).
108+
* @param defaultColor - this color will be used if prop is invalid. By default it is primary color.
109+
*/
110+
const getColor = (prop?: string, defaultColor?: string, preferVariables?: boolean): CssColor => {
111+
if (!defaultColor) {
112+
defaultColor = colors.primary
113+
}
114+
115+
if (!prop) { return defaultColor }
116+
117+
return getColorStrict(prop, preferVariables) ?? defaultColor
114118
}
115119

116120
const getComputedColor = (color: string) => {
@@ -125,7 +129,7 @@ export const useColors = () => {
125129
.keys(colors)
126130
.filter((key) => colors[key] !== undefined)
127131
.reduce((acc: Record<string, any>, colorName: string) => {
128-
acc[`--${prefix}-${kebabCase(colorName)}`] = getColor(colors[colorName], undefined, true)
132+
acc[`--${prefix}-${kebabCase(colorName)}`] = getColor(colors[colorName]!, undefined, true)
129133
acc[`--${prefix}-on-${kebabCase(colorName)}`] = getColor(getTextColor(getColor(colors[colorName]!)), undefined, true)
130134
return acc
131135
}, {})
@@ -185,6 +189,7 @@ export const useColors = () => {
185189
applyPreset,
186190
setColors,
187191
getColors,
192+
getColorStrict,
188193
getColor,
189194
getComputedColor,
190195
getBoxShadowColor,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { ComponentInternalInstance, Ref, computed } from 'vue'
2+
import { cssVariableName } from '../color/utils'
3+
import { Props } from './shared'
4+
import { useColors } from '../../composables'
5+
6+
export const createCSSVariables = (instance: ComponentInternalInstance, attributes: Props) => {
7+
const name = instance.type.name
8+
9+
const { getColorStrict } = useColors()
10+
11+
return computed(() => {
12+
const keys = Object.keys(attributes)
13+
14+
const variables = new Map<string, string>()
15+
16+
for (let i = 0; i < keys.length; i++) {
17+
const key = keys[i]
18+
let value = String(attributes[key])
19+
20+
if (!key.startsWith('style:')) { continue }
21+
22+
const color = getColorStrict(value, true)
23+
24+
if (color) {
25+
value = color
26+
}
27+
28+
variables.set(cssVariableName(name?.slice(2) + '-' + key.slice(6)), String(value))
29+
}
30+
31+
return variables
32+
})
33+
}

Diff for: packages/ui/src/services/config-transport/createRenderFn.ts

+24-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { withCtx, h, DefineComponent, VNode, isVNode, Text, createBlock } from 'vue'
1+
import { withCtx, h, DefineComponent, VNode, isVNode, Text, createBlock, ComputedRef, normalizeStyle } from 'vue'
22

33
type VueInternalRenderFunction = Function
44

@@ -49,6 +49,28 @@ export const createRenderFn = (component: DefineComponent): VueInternalRenderFun
4949
// When compile rendered function, it doesn't require thisArg
5050
const thisArg = compiledRenderedFn ? undefined : customCtx
5151

52-
return originalRenderFn.call(thisArg, customCtx, ...args.slice(1))
52+
const result: VNode = originalRenderFn.call(thisArg, customCtx, ...args.slice(1))
53+
54+
if ('ctx' in result) {
55+
const variables: ComputedRef<Map<string, string>> = (result.ctx as any).$vaCssVaraibles
56+
57+
if (!variables) {
58+
return result
59+
}
60+
61+
if (result.props === null) {
62+
result.props = {}
63+
}
64+
65+
const vars: Record<string, string> = {}
66+
67+
for (const key of variables.value.keys()) {
68+
vars[key] = variables.value.get(key)!
69+
}
70+
71+
result.props.style = normalizeStyle([result.props.style, vars])
72+
}
73+
74+
return result
5375
}
5476
}

Diff for: packages/ui/src/services/config-transport/createSetupFn.ts

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { type Props } from './shared'
55
import { createProps } from './createProps'
66
import { createAttrs } from './createAttrs'
77
import { createSlots } from './createSlots'
8+
import { createCSSVariables } from './createCSSVariables'
89

910
export const createSetupFn = <T extends DefineComponent>(component: T) => {
1011
return (originalProps: Props, ctx: SetupContext) => {
@@ -17,6 +18,7 @@ export const createSetupFn = <T extends DefineComponent>(component: T) => {
1718
const props = createProps(instance, propsFromConfig)
1819
const attrs = createAttrs(instance, attrsFromConfig)
1920
const slots = createSlots(instance, propsFromConfig)
21+
const cssVariables = createCSSVariables(instance, attrs)
2022

2123
/**
2224
* Patch instance props with Proxy.
@@ -25,6 +27,7 @@ export const createSetupFn = <T extends DefineComponent>(component: T) => {
2527
instance.props = props
2628
instance.attrs = attrs
2729
instance.slots = slots
30+
;(instance as any).$vaCssVaraibles = cssVariables
2831

2932
const setupState = component.setup?.(shallowReadonly(props), {
3033
...ctx,

0 commit comments

Comments
 (0)