feat: experimental customize theme

This commit is contained in:
07akioni 2020-06-18 01:33:55 +08:00
parent c61b31166d
commit 6ecf0bf949
15 changed files with 391 additions and 155 deletions

View File

@ -3,7 +3,8 @@ import { Vue, i18n } from './init'
import debugRouteMixin from './routes/debugRouteMixin'
import hljs from './hljs'
import demoRouterView from './demoRouterView'
import NaiveUI from '../src/index'
// import './styles/theme.scss' // test customize style scheme
import naive from '../src/index'
import '../src/_styles/index.scss'
import '../src/_styles/fonts/Lato.scss'
import '../src/_styles/fonts/FiraCode.scss'
@ -13,8 +14,13 @@ import createRouter from './routes/router'
debugRouteMixin(routes, childRoutes)
const router = createRouter(Vue, routes)
Vue.use(NaiveUI)
NaiveUI.setHljs(hljs)
Vue.use(naive)
naive.setHljs(hljs)
// naive.setStyleSchemes({
// light: {
// primaryColor: 'rgb(255, 0, 0)'
// }
// })
new Vue({
...demoRouterView,

View File

@ -0,0 +1,58 @@
<!--no-demo-->
# Experimental Features
<n-alert type="warning" title="Caveats">
The following features are <n-text strong>unstable</n-text>. Use them if you really need and perpare to follow the API changes.
</n-alert>
## Customize Theme
It is a experimental feature, since naive-ui use both CSS-in-JS and Scss. I haven't find a solution to integrate them together in an elegant way.
I'm planning to rewrite all the styles using CSS-in-JS. Before it has been done, the following methods will only be a workaround.
Currently you can only change the primary color.
Firstly, you should create a `.scss` file. Insert getter functions first and then import the entry `.scss` of naive-ui. You may need to config sass-loader.
```scss
// customized-style.scss
@function get-primary-color ($theme) {
@if $theme == 'light' {
@return rgb(255, 0, 0);
}
@return null;
}
// naive-ui will check the existence of getter functions and set the related
// variables properly
@import '~naive-ui/src/_styles/index.scss';
```
Then you need to import the `.scss` file to your app entry file.
```js
// index.js
// ...
import 'path/to/customized-style.scss'
// ... use naive-ui normally
```
Not done yet. You still need to set the primary color in the js environment. Since naive-ui use CSS-in-JS...
So let's continue. Set `theme-variants` on the naive-ui instance.
```js
// ...
import naive from 'naive-ui'
naive.setStyleSchemes({
light: {
primaryColor: 'rgb(255, 0, 0)'
}
})
Vue.use(naive)
```

View File

@ -0,0 +1,58 @@
<!--no-demo-->
# Experimental Features
<n-alert type="warning" title="Caveats">
The following features are <n-text strong>unstable</n-text>. Use them if you really need and perpare to follow the API changes.
</n-alert>
## Customize Theme
It is a experimental feature, since naive-ui use both CSS-in-JS and Scss. I haven't find a solution to integrate them together in an elegant way.
I'm planning to rewrite all the styles using CSS-in-JS. Before it has been done, the following methods will only be a workaround.
Currently you can only change the primary color.
Firstly, you should create a `.scss` file. Insert getter functions first and then import the entry `.scss` of naive-ui. You may need to config sass-loader.
```scss
// customized-style.scss
@function get-primary-color ($theme) {
@if $theme == 'light' {
@return rgb(255, 0, 0);
}
@return null;
}
// naive-ui will check the existence of getter functions and set the related
// variables properly
@import '~naive-ui/src/_styles/index.scss';
```
Then you need to import the `.scss` file to your app entry file.
```js
// index.js
// ...
import 'path/to/customized-style.scss'
// ... use naive-ui normally
```
Not done yet. You still need to set the primary color in the js environment. Since naive-ui use CSS-in-JS...
So let's continue. Set `theme-variants` on the naive-ui instance.
```js
// ...
import naive from 'naive-ui'
naive.setStyleSchemes({
light: {
primaryColor: 'rgb(255, 0, 0)'
}
})
Vue.use(naive)
```

View File

@ -63,6 +63,11 @@ export default function (instance) {
title: '创建适配主题的组件',
path: `/${instance.lang}/${instance.theme}/doc` + '/n-theme'
},
{
name: 'Experimental Features',
title: '试验性特性',
path: `/${instance.lang}/${instance.theme}/doc` + '/experimental-features'
},
appendCounts({
name: '配置组件',
group: true,
@ -510,6 +515,10 @@ export default function (instance) {
name: 'Create Themed Component',
path: `/${instance.lang}/${instance.theme}/doc` + '/n-theme'
},
{
name: 'Experimental Features',
path: `/${instance.lang}/${instance.theme}/doc` + '/experimental-features'
},
appendCounts({
name: 'Config Components',
group: true,

View File

@ -5,6 +5,7 @@ import Entry from '../entry'
import intro from '../documentation/intro/intro'
import start from '../documentation/intro/start'
import devGuildlines from '../documentation/intro/devGuidelines'
import experimentalFeatures from '../documentation/intro/experimental-features'
import nimbusServiceLayoutDemo from '../documentation/deprecated/nimbusServiceLayout'
import gradientText from '../documentation/components/gradientText'
@ -79,10 +80,12 @@ import DocEntry from '../docEntry'
import { withPrefix } from './utils'
export const childRoutes = withPrefix('/:lang/:theme/doc', [
// basic docs
{ path: '/intro', component: intro },
{ path: '/start', component: start },
{ path: '/experimental-features', component: experimentalFeatures },
{ path: '/dev-guildlines', component: devGuildlines },
{ path: '/n-nimbus-service-layout', component: nimbusServiceLayoutDemo },
// components
{ path: '/n-layout', component: layout },
{ path: '/n-gradient-text', component: gradientText },
{ path: '/n-icon', component: icon },
@ -148,7 +151,9 @@ export const childRoutes = withPrefix('/:lang/:theme/doc', [
{ path: '/n-code', component: code },
{ path: '/n-typography', component: typography },
{ path: '/n-upload', component: upload },
{ path: '/n-table', component: table }
{ path: '/n-table', component: table },
// deprecated
{ path: '/n-nimbus-service-layout', component: nimbusServiceLayoutDemo }
])
export const routes = [

8
demo/styles/theme.scss Normal file
View File

@ -0,0 +1,8 @@
@function get-primary-color ($theme) {
@if $theme == 'light' {
@return rgb(255, 0, 0);
}
@return null;
}
@import '../../src/_styles/index.scss';

View File

@ -98,13 +98,13 @@ import withapp from '../../_mixins/withapp'
import themeable from '../../_mixins/themeable'
import NIcon from '../../Icon'
import NIconSwitchTransition from '../../_transition/IconSwitchTransition'
import { read, composite, hash } from '../../_utils/color'
import { read, hash, createHoverColor, createActiveColor } from '../../_utils/color'
import { createColorStyle } from './styles/Button.cssr.js'
import { createThemedStyle } from '../../_utils/cssr'
import theme from './styles/theme'
import createTheme from './styles/theme'
const colorStyle = createColorStyle()
const typeStyle = createThemedStyle(colorStyle, theme)
let typeStyle
function mountTypeStyle (type) {
typeStyle.mount({
@ -119,8 +119,8 @@ function mountColorStyle (color, colorHash) {
const textColor = null
const rgb = read(color)
const digest = hash(rgb)
const hoverColor = composite(rgb, [255, 255, 255, 0.14])
const activeColor = composite(rgb, [0, 0, 0, 0.1])
const hoverColor = createHoverColor(rgb)
const activeColor = createActiveColor(rgb)
const focusColor = hoverColor
colorStyle.mount({
target: 'n-button-' + digest,
@ -315,6 +315,12 @@ export default {
if (color) {
mountColorStyle(color)
}
if (!typeStyle) {
typeStyle = createThemedStyle(colorStyle, createTheme(
this.$naive.styleSchemes,
this.$naive.fallbackTheme
))
}
mountTypeStyle(this.type)
},
beforeDestroy () {

View File

@ -1,145 +1,154 @@
import lightScheme from '../../../_styles-in-js/lightStyleScheme.scss'
import darkScheme from '../../../_styles-in-js/darkStyleScheme.scss'
export default function createMergedStyleSchemes (globalSchemes, fallbackTheme) {
const mergedStyleSchemes = {}
for (const theme of Object.keys(globalSchemes)) {
const scheme = globalSchemes[theme]
if (theme === fallbackTheme) {
mergedStyleSchemes.fallback = createStyleScheme(scheme)
} else {
mergedStyleSchemes[theme] = createStyleScheme(scheme)
}
}
return mergedStyleSchemes
}
const light = {
default: {
color: lightScheme.baseBackgroundColor,
hoverColor: lightScheme.baseBackgroundColor,
activeColor: lightScheme.baseBackgroundColor,
focusColor: lightScheme.baseBackgroundColor,
function createStyleScheme (scheme, theme) {
if (theme !== 'dark') {
return {
default: {
color: scheme.baseBackgroundColor,
hoverColor: scheme.baseBackgroundColor,
activeColor: scheme.baseBackgroundColor,
focusColor: scheme.baseBackgroundColor,
textColor: lightScheme.secondaryTextColor,
hoverTextColor: lightScheme.primaryHoverColor,
activeTextColor: lightScheme.primaryActiveColor,
focusTextColor: lightScheme.primaryHoverColor,
textColor: scheme.secondaryTextColor,
hoverTextColor: scheme.primaryHoverColor,
activeTextColor: scheme.primaryActiveColor,
focusTextColor: scheme.primaryHoverColor,
textTypedTextColor: lightScheme.secondaryTextColor,
textTypedHoverTextColor: lightScheme.primaryHoverColor,
textTypedActiveTextColor: lightScheme.primaryActiveColor,
textTypedFocusTextColor: lightScheme.primaryHoverColor,
textTypedTextColor: scheme.secondaryTextColor,
textTypedHoverTextColor: scheme.primaryHoverColor,
textTypedActiveTextColor: scheme.primaryActiveColor,
textTypedFocusTextColor: scheme.primaryHoverColor,
ghostTypedTextColor: lightScheme.secondaryTextColor,
ghostTypedHoverTextColor: lightScheme.primaryHoverColor,
ghostTypedActiveTextColor: lightScheme.primaryActiveColor,
ghostTypedFocusTextColor: lightScheme.primaryHoverColor,
ghostTypedTextColor: scheme.secondaryTextColor,
ghostTypedHoverTextColor: scheme.primaryHoverColor,
ghostTypedActiveTextColor: scheme.primaryActiveColor,
ghostTypedFocusTextColor: scheme.primaryHoverColor,
borderColor: lightScheme.borderColor,
hoverBorderColor: lightScheme.primaryHoverColor,
activeBorderColor: lightScheme.primaryActiveColor,
focusBorderColor: lightScheme.primaryHoverColor,
borderColor: scheme.borderColor,
hoverBorderColor: scheme.primaryHoverColor,
activeBorderColor: scheme.primaryActiveColor,
focusBorderColor: scheme.primaryHoverColor,
rippleColor: lightScheme.primaryColor,
rippleColor: scheme.primaryColor,
tertiaryDepthIconColor: lightScheme.tertiaryTextColor,
baseBackgroundColor: lightScheme.baseBackgroundColor
},
primary: {
color: lightScheme.primaryColor,
hoverColor: lightScheme.primaryHoverColor,
activeColor: lightScheme.primaryActiveColor,
focusColor: lightScheme.primaryHoverColor,
textColor: lightScheme.baseBackgroundColor
},
info: {
color: lightScheme.infoColor,
hoverColor: lightScheme.infoHoverColor,
activeColor: lightScheme.infoActiveColor,
focusColor: lightScheme.infoHoverColor,
textColor: lightScheme.baseBackgroundColor
},
success: {
color: lightScheme.successColor,
hoverColor: lightScheme.successHoverColor,
activeColor: lightScheme.successActiveColor,
focusColor: lightScheme.successHoverColor,
textColor: lightScheme.baseBackgroundColor
},
warning: {
color: lightScheme.warningColor,
hoverColor: lightScheme.warningHoverColor,
activeColor: lightScheme.warningActiveColor,
focusColor: lightScheme.warningHoverColor,
textColor: lightScheme.baseBackgroundColor
},
error: {
color: lightScheme.errorColor,
hoverColor: lightScheme.errorHoverColor,
activeColor: lightScheme.errorActiveColor,
focusColor: lightScheme.errorHoverColor,
textColor: lightScheme.baseBackgroundColor
tertiaryDepthIconColor: scheme.tertiaryTextColor,
baseBackgroundColor: scheme.baseBackgroundColor
},
primary: {
color: scheme.primaryColor,
hoverColor: scheme.primaryHoverColor,
activeColor: scheme.primaryActiveColor,
focusColor: scheme.primaryHoverColor,
textColor: scheme.baseBackgroundColor
},
info: {
color: scheme.infoColor,
hoverColor: scheme.infoHoverColor,
activeColor: scheme.infoActiveColor,
focusColor: scheme.infoHoverColor,
textColor: scheme.baseBackgroundColor
},
success: {
color: scheme.successColor,
hoverColor: scheme.successHoverColor,
activeColor: scheme.successActiveColor,
focusColor: scheme.successHoverColor,
textColor: scheme.baseBackgroundColor
},
warning: {
color: scheme.warningColor,
hoverColor: scheme.warningHoverColor,
activeColor: scheme.warningActiveColor,
focusColor: scheme.warningHoverColor,
textColor: scheme.baseBackgroundColor
},
error: {
color: scheme.errorColor,
hoverColor: scheme.errorHoverColor,
activeColor: scheme.errorActiveColor,
focusColor: scheme.errorHoverColor,
textColor: scheme.baseBackgroundColor
}
}
} else {
return {
default: {
color: 'transparent',
hoverColor: 'transparent',
activeColor: 'transparent',
focusColor: 'transparent',
textColor: scheme.secondaryTextColor,
hoverTextColor: scheme.primaryHoverColor,
activeTextColor: scheme.primaryActiveColor,
focusTextColor: scheme.primaryHoverColor,
textTypedTextColor: scheme.secondaryTextColor,
textTypedHoverTextColor: scheme.primaryHoverColor,
textTypedActiveTextColor: scheme.primaryActiveColor,
textTypedFocusTextColor: scheme.primaryHoverColor,
ghostTypedTextColor: scheme.secondaryTextColor,
ghostTypedHoverTextColor: scheme.primaryHoverColor,
ghostTypedActiveTextColor: scheme.primaryActiveColor,
ghostTypedFocusTextColor: scheme.primaryHoverColor,
borderColor: scheme.borderColor,
hoverBorderColor: scheme.primaryHoverColor,
activeBorderColor: scheme.primaryActiveColor,
focusBorderColor: scheme.primaryHoverColor,
rippleColor: scheme.primaryColor,
tertiaryDepthIconColor: scheme.tertiaryTextColor,
baseBackgroundColor: scheme.baseBackgroundColor
},
primary: {
color: scheme.primaryColor,
hoverColor: scheme.primaryHoverColor,
activeColor: scheme.primaryActiveColor,
focusColor: scheme.primaryHoverColor,
textColor: scheme.baseBackgroundColor
},
info: {
color: scheme.infoColor,
hoverColor: scheme.infoHoverColor,
activeColor: scheme.infoActiveColor,
focusColor: scheme.infoHoverColor,
textColor: scheme.baseBackgroundColor
},
success: {
color: scheme.successColor,
hoverColor: scheme.successHoverColor,
activeColor: scheme.successActiveColor,
focusColor: scheme.successHoverColor,
textColor: scheme.baseBackgroundColor
},
warning: {
color: scheme.warningColor,
hoverColor: scheme.warningHoverColor,
activeColor: scheme.warningActiveColor,
focusColor: scheme.warningHoverColor,
textColor: scheme.baseBackgroundColor
},
error: {
color: scheme.errorColor,
hoverColor: scheme.errorHoverColor,
activeColor: scheme.errorActiveColor,
focusColor: scheme.errorHoverColor,
textColor: scheme.baseBackgroundColor
}
}
}
}
const dark = {
default: {
color: 'transparent',
hoverColor: 'transparent',
activeColor: 'transparent',
focusColor: 'transparent',
textColor: darkScheme.secondaryTextColor,
hoverTextColor: darkScheme.primaryHoverColor,
activeTextColor: darkScheme.primaryActiveColor,
focusTextColor: darkScheme.primaryHoverColor,
textTypedTextColor: darkScheme.secondaryTextColor,
textTypedHoverTextColor: darkScheme.primaryHoverColor,
textTypedActiveTextColor: darkScheme.primaryActiveColor,
textTypedFocusTextColor: darkScheme.primaryHoverColor,
ghostTypedTextColor: darkScheme.secondaryTextColor,
ghostTypedHoverTextColor: darkScheme.primaryHoverColor,
ghostTypedActiveTextColor: darkScheme.primaryActiveColor,
ghostTypedFocusTextColor: darkScheme.primaryHoverColor,
borderColor: darkScheme.borderColor,
hoverBorderColor: darkScheme.primaryHoverColor,
activeBorderColor: darkScheme.primaryActiveColor,
focusBorderColor: darkScheme.primaryHoverColor,
rippleColor: darkScheme.primaryColor,
tertiaryDepthIconColor: darkScheme.tertiaryTextColor,
baseBackgroundColor: darkScheme.baseBackgroundColor
},
primary: {
color: darkScheme.primaryColor,
hoverColor: darkScheme.primaryHoverColor,
activeColor: darkScheme.primaryActiveColor,
focusColor: darkScheme.primaryHoverColor,
textColor: darkScheme.baseBackgroundColor
},
info: {
color: darkScheme.infoColor,
hoverColor: darkScheme.infoHoverColor,
activeColor: darkScheme.infoActiveColor,
focusColor: darkScheme.infoHoverColor,
textColor: darkScheme.baseBackgroundColor
},
success: {
color: darkScheme.successColor,
hoverColor: darkScheme.successHoverColor,
activeColor: darkScheme.successActiveColor,
focusColor: darkScheme.successHoverColor,
textColor: darkScheme.baseBackgroundColor
},
warning: {
color: darkScheme.warningColor,
hoverColor: darkScheme.warningHoverColor,
activeColor: darkScheme.warningActiveColor,
focusColor: darkScheme.warningHoverColor,
textColor: darkScheme.baseBackgroundColor
},
error: {
color: darkScheme.errorColor,
hoverColor: darkScheme.errorHoverColor,
activeColor: darkScheme.errorActiveColor,
focusColor: darkScheme.errorHoverColor,
textColor: darkScheme.baseBackgroundColor
}
}
export default {
fallback: light,
dark
}

View File

@ -1,6 +1,6 @@
@import "./base.scss";
@import '../../node_modules/vue-virtual-scroller/dist/vue-virtual-scroller';
@import '~vue-virtual-scroller/dist/vue-virtual-scroller';
@import "./BaseLoading.scss";
@import "./BaseSlotMachine.scss";

View File

@ -153,6 +153,8 @@
$--n-message-colored-box-shadow: null !global;
@include setup-dark-custom-variables();
@if ($in-js-env != true) {
@include setup-dark-divider;
@include setup-dark-scrollbar;
@ -218,3 +220,16 @@
@include setup-dark-base-tracking-rect;
}
}
@mixin setup-dark-custom-variables {
@if function-exists('get-primary-color') {
$primary-color: get-primary-color('dark');
@if $primary-color != null {
$primary-hover-color: composite-color($primary-color, rgba(255, 255, 255, .14));
$primary-active-color: composite-color($primary-color, rgba(0, 0, 0, .1));
$--n-primary-color: $primary-color !global;
$--n-primary-hover-color: $primary-hover-color !global;
$--n-primary-active-color: $primary-active-color !global;
}
}
}

View File

@ -153,6 +153,8 @@
$--n-message-colored-box-shadow: 0px 2px 18px 0px rgba(0, 0, 0, 0.27) !global;
@include setup-light-custom-variables();
@if ($in-js-env != true) {
@include setup-light-divider;
@include setup-light-scrollbar;
@ -218,3 +220,16 @@
@include setup-light-base-tracking-rect;
}
}
@mixin setup-light-custom-variables {
@if function-exists('get-primary-color') {
$primary-color: get-primary-color('light');
@if $primary-color != null {
$primary-hover-color: composite-color($primary-color, rgba(255, 255, 255, .14));
$primary-active-color: composite-color($primary-color, rgba(0, 0, 0, .1));
$--n-primary-color: $primary-color !global;
$--n-primary-hover-color: $primary-hover-color !global;
$--n-primary-active-color: $primary-active-color !global;
}
}
}

View File

@ -102,3 +102,11 @@ export function hash (rgb) {
if (!rgb) return null
return rgb.join('-')
}
export function createHoverColor (rgb) {
return composite(rgb, [255, 255, 255, 0.14])
}
export function createActiveColor (rgb) {
return composite(rgb, [0, 0, 0, 0.1])
}

View File

@ -1,3 +1,29 @@
import { read, createHoverColor, createActiveColor } from './_utils/color'
function extendScheme (scheme) {
const extendedScheme = Object.assign({}, scheme)
if (extendedScheme.primaryColor) {
const primaryColorRgb = read(extendedScheme.primaryColor)
if (!extendedScheme.primaryHoverColor) {
extendedScheme.primaryHoverColor = createHoverColor(primaryColorRgb)
}
if (!extendedScheme.primaryActiveColor) {
extendedScheme.primaryActiveColor = createActiveColor(primaryColorRgb)
}
}
return extendedScheme
}
function mergeStyleSchemes (baseSchemes, schemes, extend) {
const mergedSchemes = {}
Object.keys(baseSchemes).forEach(theme => {
const scheme = (schemes || {})[theme]
const extendedScheme = extend ? extendScheme(scheme) : scheme
mergedSchemes[theme] = Object.assign({}, baseSchemes[theme], extendedScheme)
})
return mergedSchemes
}
function setHljs (hljs) {
this.hljs = hljs
}
@ -13,15 +39,24 @@ function create ({
components = [],
locales,
fallbackLocale,
hljs
hljs,
styleSchemes,
fallbackTheme
}) {
const installTargets = []
const naive = {
locales: createLocalesObject(locales),
fallbackLocale: fallbackLocale || locales[0],
hljs,
styleSchemes: styleSchemes || null,
fallbackTheme: fallbackTheme || 'light',
setHljs,
setHighlightjs: setHljs,
setStyleSchemes: (schemes, extend = true) => {
naive.styleSchemes = mergeStyleSchemes(
naive.styleSchemes, schemes, extend
)
},
install
}
function install (Vue) {

View File

@ -68,9 +68,10 @@ import Upload from './Upload'
import zhCN from './locale/zhCN'
import enUS from './locale/enUS'
/**
* Deprecated Components
*/
import lightScheme from './_styles-in-js/lightStyleScheme.scss'
import darkScheme from './_styles-in-js/darkStyleScheme.scss'
// Deprecated Components
import NimbusFormCard from './_deprecated/NimbusFormCard'
import NimbusConfirmCard from './_deprecated/NimbusConfirmCard'
import NimbusServiceLayout from './_deprecated/NimbusServiceLayout'
@ -146,14 +147,17 @@ export default create({
Code,
Typography,
Upload,
/**
* Deprecated
*/
// Deprecated
NimbusServiceLayout,
NimbusConfirmCard,
NimbusFormCard,
NimbusIcon
],
locales: [zhCN, enUS],
fallbackLocale: enUS
fallbackLocale: enUS,
styleSchemes: {
light: lightScheme,
dark: darkScheme
},
fallbackTheme: 'light'
})