Browse Source

feat(gui-v2): persist theme colors and add picker for accent

pull/3249/head
braks 2 years ago
parent
commit
1bd8e778af
  1. 7
      packages/nc-gui-v2/app.vue
  2. 1
      packages/nc-gui-v2/components.d.ts
  3. 26
      packages/nc-gui-v2/composables/useTheme/index.ts
  4. 71
      packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue
  5. 2
      packages/nc-gui-v2/windi.config.ts

7
packages/nc-gui-v2/app.vue

@ -1,14 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, themeV2Colors, useRoute, useTheme } from '#imports' import { computed, provideTheme, useRoute } from '#imports'
const route = useRoute() const route = useRoute()
const disableBaseLayout = computed(() => route.path.startsWith('/nc/view') || route.path.startsWith('/nc/form')) const disableBaseLayout = computed(() => route.path.startsWith('/nc/view') || route.path.startsWith('/nc/form'))
useTheme({ provideTheme()
primaryColor: themeV2Colors['royal-blue'].DEFAULT,
accentColor: themeV2Colors.pink['500'],
})
</script> </script>
<template> <template>

1
packages/nc-gui-v2/components.d.ts vendored

@ -72,6 +72,7 @@ declare module '@vue/runtime-core' {
CilFullscreen: typeof import('~icons/cil/fullscreen')['default'] CilFullscreen: typeof import('~icons/cil/fullscreen')['default']
CilFullscreenExit: typeof import('~icons/cil/fullscreen-exit')['default'] CilFullscreenExit: typeof import('~icons/cil/fullscreen-exit')['default']
ClarityColorPickerLine: typeof import('~icons/clarity/color-picker-line')['default'] ClarityColorPickerLine: typeof import('~icons/clarity/color-picker-line')['default']
ClarityColorPickerSolid: typeof import('~icons/clarity/color-picker-solid')['default']
ClarityImageLine: typeof import('~icons/clarity/image-line')['default'] ClarityImageLine: typeof import('~icons/clarity/image-line')['default']
ClaritySuccessLine: typeof import('~icons/clarity/success-line')['default'] ClaritySuccessLine: typeof import('~icons/clarity/success-line')['default']
EvaEmailOutline: typeof import('~icons/eva/email-outline')['default'] EvaEmailOutline: typeof import('~icons/eva/email-outline')['default']

26
packages/nc-gui-v2/composables/useTheme/index.ts

@ -1,8 +1,10 @@
import { ConfigProvider } from 'ant-design-vue' import { ConfigProvider } from 'ant-design-vue'
import type { Theme as AntTheme } from 'ant-design-vue/es/config-provider' import type { Theme as AntTheme } from 'ant-design-vue/es/config-provider'
import { hexToRGB, ref, useCssVar, useInjectionState } from '#imports' import { useStorage } from '@vueuse/core'
import { NOCO, hexToRGB, themeV2Colors, toRefs, useCssVar, useInjectionState } from '#imports'
interface ThemeConfig extends AntTheme { interface ThemeConfig extends AntTheme {
primaryColor: string
accentColor: string accentColor: string
} }
@ -11,17 +13,26 @@ const [setup, use] = useInjectionState((config?: Partial<ThemeConfig>) => {
const accentColor = useCssVar('--color-accent', typeof document !== 'undefined' ? document.documentElement : null) const accentColor = useCssVar('--color-accent', typeof document !== 'undefined' ? document.documentElement : null)
/** current theme config */ /** current theme config */
const currentTheme = ref<Partial<ThemeConfig>>() const currentTheme = useStorage<ThemeConfig>(
`${NOCO}db-theme`,
/** set initial config if exists */ {
if (config) setTheme(config) primaryColor: themeV2Colors['royal-blue'].DEFAULT,
accentColor: themeV2Colors.pink['500'],
},
localStorage,
{ mergeDefaults: true },
)
/** set initial config */
setTheme(config ?? currentTheme.value)
/** set theme (persists in localstorage) */
function setTheme(theme: Partial<ThemeConfig>) { function setTheme(theme: Partial<ThemeConfig>) {
// convert hex colors to rgb values // convert hex colors to rgb values
if (theme.primaryColor) primaryColor.value = hexToRGB(theme.primaryColor) if (theme.primaryColor) primaryColor.value = hexToRGB(theme.primaryColor)
if (theme.accentColor) accentColor.value = hexToRGB(theme.accentColor) if (theme.accentColor) accentColor.value = hexToRGB(theme.accentColor)
currentTheme.value = theme currentTheme.value = theme as ThemeConfig
ConfigProvider.config({ ConfigProvider.config({
theme, theme,
@ -31,7 +42,6 @@ const [setup, use] = useInjectionState((config?: Partial<ThemeConfig>) => {
return { return {
theme: currentTheme, theme: currentTheme,
setTheme, setTheme,
cssVars: { primaryColor, accentColor },
} }
}) })

71
packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue

@ -53,18 +53,19 @@ const dropdownOpen = ref(false)
/** Sidebar ref */ /** Sidebar ref */
const sidebar = ref() const sidebar = ref()
const pickedColor = ref<any>('') const pickedColor = ref<any>('#ffffff')
const pickerActive = ref(false) let pickerActive = $ref<boolean | 'primary' | 'accent'>(false)
const email = computed(() => user.value?.email ?? '---') const email = computed(() => user.value?.email ?? '---')
const { setTheme } = useTheme() const { setTheme, theme } = useTheme()
watch(pickedColor, (nextColor) => { watch(pickedColor, (nextColor) => {
if (nextColor) { if (pickerActive && nextColor.hex) {
setTheme({ setTheme({
primaryColor: nextColor.hex, primaryColor: pickerActive === 'primary' ? nextColor.hex : theme.value.primaryColor,
accentColor: pickerActive === 'accent' ? nextColor.hex : theme.value.accentColor,
}) })
} }
}) })
@ -124,6 +125,22 @@ const copyAuthToken = async () => {
message.error(e.message) message.error(e.message)
} }
} }
const openColorPicker = (type: 'primary' | 'accent') => {
if (!pickerActive || pickerActive !== type) {
pickedColor.value = type === 'primary' ? theme.value.primaryColor : theme.value.accentColor
pickerActive = type
} else {
pickerActive = false
}
}
const onMenuClose = (visible: boolean) => {
if (!visible) {
pickedColor.value = '#ffffff'
pickerActive = false
}
}
</script> </script>
<template> <template>
@ -171,7 +188,7 @@ const copyAuthToken = async () => {
</template> </template>
</div> </div>
<a-dropdown v-else class="h-full min-w-0 flex-1" :trigger="['click']" placement="bottom"> <a-dropdown v-else class="h-full min-w-0 flex-1" :trigger="['click']" placement="bottom" @visible-change="onMenuClose">
<div <div
:style="{ width: isOpen ? 'calc(100% - 40px) pr-2' : '100%' }" :style="{ width: isOpen ? 'calc(100% - 40px) pr-2' : '100%' }"
:class="[isOpen ? '' : 'justify-center']" :class="[isOpen ? '' : 'justify-center']"
@ -331,14 +348,42 @@ const copyAuthToken = async () => {
<a-menu-divider /> <a-menu-divider />
<a-menu-item class="relative"> <a-sub-menu>
<div class="nc-project-menu-item group" @click.stop="pickerActive = !pickerActive"> <template #title>
<ClarityImageLine class="group-hover:text-accent" /> <div class="nc-project-menu-item group">
Theme <ClarityImageLine class="group-hover:text-accent" />
Theme
<Chrome v-if="pickerActive" v-model="pickedColor" class="absolute top-0 right-5" @click.stop /> <div class="flex-1" />
</div>
</a-menu-item> <MaterialSymbolsChevronRightRounded
class="transform group-hover:(scale-115 text-accent) text-xl text-gray-400"
/>
</div>
</template>
<a-menu-item>
<div class="nc-project-menu-item group" @click.stop="openColorPicker('primary')">
<ClarityColorPickerSolid class="group-hover:text-accent" />
Primary Color
</div>
</a-menu-item>
<a-menu-item>
<div class="nc-project-menu-item group" @click.stop="openColorPicker('accent')">
<ClarityColorPickerSolid class="group-hover:text-accent" />
Accent Color
</div>
</a-menu-item>
</a-sub-menu>
<Chrome
v-if="pickerActive"
v-model="pickedColor"
class="z-99 absolute right-[-225px]"
@click.stop
@blur="onMenuClose(false)"
/>
</a-menu-item-group> </a-menu-item-group>
</a-menu> </a-menu>
</template> </template>

2
packages/nc-gui-v2/windi.config.ts

@ -27,7 +27,7 @@ export default defineConfig({
questionMark, questionMark,
formsPlugin, formsPlugin,
typographyPlugin({ typographyPlugin({
dark: false, dark: true,
}), }),
aspectRatioPlugin, aspectRatioPlugin,
lineClampPlugin, lineClampPlugin,

Loading…
Cancel
Save