未验证 提交 d5a580b3 编写于 作者: O oasis-cloud 提交者: GitHub

chore: 增加暗黑主题以及迁移可视化主题定制站点 (#396)

上级 d6cea1f5
......@@ -40,8 +40,10 @@
"add": "node scripts/createComponentMode.js",
"generate:file": "node scripts/generate-nutui.js",
"generate:themes": "node scripts/generate-themes.js",
"generate:themes-dev": "node scripts/generate-themes-dev.js",
"checked": "npm run generate:file && tsc",
"dev": "npm run checked && vite --open --force",
"dev:theme": "npm run generate:themes-dev && npm run checked && vite --config vite.config.theme.ts --open --force",
"replace:scss": "node scripts/replace-scss-alias.js",
"build:dts": "node scripts/export-props.js && npx rollup -c rollup.config.dts.js",
"build:loader-style": "node scripts/generate-loader-style.js",
......@@ -52,6 +54,7 @@
"build:locales": "vite build --config vite.config.build.locales.ts",
"build": "npm run checked && vite build --config vite.config.build.ts && npm run build:es && npm run build:css && npm run build:loader-style && npm run build:dts && npm run build:locales",
"build:site": "npm run checked && vite build --config vite.config.build.site.ts",
"build:theme:site": "npm run checked && vite build --config vite.config.theme.ts && npm run generate:themes-dev",
"lint": "eslint ./src/packages/calendar",
"lint:fix": "eslint --fix ./src/packages/calendar",
"publish:beta": "npm publish --tag beta",
......@@ -83,6 +86,7 @@
"@react-spring/web": "^9.3.2",
"@use-gesture/react": "^10.2.9",
"classnames": "^2.3.1",
"mobx-react-lite": "^3.4.0",
"react-router-dom": "^5.2.0",
"react-transition-group": "^4.4.2"
},
......@@ -143,14 +147,17 @@
"lzutf8": "0.6.0",
"map-stream": "0.0.7",
"marked": "^2.0.3",
"mobx": "^6.6.2",
"postcss-import": "^14.0.2",
"postcss-modules": "^4.2.2",
"prettier": "2.3.0",
"react": "^18.2.0",
"react-color": "^2.19.3",
"react-dom": "^18.2.0",
"react-markdown": "^7.1.2",
"react-syntax-highlighter": "^15.4.5",
"react-test-renderer": "^18.2.0",
"reactcss": "^1.2.3",
"remark-directive": "^2.0.1",
"remark-gfm": "^3.0.1",
"rollup-plugin-delete": "^2.0.0",
......
const path = require('path')
const fse = require('fs-extra')
const themeDir = path.join(__dirname, '../dist/theme-react')
const siteDir = path.join(process.cwd(), '../nutui-site/dist')
fse.copy(themeDir, path.join(siteDir, 'theme-react'))
const config = require('../src/config.json')
const path = require('path')
const fs = require('fs-extra')
let fileStr = `@import '../variables.scss';\n`
let tasks = []
let sassStyles = ''
config.nav.map((item) => {
item.packages.forEach((element) => {
let folderName = element.name.toLowerCase()
sassStyles += '\n'
sassStyles += fs.readFileSync(
path.resolve(
__dirname,
`../src/packages/${folderName}/${folderName}.scss`
),
'utf8'
)
fileStr += `@import '../../packages/${folderName}/${folderName}.scss';\n`
})
})
tasks.push(
fs.copy(
path.resolve(__dirname, '../src/styles'),
path.resolve(__dirname, '../dist/theme-react/source/styles')
)
)
Promise.all(tasks).then((res) => {
fs.copy(
path.resolve(__dirname, '../src/styles/variables.scss'),
path.resolve(
__dirname,
'../dist/theme-react/source/styles/variables.scss_source'
)
)
fs.writeFile(
path.resolve(
__dirname,
`../dist/theme-react/source/styles/sass-styles.scss_source`
),
sassStyles,
'utf8'
)
fs.outputFile(
path.resolve(
__dirname,
'../dist/theme-react/source/styles/themes/default.scss'
),
fileStr,
'utf8',
(error) => {
// logger.success(`文件写入成功`);
}
)
})
@import '../popup/popup.scss';
.nut-theme-dark {
.nut-actionsheet-panel {
.nut-actionsheet-cancel {
border-top: 1px solid $dark-background2;
}
.nut-actionsheet-title {
border-bottom: 1px solid $dark-background2;
}
.nut-actionsheet-cancel,
.nut-actionsheet-item,
.nut-actionsheet-title {
background: $dark-background;
color: $dark-color;
}
}
}
.nut-actionsheet {
display: block;
padding-bottom: constant(safe-area-inset-bottom);
......
@import '../icon/icon.scss';
@import '../popup/popup.scss';
@import '../elevator/elevator.scss';
.nut-theme-dark {
.nut-address {
&__header {
color: $dark-color;
&__title {
color: $dark-color;
}
}
.custom-address {
.region-tab {
color: $dark-color;
}
.region-con {
.region-group {
.region-item {
color: $dark-color;
}
}
}
}
.exist-address {
.exist-address-group {
.exist-ul {
.exist-item {
color: $dark-color;
}
}
}
.choose-other {
border-top: 1px solid $dark-background;
}
}
&-custom-buttom {
border-top: 1px solid $dark-background;
}
}
}
.nut-address {
display: block;
&__header {
......
.nut-theme-dark {
.nut-badge {
&.show {
background: $dark-background;
color: $dark-color;
}
}
}
.nut-badge {
position: relative;
display: inline-block;
......
@import '../popup/popup.scss';
.nut-theme-dark {
.nut-calendar {
background: $dark-background;
color: $dark-color;
.nut-calendar-header {
background: $dark-background;
color: $dark-color;
}
.nut-calendar-content {
.calendar-months-panel {
.calendar-month-con {
.calendar-month-day {
&-choose {
background-color: $dark-calendar-choose-color;
color: $calendar-choose-font-color;
}
}
}
}
}
.nut-calendar-footer {
background: $dark-background2;
color: $dark-color;
}
}
}
.nut-calendar {
position: relative;
display: flex;
......
@import '../../styles/mixins/text-ellipsis.scss';
@import '../price/Price.scss';
@import '../tag/Tag.scss';
@import '../../styles/mixins/text-ellipsis.scss';
.nut-theme-dark {
.nut-card {
.nut-card__right {
color: $dark-color;
}
}
}
.nut-card {
width: 100%;
......
@import '../popup/popup.scss';
@import '../tabs/tabs.scss';
.nut-theme-dark {
.nut-cascader__bar {
background: $dark-background2;
color: $dark-color;
}
.nut-tabs__titles {
background: $dark-background3 !important;
}
.nut-cascader-item {
color: $dark-color-gray;
}
}
.nut-cascader {
width: 100%;
font-size: $cascader-font-size;
......
@import '../icon/icon';
.nut-theme-dark {
.nut-cell {
background: $dark-background2;
color: $dark-color;
box-shadow: none;
}
}
.nut-cell {
position: relative;
......
@import '../icon/icon.scss';
.nut-theme-dark {
.nut-checkbox {
&__label {
color: $dark-color;
&--disabled {
color: $checkbox-label-disable-color;
}
}
}
}
.nut-checkbox {
display: flex;
align-items: center;
......
.nut-theme-dark {
.nut-circleprogress-text {
color: $dark-color;
}
}
.demo-circleprogress {
min-height: 100vh;
}
......
@import '../icon/icon.scss';
.nut-theme-dark {
.nut-collapse-item {
.collapse-item {
background: $dark-background;
color: $dark-color;
box-shadow: none;
}
.collapse-wrapper {
.collapse-content,
.collapse-extraRender {
background: $dark-background;
color: $dark-color;
}
}
.collapse-extraWrapper {
.collapse-extraRender {
background: $dark-background;
}
}
}
}
.nut-collapseitem {
}
......
.nut-theme-dark {
.nut-elevator {
background-color: $dark-background2;
&__list {
&__item {
color: $dark-color;
&__code {
color: $dark-color;
}
}
&__fixed {
background-color: $dark-background2;
}
}
}
}
.nut-elevator {
width: 100%;
display: block;
......
.nut-theme-dark {
.nut-empty {
background: $dark-background;
}
}
.nut-empty {
box-sizing: border-box;
display: flex;
......
@import '../icon/icon.scss';
.nut-theme-dark {
.nut-infiniteloading {
.nut-infinite-top {
.top-text {
color: $dark-color;
}
}
.nut-infinite-bottom {
color: $dark-color;
.bottom-text {
color: $dark-color;
}
}
}
}
.nut-infiniteloading {
display: block;
width: 100%;
......
@import '../icon/icon.scss';
.nut-theme-dark {
.nut-inputnumber {
&__icon {
color: $dark-color;
&--disabled {
color: $dark-color-gray;
}
}
input,
&__text--readonly {
background-color: $dark-background;
color: $dark-color;
border: 1px solid $dark-color-gray;
}
&--disabled {
input {
color: $dark-color-gray;
}
}
}
}
.nut-inputnumber {
display: flex;
align-items: center;
......
.nut-theme-dark {
.nut-navbar {
background: $dark-background;
color: $dark-color;
.title {
color: $dark-color;
}
}
}
.nut-navbar {
position: relative;
display: flex;
......
.nut-theme-dark {
.nut-numberkeyboard {
background-color: $dark-background4;
.number-board-body .key-board-wrapper .key {
background-color: $dark-background5;
color: $dark-color;
}
}
}
.nut-numberkeyboard {
width: $numberkeyboard-width;
padding: $numberkeyboard-padding;
......
.nut-theme-dark {
.nut-pagination {
&-prev,
&-item,
&-next {
background: $dark-background;
border-color: $dark-color-gray;
}
&-simple {
background: $dark-background;
}
.simple-border {
border-color: $dark-color-gray;
}
.disabled {
background: $dark-background;
}
}
}
.nut-pagination {
display: flex;
font-size: $pagination-font-size;
......@@ -35,7 +54,8 @@
padding: $pagination-prev-next-padding;
}
.simple-border {
border-right: $pagination-item-border-width solid $pagination-item-border-color;
border-right: $pagination-item-border-width solid
$pagination-item-border-color;
}
.active {
color: $white;
......
@import '../popup/popup.scss';
.nut-theme-dark {
.nut-picker {
&__title {
color: $dark-color;
}
&-roller {
&-item {
color: $dark-color;
}
&-item-tile {
color: $dark-color;
}
}
&-roller-mask {
background-image: linear-gradient(
180deg,
rgba(27, 27, 27, 0.9),
rgba(27, 27, 27, 0.4)
),
linear-gradient(0deg, rgba(27, 27, 27, 0.9), rgba(27, 27, 27, 0.4));
background-repeat: no-repeat;
background-position: top, bottom;
background-size: 100% 108px;
z-index: 1;
}
&-content {
color: $dark-color;
}
&-item {
color: $dark-color;
}
}
}
.nut-picker {
background-color: #fff;
width: 100%;
......
@import '../icon/icon.scss';
@import '../overlay/overlay.scss';
.nut-theme-dark {
.nut-popup {
background: $dark-background2;
}
.nutui-popup {
&__close-icon {
color: $dark-color;
}
}
}
.popup-slide {
&-center-enter,
&-center-exit {
......
@import '../icon/icon.scss';
.nut-theme-dark {
.nut-radio {
&__label {
color: $dark-color;
&--disabled {
color: $radio-label-disable-color;
}
}
&__button {
background: $dark-background;
color: $dark-color;
&--disabled {
color: $radio-label-disable-color;
border: 1px solid $radio-label-disable-color;
}
}
}
}
.nut-radio {
display: flex;
......
@import '../icon/icon.scss';
@import '../popup/popup.scss';
.nut-theme-dark {
.nut-shortpsd-title {
color: $dark-color;
}
.nut-input-normalw {
.nut-input-real {
background: $dark-background3;
}
.nut-shortpsd-fake {
border: none;
background: $dark-background3;
.nut-shortpsd-li {
.nut-shortpsd-icon {
background: $dark-color;
}
}
}
}
}
.nut-shortpsd-title {
line-height: 1;
font-size: $font-size-3;
color: $title-color;
}
.nut-shortpsd-subtitle {
display: block;
margin-top: 12px;
margin-bottom: 24px;
line-height: 1;
font-size: $font-size-1;
color: $text-color;
}
.nut-shortpsdWx-subtitle {
display: block;
margin-top: 12px;
line-height: 1;
font-size: $font-size-1;
color: $text-color;
}
.nut-input-w {
padding: 24px 0 10px 0;
text-align: center;
position: relative;
.nut-input-site {
width: 247px;
height: 41px;
border-radius: 4px;
}
.nut-input-real {
position: absolute;
right: 247px;
width: 247px;
height: 41px;
outline: 0 none;
border: 0;
text-decoration: none;
}
}
.nut-shortpassword {
&__title {
line-height: 1;
......
.nut-theme-dark {
.nut-sidenavbaritem {
background-color: $dark-background2;
&__title {
background-color: $dark-background2;
color: $dark-color;
}
}
}
$nutSidenavbarItemHeight: 40px;
.nut-subsidenavbar {
......
.nut-theme-dark {
.nut-subsidenavbar {
background-color: $dark-background2;
&__title {
background-color: $dark-background3;
color: $dark-color;
&__text {
color: $dark-color;
}
}
}
}
$nutSidenavbarItemHeight: 40px;
.nut-subsidenavbar {
......
.nut-theme-dark {
.nut-tabbar {
background: $dark-background;
}
}
.nut-tabbar {
border: 0px;
box-shadow: $tabbar-box-shadow;
......
@import '../icon/icon.scss';
.nut-theme-dark {
.nut-tabbar-item {
&__icon--unactive {
color: $dark-color-gray;
}
}
}
.nut-tabbar-item {
flex: 1;
......
.nut-theme-dark {
.nut-table {
&__main {
color: $dark-color;
background-color: $dark-background2;
&--striped {
.nut-table__main__head {
&__tr {
background-color: $dark-background3;
}
}
.nut-table__main__body {
&__tr:nth-child(odd) {
background-color: $dark-color-gray;
}
}
.nut-table__main__body {
&__tr:nth-child(even) {
background-color: $dark-background3;
}
}
}
}
&__summary {
color: $dark-color;
background-color: $dark-background;
}
&__nodata {
color: $dark-color;
background-color: $dark-background;
}
}
}
.nut-table {
display: flex;
width: 100%;
......
.nut-theme-dark {
.nut-tabpane {
background: $dark-background2;
}
}
.nut-tabpane {
width: 100%;
flex-shrink: 0;
......
@import '../../styles/mixins/index';
@import '../tabpane/tabpane.scss';
.nut-theme-dark {
.nut-tabs {
&__titles {
background: $dark-background3;
&-item {
color: $dark-color-gray;
&.active {
color: $dark-color;
}
}
}
&.vertical {
.nut-tabs__titles {
&-item {
&.active {
background-color: $dark-background2;
}
}
}
}
}
}
.nut-tabs {
display: flex;
......
.nut-theme-dark {
.nut-timedetail {
background-color: $dark-background2;
&__detail {
&__list {
&__item {
background-color: $dark-background;
color: $dark-color;
&--curr {
color: $timeselect-timedetail-item-cur-text-color;
}
}
}
}
}
}
.nut-timedetail {
display: flex;
align-content: flex-start;
......
.nut-theme-dark {
.nut-timepannel {
background-color: $dark-background3;
color: $dark-color-gray;
&--curr {
background-color: $dark-background2;
color: $dark-color;
}
}
}
.nut-timepannel {
height: 40px;
line-height: 40px;
......
@import '../timepannel/timepannel.scss';
@import '../timedetail/timedetail.scss';
.nut-theme-dark {
.nut-timeselect {
background-color: $dark-background2;
&__title {
background-color: $dark-background2;
color: $dark-color;
}
&__content {
&__pannel {
background-color: $dark-background3;
color: $dark-color;
}
&__detail {
background-color: $dark-background2;
}
}
}
}
.nut-timeselect {
display: flex;
......
@import '../icon/icon.scss';
@import '../../styles/mixins/text-ellipsis.scss';
.nut-theme-dark {
.nut-uploader__preview-list {
background: $dark-background2;
color: $dark-color;
}
.close {
color: $dark-color !important;
}
}
.nut-uploader {
position: relative;
display: flex;
......
type EnvConfig = {
baseUrl: string
themeUrl: string
isPrd: boolean
locales: string[]
}
......@@ -13,6 +14,7 @@ type EnvConfig = {
const config: EnvConfig = {
baseUrl: '',
themeUrl: '',
isPrd: true, // 是否为线上
locales: ['zh-CN', 'zh-TW', 'en-US', 'th'],
}
......@@ -23,6 +25,7 @@ switch (import.meta.env.MODE) {
*/
config.isPrd = false
config.baseUrl = '/devServer'
config.themeUrl = '/theme-react/dist/theme-react/source'
break
case 'production':
/*
......@@ -30,6 +33,7 @@ switch (import.meta.env.MODE) {
*/
config.isPrd = true
config.baseUrl = 'https://nutui.jd.com'
config.themeUrl = './source'
break
}
export default config
::selection {
background: $doc-default-color;
color: #fff;
}
$doc-title-height: 137px;
#doc {
background: #fff;
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
}
.doc {
&-title {
width: 100%;
height: $doc-title-height;
z-index: 2;
&-position {
top: 0px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 40px;
// line-height: 56px;
border-bottom: 1px solid #eee;
background: #fff;
visibility: visible;
opacity: 1;
// transition: opacity 0.8s linear, visibility 0.8s linear;
transition: opacity 0.8s;
&.fixed {
width: calc(100% - 290px);
position: fixed;
padding: 24px 48px;
.title {
font-size: 24px;
font-weight: bold;
}
}
&.hidden {
visibility: hidden;
opacity: 0;
}
.title {
font-size: 40px;
font-weight: bold;
}
}
}
&-content {
margin-left: 290px;
display: flex;
flex-direction: column;
&-document {
min-height: 800px;
.markdown-body {
min-height: 600px;
}
}
&-tabs {
position: absolute;
right: 475px;
top: 48px;
display: flex;
height: 40px;
align-items: center;
justify-content: space-between;
z-index: 1;
padding: 2px;
box-sizing: border-box;
border-radius: 2px;
background: #eee;
box-shadow: rgb(0 0 0 / 15%) 0px 2px 4px;
&.single {
padding: 0;
.tab-item {
line-height: 40px;
cursor: auto;
}
}
.tab-item {
position: relative;
padding: 0 10px;
line-height: 36px;
cursor: pointer;
font-size: 16px;
color: #323232;
text-align: center;
border-radius: 2px;
background: #eee;
&.cur {
font-weight: bold;
color: #323232;
background: #fff;
}
}
}
}
}
.nutui-react--demo-button {
display: none;
}
.nutui-react--demo-wrapper {
.nutui-react--demo-button {
display: block;
}
}
import React from 'react'
import { HashRouter } from 'react-router-dom'
import './App.scss'
import Nav from '@/sites/theme/components/nav'
import Header from '@/sites/theme/components/header'
import DemoPreview from '@/sites/theme/components/demo-preview'
import ThemeSetting from '@/sites/theme/components/theme-setting'
const App = () => {
console.log('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
return (
<div>
<HashRouter>
<Header></Header>
<Nav></Nav>
<div className="doc-content">
<div className="doc-content-document">
<ThemeSetting></ThemeSetting>
</div>
<div className="markdown-body">
<DemoPreview></DemoPreview>
</div>
</div>
</HashRouter>
</div>
)
}
export default App
.doc {
&-demo-preview {
height: 667px;
width: 375px;
position: fixed;
right: 30px;
top: 100px;
box-shadow: #ebedf0 0 4px 12px;
border-radius: 12px;
overflow: hidden;
&.fixed {
position: fixed;
top: 120px;
}
iframe {
// height: calc(100% - 40px);
height: 100%;
width: 100%;
}
}
}
import React, { useEffect, useState } from 'react'
import './demo-preview.scss'
import { useHistory, useLocation } from 'react-router-dom'
const DemoPreview = (props: any) => {
const location = useLocation()
const path = location.pathname.split('/')
const [URL, setURL] = useState(path[path.length - 1])
useEffect(() => {
const path = location.pathname.split('/')
setURL(path[path.length - 1])
}, [location])
return (
<div className={`doc-demo-preview ${props.className}`}>
<iframe src={`demo.html#${URL}`} frameBorder="0"></iframe>
</div>
)
}
export default DemoPreview
import DemoPreview from '@/sites/theme/components/demo-preview/demo-preview'
export default DemoPreview
.doc {
&-header {
//position: fixed;
z-index: 2;
top: 0;
left: 0;
right: 0;
min-width: 1300px;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: $doc-header-height;
line-height: $doc-header-height;
text-align: left;
padding: 0 50px;
font-size: 20px;
}
}
.header {
&-logo {
position: relative;
display: inline-block;
width: 240px;
height: 64px;
.logo-link {
width: 120px;
height: 46px;
vertical-align: middle;
position: absolute;
top: 50%;
margin-top: -23px;
}
.logo-border {
width: 1px;
height: 26px;
position: absolute;
right: 0;
top: 50%;
margin-top: -13px;
}
.version {
position: absolute;
right: 70px;
font-size: 14px;
}
}
&-nav {
display: flex;
justify-content: space-between;
align-items: center;
float: right;
width: calc(100% - 240px);
min-width: 900px;
padding: 0 40px;
> a {
color: #fff;
}
.nav-box {
margin-right: 140px;
.nav-list {
min-width: 490px;
display: flex;
list-style: none;
align-items: center;
justify-content: space-around;
}
.nav-item {
position: relative;
margin-right: 30px;
font-size: 14px;
height: 63px;
line-height: 63px;
text-align: center;
cursor: pointer;
flex-shrink: 0;
a {
display: inline-block;
line-height: 64px;
}
// overflow: hidden;
&.active {
font-weight: bold;
&:after {
content: '';
display: inline-block;
width: 35px;
height: 13px;
position: absolute;
bottom: 3px;
left: 50%;
margin-left: -17.5px;
background: url('../../assets/images/item-active.png');
}
}
&:last-of-type {
margin-right: 0;
}
}
.user-link {
display: inline-block;
width: 26px;
height: 26px;
vertical-align: middle;
background-repeat: no-repeat;
background-image: url('../../assets/images/icon-user.png');
background-size: 26px;
&.gitee {
margin-left: 8px;
background-image: url('../../assets/images/icon-gitee.png');
}
}
}
}
}
.header-select {
&-box {
position: relative;
display: inline-block;
vertical-align: middle;
outline: 0;
}
&-hd {
min-width: 77px;
height: 28px;
padding: 0 30px 0 15px;
line-height: 26px;
font-size: 14px;
color: $theme-red-word;
background-position: right 15px top 12px;
background-size: 8px 5px;
background-repeat: no-repeat;
border-radius: 14px;
}
&-bd {
position: absolute;
top: 30px;
left: 50%;
margin-left: -60px;
border-radius: 3px;
overflow: hidden;
}
&-item {
width: 120px;
height: 28px;
padding: 0 12px;
line-height: 26px;
font-size: 14px;
border-width: 0px 1px 1px;
border-style: solid;
cursor: pointer;
&:first-of-type {
border-top-width: 1px;
}
}
}
// 颜色
.doc-header {
// 红色
&-red {
background-image: $theme-red-header-bg;
color: $theme-red-word;
.header {
&-logo {
.logo-link {
background: url('../../assets/images/logo-header-white.png') no-repeat
center/100%;
}
.logo-border {
background: $theme-red-border;
}
}
&-nav {
.search-box {
.search-input {
color: $theme-red-word;
background-position: 0 0;
&::-webkit-input-placeholder {
color: $theme-red-input;
}
}
}
.nav-box {
.nav-item {
color: $theme-red-word;
a {
color: $theme-red-word;
}
&.active {
color: $theme-red-actice;
&:after {
background-position: 0 0;
}
a {
color: $theme-red-actice;
}
}
}
.user-link {
background-position: 0 0;
// &:hover {
// background-position: -26px 0;
// }
}
}
}
}
.header-select {
&-box {
&.select-down {
.header-select-hd {
background-image: url('../../assets/images/icon-select-white-down.png');
}
}
&.select-up {
.header-select-hd {
background-image: url('../../assets/images/icon-select-white-up.png');
}
}
}
&-hd {
color: $theme-red-word;
border: 1px solid $theme-white-select-border;
}
&-bd {
color: $theme-white-select-word;
}
&-item {
border-color: $theme-red-select-border;
background-color: $theme-red-select-bg;
&:hover {
color: $theme-red;
}
}
}
}
// 白色
&-white {
background: $white;
color: $theme-white-word;
border-bottom: 1px solid $theme-white-box-border;
.header {
&-logo {
.logo-link {
background: url('../../assets/images/logo-header-red.png') no-repeat
center/100%;
}
.logo-border {
background: $theme-white-border;
}
}
&-nav {
.search-box {
.search-input {
color: $theme-white-word;
background-position: 0 -22px;
&::-webkit-input-placeholder {
color: $theme-white-input;
}
}
}
.nav-box {
.nav-item {
color: $theme-white-word;
a {
color: $theme-white-word;
}
&.active {
color: $theme-white-actice;
&:after {
background-position: 0 -13px;
}
a {
color: $theme-white-actice;
}
}
}
.user-link {
background-position: 0 -25px;
// &:hover {
// background-position: -26px -25px;
// }
}
}
}
}
.header-select {
&-box {
&.select-down {
.header-select-hd {
background-image: url('../../assets/images/icon-select-gray-down.png');
}
}
&.select-up {
.header-select-hd {
background-image: url('../../assets/images/icon-select-gray-up.png');
}
}
}
&-hd {
color: $theme-white-select-word;
border: 1px solid $theme-white-select-border;
}
&-bd {
color: $theme-white-select-word;
}
&-item {
border-color: $theme-white-select-border;
background-color: $theme-white-select-bg;
&:hover {
color: $theme-white-actice;
}
}
}
}
// 黑色
&-black {
background: $black;
color: $theme-black-word;
border-bottom: 1px solid $theme-black-box-border;
.header {
&-logo {
.logo-link {
background: url('../../assets/images/logo-header-red.png') no-repeat
center/100%;
}
.logo-border {
background: $theme-black-border;
}
}
&-nav {
.search-box {
.search-input {
color: $theme-black-word;
background-position: 0 -44px;
&::-webkit-input-placeholder {
color: $theme-black-input;
}
}
}
.nav-box {
.nav-item {
color: $theme-black-word;
a {
color: $theme-black-word;
}
&.active {
color: $theme-black-actice;
&:after {
background-position: 0 -13px;
}
a {
color: $theme-black-actice;
}
}
}
.user-link {
background-position: 0 -51px;
// &:hover {
// background-position: -26px -51px;
// }
}
}
}
}
.header-select {
&-box {
&.select-down {
.header-select-hd {
background-image: url('../../assets/images/icon-select-white-down.png');
}
}
&.select-up {
.header-select-hd {
background-image: url('../../assets/images/icon-select-white-up.png');
}
}
}
&-hd {
color: $theme-black-select-word;
background-color: $theme-black-select-bg;
border: 1px solid $theme-black-select-border;
}
&-bd {
color: $theme-black-select-word;
}
&-item {
background-color: $theme-black-select-bg;
border-color: $theme-black-select-bg;
&:hover {
background-color: $theme-black-select-hover;
border-color: $theme-black-select-hover;
}
}
}
}
}
.nut-popover {
height: 100%;
position: relative;
}
.curr-lang {
display: block;
font-size: 12px;
}
.switch {
position: absolute;
right: 60px;
top: 0;
height: 100%;
width: 150px;
cursor: pointer;
.nut-popover--dark {
width: 100%;
margin-right: 0;
background: transparent;
text-align: center;
}
.title-item {
height: 30px;
}
}
.switch-content {
position: relative;
}
// 下拉列表选择动画效果
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
import React, { useEffect, useState } from 'react'
import { nav } from '@/config.json'
// @ts-ignore
import { version } from '/package.json'
import config from '@/sites/config/env'
import './header.scss'
import { useHistory, useLocation } from 'react-router-dom'
import '@/packages/popover/popover.scss'
import Popover from '@/packages/popover'
const Header = () => {
const history = useHistory()
const location = useLocation()
const [currLang, setCurrLang] = useState({})
const toHome = () => {
history.replace('/')
}
useEffect(() => {
let packages = [] as any[]
nav.forEach((item) => {
packages.push(...item.packages)
})
}, [])
useEffect(() => {
const lang = langs.filter(
(l) => location.pathname.indexOf(l.locale) > -1
)[0]
setCurrLang(lang)
}, [location])
const langs = [
{ name: '中文', locale: 'zh-CN' },
{ name: '中文(繁体)', locale: 'zh-TW' },
{ name: 'English', locale: 'en-US' },
{ name: 'Thai', locale: 'th' },
]
const [visible, setVisible] = useState(false)
const handleSwitchLocale = (e: any) => {
const classList: string[] = [].slice.call(e.target.classList)
if (classList.indexOf('curr-lang') > -1) {
return setVisible(!visible)
}
const name = e.target.innerText
setVisible(!visible)
const [{ locale }] = langs.filter((l) => name == l.name)
let link = ''
if (config.locales.some((l) => window.location.href.indexOf(l) > -1)) {
link = window.location.href.replace(/\#\/([a-z-]+)/gi, `#/${locale}`)
} else {
link = window.location.href.replace(/\#\//gi, `#/${locale}/`)
}
window.location.href = link
}
return (
<div className="doc-header doc-header-red">
<div className="header-logo">
<a className="logo-link" href="#/" onClick={toHome}></a>
<span className="logo-border"></span>
<span className="version">{version}</span>
</div>
<div className="header-nav">
<a href="https//github.com/jdf2e/nutui-docs" target="_blank">
当前环境:development ,代码 PR 合并后,文档会自动同步至
https//github.com/jdf2e/nutui-docs
</a>
</div>
{/*<div className={'switch'}>*/}
{/* <div className={'switch-content'}>*/}
{/* <Popover visible={visible} theme={'dark'} onClick={handleSwitchLocale} list={langs}>*/}
{/* <span className={'curr-lang'}>{currLang ? currLang['name'] : '中文'}</span>*/}
{/* </Popover>*/}
{/* </div>*/}
{/*</div>*/}
</div>
)
}
export default Header
import Header from '@/sites/theme/components/header/header'
export default Header
import Nav from '@/sites/theme/components/nav/nav'
export default Nav
.doc {
&-nav {
position: absolute;
top: $doc-header-height;
left: 0;
bottom: 0;
z-index: 1;
background: $white;
width: 290px;
height: 100vh;
border-right: 1px solid #eee;
overflow: auto;
padding-left: 35px;
&.fixed {
position: fixed;
top: 0;
}
ol {
&.introduce {
padding-left: 5px;
li {
cursor: pointer;
&:hover {
color: $doc-default-color;
}
}
}
li {
height: 48px;
line-height: 48px;
font-size: 14px;
color: $doc-default-nav-color;
font-weight: bold;
position: relative;
&.active {
&::before {
position: absolute;
content: '';
left: 0;
top: 50%;
width: 22px;
margin-top: -5px;
height: 10px;
transform: rotate(90deg);
background: url(https://img10.360buyimg.com/imagetools/jfs/t1/136135/19/14659/946/5fa20aa8E33a9aa26/d329fbe669171208.png)
no-repeat;
background-size: 100% 100%;
}
}
}
> ul {
li {
padding-left: 29px;
cursor: pointer;
&:hover {
a {
color: $doc-default-color;
}
}
a {
&.router-link-active,
&.active {
color: $doc-default-color !important;
}
&:link,
&:visited {
color: $title-color;
}
&:hover {
color: $doc-default-color;
&:visited {
color: $doc-default-color;
}
}
height: 100%;
b {
font-weight: normal;
font-size: 12px;
}
}
}
}
}
.selected {
color: #fa2c19 !important;
li {
color: #fa2c19 !important;
}
}
}
}
import React, { useEffect, useState } from 'react'
import { nav } from '@/config.json'
import { NavLink } from 'react-router-dom'
import './nav.scss'
import useLocale from '@/sites/assets/locale/uselocale'
const Nav = () => {
const [cNav] = useState<any>(nav)
const [lang] = useLocale()
const [fixed, setFixed] = useState(false)
const scrollNav = () => {
let top = document.documentElement.scrollTop
if (top > 64) {
setFixed(true)
} else {
setFixed(false)
}
}
useEffect(() => {
document.addEventListener('scroll', scrollNav)
}, [])
return (
<div className={`doc-nav ${fixed ? 'fixed' : ''}`}>
<ol>
<li>全局样式</li>
<ul>
<NavLink key={'navlinkbase'} activeClassName="selected" to={'base'}>
<li>
<b>基础样式</b>
</li>
</NavLink>
</ul>
</ol>
<ol>
{cNav.map((cn: any) => {
return (
<>
<li>{cn.name}</li>
<ul>
{cn.packages.map((cp: any) => {
if (!cp.show) return null
return (
<NavLink
key={Math.random()}
activeClassName="selected"
to={`${lang ? `/${lang}` : ''}/component/${cp.name}`}
>
<li>
{cp.name}&nbsp;&nbsp;<b>{cp.cName}</b>
</li>
</NavLink>
)
})}
</ul>
</>
)
})}
</ol>
</div>
)
}
export default Nav
import React from 'react'
// @ts-ignore
import reactCSS from 'reactcss'
// @ts-ignore
import { SketchPicker } from 'react-color'
type RGB = `rgb(${number}, ${number}, ${number})`
type RGBA = `rgba(${number}, ${number}, ${number}, ${number})`
type HEX = `#${string}`
type Color = RGB | HEX | RGBA
interface ColorPickerProps {
type: 'hex' | 'rgb'
color: Color
onChange: (color: Color) => void
}
interface ColorPickerState {
displayColorPicker: boolean
color: Color
}
class ColorPicker extends React.Component<ColorPickerProps, ColorPickerState> {
state: ColorPickerState = {
displayColorPicker: false,
color: this.props.color,
}
handleClick = () => {
this.setState({ displayColorPicker: !this.state.displayColorPicker })
}
handleClose = () => {
this.setState({ displayColorPicker: false })
}
handleChange = (color: any) => {
const finalyColor =
this.props.type == 'hex'
? color.hex
: `rgba(${color.rgb.r}, ${color.rgb.g},${color.rgb.b},${color.rgb.a})`
this.setState({ color: finalyColor })
this.props.onChange(finalyColor)
}
render() {
const styles = reactCSS({
default: {
color: {
height: '24px',
lineHeight: '24px',
textAlign: 'center',
borderRadius: '2px',
color: '#fff',
background: this.state.color,
},
swatch: {
padding: '5px',
background: '#fff',
borderRadius: '1px',
boxShadow: '0 0 0 1px rgba(0,0,0,.1)',
display: 'block',
cursor: 'pointer',
},
popover: {
position: 'absolute',
zIndex: '2',
},
cover: {
position: 'fixed',
top: '0px',
right: '0px',
bottom: '0px',
left: '0px',
},
},
})
return (
<div>
<div style={styles.swatch} onClick={this.handleClick}>
<div style={styles.color}>{this.state.color}</div>
</div>
{this.state.displayColorPicker ? (
<div style={styles.popover}>
<div style={styles.cover} onClick={this.handleClose} />
<SketchPicker
color={this.state.color}
onChange={this.handleChange}
/>
</div>
) : null}
</div>
)
}
}
export default ColorPicker
import config from '@/sites/config/env'
import configs from '@/config.json'
import { useHistory } from 'react-router-dom'
import { makeAutoObservable, observable, computed, action, toJS } from 'mobx'
import { useEffect } from 'react'
interface VariablesMap {
[key: string]: any
}
let cachedStyles = ''
let timer: any = null
class Store {
init
variables: any[]
variablesMap: VariablesMap
rawStyles
pathname
constructor() {
this.init = false
this.variables = []
this.variablesMap = {}
this.rawStyles = ''
this.pathname = 'base'
makeAutoObservable(this, {
variables: observable,
variablesMap: observable,
rawStyles: observable,
cssText: computed,
formItems: computed,
updateVariables: action,
updateVariablesMap: action,
updatePathname: action,
updateVariablesByItem: action,
})
}
get formItems() {
const name = this.pathname
console.log('name,,', name)
const res = toJS(this.variables).filter(
({ lowerCaseName }) => lowerCaseName === name
)
console.log(res)
return toJS(this.variables).filter(
({ lowerCaseName }) => lowerCaseName === name
)
}
get cssText() {
const variablesText = this.variables
.map(({ key, value }) => `${key}:${value}`)
.join(';')
cachedStyles = cachedStyles || extractStyle(this.rawStyles)
return `${variablesText};${cachedStyles}`
}
updatePathname(pathname: string) {
console.log('updatePathname', pathname)
this.pathname = pathname.replace('/', '').toLowerCase()
}
updateVariables(variables: any[]) {
this.variables = variables
}
updateVariablesByItem(item: any) {
this.variables.map((variable) => {
const { lowerCaseName, key } = variable
if (lowerCaseName == item.lowerCaseName && key == item.key) {
variable.value = item.value
}
})
this.updateIframeStyle()
}
updateVariablesMap(variablesMap: any) {
this.variablesMap = variablesMap
}
updateRawStyles(style: string) {
this.rawStyles = style
}
updateIframeStyle() {
clearTimeout(timer)
timer = setTimeout(() => {
const Sass = (window as any).Sass
console.log('Sass', Sass)
let beginTime = new Date().getTime()
console.log('sass编译开始', beginTime)
Sass &&
Sass.compile(this.cssText, async (res: Obj) => {
await awaitIframe()
const iframe = window.frames[0] as any
console.log(res.text)
if (res.text && iframe) {
console.log('sass编译成功', new Date().getTime() - beginTime)
if (!iframe.__styleEl) {
const style = iframe.document.createElement('style')
style.id = 'theme'
iframe.__styleEl = style
}
iframe.__styleEl.innerHTML = res.text
iframe.document.head.appendChild(iframe.__styleEl)
} else {
console.log('sass编译失败', new Date().getTime() - beginTime)
console.error(res)
}
if (res.status !== 0 && res.message) {
console.log(res.message)
}
})
}, 300)
}
}
export const store = new Store()
const components = configs.nav
.map(({ packages }) => packages.map(({ name }) => name))
.flat(1)
const awaitIframe = async () => {
while (
!window.frames[0] ||
!window.frames[0].document.querySelector('#nav')
) {
await new Promise((r) => setTimeout(r, 100))
}
}
const loadScript = async (url: string) =>
new Promise((resolve, reject) => {
const script = document.createElement('script')
script.onload = resolve
script.onerror = reject
script.src = url
document.head.appendChild(script)
})
const zhKeyDesc = {
'$primary-color': '全局主题色',
'$primary-color-end': '全局主题色结束颜色(主题色渐变,例如 Button)',
} as any
type Obj = {
[k: string]: any
}
// 提取样式代码,只保留有使用变量的行
const extractStyle = (style: string) => {
if (!store.variables.length) {
return ''
}
// import 由于 React 版本的 sass 依赖放在了 sass 文件中,所以在这里需要剔除
style = style
.split('\n')
.filter((str) => !/^(\s+)?@import/.test(str))
.join('\n')
// comment
style = style
.split('\n')
.filter((str) => !/^(\s+)?\/\//.test(str))
.join('\n')
// todo: parse mixin
style = style
.split('\n')
.filter((str) => !/^(\s+)?@include/.test(str))
.join('\n')
style = style.replace(
/(?:({|;|\s|\n))[\w-]+:([^;{}]|;base64)+;(?!base64)/g,
(matched) => {
const matchedKey = matched.match(/\$[\w-]+\b/g)
if (matchedKey && matchedKey.some((k) => store.variablesMap[k])) {
return matched
}
return ''
}
)
return style
}
const getInputType = (value: string) => {
if (/^\d+$/.test(value)) {
return 'number'
}
if (/^#[A-Za-z0-9]+$/.test(value)) {
return 'hex'
}
if (
/^(rgb|hsl)a?\((\s*\/?\s*[+-]?\d*(\.\d+)?%?,?\s*){3,5}\)/gim.test(value)
) {
return 'rgb'
}
return 'input'
}
// 提取变量
const extractVariables = (
matched: string[],
name: string,
lowerCaseName: string
) =>
matched.reduce((res, str) => {
const extract = str
.replace(/\s+!default/, '')
.match(/(.*):(?:\s+)?([\s\S]*)(?:\s+)?;/)
if (extract) {
const key = extract[1]
const value = extract[2]
res.push({
name, // 组件名
lowerCaseName, // 组件名小写
key, // 变量名
key_zh: zhKeyDesc[key],
rawValue: value, // 原始值
computedRawValue: '', // 计算后的原始值
value, // 编辑的值
// 编辑的类型
inputType: getInputType(value),
})
}
return res
}, [] as Obj[])
const parseSassVariables = (text: string, components: string[]) => {
const matchedComponentVariables = components
.map((name) => {
const lowerCaseName = name.toLowerCase()
const reg = new RegExp(
`(?<!\\/\\/(\\s+)?)\\$(${name}|${lowerCaseName})\\b[\\w-]+:([^;{}]|;base64)+;(?!base64)`,
'g'
)
const matched = text.match(reg)
if (matched) {
return extractVariables(matched, name, lowerCaseName)
}
})
.filter(Boolean)
.flat(2)
const baseVariablesReg = new RegExp(
`\\$(?!(${matchedComponentVariables
.map((item) => (item && `${item.name}|${item.lowerCaseName}`) || '')
.join('|')})\\b)[\\w-]+:[^:]+;`,
'g'
)
const variables = matchedComponentVariables as Obj[]
const matchedBaseVariables = text.match(baseVariablesReg)
// 组件变量以外的都作为基础变量
if (matchedBaseVariables) {
variables.unshift(...extractVariables(matchedBaseVariables, 'Base', 'base'))
}
return variables
}
const getRawFileText = async function (url: string) {
const response = await fetch(url)
const res = await response.text()
return res
}
const getSassVariables = async () => {
// vite 启动模式 bug 待修复
const rawVariablesText = await getRawFileText(
`${config.themeUrl}/styles/variables.scss_source`
)
console.log(rawVariablesText)
const rawVariables = parseSassVariables(rawVariablesText, components)
// 固定自定义主题的访问链接: https://nutui.jd.com/theme/?theme=自定义变量的文件地址#/
// e.g. https://nutui.jd.com/theme/?theme=xxx.com%2variables.scss#/
// vite issue https://github.com/vitejs/vite/issues/6894
const params = new URLSearchParams(window.location.search)
const customUrl = params.get('theme')
if (customUrl) {
const customVariablesText = await getRawFileText(customUrl)
const customVariables = parseSassVariables(customVariablesText, components)
// merge
rawVariables.forEach((item) => {
const custom = customVariables.find(({ key }) => key === item.key)
if (custom) {
item.value = custom.value
}
})
}
const variablesMap = rawVariables.reduce((map, item) => {
map[item.key] = 1
return map
}, {})
store.updateVariables(rawVariables)
store.updateVariablesMap(variablesMap)
}
export const getRawSassStyle = async (): Promise<void> => {
const style = await getRawFileText(
`${config.themeUrl}/styles/sass-styles.scss_source`
)
store.updateRawStyles(style)
}
export const initSass = async () => {
await loadScript('https://cdnout.com/sass.js/sass.sync.min.js')
await Promise.all([getSassVariables(), getRawSassStyle()])
}
export const useThemeEditor = () => {
const history = useHistory()
useEffect(() => {
const pathname = history.location.pathname
const pathnameArr = pathname.split('/')
store.updatePathname(
pathname.endsWith('/') === '/'
? '/Base'
: pathnameArr[pathnameArr.length - 1]
)
}, [history.location.pathname])
useEffect(() => {
const innerAsync = async () => {
await initSass()
}
innerAsync()
}, [])
}
import { ThemeSetting } from '@/sites/theme/components/theme-setting/theme-setting'
export default ThemeSetting
.theme-setting {
.outline-btn {
display: inline-flex;
align-items: center;
flex-wrap: nowrap;
height: 38px;
padding: 0 10px;
line-height: 1;
font-weight: 400;
font-size: 14px;
border: 1px dashed #2080f0;
border-radius: 3px;
color: #2080f0;
margin-right: 20px;
}
ul {
li {
list-style: none;
margin-bottom: 12px;
margin-left: 0px;
padding-left: 0px;
&::before {
display: none;
}
.color-picker {
width: 300px;
margin-left: 20px;
}
p {
font-size: 14px;
text-overflow: ellipsis;
}
}
}
.theme-input {
padding-left: 0;
border: 1px solid #dddbdb;
border-radius: 3px;
.nut-input__label {
display: none;
}
.input-text {
}
}
}
import React, { useState } from 'react'
import { observer } from 'mobx-react-lite'
import {
store,
useThemeEditor,
} from '@/sites/theme/components/theme-setting/helpers'
import './theme-setting.scss'
import '@/packages/button/button.scss'
import '@/packages/input/input.scss'
import Button from '@/packages/button'
import Input from '@/packages/input'
import { useHistory } from 'react-router-dom'
import ColorPicker from '@/sites/theme/components/theme-setting/color-picker'
function download(content: string, filename: string) {
const eleLink = document.createElement('a')
eleLink.download = filename
eleLink.style.display = 'none'
const blob = new Blob([content])
eleLink.href = URL.createObjectURL(blob)
document.body.appendChild(eleLink)
eleLink.click()
document.body.removeChild(eleLink)
}
export const ThemeSetting: React.FC<unknown> = observer((props) => {
useThemeEditor()
const history = useHistory()
const [componentName, setComponentName] = useState('Base')
console.log('componentName', componentName)
history.listen((location) => {
// const name = location.pathname.replace('/', '')
const path = location.pathname.split('/')
console.log(location.pathname, path)
const name = path[path.length - 1]
console.log(name, store.formItems)
setComponentName(name || 'Base')
})
const handleChange = (val: any, variableItem: any) => {
variableItem.value = val
store.updateVariablesByItem(variableItem)
}
const downloadScssVariables = () => {
if (!store.variables.length) {
return
}
let temp = ''
const variablesText = store.variables
.map(({ name, key, value }) => {
let comment = ''
if (temp !== name) {
temp = name
comment = `\n// ${name}\n`
}
return comment + `${key}: ${value};`
})
.join('\n')
download(`// NutUI主题定制\n${variablesText}`, 'custom_theme.scss')
}
return (
<>
<div className="theme-setting">
<div>
<div className={'outline-btn'}>
主题定制:{componentName.replace('/', '')}组件
</div>
<Button
shape={'square'}
type={'info'}
onClick={downloadScssVariables}
>
下载主题变量
</Button>
</div>
<ul>
{store.formItems.map((item) => (
<li key={`${Math.random()}${item.name}${item.key}`}>
<p>
{item.key_zh}
{item.name}
</p>
<p>
<b>{item.key}</b>:{item.value} (<del>{item.rawValue}</del>)
</p>
<div>
{['hex', 'rgb'].includes(item.inputType) ? (
<ColorPicker
type={item.inputType}
color={item.value}
onChange={(val) => handleChange(val, item)}
></ColorPicker>
) : (
<Input
className={'theme-input'}
defaultValue={item.value}
clearable={false}
labelWidth={0}
change={(val) => handleChange(val, item)}
></Input>
)}
</div>
</li>
))}
</ul>
</div>
</>
)
})
import React from 'react'
import * as ReactDOM from 'react-dom/client'
import '@/sites/assets/styles/reset.scss'
import '@/sites/assets/styles/md-style.scss'
import App from './AppTheme'
const rootElement = document.querySelector('#doc')
if (rootElement != null) {
const root = ReactDOM.createRoot(rootElement)
root.render(<App />)
}
const modulesPage = import.meta.globEager('/src/packages/**/doc.md', {
as: 'raw',
})
const routes: any[] = []
for (const path in modulesPage) {
let name = (/packages\/(.*)\/doc\.md/.exec(path) as any[])[1]
routes.push({
path: '/zh-CN/component/' + name,
component: modulesPage[path],
name,
})
}
const modulesENPage = import.meta.globEager('/src/packages/**/doc.en-US.md', {
as: 'raw',
})
// console.log('modulesENPage', modulesENPage)
for (const path in modulesENPage) {
let name = (/packages\/(.*)\/doc\.en-US\.md/.exec(path) as any[])[1]
routes.push({
path: '/en-US/component/' + name,
component: modulesENPage[path],
name,
})
}
const modulesTaroPage = import.meta.globEager('/src/packages/**/doc.taro.md', {
as: 'raw',
})
// console.log('modulesTaroPage', modulesTaroPage)
for (const path in modulesTaroPage) {
let name = (/packages\/(.*)\/doc\.taro\.md/.exec(path) as any[])[1]
routes.push({
path: '/en-US/component/' + name + '-taro',
component: modulesTaroPage[path],
name: name + '-taro',
})
routes.push({
path: '/zh-CN/component/' + name + '-taro',
component: modulesTaroPage[path],
name: name + '-taro',
})
}
export default routes
......@@ -17,11 +17,18 @@ $white: #fff;
$black: #000;
$required-color: #fa2c19 !default;
$dark-background: #1a1919 !default;
$dark-background2: #1d1b1c !default;
$dark-background: #131313 !default;
$dark-background2: #1b1b1b !default;
$dark-background3: #141414 !default;
$dark-background4: #323233 !default;
$dark-background5: #646566 !default;
$dark-background6: #380e08 !default;
$dark-background7: #707070 !default;
$dark-color: $white !default;
$dark-color2: #f2270c !default;
$dark-color3: rgba(232, 230, 227, 0.8) !default;
$dark-color-gray: $text-color !default;
$dark-calendar-choose-color: rgba(227, 227, 227, 0.2) !default;
// padding
$padding-xs: 12px;
......@@ -698,3 +705,5 @@ $grid-item-content-bg-color: $white !default;
$grid-item-text-margin: 8px !default;
$grid-item-text-color: $title-color2 !default;
$grid-item-text-font-size: $font-size-1 !default;
$timeselect-timedetail-item-cur-text-color: $primary-color !default;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta content="telephone=no" name="format-detection" />
<link rel="shortcut icon" href="https://img14.360buyimg.com/imagetools/jfs/t1/167902/2/8762/791358/603742d7E9b4275e3/e09d8f9a8bf4c0ef.png" />
<meta
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
name="viewport"
/>
<title>NutUI - 移动端Vue组件库</title>
<script src="https://h5.m.jd.com/babelDiy/Zeus/2846ykuM7PwipD9E2RzMj2BGEQpA/plugin/share.min.js"></script>
<style>
html {
background: #f7f7f7;
}
a[title="站长统计"] {
display: none;
}
</style>
<!-- Hotjar Tracking Code for nutui.jd.com -->
<script async>
(function(h, o, t, j, a, r) {
h.hj =
h.hj ||
function() {
(h.hj.q = h.hj.q || []).push(arguments);
};
h._hjSettings = { hjid: 1900179, hjsv: 6 };
a = o.getElementsByTagName("head")[0];
r = o.createElement("script");
r.async = 1;
r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv;
a.appendChild(r);
})(window, document, "https://static.hotjar.com/c/hotjar-", ".js?sv=");
</script>
</head>
<body>
<noscript>
<strong
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
properly without JavaScript enabled. Please enable it to
continue.</strong
>
</noscript>
<div id="doc"></div>
<script type="module" src="./src/sites/theme/mainTheme.tsx"></script>
<script>
//分享配置
var shareOption = {
iconUrl: "https://nutui.jd.com/img/logo_share.png",
url: "https://nutui.jd.com/demo.html#/index",
title: "轻量级移动端Vue组件库 - NutUI 3.0",
desc: "京东风格的Vue组件库",
};
try {
/*初始化分享*/
share.shareInit(shareOption);
} catch (e) {
console.log(e);
}
</script>
<script type="text/javascript">
var jaq = jaq || [];
jaq.push(["account", "JA2018_1831300"]);
jaq.push(["domain", "jd.com"]);
(function() {
var ja = document.createElement("script");
ja.type = "text/javascript";
ja.async = true;
ja.src = "//wl.jd.com/joya.js";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(ja, s);
})();
</script>
<script
type="text/javascript"
src="https://s23.cnzz.com/z_stat.php?id=1276268086&web_id=1276268086"
></script>
</body>
</html>
import { defineConfig } from 'vite'
import reactRefresh from '@vitejs/plugin-react-refresh'
import autoprefixer from 'autoprefixer'
const config = require('./package.json')
const atImport = require('postcss-import')
const path = require('path')
const { resolve } = path
// https://vitejs.dev/config/
export default defineConfig({
base: '/theme-react/',
publicDir: './src/sites/assets',
server: {
port: 2022,
host: '0.0.0.0',
proxy: {
'/devServer': {
target: 'https://nutui.jd.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/devServer/, ''),
},
'/devTheme': {
target: 'https://nutui.jd.com/theme-react/source',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/devTheme/, ''),
},
},
open: '/theme-react/theme.html#/',
},
resolve: {
alias: [{ find: '@', replacement: resolve(__dirname, './src') }],
},
css: {
preprocessorOptions: {
scss: {
// example : additionalData: `@import "./src/design/styles/variables";`
// dont need include file extend .scss
additionalData: `@import "@/styles/variables.scss";@import "@/sites/assets/styles/variables.scss";`,
},
postcss: {
plugins: [
atImport({ path: path.join(__dirname, 'src`') }),
autoprefixer({
overrideBrowserslist: [
'> 0.5%',
'last 2 versions',
'ie > 11',
'iOS >= 10',
'Android >= 5',
],
}),
],
},
},
},
plugins: [reactRefresh()],
build: {
target: 'es2015',
outDir: './dist/theme-react/',
emptyOutDir: true,
cssCodeSplit: true,
rollupOptions: {
input: {
theme: resolve(__dirname, 'theme.html'),
mobile: resolve(__dirname, 'demo.html'),
},
},
},
})
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册