Skip to content

Commit

Permalink
feat(va-config): colors prop
Browse files Browse the repository at this point in the history
  • Loading branch information
m0ksem committed May 1, 2023
1 parent cf57c63 commit 3e2462a
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 16 deletions.
15 changes: 9 additions & 6 deletions packages/docs/layouts/landing.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<template>
<NuxtPage />
<VaConfig :colors="{ currentPresetName: 'landing' }">
<div class="landing">
<NuxtPage />
</div>
</VaConfig>
</template>

<script lang="ts" setup>
import { useColors } from 'vuestic-ui'
const { applyPreset } = useColors()
applyPreset('landing')
const { presets } = useColors()
useHead({
title: 'Vuestic UI — Vue 3 UI framework',
Expand All @@ -17,11 +19,12 @@ useHead({
})
</script>

<style lang="scss">
<style lang="scss" scoped>
// Need to import tailwind in layout, because otherwise Vuestic component's css will has a higher priority
// @import '~/assets/css/tailwind.css';
body {
.landing {
background: var(--va-background-secondary);
color: var(--va-on-background-secondary)
}
</style>
12 changes: 12 additions & 0 deletions packages/ui/src/components/va-config/VaConfig.demo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@
</va-button>
</va-config>
</VbCard>

<VbCard title="Colors">
<va-config :colors="{ variables: {
primary: '#f0f',
} }">
<va-button>
Button inside va-config
</va-button>

<div style="background-color: var(--va-primary); color: var(--va-on-primary);">CSS variables</div>
</va-config>
</VbCard>
</VbDemo>
</template>

Expand Down
41 changes: 38 additions & 3 deletions packages/ui/src/components/va-config/VaConfig.vue
Original file line number Diff line number Diff line change
@@ -1,28 +1,63 @@
<template>
<slot />
<CssVarsRenderer v-bind="$attrs">
<slot />
</CssVarsRenderer>
</template>

<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'
import { computed, defineComponent, PropType, h, Fragment } from 'vue'
import { useComponentPresetProp } from '../../composables/useComponentPreset'
import { ComponentConfig } from '../../services/component-config'
import { provideLocalConfig, useLocalConfig } from '../../composables/useLocalConfig'
import { useGlobalConfigProvider } from './hooks/useGlobalConfigProvider'
import { PartialGlobalConfig } from '../../services/global-config'
import { renderSlotNodes } from '../../utils/headless'
import { useColors } from '../../composables'
const CssVarsRenderer = defineComponent({
name: 'VaCssVarsRenderer',
inheritAttrs: false,
setup (props, { slots, attrs }) {
const { colorsToCSSVariable, colors } = useColors()
const style = computed(() => {
return colorsToCSSVariable(colors)
})
return () => h(Fragment, attrs, renderSlotNodes(slots.default, {}, {
style: style.value,
}) || undefined)
},
})
export default defineComponent({
name: 'VaConfig',
components: { CssVarsRenderer },
props: {
...useComponentPresetProp,
components: { type: Object as PropType<ComponentConfig>, default: () => ({}) },
colors: { type: Object as PropType<PartialGlobalConfig['colors']>, default: () => ({}) },
},
inheritAttrs: false,
setup (props) {
const prevChain = useLocalConfig()
// We want it to be an array and not a merged object for optimization purposes
const nextChain = computed(() => [...prevChain.value, props.components])
provideLocalConfig(nextChain)
return {}
const newConfig = useGlobalConfigProvider(computed(() => {
return {
colors: props.colors,
}
}))
return {
newConfig,
}
},
})
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { mergeDeep } from './../../../utils/merge-deep'
import cloneDeep from 'lodash/cloneDeep'
import { provide, computed, Ref } from 'vue'
import { useGlobalConfig } from '../../../composables'
import { GLOBAL_CONFIG, GlobalConfig, GlobalConfigUpdater, PartialGlobalConfig } from '../../../services/global-config'

export const useGlobalConfigProvider = (next: Ref<PartialGlobalConfig>) => {
const { globalConfig } = useGlobalConfig()

const nextChain = computed(() => {
const gcCopy = cloneDeep(globalConfig.value)
const compiledCopy: GlobalConfig = {
...gcCopy,
colors: {
...gcCopy.colors,
get variables () {
return this.presets[this.currentPresetName]
},
set variables (value) {
this.presets[this.currentPresetName] = value
},
},
}

return mergeDeep(compiledCopy, next.value) as GlobalConfig
})

const getGlobalConfig = (): GlobalConfig => nextChain.value
const setGlobalConfig = (updater: GlobalConfig | GlobalConfigUpdater<GlobalConfig>) => {
throw new Error('setGlobalConfig can not be used in VaConfig')
}

const mergeGlobalConfig = (updater: PartialGlobalConfig | GlobalConfigUpdater<PartialGlobalConfig>) => {
throw new Error('mergeGlobalConfig can not be used in VaConfig')
}

provide(GLOBAL_CONFIG, {
getGlobalConfig,
setGlobalConfig,
mergeGlobalConfig,
globalConfig: nextChain,
})

return nextChain
}
4 changes: 3 additions & 1 deletion packages/ui/src/composables/useColors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
normalizeColorName,
} from '../services/color/utils'
import { isDev } from '../utils/env'
import kebabCase from 'lodash/kebabCase'

/**
* You can add these props to any component by destructuring them inside props option.
Expand Down Expand Up @@ -111,7 +112,8 @@ export const useColors = () => {
.keys(colors)
.filter((key) => colors[key] !== undefined)
.reduce((acc: Record<string, any>, colorName: string) => {
acc[`--${prefix}-${colorName}`] = getColor(colors[colorName], undefined, true)
acc[`--${prefix}-${kebabCase(colorName)}`] = getColor(colors[colorName], undefined, true)
acc[`--${prefix}-on-${kebabCase(colorName)}`] = getColor(getTextColor(getColor(colors[colorName]!)), undefined, true)
return acc
}, {})
}
Expand Down
8 changes: 4 additions & 4 deletions packages/ui/src/services/color/tests/color-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ describe('useColors', () => {
)

it.each([
[[{ color: '#000000' }, 'va-test'], { '--va-test-color': '#000000' }],
[[{ color: 'secondary' }, 'va-test'], { '--va-test-color': 'var(--va-secondary)' }],
[[{ color: 'var(--va-primary)' }, 'va-test'], { '--va-test-color': 'var(--va-primary)' }], // TODO call Oleg
[[{ color: 'bad-color' }, 'va-test'], { '--va-test-color': '#154EC1' }],
[[{ color: '#000000' }, 'va-test'], { '--va-test-color': '#000000', '--va-test-on-color': 'var(--va-text-inverted)' }],
[[{ color: 'secondary' }, 'va-test'], { '--va-test-color': 'var(--va-secondary)', '--va-test-on-color': 'var(--va-text-inverted)' }],
[[{ color: 'var(--va-primary)' }, 'va-test'], { '--va-test-color': 'var(--va-primary)', '--va-test-on-color': 'var(--va-text-inverted)' }],
[[{ color: 'bad-color' }, 'va-test'], { '--va-test-color': '#154EC1', '--va-test-on-color': 'var(--va-text-inverted)' }],
[[{}, 'va-test'], {}],
])(
'colorToCssVariableArgs %s should be %s',
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/utils/headless.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const toNode = (v: any, attrs: NodeAttributes): VNode | null => {

if (v.type === Fragment) {
if (v.children === null) { return v }
return toNode(v.children[0], attrs)
return h(Fragment, v.props, v.children.map((v: any) => toNode(v, attrs)))
}

return h(v, attrs)
Expand Down
5 changes: 4 additions & 1 deletion packages/ui/src/utils/merge-deep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ export const mergeDeep = (target: any, source: any): any => {
const sourceValue = source[key]

if (isObject(targetValue) && isObject(sourceValue)) {
target[key] = mergeDeep(Object.assign({}, targetValue), sourceValue)
target[key] = mergeDeep(Object.create(
Object.getPrototypeOf(targetValue),
Object.getOwnPropertyDescriptors(targetValue),
), sourceValue)
} else {
target[key] = sourceValue
}
Expand Down

0 comments on commit 3e2462a

Please sign in to comment.