Compare commits
18 Commits
main
...
feat/vapor
| Author | SHA1 | Date |
|---|---|---|
|
|
a15bb9cfb8 | |
|
|
5cc4a63480 | |
|
|
bacb790bfb | |
|
|
5a768e2ae4 | |
|
|
30afa132a2 | |
|
|
23ebeea4b7 | |
|
|
d7ca354b87 | |
|
|
428fdfc182 | |
|
|
73dd6d7625 | |
|
|
2cf758d770 | |
|
|
136543398d | |
|
|
8f73247bed | |
|
|
36a6ed5e34 | |
|
|
ec4017d86f | |
|
|
8d33f2f843 | |
|
|
312f82a5b6 | |
|
|
c49a9aa6c3 | |
|
|
f5560db05d |
|
|
@ -1,36 +0,0 @@
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
const restCssPath = path.join(process.cwd(), 'components', 'style', 'reset.css');
|
|
||||||
const tokenStatisticPath = path.join(process.cwd(), 'components', 'version', 'token.json');
|
|
||||||
const tokenMetaPath = path.join(process.cwd(), 'components', 'version', 'token-meta.json');
|
|
||||||
|
|
||||||
function finalizeCompile() {
|
|
||||||
if (fs.existsSync(path.join(__dirname, './es'))) {
|
|
||||||
fs.copyFileSync(restCssPath, path.join(process.cwd(), 'es', 'style', 'reset.css'));
|
|
||||||
fs.copyFileSync(tokenStatisticPath, path.join(process.cwd(), 'es', 'version', 'token.json'));
|
|
||||||
fs.copyFileSync(tokenMetaPath, path.join(process.cwd(), 'es', 'version', 'token-meta.json'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fs.existsSync(path.join(__dirname, './lib'))) {
|
|
||||||
fs.copyFileSync(restCssPath, path.join(process.cwd(), 'lib', 'style', 'reset.css'));
|
|
||||||
fs.copyFileSync(tokenStatisticPath, path.join(process.cwd(), 'lib', 'version', 'token.json'));
|
|
||||||
fs.copyFileSync(tokenMetaPath, path.join(process.cwd(), 'lib', 'version', 'token-meta.json'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function finalizeDist() {
|
|
||||||
if (fs.existsSync(path.join(__dirname, './dist'))) {
|
|
||||||
fs.copyFileSync(restCssPath, path.join(process.cwd(), 'dist', 'reset.css'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
compile: {
|
|
||||||
finalize: finalizeCompile,
|
|
||||||
},
|
|
||||||
dist: {
|
|
||||||
finalize: finalizeDist,
|
|
||||||
},
|
|
||||||
bail: true,
|
|
||||||
};
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
codecov:
|
|
||||||
branch: master
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
# 🎨 editorconfig.org
|
|
||||||
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
end_of_line = lf
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
node_modules/
|
|
||||||
**/*.spec.*
|
|
||||||
**/style/
|
|
||||||
*.html
|
|
||||||
/components/test/*
|
|
||||||
es/
|
|
||||||
lib/
|
|
||||||
_site/
|
|
||||||
dist/
|
|
||||||
site/dist/
|
|
||||||
components/version/version.ts
|
|
||||||
site/src/router/demoRoutes.js
|
|
||||||
locale/
|
|
||||||
112
.eslintrc.js
112
.eslintrc.js
|
|
@ -1,112 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
node: true,
|
|
||||||
jasmine: true,
|
|
||||||
jest: true,
|
|
||||||
es6: true,
|
|
||||||
},
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
parserOptions: {
|
|
||||||
parser: 'babel-eslint',
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
'plugin:vue/vue3-recommended',
|
|
||||||
'plugin:import/recommended',
|
|
||||||
'plugin:import/typescript',
|
|
||||||
'@vue/typescript/recommended',
|
|
||||||
'@vue/prettier',
|
|
||||||
// 'prettier',
|
|
||||||
],
|
|
||||||
// extends: [
|
|
||||||
// 'eslint:recommended',
|
|
||||||
// 'plugin:vue/vue3-recommended',
|
|
||||||
// '@vue/typescript/recommended',
|
|
||||||
// '@vue/prettier',
|
|
||||||
// ],
|
|
||||||
plugins: ['markdown', 'jest', '@typescript-eslint', 'import'],
|
|
||||||
globals: {
|
|
||||||
h: true,
|
|
||||||
defineProps: 'readonly',
|
|
||||||
},
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ['*.md'],
|
|
||||||
processor: 'markdown/markdown',
|
|
||||||
rules: {
|
|
||||||
'no-console': 'off',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['*.ts', '*.tsx'],
|
|
||||||
// extends: ['@vue/typescript/recommended', '@vue/prettier'],
|
|
||||||
parserOptions: {
|
|
||||||
project: './tsconfig.json',
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/ban-types': 0,
|
|
||||||
'@typescript-eslint/consistent-type-imports': 'error',
|
|
||||||
'@typescript-eslint/explicit-module-boundary-types': 0,
|
|
||||||
'@typescript-eslint/no-empty-function': 0,
|
|
||||||
'@typescript-eslint/no-non-null-assertion': 0,
|
|
||||||
'@typescript-eslint/no-unused-vars': [
|
|
||||||
'error',
|
|
||||||
{ vars: 'all', args: 'after-used', ignoreRestSiblings: true },
|
|
||||||
],
|
|
||||||
'@typescript-eslint/ban-ts-comment': 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['*.vue'],
|
|
||||||
parser: 'vue-eslint-parser',
|
|
||||||
parserOptions: {
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
ecmaVersion: 2021,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
'no-console': 'off',
|
|
||||||
'vue/no-reserved-component-names': 'off',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/no-explicit-any': 0,
|
|
||||||
'@typescript-eslint/no-empty-function': 0,
|
|
||||||
'@typescript-eslint/no-unused-vars': [
|
|
||||||
'error',
|
|
||||||
{ vars: 'all', args: 'after-used', ignoreRestSiblings: true, argsIgnorePattern: '^_' },
|
|
||||||
],
|
|
||||||
'import/no-named-as-default': 'off',
|
|
||||||
'import/namespace': [2, { allowComputed: true }],
|
|
||||||
'import/no-named-as-default-member': 'off',
|
|
||||||
'import/no-unresolved': [2, { ignore: ['ant-design-vue'] }],
|
|
||||||
'comma-dangle': [2, 'always-multiline'],
|
|
||||||
'no-var': 'error',
|
|
||||||
'no-console': [2, { allow: ['warn', 'error'] }],
|
|
||||||
'object-shorthand': 2,
|
|
||||||
'no-unused-vars': [2, { ignoreRestSiblings: true, argsIgnorePattern: '^_' }],
|
|
||||||
'no-undef': 2,
|
|
||||||
camelcase: 'off',
|
|
||||||
'no-extra-boolean-cast': 'off',
|
|
||||||
semi: ['error', 'always'],
|
|
||||||
'vue/no-v-html': 'off',
|
|
||||||
'vue/require-explicit-emits': 'off',
|
|
||||||
'vue/require-prop-types': 'off',
|
|
||||||
'vue/require-default-prop': 'off',
|
|
||||||
'vue/no-reserved-keys': 'off',
|
|
||||||
'vue/comment-directive': 'off',
|
|
||||||
'vue/prop-name-casing': 'off',
|
|
||||||
'vue/one-component-per-file': 'off',
|
|
||||||
'vue/custom-event-name-casing': 'off',
|
|
||||||
'vue/v-on-event-hyphenation': 'off',
|
|
||||||
'vue/max-attributes-per-line': [
|
|
||||||
2,
|
|
||||||
{
|
|
||||||
singleline: 20,
|
|
||||||
multiline: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'vue/multi-word-component-names': 'off',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -66,9 +66,6 @@ package-lock.json
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
/coverage
|
/coverage
|
||||||
|
|
||||||
# 备份文件
|
|
||||||
/components/test/*
|
|
||||||
list.txt
|
|
||||||
|
|
||||||
site/dev.js
|
site/dev.js
|
||||||
|
|
||||||
|
|
@ -77,10 +74,39 @@ vetur/
|
||||||
|
|
||||||
report.html
|
report.html
|
||||||
|
|
||||||
site/src/router/demoRoutes.js
|
|
||||||
|
|
||||||
components/version/version.ts
|
|
||||||
components/version/version.tsx
|
# Local env files
|
||||||
components/version/token.json
|
.env
|
||||||
components/version/token-meta.json
|
.env.*
|
||||||
~component-api.json
|
!.env.template
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# Turbo
|
||||||
|
.turbo
|
||||||
|
|
||||||
|
# Vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# Build Outputs
|
||||||
|
.next/
|
||||||
|
out/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
storybook-static/
|
||||||
|
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
vite.config.*.timestamp*
|
||||||
|
*.tsbuildinfo
|
||||||
|
*.log
|
||||||
|
|
||||||
|
.npmrc
|
||||||
|
.tsup/
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
npx --no-install pretty-quick --staged
|
|
||||||
7
.huskyrc
7
.huskyrc
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"hooks": {
|
|
||||||
"pre-commit": "pretty-quick --staged",
|
|
||||||
"pre-publish": "npm run lint",
|
|
||||||
"commit-msg": "commitlint -x @commitlint/config-conventional -e $GIT_PARAMS"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
60
.jest.js
60
.jest.js
|
|
@ -1,60 +0,0 @@
|
||||||
const libDir = process.env.LIB_DIR;
|
|
||||||
|
|
||||||
const transformIgnorePatterns = [
|
|
||||||
'/dist/',
|
|
||||||
// Ignore modules without es dir.
|
|
||||||
// Update: @babel/runtime should also be transformed
|
|
||||||
// 'node_modules/(?!.*(@babel|lodash-es))',
|
|
||||||
'node_modules/(?!@ant-design/icons-vue|@ant-design/icons-svg|lodash-es)/',
|
|
||||||
];
|
|
||||||
const testPathIgnorePatterns = ['/node_modules/', 'node'];
|
|
||||||
|
|
||||||
function getTestRegex(libDir) {
|
|
||||||
if (libDir === 'dist') {
|
|
||||||
return 'demo\\.test\\.js$';
|
|
||||||
}
|
|
||||||
return '.*\\.test\\.(j|t)sx?$';
|
|
||||||
}
|
|
||||||
module.exports = {
|
|
||||||
verbose: true,
|
|
||||||
setupFiles: ['./tests/setup.js'],
|
|
||||||
setupFilesAfterEnv: ['./tests/setupAfterEnv.ts'],
|
|
||||||
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'vue', 'md', 'jpg'],
|
|
||||||
modulePathIgnorePatterns: ['/_site/'],
|
|
||||||
testPathIgnorePatterns: testPathIgnorePatterns,
|
|
||||||
transform: {
|
|
||||||
'\\.(vue|md)$': '<rootDir>/node_modules/@vue/vue3-jest',
|
|
||||||
'\\.(js|jsx)$': '<rootDir>/node_modules/babel-jest',
|
|
||||||
'\\.(ts|tsx)$': '<rootDir>/node_modules/ts-jest',
|
|
||||||
'\\.svg$': '<rootDir>/node_modules/jest-transform-stub',
|
|
||||||
},
|
|
||||||
testRegex: getTestRegex(libDir),
|
|
||||||
moduleNameMapper: {
|
|
||||||
'^@/(.*)$/': '<rootDir>/$1',
|
|
||||||
'^ant-design-vue$': '<rootDir>/components/index',
|
|
||||||
'^ant-design-vue/es/(.*)$': '<rootDir>/components/$1',
|
|
||||||
},
|
|
||||||
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
|
|
||||||
collectCoverage: process.env.COVERAGE === 'true',
|
|
||||||
collectCoverageFrom: [
|
|
||||||
'components/**/*.{js,jsx,vue}',
|
|
||||||
'!components/*/__tests__/**/type.{js,jsx}',
|
|
||||||
'!components/vc-*/**/*',
|
|
||||||
'!components/*/demo/**/*',
|
|
||||||
'!components/_util/**/*',
|
|
||||||
'!components/align/**/*',
|
|
||||||
'!components/trigger/**/*',
|
|
||||||
'!**/node_modules/**',
|
|
||||||
],
|
|
||||||
testEnvironment: 'jsdom',
|
|
||||||
testEnvironmentOptions: {
|
|
||||||
url: 'http://localhost',
|
|
||||||
customExportConditions: ['node', 'node-addons'],
|
|
||||||
},
|
|
||||||
transformIgnorePatterns,
|
|
||||||
globals: {
|
|
||||||
'ts-jest': {
|
|
||||||
babelConfig: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
**/*.svg
|
|
||||||
lib/
|
|
||||||
es/
|
|
||||||
dist/
|
|
||||||
_site/
|
|
||||||
coverage/
|
|
||||||
CNAME
|
|
||||||
LICENSE
|
|
||||||
yarn.lock
|
|
||||||
netlify.toml
|
|
||||||
yarn-error.log
|
|
||||||
*.sh
|
|
||||||
*.snap
|
|
||||||
.gitignore
|
|
||||||
.npmignore
|
|
||||||
.prettierignore
|
|
||||||
.DS_Store
|
|
||||||
.editorconfig
|
|
||||||
.eslintignore
|
|
||||||
**/*.yml
|
|
||||||
**/assets
|
|
||||||
.gitattributes
|
|
||||||
.stylelintrc
|
|
||||||
.vcmrc
|
|
||||||
.png
|
|
||||||
.npmrc.template
|
|
||||||
.huskyrc
|
|
||||||
.gitmodules
|
|
||||||
*.png
|
|
||||||
v2-doc/
|
|
||||||
|
|
||||||
17
.prettierrc
17
.prettierrc
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "all",
|
|
||||||
"endOfLine": "lf",
|
|
||||||
"printWidth": 100,
|
|
||||||
"proseWrap": "never",
|
|
||||||
"arrowParens": "avoid",
|
|
||||||
"htmlWhitespaceSensitivity": "ignore",
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": ".prettierrc",
|
|
||||||
"options": {
|
|
||||||
"parser": "json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
23
.stylelintrc
23
.stylelintrc
|
|
@ -1,23 +0,0 @@
|
||||||
{
|
|
||||||
"extends": ["stylelint-config-standard", "stylelint-config-prettier"],
|
|
||||||
"rules": {
|
|
||||||
"comment-empty-line-before": null,
|
|
||||||
"declaration-empty-line-before": null,
|
|
||||||
"function-comma-newline-after": null,
|
|
||||||
"function-name-case": null,
|
|
||||||
"function-parentheses-newline-inside": null,
|
|
||||||
"function-max-empty-lines": null,
|
|
||||||
"function-whitespace-after": null,
|
|
||||||
"indentation": null,
|
|
||||||
"number-leading-zero": null,
|
|
||||||
"number-no-trailing-zeros": null,
|
|
||||||
"rule-empty-line-before": null,
|
|
||||||
"selector-combinator-space-after": null,
|
|
||||||
"selector-list-comma-newline-after": null,
|
|
||||||
"selector-pseudo-element-colon-notation": null,
|
|
||||||
"unit-no-unknown": null,
|
|
||||||
"value-list-max-empty-lines": null,
|
|
||||||
"font-family-no-missing-generic-family-keyword": null,
|
|
||||||
"no-descending-specificity": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
{
|
|
||||||
"extends": [
|
|
||||||
"stylelint-config-standard",
|
|
||||||
"stylelint-config-rational-order",
|
|
||||||
"stylelint-config-prettier"
|
|
||||||
],
|
|
||||||
"customSyntax": "postcss-less",
|
|
||||||
"plugins": ["stylelint-declaration-block-no-ignored-properties"],
|
|
||||||
"rules": {
|
|
||||||
"function-name-case": ["lower"],
|
|
||||||
"function-no-unknown": [
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
"ignoreFunctions": [
|
|
||||||
"fade",
|
|
||||||
"fadeout",
|
|
||||||
"tint",
|
|
||||||
"darken",
|
|
||||||
"ceil",
|
|
||||||
"fadein",
|
|
||||||
"floor",
|
|
||||||
"unit",
|
|
||||||
"shade",
|
|
||||||
"lighten",
|
|
||||||
"percentage",
|
|
||||||
"-"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"import-notation": null,
|
|
||||||
"no-descending-specificity": null,
|
|
||||||
"no-invalid-position-at-import-rule": null,
|
|
||||||
"declaration-empty-line-before": null,
|
|
||||||
"keyframes-name-pattern": null,
|
|
||||||
"custom-property-pattern": null,
|
|
||||||
"number-max-precision": 8,
|
|
||||||
"alpha-value-notation": "number",
|
|
||||||
"color-function-notation": "legacy",
|
|
||||||
"selector-class-pattern": null,
|
|
||||||
"selector-id-pattern": null,
|
|
||||||
"selector-not-notation": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
17
.vcmrc
17
.vcmrc
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"helpMessage": "\nPlease fix your commit message (and consider using https://www.npmjs.com/package/commitizen)\n",
|
|
||||||
"types": [
|
|
||||||
"feat",
|
|
||||||
"fix",
|
|
||||||
"docs",
|
|
||||||
"style",
|
|
||||||
"refactor",
|
|
||||||
"perf",
|
|
||||||
"test",
|
|
||||||
"chore",
|
|
||||||
"revert",
|
|
||||||
"ci"
|
|
||||||
],
|
|
||||||
"warnOnFail": false,
|
|
||||||
"autoFix": false
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
@import '@ant-design-vue/tailwind-config';
|
||||||
|
@source '../index.html';
|
||||||
|
@source '../src/**/*.{vue,ts}';
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--color-base-300) transparent;
|
||||||
|
}
|
||||||
|
*:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shiki.github-dark,
|
||||||
|
.dark-scrollbar {
|
||||||
|
scrollbar-color: rgba(121, 121, 121, 0.4) transparent;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
export { default } from '@ant-design-vue/eslint-config/vue'
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Playground</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"name": "playground",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "vite build",
|
||||||
|
"dev": "vite",
|
||||||
|
"lint": "eslint . --fix",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"tsc": "vue-tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/vue": "^1.1.5",
|
||||||
|
"@heroicons/vue": "^2.1.5",
|
||||||
|
"@ant-design-vue/ui": "*",
|
||||||
|
"@simonwep/pickr": "^1.9.1",
|
||||||
|
"@trpc/client": "^11.0.0",
|
||||||
|
"@trpc/server": "^11.0.0",
|
||||||
|
"@wdns/vue-code-block": "^2.3.3",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"cookies": "^0.9.1",
|
||||||
|
"uuid": "^10.0.0",
|
||||||
|
"vue": "^3.4.34",
|
||||||
|
"vue-router": "^4.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@ant-design-vue/eslint-config": "*",
|
||||||
|
"@ant-design-vue/prettier-config": "*",
|
||||||
|
"@ant-design-vue/typescript-config": "*",
|
||||||
|
"@ant-design-vue/vite-config": "*",
|
||||||
|
"@ant-design-vue/tailwind-config": "*",
|
||||||
|
"@tailwindcss/vite": "^4.1.3",
|
||||||
|
"@types/cookies": "^0.9.0",
|
||||||
|
"@types/node": "^20.0.0",
|
||||||
|
"@vitejs/plugin-vue": "^5.1.3",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||||
|
"tailwindcss": "^4.1.3",
|
||||||
|
"typescript": "^5.8.2",
|
||||||
|
"vite": "^5.3.5",
|
||||||
|
"vite-plugin-dts": "^3.9.1",
|
||||||
|
"vite-svg-loader": "^5.1.0",
|
||||||
|
"vue-tsc": "^3.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
export { default } from "@ant-design-vue/prettier-config/tailwind";
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<template>
|
||||||
|
<button @click="toggleTheme" class="fixed top-2 right-2">toggle {{ appearance }}</button>
|
||||||
|
<a-theme :appearance="appearance">
|
||||||
|
<RouterView />
|
||||||
|
</a-theme>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const appearance = ref('light')
|
||||||
|
|
||||||
|
const toggleTheme = () => {
|
||||||
|
appearance.value = appearance.value === 'light' ? 'dark' : 'light'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex h-screen" :class="pageClass">
|
||||||
|
<TheNavbar v-if="!hideNavbar" :items="navs"></TheNavbar>
|
||||||
|
<div class="flex-1 justify-center px-4 py-16" :class="contentClass">
|
||||||
|
<RouterView></RouterView>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { provideLayoutOptions } from '@/composables/layout'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
import TheNavbar from './TheNavbar.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
navs: { name: string; path: string }[]
|
||||||
|
hideNavbar?: boolean
|
||||||
|
hideBreadcrumbs?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const pageClass = ref<string>()
|
||||||
|
const contentClass = ref<string>()
|
||||||
|
|
||||||
|
provideLayoutOptions({
|
||||||
|
pageClass,
|
||||||
|
contentClass,
|
||||||
|
hideNavbar: computed(() => props.hideNavbar),
|
||||||
|
hideBreadcrumbs: computed(() => props.hideBreadcrumbs),
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-wrap gap-8 px-8">
|
||||||
|
<RouterLink v-for="item in items" :key="item.path" :to="item.path">
|
||||||
|
<div
|
||||||
|
class="shadow-xs relative flex w-72 flex-col rounded-lg bg-primary capitalize text-primary-content transition-all hover:scale-105 hover:shadow-xl"
|
||||||
|
>
|
||||||
|
<div className="flex-col gap-2 flex flex-auto p-4 text-sm items-center text-center">
|
||||||
|
<h2 className="font-semibold flex items-center gap-2 text-xl mb-1">
|
||||||
|
{{ item.name }}
|
||||||
|
</h2>
|
||||||
|
<div className="flex flex-wrap items-start gap-2 justify-end">
|
||||||
|
<ArrowRightIcon class="size-5" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ArrowRightIcon } from '@heroicons/vue/20/solid'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
items: { name: string; path: string }[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
<template>
|
||||||
|
<div class="max-w-full select-none overflow-x-auto px-4 py-2 text-sm">
|
||||||
|
<ul class="flex min-h-min items-center whitespace-nowrap capitalize">
|
||||||
|
<template v-for="(item, i) in items" :key="item.path">
|
||||||
|
<li class="flex items-center">
|
||||||
|
<template v-if="i > 0">
|
||||||
|
<span
|
||||||
|
class="ml-2 mr-3 block size-1.5 rotate-45 transform border-r-[1px] border-t-[1px] border-base-content/70 bg-transparent"
|
||||||
|
></span>
|
||||||
|
</template>
|
||||||
|
<template v-if="item.path !== route.path">
|
||||||
|
<RouterLink class="flex items-center hover:underline" :to="item.path">
|
||||||
|
{{ item.name }}
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span class="text-base-content/70">{{ item.name }}</span>
|
||||||
|
</template>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
defineProps<{ items: { name: string; path: string }[] }>()
|
||||||
|
const route = useRoute()
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
<template>
|
||||||
|
<div class="bg-base-100/90 text-base-content border-base-content/10 border-r">
|
||||||
|
<div class="flex min-h-16 w-full items-center p-2">
|
||||||
|
<div class="justify-start">
|
||||||
|
<div class="group relative inline-block">
|
||||||
|
<ul
|
||||||
|
tabindex="0"
|
||||||
|
class="bg-base-100 z-[1] mt-3 flex w-52 origin-top scale-95 flex-col flex-wrap rounded-lg p-2 text-sm capitalize"
|
||||||
|
>
|
||||||
|
<li v-for="item in items" :key="item.name">
|
||||||
|
<RouterLink
|
||||||
|
:aria-disabled="item.path === route.path"
|
||||||
|
:to="item.path"
|
||||||
|
@click.stop="$event.currentTarget.blur()"
|
||||||
|
class="hover:bg-base-content/10 flex cursor-pointer flex-col rounded-lg px-3 py-2 transition duration-200"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</RouterLink>
|
||||||
|
<ul v-if="item.children">
|
||||||
|
<li v-for="child in item.children" :key="child.name">
|
||||||
|
<RouterLink
|
||||||
|
:to="child.path"
|
||||||
|
@click.stop="$event.currentTarget.blur()"
|
||||||
|
class="hover:bg-base-content/10 flex cursor-pointer flex-col rounded-lg px-3 py-2 text-xs opacity-80 transition duration-200"
|
||||||
|
>
|
||||||
|
{{ child.name }}
|
||||||
|
</RouterLink>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
items: { name: string; path: string; children?: { name: string; path: string }[] }[]
|
||||||
|
}>()
|
||||||
|
const route = useRoute()
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { inject, InjectionKey, provide, Ref } from 'vue'
|
||||||
|
|
||||||
|
export interface LayoutOptions {
|
||||||
|
pageClass: Ref<string | undefined>
|
||||||
|
contentClass: Ref<string | undefined>
|
||||||
|
hideNavbar: Ref<boolean>
|
||||||
|
hideBreadcrumbs: Ref<boolean>
|
||||||
|
}
|
||||||
|
|
||||||
|
const LayoutOptionsToken: InjectionKey<LayoutOptions> = Symbol()
|
||||||
|
|
||||||
|
export function provideLayoutOptions(options: LayoutOptions) {
|
||||||
|
provide(LayoutOptionsToken, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function injectLayoutOptions() {
|
||||||
|
const options = inject(LayoutOptionsToken)
|
||||||
|
if (!options) {
|
||||||
|
throw new Error('"injectLayoutOptions" must be called inside pages')
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import '~/tailwind.css'
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
import App from './App.vue'
|
||||||
|
import routes from './routes'
|
||||||
|
import antd from '@ant-design-vue/ui'
|
||||||
|
import '@ant-design-vue/ui/tailwind.css'
|
||||||
|
import '@ant-design-vue/ui/style.css'
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes,
|
||||||
|
})
|
||||||
|
|
||||||
|
createApp(App).use(router).use(antd).mount('#app')
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex h-[200vh] flex-col gap-2">
|
||||||
|
<a-affix :offset-top="top" @change="onChange">
|
||||||
|
<a-button type="primary" @click="top += 10">Affix top</a-button>
|
||||||
|
</a-affix>
|
||||||
|
<br />
|
||||||
|
<a-affix :offset-bottom="bottom">
|
||||||
|
<a-button type="primary" @click="bottom += 10">Affix bottom</a-button>
|
||||||
|
</a-affix>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
const top = ref<number>(10)
|
||||||
|
const bottom = ref<number>(10)
|
||||||
|
const onChange = (lastAffix: boolean) => {
|
||||||
|
console.log('onChange', lastAffix)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<a-button variant="solid" size="lg">Solid Button</a-button>
|
||||||
|
<a-button variant="solid" size="lg" disabled>Solid Button</a-button>
|
||||||
|
<a-button variant="solid" size="lg" loading>Solid Button</a-button>
|
||||||
|
<a-button variant="solid" size="lg" danger>Danger Solid Button</a-button>
|
||||||
|
<a-button variant="solid" size="lg" danger disabled>Disabled Danger Solid Button</a-button>
|
||||||
|
<br />
|
||||||
|
<a-button variant="outlined" size="sm">Outlined Button</a-button>
|
||||||
|
<a-button variant="outlined" size="md">Outlined Button</a-button>
|
||||||
|
<a-button variant="outlined" size="lg">Outlined Button</a-button>
|
||||||
|
<a-button variant="outlined" size="lg" disabled>Disabled Outlined Button</a-button>
|
||||||
|
<a-button variant="outlined" size="lg" loading>Loading Outlined Button</a-button>
|
||||||
|
<a-button variant="outlined" size="lg" danger>Danger Outlined Button</a-button>
|
||||||
|
<a-button variant="outlined" size="lg" danger disabled>
|
||||||
|
Disabled Danger Outlined Button
|
||||||
|
</a-button>
|
||||||
|
<br />
|
||||||
|
<a-button variant="text" size="sm">Text Button</a-button>
|
||||||
|
<a-button variant="text" size="md">Text Button</a-button>
|
||||||
|
<a-button variant="text" size="lg">Text Button</a-button>
|
||||||
|
<a-button variant="text" size="lg" disabled>Disabled Text Button</a-button>
|
||||||
|
<a-button variant="text" size="lg" loading>Loading Text Button</a-button>
|
||||||
|
<a-button variant="text" size="lg" danger>Danger Text Button</a-button>
|
||||||
|
<a-button variant="text" size="lg" danger disabled>Disabled Danger Text Button</a-button>
|
||||||
|
<br />
|
||||||
|
<a-button variant="link" size="sm">Link Button</a-button>
|
||||||
|
<a-button variant="link" size="md">Link Button</a-button>
|
||||||
|
<a-button variant="link" size="lg">Link Button</a-button>
|
||||||
|
<a-button variant="link" size="lg" disabled>Disabled Link Button</a-button>
|
||||||
|
<a-button variant="link" size="lg" loading>Loading Link Button</a-button>
|
||||||
|
<a-button variant="link" size="lg" danger>Danger Link Button</a-button>
|
||||||
|
<a-button variant="link" size="lg" danger disabled>Disabled Danger Link Button</a-button>
|
||||||
|
<br />
|
||||||
|
<a-button variant="dashed" size="sm">Dashed Button</a-button>
|
||||||
|
<a-button variant="dashed" size="md">Dashed Button</a-button>
|
||||||
|
<a-button variant="dashed" size="lg">Dashed Button</a-button>
|
||||||
|
<a-button variant="dashed" size="lg" disabled>Disabled Dashed Button</a-button>
|
||||||
|
<a-button variant="dashed" size="lg" loading>Loading Dashed Button</a-button>
|
||||||
|
<a-button variant="dashed" size="lg" danger>Danger Dashed Button</a-button>
|
||||||
|
<a-button variant="dashed" size="lg" danger disabled>Disabled Danger Dashed Button</a-button>
|
||||||
|
<br />
|
||||||
|
<a-button variant="filled" size="sm">Filled Button</a-button>
|
||||||
|
<a-button variant="filled" size="md">Filled Button</a-button>
|
||||||
|
<a-button variant="filled" size="lg">Filled Button</a-button>
|
||||||
|
<a-button variant="filled" size="lg" disabled>Disabled Filled Button</a-button>
|
||||||
|
<a-button variant="filled" size="lg" loading>Loading Filled Button</a-button>
|
||||||
|
<a-button variant="filled" size="lg" danger>Danger Filled Button</a-button>
|
||||||
|
<a-button variant="filled" size="lg" danger disabled>Disabled Danger Filled Button</a-button>
|
||||||
|
|
||||||
|
<a-button color="purple">Purple Button</a-button>
|
||||||
|
<a-button color="blue">Blue Button</a-button>
|
||||||
|
<a-button color="green">Green Button</a-button>
|
||||||
|
<a-button color="red">Red Button</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<button class="btn btn-primary">Primary</button>
|
||||||
|
<button class="btn btn-secondary">Default</button>
|
||||||
|
<button class="btn btn-error">Danger</button>
|
||||||
|
<button class="btn btn-link">Link</button>
|
||||||
|
<button class="btn btn-ghost">Ghost</button>
|
||||||
|
<button class="btn btn-link">Link</button>
|
||||||
|
<button class="btn btn-link">Link</button>
|
||||||
|
<button class="btn btn-link">Link</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
<template>
|
||||||
|
<a-flex gap="middle" vertical>
|
||||||
|
<label>
|
||||||
|
Select axis:
|
||||||
|
<select v-model="axis">
|
||||||
|
<option v-for="item in axisOptions" :key="item">{{ item }}</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<a-flex :vertical="axis === 'vertical'">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in new Array(4)"
|
||||||
|
:key="item"
|
||||||
|
:style="{ ...baseStyle, background: `${index % 2 ? '#1677ff' : '#1677ffbf'}` }"
|
||||||
|
/>
|
||||||
|
</a-flex>
|
||||||
|
<hr/>
|
||||||
|
<label>
|
||||||
|
Select justify:
|
||||||
|
<select v-model="justify">
|
||||||
|
<option v-for="item in justifyOptions" :key="item">{{ item }}</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Select align:
|
||||||
|
<select v-model="align">
|
||||||
|
<option v-for="item in alignOptions" :key="item">{{ item }}</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<a-flex :style="{ ...boxStyle }" :justify="justify" :align="align">
|
||||||
|
<a-button variant="solid">Primary</a-button>
|
||||||
|
<a-button variant="solid">Primary</a-button>
|
||||||
|
<a-button variant="solid">Primary</a-button>
|
||||||
|
<a-button variant="solid">Primary</a-button>
|
||||||
|
</a-flex>
|
||||||
|
<hr/>
|
||||||
|
<a-flex gap="middle" vertical>
|
||||||
|
<label>
|
||||||
|
Select gap size:
|
||||||
|
<select v-model="gapSize">
|
||||||
|
<option v-for="item in gapSizeOptions" :key="item">{{ item }}</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<a-flex :gap="gapSize">
|
||||||
|
<a-button variant="solid">Primary</a-button>
|
||||||
|
<a-button>Default</a-button>
|
||||||
|
<a-button variant="dashed">Dashed</a-button>
|
||||||
|
<a-button variant="link">Link</a-button>
|
||||||
|
</a-flex>
|
||||||
|
</a-flex>
|
||||||
|
<hr/>
|
||||||
|
<label>
|
||||||
|
Auto wrap:
|
||||||
|
</label>
|
||||||
|
<a-flex wrap="wrap" gap="small">
|
||||||
|
<a-button v-for="item in new Array(24)" :key="item" variant="solid">Button</a-button>
|
||||||
|
</a-flex>
|
||||||
|
</a-flex>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { CSSProperties } from 'vue';
|
||||||
|
import { ref, reactive } from 'vue';
|
||||||
|
|
||||||
|
const baseStyle: CSSProperties = {
|
||||||
|
width: '25%',
|
||||||
|
height: '54px',
|
||||||
|
};
|
||||||
|
const boxStyle: CSSProperties = {
|
||||||
|
width: '100%',
|
||||||
|
height: '120px',
|
||||||
|
borderRadius: '6px',
|
||||||
|
border: '1px solid #40a9ff',
|
||||||
|
};
|
||||||
|
|
||||||
|
const axisOptions = reactive(['horizontal', 'vertical']);
|
||||||
|
const axis = ref(axisOptions[0]);
|
||||||
|
|
||||||
|
const justifyOptions = reactive([
|
||||||
|
'flex-start',
|
||||||
|
'center',
|
||||||
|
'flex-end',
|
||||||
|
'space-between',
|
||||||
|
'space-around',
|
||||||
|
'space-evenly',
|
||||||
|
]);
|
||||||
|
const justify = ref(justifyOptions[0]);
|
||||||
|
|
||||||
|
const alignOptions = reactive(['flex-start', 'center', 'flex-end']);
|
||||||
|
const align = ref(alignOptions[0]);
|
||||||
|
|
||||||
|
const gapSizeOptions = reactive(['small', 'middle', 'large']);
|
||||||
|
const gapSize = ref(gapSizeOptions[0]);
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { RouteRecordRaw, RouterView } from 'vue-router'
|
||||||
|
import BasicLayout from './components/BasicLayout.vue'
|
||||||
|
import { Fragment, h } from 'vue'
|
||||||
|
|
||||||
|
// /pages/button/basic.vue
|
||||||
|
const items = import.meta.glob('./pages/*/*.vue', { import: 'default', eager: true })
|
||||||
|
|
||||||
|
const categoryRoutes: Record<string, RouteRecordRaw[]> = {}
|
||||||
|
|
||||||
|
Object.keys(items).forEach(path => {
|
||||||
|
const route = path.replace('./pages/', '').replace('.vue', '')
|
||||||
|
const [category, demo] = route.split('/')
|
||||||
|
|
||||||
|
if (!categoryRoutes[category]) {
|
||||||
|
categoryRoutes[category] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryRoutes[category].push({
|
||||||
|
path: demo,
|
||||||
|
component: items[path],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const routes: RouteRecordRaw[] = Object.entries(categoryRoutes).map(([category, children]) => {
|
||||||
|
const renderComponents = () =>
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
children.map(child => h(child.component)),
|
||||||
|
)
|
||||||
|
renderComponents.displayName = 'renderComponents'
|
||||||
|
return {
|
||||||
|
path: `/${category}`,
|
||||||
|
component: RouterView,
|
||||||
|
children: [
|
||||||
|
...children,
|
||||||
|
{
|
||||||
|
path: ':demo*',
|
||||||
|
component: renderComponents,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const navs = Object.keys(categoryRoutes).map(category => ({
|
||||||
|
name: category,
|
||||||
|
path: `/${category}`,
|
||||||
|
children: categoryRoutes[category].map(child => ({
|
||||||
|
name: child.path,
|
||||||
|
path: `/${category}/${child.path}`,
|
||||||
|
})),
|
||||||
|
}))
|
||||||
|
routes.push({
|
||||||
|
path: '/:pathMatch(.*)*',
|
||||||
|
component: h('div', 'demo not found'),
|
||||||
|
})
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: BasicLayout,
|
||||||
|
children: routes,
|
||||||
|
props: {
|
||||||
|
navs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
declare module '*.vue' {
|
||||||
|
import type { DefineComponent } from 'vue'
|
||||||
|
const component: DefineComponent<Record<string, never>, Record<string, never>, any>
|
||||||
|
export default component
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
/* eslint-disable @typescript-eslint/consistent-type-imports */
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
AButton: typeof import('@ant-design-vue/ui').Button
|
||||||
|
AAffix: typeof import('@ant-design-vue/ui').Affix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export {}
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { RouteRecordRaw } from 'vue-router'
|
||||||
|
|
||||||
|
export function globRoutes(
|
||||||
|
baseName: string,
|
||||||
|
globs: Record<string, () => Promise<unknown>>,
|
||||||
|
): RouteRecordRaw {
|
||||||
|
const items = Object.entries(globs).map(([path, component]) => {
|
||||||
|
const match = path.match(/^\.\/pages\/(.+)\/index\.ts$/)
|
||||||
|
if (!match) {
|
||||||
|
throw new Error('invalid glob')
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name: match[1],
|
||||||
|
component,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const home: RouteRecordRaw = {
|
||||||
|
path: '',
|
||||||
|
components: {
|
||||||
|
default: () => import('@/components/HomePage.vue'),
|
||||||
|
breadcrumbs: () => import('@/components/TheBreadcrumbs.vue'),
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
default: {
|
||||||
|
items: items.map(item => {
|
||||||
|
return {
|
||||||
|
name: item.name,
|
||||||
|
path: `/${baseName}/${item.name}`,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
breadcrumbs: {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'home',
|
||||||
|
path: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: baseName,
|
||||||
|
path: `/${baseName}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
name: baseName,
|
||||||
|
title: baseName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const pages: RouteRecordRaw[] = items.map(item => {
|
||||||
|
return {
|
||||||
|
path: item.name,
|
||||||
|
components: {
|
||||||
|
default: item.component,
|
||||||
|
breadcrumbs: () => import('@/components/TheBreadcrumbs.vue'),
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
breadcrumbs: {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'home',
|
||||||
|
path: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: baseName,
|
||||||
|
path: `/${baseName}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: item.name,
|
||||||
|
path: `/${baseName}/${item.name}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
name: item.name,
|
||||||
|
title: `${baseName} - ${item.name}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
path: `/${baseName}`,
|
||||||
|
component: () => import('@/components/BasicLayout.vue'),
|
||||||
|
props: {
|
||||||
|
navs: [
|
||||||
|
{
|
||||||
|
name: 'home',
|
||||||
|
path: '/',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
hideNavbar: true,
|
||||||
|
hideBreadcrumbs: true,
|
||||||
|
},
|
||||||
|
children: [home, ...pages],
|
||||||
|
} as RouteRecordRaw
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"extends": "@ant-design-vue/typescript-config/tsconfig.vue.json",
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.vue"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }],
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"],
|
||||||
|
"~/*": ["./assets/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"extends": "@ant-design-vue/typescript-config/tsconfig.node.json",
|
||||||
|
"include": ["vite.config.*"]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import { resolve } from 'node:path'
|
||||||
|
import { defineConfig, Plugin } from 'vite'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue(), tailwindcss()],
|
||||||
|
server: {
|
||||||
|
watch: {
|
||||||
|
ignored: ['!**/node_modules/@ant-design-vue/**'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': resolve(__dirname, './src'),
|
||||||
|
'~': resolve(__dirname, './assets'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
env: {
|
|
||||||
test: {
|
|
||||||
presets: [['@babel/preset-env']],
|
|
||||||
plugins: [
|
|
||||||
['@vue/babel-plugin-jsx', { mergeProps: false, enableObjectSlots: false }],
|
|
||||||
'@babel/plugin-proposal-optional-chaining',
|
|
||||||
'@babel/plugin-transform-object-assign',
|
|
||||||
'@babel/plugin-proposal-object-rest-spread',
|
|
||||||
'@babel/plugin-proposal-export-default-from',
|
|
||||||
'@babel/plugin-proposal-export-namespace-from',
|
|
||||||
'@babel/plugin-proposal-class-properties',
|
|
||||||
'@babel/plugin-syntax-dynamic-import',
|
|
||||||
'@babel/plugin-transform-runtime',
|
|
||||||
'transform-require-context',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
5
build.sh
5
build.sh
|
|
@ -1,5 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
rm -rf dist
|
|
||||||
mkdir dist
|
|
||||||
./node_modules/.bin/webpack --config webpack.site.config.js
|
|
||||||
cp dist/index.html index.html
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
export type KeyType = string | number;
|
export type KeyType = string | number;
|
||||||
type ValueType = [number, any]; // [times, realValue]
|
type ValueType = [number, any]; // [times, realValue]
|
||||||
|
|
||||||
const SPLIT = '%';
|
const SPLIT = '%';
|
||||||
|
|
||||||
class Entity {
|
class Entity {
|
||||||
instanceId: string;
|
instanceId: string;
|
||||||
constructor(instanceId: string) {
|
constructor(instanceId: string) {
|
||||||
this.instanceId = instanceId;
|
this.instanceId = instanceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @private Internal cache map. Do not access this directly */
|
/** @private Internal cache map. Do not access this directly */
|
||||||
cache = new Map<string, ValueType>();
|
cache = new Map<string, ValueType>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ export function createCache() {
|
||||||
Array.from(styles).forEach(style => {
|
Array.from(styles).forEach(style => {
|
||||||
(style as any)[CSS_IN_JS_INSTANCE] = (style as any)[CSS_IN_JS_INSTANCE] || cssinjsInstanceId;
|
(style as any)[CSS_IN_JS_INSTANCE] = (style as any)[CSS_IN_JS_INSTANCE] || cssinjsInstanceId;
|
||||||
|
|
||||||
// Not force move if no head
|
|
||||||
// Not force move if no head
|
// Not force move if no head
|
||||||
if ((style as any)[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
|
if ((style as any)[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
|
||||||
document.head.insertBefore(style, firstChild);
|
document.head.insertBefore(style, firstChild);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
import type Cache from './Cache';
|
||||||
|
import { extract as tokenExtractStyle, TOKEN_PREFIX } from './hooks/useCacheToken';
|
||||||
|
import { CSS_VAR_PREFIX, extract as cssVarExtractStyle } from './hooks/useCSSVarRegister';
|
||||||
|
import { extract as styleExtractStyle, STYLE_PREFIX } from './hooks/useStyleRegister';
|
||||||
|
import { toStyleStr } from './util';
|
||||||
|
import { ATTR_CACHE_MAP, serialize as serializeCacheMap } from './util/cacheMapUtil';
|
||||||
|
|
||||||
|
const ExtractStyleFns = {
|
||||||
|
[STYLE_PREFIX]: styleExtractStyle,
|
||||||
|
[TOKEN_PREFIX]: tokenExtractStyle,
|
||||||
|
[CSS_VAR_PREFIX]: cssVarExtractStyle,
|
||||||
|
};
|
||||||
|
|
||||||
|
type ExtractStyleType = keyof typeof ExtractStyleFns;
|
||||||
|
|
||||||
|
function isNotNull<T>(value: T | null): value is T {
|
||||||
|
return value !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function extractStyle(
|
||||||
|
cache: Cache,
|
||||||
|
options?:
|
||||||
|
| boolean
|
||||||
|
| {
|
||||||
|
plain?: boolean;
|
||||||
|
types?: ExtractStyleType | ExtractStyleType[];
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const { plain = false, types = ['style', 'token', 'cssVar'] } =
|
||||||
|
typeof options === 'boolean' ? { plain: options } : options || {};
|
||||||
|
|
||||||
|
const matchPrefixRegexp = new RegExp(
|
||||||
|
`^(${(typeof types === 'string' ? [types] : types).join('|')})%`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// prefix with `style` is used for `useStyleRegister` to cache style context
|
||||||
|
const styleKeys = Array.from(cache.cache.keys()).filter(key => matchPrefixRegexp.test(key));
|
||||||
|
|
||||||
|
// Common effect styles like animation
|
||||||
|
const effectStyles: Record<string, boolean> = {};
|
||||||
|
|
||||||
|
// Mapping of cachePath to style hash
|
||||||
|
const cachePathMap: Record<string, string> = {};
|
||||||
|
|
||||||
|
let styleText = '';
|
||||||
|
|
||||||
|
styleKeys
|
||||||
|
.map<[number, string] | null>(key => {
|
||||||
|
const cachePath = key.replace(matchPrefixRegexp, '').replace(/%/g, '|');
|
||||||
|
const [prefix] = key.split('%');
|
||||||
|
const extractFn = ExtractStyleFns[prefix as keyof typeof ExtractStyleFns];
|
||||||
|
const extractedStyle = extractFn(cache.cache.get(key)![1], effectStyles, {
|
||||||
|
plain,
|
||||||
|
});
|
||||||
|
if (!extractedStyle) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const [order, styleId, styleStr] = extractedStyle;
|
||||||
|
if (key.startsWith('style')) {
|
||||||
|
cachePathMap[cachePath] = styleId;
|
||||||
|
}
|
||||||
|
return [order, styleStr];
|
||||||
|
})
|
||||||
|
.filter(isNotNull)
|
||||||
|
.sort(([o1], [o2]) => o1 - o2)
|
||||||
|
.forEach(([, style]) => {
|
||||||
|
styleText += style;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==================== Fill Cache Path ====================
|
||||||
|
styleText += toStyleStr(
|
||||||
|
`.${ATTR_CACHE_MAP}{content:"${serializeCacheMap(cachePathMap)}";}`,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
[ATTR_CACHE_MAP]: ATTR_CACHE_MAP,
|
||||||
|
},
|
||||||
|
plain,
|
||||||
|
);
|
||||||
|
|
||||||
|
return styleText;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
import { removeCSS, updateCSS } from '../../../vc-util/Dom/dynamicCSS';
|
||||||
|
import { ATTR_MARK, ATTR_TOKEN, CSS_IN_JS_INSTANCE, useStyleInject } from '../StyleContext';
|
||||||
|
import { isClientSide, toStyleStr } from '../util';
|
||||||
|
import type { TokenWithCSSVar } from '../util/css-variables';
|
||||||
|
import { transformToken } from '../util/css-variables';
|
||||||
|
import type { ExtractStyle } from './useGlobalCache';
|
||||||
|
import useGlobalCache from './useGlobalCache';
|
||||||
|
import { uniqueHash } from './useStyleRegister';
|
||||||
|
import type { ComputedRef } from 'vue';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
export const CSS_VAR_PREFIX = 'cssVar';
|
||||||
|
|
||||||
|
type CSSVarCacheValue<V, T extends Record<string, V> = Record<string, V>> = [
|
||||||
|
cssVarToken: TokenWithCSSVar<V, T>,
|
||||||
|
cssVarStr: string,
|
||||||
|
styleId: string,
|
||||||
|
cssVarKey: string,
|
||||||
|
];
|
||||||
|
|
||||||
|
const useCSSVarRegister = <V, T extends Record<string, V>>(
|
||||||
|
config: ComputedRef<{
|
||||||
|
path: string[];
|
||||||
|
key: string;
|
||||||
|
prefix?: string;
|
||||||
|
unitless?: Record<string, boolean>;
|
||||||
|
ignore?: Record<string, boolean>;
|
||||||
|
scope?: string;
|
||||||
|
token: any;
|
||||||
|
}>,
|
||||||
|
fn: () => T,
|
||||||
|
) => {
|
||||||
|
const styleContext = useStyleInject();
|
||||||
|
|
||||||
|
const stylePath = computed(() => {
|
||||||
|
return [
|
||||||
|
...config.value.path,
|
||||||
|
config.value.key,
|
||||||
|
config.value.scope || '',
|
||||||
|
config.value.token?._tokenKey,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
const cache = useGlobalCache<CSSVarCacheValue<V, T>>(
|
||||||
|
CSS_VAR_PREFIX,
|
||||||
|
stylePath,
|
||||||
|
() => {
|
||||||
|
const originToken = fn();
|
||||||
|
const [mergedToken, cssVarsStr] = transformToken<V, T>(originToken, config.value.key, {
|
||||||
|
prefix: config.value.prefix,
|
||||||
|
unitless: config.value.unitless,
|
||||||
|
ignore: config.value.ignore,
|
||||||
|
scope: config.value.scope || '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const styleId = uniqueHash(stylePath.value, cssVarsStr);
|
||||||
|
return [mergedToken, cssVarsStr, styleId, config.value.key];
|
||||||
|
},
|
||||||
|
([, , styleId]) => {
|
||||||
|
if (isClientSide) {
|
||||||
|
removeCSS(styleId, { mark: ATTR_MARK });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
([, cssVarsStr, styleId]) => {
|
||||||
|
if (!cssVarsStr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const style = updateCSS(cssVarsStr, styleId, {
|
||||||
|
mark: ATTR_MARK,
|
||||||
|
prepend: 'queue',
|
||||||
|
attachTo: styleContext.value.container,
|
||||||
|
priority: -999,
|
||||||
|
});
|
||||||
|
|
||||||
|
(style as any)[CSS_IN_JS_INSTANCE] = styleContext.value.cache?.instanceId;
|
||||||
|
|
||||||
|
// Used for `useCacheToken` to remove on batch when token removed
|
||||||
|
style.setAttribute(ATTR_TOKEN, config.value.key);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const extract: ExtractStyle<CSSVarCacheValue<any>> = (cache, _effectStyles, options) => {
|
||||||
|
const [, styleStr, styleId, cssVarKey] = cache;
|
||||||
|
const { plain } = options || {};
|
||||||
|
|
||||||
|
if (!styleStr) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const order = -999;
|
||||||
|
|
||||||
|
// ====================== Style ======================
|
||||||
|
// Used for rc-util
|
||||||
|
const sharedAttrs = {
|
||||||
|
'data-vc-order': 'prependQueue',
|
||||||
|
'data-vc-priority': `${order}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const styleText = toStyleStr(styleStr, cssVarKey, styleId, sharedAttrs, plain);
|
||||||
|
|
||||||
|
return [order, styleId, styleText];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useCSSVarRegister;
|
||||||
|
|
@ -1,20 +1,19 @@
|
||||||
import hash from '@emotion/hash';
|
import hash from '@emotion/hash';
|
||||||
import { ATTR_TOKEN, CSS_IN_JS_INSTANCE, useStyleInject } from '../StyleContext';
|
import { updateCSS } from '../../../vc-util/Dom/dynamicCSS';
|
||||||
|
import { ATTR_MARK, ATTR_TOKEN, CSS_IN_JS_INSTANCE, useStyleInject } from '../StyleContext';
|
||||||
import type Theme from '../theme/Theme';
|
import type Theme from '../theme/Theme';
|
||||||
|
import { flattenToken, memoResult, token2key, toStyleStr } from '../util';
|
||||||
|
import { transformToken } from '../util/css-variables';
|
||||||
|
import type { ExtractStyle } from './useGlobalCache';
|
||||||
import useGlobalCache from './useGlobalCache';
|
import useGlobalCache from './useGlobalCache';
|
||||||
import { flattenToken, token2key } from '../util';
|
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
|
|
||||||
const EMPTY_OVERRIDE = {};
|
const EMPTY_OVERRIDE = {};
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
|
||||||
// nuxt generate when NODE_ENV is prerender
|
|
||||||
const isPrerender = process.env.NODE_ENV === 'prerender';
|
|
||||||
|
|
||||||
// Generate different prefix to make user selector break in production env.
|
// Generate different prefix to make user selector break in production env.
|
||||||
// This helps developer not to do style override directly on the hash id.
|
// This helps developer not to do style override directly on the hash id.
|
||||||
const hashPrefix = !isProduction && !isPrerender ? 'css-dev-only-do-not-override' : 'css';
|
const hashPrefix = process.env.NODE_ENV !== 'production' ? 'css-dev-only-do-not-override' : 'css';
|
||||||
|
|
||||||
export interface Option<DerivativeToken, DesignToken> {
|
export interface Option<DerivativeToken, DesignToken> {
|
||||||
/**
|
/**
|
||||||
|
|
@ -46,6 +45,22 @@ export interface Option<DerivativeToken, DesignToken> {
|
||||||
override: object,
|
override: object,
|
||||||
theme: Theme<any, any>,
|
theme: Theme<any, any>,
|
||||||
) => DerivativeToken;
|
) => DerivativeToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform token to css variables.
|
||||||
|
*/
|
||||||
|
cssVar?: {
|
||||||
|
/** Prefix for css variables */
|
||||||
|
prefix?: string;
|
||||||
|
/** Tokens that should not be appended with unit */
|
||||||
|
unitless?: Record<string, boolean>;
|
||||||
|
/** Tokens that should not be transformed to css variables */
|
||||||
|
ignore?: Record<string, boolean>;
|
||||||
|
/** Tokens that preserves origin value */
|
||||||
|
preserve?: Record<string, boolean>;
|
||||||
|
/** Key for current theme. Useful for customizing and should be unique */
|
||||||
|
key?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenKeys = new Map<string, number>();
|
const tokenKeys = new Map<string, number>();
|
||||||
|
|
@ -94,6 +109,7 @@ export const getComputedToken = <DerivativeToken = object, DesignToken = Derivat
|
||||||
format?: (token: DesignToken) => DerivativeToken,
|
format?: (token: DesignToken) => DerivativeToken,
|
||||||
) => {
|
) => {
|
||||||
const derivativeToken = theme.getDerivativeToken(originToken);
|
const derivativeToken = theme.getDerivativeToken(originToken);
|
||||||
|
|
||||||
// Merge with override
|
// Merge with override
|
||||||
let mergedDerivativeToken = {
|
let mergedDerivativeToken = {
|
||||||
...derivativeToken,
|
...derivativeToken,
|
||||||
|
|
@ -108,6 +124,16 @@ export const getComputedToken = <DerivativeToken = object, DesignToken = Derivat
|
||||||
return mergedDerivativeToken;
|
return mergedDerivativeToken;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const TOKEN_PREFIX = 'token';
|
||||||
|
|
||||||
|
type TokenCacheValue<DerivativeToken> = [
|
||||||
|
token: DerivativeToken & { _tokenKey: string; _themeKey: string },
|
||||||
|
hashId: string,
|
||||||
|
realToken: DerivativeToken & { _tokenKey: string },
|
||||||
|
cssVarStr: string,
|
||||||
|
cssVarKey: string,
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache theme derivative token as global shared one
|
* Cache theme derivative token as global shared one
|
||||||
* @param theme Theme entity
|
* @param theme Theme entity
|
||||||
|
|
@ -119,21 +145,27 @@ export default function useCacheToken<DerivativeToken = object, DesignToken = De
|
||||||
theme: Ref<Theme<any, any>>,
|
theme: Ref<Theme<any, any>>,
|
||||||
tokens: Ref<Partial<DesignToken>[]>,
|
tokens: Ref<Partial<DesignToken>[]>,
|
||||||
option: Ref<Option<DerivativeToken, DesignToken>> = ref({}),
|
option: Ref<Option<DerivativeToken, DesignToken>> = ref({}),
|
||||||
) {
|
): Ref<TokenCacheValue<DerivativeToken>> {
|
||||||
const style = useStyleInject();
|
const styleContext = useStyleInject();
|
||||||
|
|
||||||
// Basic - We do basic cache here
|
// Basic - We do basic cache here
|
||||||
const mergedToken = computed(() => Object.assign({}, ...tokens.value));
|
const mergedToken = computed(() =>
|
||||||
const tokenStr = computed(() => flattenToken(mergedToken.value));
|
memoResult(() => Object.assign({}, ...tokens.value), tokens.value),
|
||||||
const overrideTokenStr = computed(() => flattenToken(option.value.override || EMPTY_OVERRIDE));
|
);
|
||||||
|
|
||||||
const cachedToken = useGlobalCache<[DerivativeToken & { _tokenKey: string }, string]>(
|
const tokenStr = computed(() => flattenToken(mergedToken.value));
|
||||||
'token',
|
const overrideTokenStr = computed(() => flattenToken(option.value.override ?? EMPTY_OVERRIDE));
|
||||||
|
|
||||||
|
const cssVarStr = computed(() => (option.value.cssVar ? flattenToken(option.value.cssVar) : ''));
|
||||||
|
|
||||||
|
const cachedToken = useGlobalCache<TokenCacheValue<DerivativeToken>>(
|
||||||
|
TOKEN_PREFIX,
|
||||||
computed(() => [
|
computed(() => [
|
||||||
option.value.salt || '',
|
option.value.salt ?? '',
|
||||||
theme.value.id,
|
theme.value?.id,
|
||||||
tokenStr.value,
|
tokenStr.value,
|
||||||
overrideTokenStr.value,
|
overrideTokenStr.value,
|
||||||
|
cssVarStr.value,
|
||||||
]),
|
]),
|
||||||
() => {
|
() => {
|
||||||
const {
|
const {
|
||||||
|
|
@ -141,25 +173,82 @@ export default function useCacheToken<DerivativeToken = object, DesignToken = De
|
||||||
override = EMPTY_OVERRIDE,
|
override = EMPTY_OVERRIDE,
|
||||||
formatToken,
|
formatToken,
|
||||||
getComputedToken: compute,
|
getComputedToken: compute,
|
||||||
|
cssVar,
|
||||||
} = option.value;
|
} = option.value;
|
||||||
const mergedDerivativeToken = compute
|
let mergedDerivativeToken = compute
|
||||||
? compute(mergedToken.value, override, theme.value)
|
? compute(mergedToken.value, override, theme.value)
|
||||||
: getComputedToken(mergedToken.value, override, theme.value, formatToken);
|
: getComputedToken(mergedToken.value, override, theme.value, formatToken);
|
||||||
|
|
||||||
|
// Replace token value with css variables
|
||||||
|
const actualToken = { ...mergedDerivativeToken };
|
||||||
|
let cssVarsStr = '';
|
||||||
|
if (!!cssVar) {
|
||||||
|
[mergedDerivativeToken, cssVarsStr] = transformToken(mergedDerivativeToken, cssVar.key!, {
|
||||||
|
prefix: cssVar.prefix,
|
||||||
|
ignore: cssVar.ignore,
|
||||||
|
unitless: cssVar.unitless,
|
||||||
|
preserve: cssVar.preserve,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Optimize for `useStyleRegister` performance
|
// Optimize for `useStyleRegister` performance
|
||||||
const tokenKey = token2key(mergedDerivativeToken, salt);
|
const tokenKey = token2key(mergedDerivativeToken, salt);
|
||||||
mergedDerivativeToken._tokenKey = tokenKey;
|
mergedDerivativeToken._tokenKey = tokenKey;
|
||||||
recordCleanToken(tokenKey);
|
actualToken._tokenKey = token2key(actualToken, salt);
|
||||||
|
|
||||||
|
const themeKey = cssVar?.key ?? tokenKey;
|
||||||
|
mergedDerivativeToken._themeKey = themeKey;
|
||||||
|
recordCleanToken(themeKey);
|
||||||
|
|
||||||
const hashId = `${hashPrefix}-${hash(tokenKey)}`;
|
const hashId = `${hashPrefix}-${hash(tokenKey)}`;
|
||||||
mergedDerivativeToken._hashId = hashId; // Not used
|
mergedDerivativeToken._hashId = hashId; // Not used
|
||||||
return [mergedDerivativeToken, hashId];
|
|
||||||
|
return [mergedDerivativeToken, hashId, actualToken, cssVarsStr, cssVar?.key || ''];
|
||||||
},
|
},
|
||||||
cache => {
|
cache => {
|
||||||
// Remove token will remove all related style
|
// Remove token will remove all related style
|
||||||
cleanTokenStyle(cache[0]._tokenKey, style.value?.cache.instanceId);
|
cleanTokenStyle(cache[0]._themeKey, styleContext.value?.cache?.instanceId);
|
||||||
|
},
|
||||||
|
([token, , , cssVarsStr]) => {
|
||||||
|
const { cssVar } = option.value;
|
||||||
|
if (cssVar && cssVarsStr) {
|
||||||
|
const style = updateCSS(cssVarsStr, hash(`css-variables-${token._themeKey}`), {
|
||||||
|
mark: ATTR_MARK,
|
||||||
|
prepend: 'queue',
|
||||||
|
attachTo: styleContext.value?.container,
|
||||||
|
priority: -999,
|
||||||
|
});
|
||||||
|
|
||||||
|
(style as any)[CSS_IN_JS_INSTANCE] = styleContext.value?.cache?.instanceId;
|
||||||
|
|
||||||
|
// Used for `useCacheToken` to remove on batch when token removed
|
||||||
|
style.setAttribute(ATTR_TOKEN, token._themeKey);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return cachedToken;
|
return cachedToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const extract: ExtractStyle<TokenCacheValue<any>> = (cache, _effectStyles, options) => {
|
||||||
|
const [, , realToken, styleStr, cssVarKey] = cache;
|
||||||
|
const { plain } = options || {};
|
||||||
|
|
||||||
|
if (!styleStr) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styleId = realToken._tokenKey;
|
||||||
|
const order = -999;
|
||||||
|
|
||||||
|
// ====================== Style ======================
|
||||||
|
// Used for rc-util
|
||||||
|
const sharedAttrs = {
|
||||||
|
'data-vc-order': 'prependQueue',
|
||||||
|
'data-vc-priority': `${order}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const styleText = toStyleStr(styleStr, cssVarKey, styleId, sharedAttrs, plain);
|
||||||
|
|
||||||
|
return [order, styleId, styleText];
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
// import canUseDom from 'rc-util/lib/Dom/canUseDom';
|
||||||
|
import useLayoutEffect from '../../../_util/hooks/useLayoutEffect';
|
||||||
|
import type { ShallowRef, WatchCallback } from 'vue';
|
||||||
|
import { watch } from 'vue';
|
||||||
|
|
||||||
|
type UseCompatibleInsertionEffect = (
|
||||||
|
renderEffect: WatchCallback,
|
||||||
|
effect: (polyfill?: boolean) => ReturnType<WatchCallback>,
|
||||||
|
deps: ShallowRef,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polyfill `useInsertionEffect` for React < 18
|
||||||
|
* @param renderEffect will be executed in `useMemo`, and do not have callback
|
||||||
|
* @param effect will be executed in `useLayoutEffect`
|
||||||
|
* @param deps
|
||||||
|
*/
|
||||||
|
const useInsertionEffectPolyfill: UseCompatibleInsertionEffect = (renderEffect, effect, deps) => {
|
||||||
|
watch(deps, renderEffect, { immediate: true });
|
||||||
|
useLayoutEffect(() => effect(true), deps);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compatible `useInsertionEffect`
|
||||||
|
* will use `useInsertionEffect` if React version >= 18,
|
||||||
|
* otherwise use `useInsertionEffectPolyfill`.
|
||||||
|
*/
|
||||||
|
const useCompatibleInsertionEffect: UseCompatibleInsertionEffect = useInsertionEffectPolyfill;
|
||||||
|
|
||||||
|
export default useCompatibleInsertionEffect;
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
const useRun = () => {
|
||||||
|
return function (fn: () => void) {
|
||||||
|
fn();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const useEffectCleanupRegister = useRun;
|
||||||
|
|
||||||
|
export default useEffectCleanupRegister;
|
||||||
|
|
@ -1,58 +1,115 @@
|
||||||
import { useStyleInject } from '../StyleContext';
|
import { useStyleInject } from '../StyleContext';
|
||||||
import type { KeyType } from '../Cache';
|
import type { KeyType } from '../Cache';
|
||||||
|
import useCompatibleInsertionEffect from './useCompatibleInsertionEffect';
|
||||||
import useHMR from './useHMR';
|
import useHMR from './useHMR';
|
||||||
import type { ShallowRef, Ref } from 'vue';
|
import type { ShallowRef, Ref } from 'vue';
|
||||||
import { onBeforeUnmount, watch, watchEffect, shallowRef } from 'vue';
|
import { onBeforeUnmount, watch, computed } from 'vue';
|
||||||
export default function useClientCache<CacheType>(
|
|
||||||
|
export type ExtractStyle<CacheValue> = (
|
||||||
|
cache: CacheValue,
|
||||||
|
effectStyles: Record<string, boolean>,
|
||||||
|
options?: {
|
||||||
|
plain?: boolean;
|
||||||
|
},
|
||||||
|
) => [order: number, styleId: string, style: string] | null;
|
||||||
|
|
||||||
|
export default function useGlobalCache<CacheType>(
|
||||||
prefix: string,
|
prefix: string,
|
||||||
keyPath: Ref<KeyType[]>,
|
keyPath: Ref<KeyType[]>,
|
||||||
cacheFn: () => CacheType,
|
cacheFn: () => CacheType,
|
||||||
onCacheRemove?: (cache: CacheType, fromHMR: boolean) => void,
|
onCacheRemove?: (cache: CacheType, fromHMR: boolean) => void,
|
||||||
|
// Add additional effect trigger by `useInsertionEffect`
|
||||||
|
onCacheEffect?: (cachedValue: CacheType) => void,
|
||||||
): ShallowRef<CacheType> {
|
): ShallowRef<CacheType> {
|
||||||
const styleContext = useStyleInject();
|
const styleContext = useStyleInject();
|
||||||
const fullPathStr = shallowRef('');
|
const globalCache = computed(() => styleContext.value?.cache);
|
||||||
const res = shallowRef<CacheType>();
|
const deps = computed(() => [prefix, ...keyPath.value].join('%'));
|
||||||
watchEffect(() => {
|
|
||||||
fullPathStr.value = [prefix, ...keyPath.value].join('%');
|
|
||||||
});
|
|
||||||
const HMRUpdate = useHMR();
|
const HMRUpdate = useHMR();
|
||||||
const clearCache = (pathStr: string) => {
|
|
||||||
styleContext.value.cache.update(pathStr, prevCache => {
|
type UpdaterArgs = [times: number, cache: CacheType];
|
||||||
const [times = 0, cache] = prevCache || [];
|
|
||||||
const nextCount = times - 1;
|
const buildCache = (updater?: (data: UpdaterArgs) => UpdaterArgs) => {
|
||||||
if (nextCount === 0) {
|
globalCache.value.update(deps.value, prevCache => {
|
||||||
onCacheRemove?.(cache, false);
|
const [times = 0, cache] = prevCache || [undefined, undefined];
|
||||||
return null;
|
|
||||||
|
// HMR should always ignore cache since developer may change it
|
||||||
|
let tmpCache = cache;
|
||||||
|
if (process.env.NODE_ENV !== 'production' && cache && HMRUpdate) {
|
||||||
|
onCacheRemove?.(tmpCache, HMRUpdate);
|
||||||
|
tmpCache = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [times - 1, cache];
|
const mergedCache = tmpCache || cacheFn();
|
||||||
|
|
||||||
|
const data: UpdaterArgs = [times, mergedCache];
|
||||||
|
|
||||||
|
// Call updater if need additional logic
|
||||||
|
return updater ? updater(data) : data;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
fullPathStr,
|
deps,
|
||||||
(newStr, oldStr) => {
|
() => {
|
||||||
if (oldStr) clearCache(oldStr);
|
buildCache();
|
||||||
// Create cache
|
|
||||||
styleContext.value.cache.update(newStr, prevCache => {
|
|
||||||
const [times = 0, cache] = prevCache || [];
|
|
||||||
|
|
||||||
// HMR should always ignore cache since developer may change it
|
|
||||||
let tmpCache = cache;
|
|
||||||
if (process.env.NODE_ENV !== 'production' && cache && HMRUpdate) {
|
|
||||||
onCacheRemove?.(tmpCache, HMRUpdate);
|
|
||||||
tmpCache = null;
|
|
||||||
}
|
|
||||||
const mergedCache = tmpCache || cacheFn();
|
|
||||||
|
|
||||||
return [times + 1, mergedCache];
|
|
||||||
});
|
|
||||||
res.value = styleContext.value.cache.get(fullPathStr.value)![1];
|
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let cacheEntity = globalCache.value.get(deps.value);
|
||||||
|
|
||||||
|
// HMR clean the cache but not trigger `useMemo` again
|
||||||
|
// Let's fallback of this
|
||||||
|
// ref https://github.com/ant-design/cssinjs/issues/127
|
||||||
|
if (process.env.NODE_ENV !== 'production' && !cacheEntity) {
|
||||||
|
buildCache();
|
||||||
|
cacheEntity = globalCache.value.get(deps.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheContent = computed(
|
||||||
|
() =>
|
||||||
|
(globalCache.value.get(deps.value) && globalCache.value.get(deps.value)![1]) ||
|
||||||
|
cacheEntity![1],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove if no need anymore
|
||||||
|
useCompatibleInsertionEffect(
|
||||||
|
() => {
|
||||||
|
onCacheEffect?.(cacheContent.value);
|
||||||
|
},
|
||||||
|
polyfill => {
|
||||||
|
// It's bad to call build again in effect.
|
||||||
|
// But we have to do this since StrictMode will call effect twice
|
||||||
|
// which will clear cache on the first time.
|
||||||
|
buildCache(([times, cache]) => {
|
||||||
|
if (polyfill && times === 0) {
|
||||||
|
onCacheEffect?.(cacheContent.value);
|
||||||
|
}
|
||||||
|
return [times + 1, cache];
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
globalCache.value.update(deps.value, prevCache => {
|
||||||
|
const [times = 0, cache] = prevCache || [];
|
||||||
|
const nextCount = times - 1;
|
||||||
|
if (nextCount <= 0) {
|
||||||
|
if (polyfill || !globalCache.value.get(deps.value)) {
|
||||||
|
onCacheRemove?.(cache, false);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [times - 1, cache];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
deps,
|
||||||
|
);
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
clearCache(fullPathStr.value);
|
buildCache();
|
||||||
});
|
});
|
||||||
return res;
|
|
||||||
|
return cacheContent;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,38 +3,30 @@ import type * as CSS from 'csstype';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import unitless from '@emotion/unitless';
|
import unitless from '@emotion/unitless';
|
||||||
import { compile, serialize, stringify } from 'stylis';
|
import { compile, serialize, stringify } from 'stylis';
|
||||||
import type { Theme, Transformer } from '../..';
|
import type { Theme, Transformer } from '..';
|
||||||
import type Cache from '../../Cache';
|
import type Keyframes from '../Keyframes';
|
||||||
import type Keyframes from '../../Keyframes';
|
import type { Linter } from '../linters';
|
||||||
import type { Linter } from '../../linters';
|
import { contentQuotesLinter, hashedAnimationLinter } from '../linters';
|
||||||
import { contentQuotesLinter, hashedAnimationLinter } from '../../linters';
|
import type { HashPriority } from '../StyleContext';
|
||||||
import type { HashPriority } from '../../StyleContext';
|
|
||||||
import {
|
import {
|
||||||
useStyleInject,
|
useStyleInject,
|
||||||
ATTR_CACHE_PATH,
|
ATTR_CACHE_PATH,
|
||||||
ATTR_MARK,
|
ATTR_MARK,
|
||||||
ATTR_TOKEN,
|
ATTR_TOKEN,
|
||||||
CSS_IN_JS_INSTANCE,
|
CSS_IN_JS_INSTANCE,
|
||||||
} from '../../StyleContext';
|
} from '../StyleContext';
|
||||||
import { supportLayer } from '../../util';
|
import { isClientSide, supportLayer, toStyleStr } from '../util';
|
||||||
import useGlobalCache from '../useGlobalCache';
|
import { CSS_FILE_STYLE, existPath, getStyleAndHash } from '../util/cacheMapUtil';
|
||||||
import { removeCSS, updateCSS } from '../../../../vc-util/Dom/dynamicCSS';
|
import type { ExtractStyle } from './useGlobalCache';
|
||||||
|
import useGlobalCache from './useGlobalCache';
|
||||||
|
import { removeCSS, updateCSS } from '../../../vc-util/Dom/dynamicCSS';
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import type { VueNode } from '../../../type';
|
import type { VueNode } from '../../type';
|
||||||
import canUseDom from '../../../../_util/canUseDom';
|
|
||||||
|
|
||||||
import {
|
|
||||||
ATTR_CACHE_MAP,
|
|
||||||
existPath,
|
|
||||||
getStyleAndHash,
|
|
||||||
serialize as serializeCacheMap,
|
|
||||||
} from './cacheMapUtil';
|
|
||||||
|
|
||||||
const isClientSide = canUseDom();
|
|
||||||
|
|
||||||
const SKIP_CHECK = '_skip_check_';
|
const SKIP_CHECK = '_skip_check_';
|
||||||
const MULTI_VALUE = '_multi_value_';
|
const MULTI_VALUE = '_multi_value_';
|
||||||
|
|
||||||
export type CSSProperties = Omit<CSS.PropertiesFallback<number | string>, 'animationName'> & {
|
export type CSSProperties = Omit<CSS.PropertiesFallback<number | string>, 'animationName'> & {
|
||||||
animationName?: CSS.PropertiesFallback<number | string>['animationName'] | Keyframes;
|
animationName?: CSS.PropertiesFallback<number | string>['animationName'] | Keyframes;
|
||||||
};
|
};
|
||||||
|
|
@ -60,6 +52,7 @@ export type CSSInterpolation = InterpolationPrimitive | ArrayCSSInterpolation |
|
||||||
|
|
||||||
export type CSSOthersObject = Record<string, CSSInterpolation>;
|
export type CSSOthersObject = Record<string, CSSInterpolation>;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
export interface CSSObject extends CSSPropertiesWithMultiValues, CSSPseudos, CSSOthersObject {}
|
export interface CSSObject extends CSSPropertiesWithMultiValues, CSSPseudos, CSSOthersObject {}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -114,16 +107,6 @@ export interface ParseInfo {
|
||||||
parentSelectors: string[];
|
parentSelectors: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global effect style will mount once and not removed
|
|
||||||
// The effect will not save in SSR cache (e.g. keyframes)
|
|
||||||
const globalEffectStyleKeys = new Set();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private Test only. Clear the global effect style keys.
|
|
||||||
*/
|
|
||||||
export const _cf =
|
|
||||||
process.env.NODE_ENV !== 'production' ? () => globalEffectStyleKeys.clear() : undefined;
|
|
||||||
|
|
||||||
// Parse CSSObject to style content
|
// Parse CSSObject to style content
|
||||||
export const parseStyle = (
|
export const parseStyle = (
|
||||||
interpolation: CSSInterpolation,
|
interpolation: CSSInterpolation,
|
||||||
|
|
@ -258,6 +241,7 @@ export const parseStyle = (
|
||||||
|
|
||||||
styleStr += `${styleName}:${formatValue};`;
|
styleStr += `${styleName}:${formatValue};`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const actualValue = (value as any)?.value ?? value;
|
const actualValue = (value as any)?.value ?? value;
|
||||||
if (
|
if (
|
||||||
typeof value === 'object' &&
|
typeof value === 'object' &&
|
||||||
|
|
@ -295,7 +279,7 @@ export const parseStyle = (
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// == Register ==
|
// == Register ==
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
function uniqueHash(path: (string | number)[], styleStr: string) {
|
export function uniqueHash(path: (string | number)[], styleStr: string) {
|
||||||
return hash(`${path.join('%')}${styleStr}`);
|
return hash(`${path.join('%')}${styleStr}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,6 +287,17 @@ function uniqueHash(path: (string | number)[], styleStr: string) {
|
||||||
// return null;
|
// return null;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
export const STYLE_PREFIX = 'style';
|
||||||
|
|
||||||
|
type StyleCacheValue = [
|
||||||
|
styleStr: string,
|
||||||
|
tokenKey: string,
|
||||||
|
styleId: string,
|
||||||
|
effectStyle: Record<string, string>,
|
||||||
|
clientOnly: boolean | undefined,
|
||||||
|
order: number,
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a style to the global style sheet.
|
* Register a style to the global style sheet.
|
||||||
*/
|
*/
|
||||||
|
|
@ -337,22 +332,14 @@ export default function useStyleRegister(
|
||||||
}
|
}
|
||||||
|
|
||||||
// const [cacheStyle[0], cacheStyle[1], cacheStyle[2]]
|
// const [cacheStyle[0], cacheStyle[1], cacheStyle[2]]
|
||||||
useGlobalCache<
|
useGlobalCache<StyleCacheValue>(
|
||||||
[
|
STYLE_PREFIX,
|
||||||
styleStr: string,
|
|
||||||
tokenKey: string,
|
|
||||||
styleId: string,
|
|
||||||
effectStyle: Record<string, string>,
|
|
||||||
clientOnly: boolean | undefined,
|
|
||||||
order: number,
|
|
||||||
]
|
|
||||||
>(
|
|
||||||
'style',
|
|
||||||
fullPath,
|
fullPath,
|
||||||
// Create cache if needed
|
// Create cache if needed
|
||||||
() => {
|
() => {
|
||||||
const { path, hashId, layer, nonce, clientOnly, order = 0 } = info.value;
|
const { path, hashId, layer, clientOnly, order = 0 } = info.value;
|
||||||
const cachePath = fullPath.value.join('|');
|
const cachePath = fullPath.value.join('|');
|
||||||
|
|
||||||
// Get style from SSR inline style directly
|
// Get style from SSR inline style directly
|
||||||
if (existPath(cachePath)) {
|
if (existPath(cachePath)) {
|
||||||
const [inlineCacheStyleStr, styleHash] = getStyleAndHash(cachePath);
|
const [inlineCacheStyleStr, styleHash] = getStyleAndHash(cachePath);
|
||||||
|
|
@ -360,8 +347,10 @@ export default function useStyleRegister(
|
||||||
return [inlineCacheStyleStr, tokenKey.value, styleHash, {}, clientOnly, order];
|
return [inlineCacheStyleStr, tokenKey.value, styleHash, {}, clientOnly, order];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate style
|
||||||
const styleObj = styleFn();
|
const styleObj = styleFn();
|
||||||
const { hashPriority, container, transformers, linters, cache } = styleContext.value;
|
const { hashPriority, transformers, linters } = styleContext.value;
|
||||||
|
|
||||||
const [parsedStyle, effectStyle] = parseStyle(styleObj, {
|
const [parsedStyle, effectStyle] = parseStyle(styleObj, {
|
||||||
hashId,
|
hashId,
|
||||||
|
|
@ -371,18 +360,32 @@ export default function useStyleRegister(
|
||||||
transformers,
|
transformers,
|
||||||
linters,
|
linters,
|
||||||
});
|
});
|
||||||
|
|
||||||
const styleStr = normalizeStyle(parsedStyle);
|
const styleStr = normalizeStyle(parsedStyle);
|
||||||
const styleId = uniqueHash(fullPath.value, styleStr);
|
const styleId = uniqueHash(fullPath.value, styleStr);
|
||||||
|
|
||||||
if (isMergedClientSide) {
|
return [styleStr, tokenKey.value, styleId, effectStyle, clientOnly, order];
|
||||||
|
},
|
||||||
|
|
||||||
|
// Remove cache if no need
|
||||||
|
([, , styleId], fromHMR) => {
|
||||||
|
if ((fromHMR || styleContext.value.autoClear) && isClientSide) {
|
||||||
|
removeCSS(styleId, { mark: ATTR_MARK });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Effect: Inject style here
|
||||||
|
([styleStr, , styleId, effectStyle]) => {
|
||||||
|
if (isMergedClientSide && styleStr !== CSS_FILE_STYLE) {
|
||||||
const mergedCSSConfig: Parameters<typeof updateCSS>[2] = {
|
const mergedCSSConfig: Parameters<typeof updateCSS>[2] = {
|
||||||
mark: ATTR_MARK,
|
mark: ATTR_MARK,
|
||||||
prepend: 'queue',
|
prepend: 'queue',
|
||||||
attachTo: container,
|
attachTo: styleContext.value.container,
|
||||||
priority: order,
|
priority: info.value.order,
|
||||||
};
|
};
|
||||||
|
|
||||||
const nonceStr = typeof nonce === 'function' ? nonce() : nonce;
|
const nonceStr =
|
||||||
|
typeof info.value.nonce === 'function' ? info.value.nonce() : info.value.nonce;
|
||||||
|
|
||||||
if (nonceStr) {
|
if (nonceStr) {
|
||||||
mergedCSSConfig.csp = { nonce: nonceStr };
|
mergedCSSConfig.csp = { nonce: nonceStr };
|
||||||
|
|
@ -390,45 +393,33 @@ export default function useStyleRegister(
|
||||||
|
|
||||||
const style = updateCSS(styleStr, styleId, mergedCSSConfig);
|
const style = updateCSS(styleStr, styleId, mergedCSSConfig);
|
||||||
|
|
||||||
(style as any)[CSS_IN_JS_INSTANCE] = cache.instanceId;
|
(style as any)[CSS_IN_JS_INSTANCE] = styleContext.value.cache.instanceId;
|
||||||
|
|
||||||
// Used for `useCacheToken` to remove on batch when token removed
|
// Used for `useCacheToken` to remove on batch when token removed
|
||||||
style.setAttribute(ATTR_TOKEN, tokenKey.value);
|
style.setAttribute(ATTR_TOKEN, tokenKey.value);
|
||||||
|
|
||||||
// Dev usage to find which cache path made this easily
|
// Debug usage. Dev only
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
style.setAttribute(ATTR_CACHE_PATH, fullPath.value.join('|'));
|
style.setAttribute(ATTR_CACHE_PATH, fullPath.value.join('|'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject client side effect style
|
// Inject client side effect style
|
||||||
Object.keys(effectStyle).forEach(effectKey => {
|
Object.keys(effectStyle).forEach(effectKey => {
|
||||||
if (!globalEffectStyleKeys.has(effectKey)) {
|
updateCSS(
|
||||||
globalEffectStyleKeys.add(effectKey);
|
normalizeStyle(effectStyle[effectKey]),
|
||||||
|
`_effect-${effectKey}`,
|
||||||
// Inject
|
mergedCSSConfig,
|
||||||
updateCSS(normalizeStyle(effectStyle[effectKey]), `_effect-${effectKey}`, {
|
);
|
||||||
mark: ATTR_MARK,
|
|
||||||
prepend: 'queue',
|
|
||||||
attachTo: container,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return [styleStr, tokenKey.value, styleId, effectStyle, clientOnly, order];
|
|
||||||
},
|
|
||||||
// Remove cache if no need
|
|
||||||
([, , styleId], fromHMR) => {
|
|
||||||
if ((fromHMR || styleContext.value.autoClear) && isClientSide) {
|
|
||||||
removeCSS(styleId, { mark: ATTR_MARK });
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return (node: VueNode) => {
|
return (node: VueNode) => {
|
||||||
return node;
|
return node;
|
||||||
// let styleNode: VueNode;
|
// let styleNode: VueNode;
|
||||||
// if (!styleContext.ssrInline || isMergedClientSide || !styleContext.defaultCache) {
|
|
||||||
|
// if (!styleContext.value.ssrInline || isMergedClientSide || !styleContext.value.defaultCache) {
|
||||||
// styleNode = <Empty />;
|
// styleNode = <Empty />;
|
||||||
// } else {
|
// } else {
|
||||||
// styleNode = (
|
// styleNode = (
|
||||||
|
|
@ -451,116 +442,43 @@ export default function useStyleRegister(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
export const extract: ExtractStyle<StyleCacheValue> = (cache, effectStyles, options) => {
|
||||||
// == SSR ==
|
const [styleStr, tokenKey, styleId, effectStyle, clientOnly, order]: StyleCacheValue = cache;
|
||||||
// ============================================================================
|
const { plain } = options || {};
|
||||||
export function extractStyle(cache: Cache, plain = false) {
|
|
||||||
const matchPrefix = `style%`;
|
|
||||||
|
|
||||||
// prefix with `style` is used for `useStyleRegister` to cache style context
|
// Skip client only style
|
||||||
const styleKeys = Array.from(cache.cache.keys()).filter(key => key.startsWith(matchPrefix));
|
if (clientOnly) {
|
||||||
|
return null;
|
||||||
// Common effect styles like animation
|
|
||||||
const effectStyles: Record<string, boolean> = {};
|
|
||||||
|
|
||||||
// Mapping of cachePath to style hash
|
|
||||||
const cachePathMap: Record<string, string> = {};
|
|
||||||
|
|
||||||
let styleText = '';
|
|
||||||
|
|
||||||
function toStyleStr(
|
|
||||||
style: string,
|
|
||||||
tokenKey?: string,
|
|
||||||
styleId?: string,
|
|
||||||
customizeAttrs: Record<string, string> = {},
|
|
||||||
) {
|
|
||||||
const attrs: Record<string, string | undefined> = {
|
|
||||||
...customizeAttrs,
|
|
||||||
[ATTR_TOKEN]: tokenKey,
|
|
||||||
[ATTR_MARK]: styleId,
|
|
||||||
};
|
|
||||||
|
|
||||||
const attrStr = Object.keys(attrs)
|
|
||||||
.map(attr => {
|
|
||||||
const val = attrs[attr];
|
|
||||||
return val ? `${attr}="${val}"` : null;
|
|
||||||
})
|
|
||||||
.filter(v => v)
|
|
||||||
.join(' ');
|
|
||||||
|
|
||||||
return plain ? style : `<style ${attrStr}>${style}</style>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================== Fill Style ======================
|
let keyStyleText = styleStr;
|
||||||
type OrderStyle = [order: number, style: string];
|
|
||||||
|
|
||||||
const orderStyles: OrderStyle[] = styleKeys
|
// ====================== Style ======================
|
||||||
.map(key => {
|
// Used for rc-util
|
||||||
const cachePath = key.slice(matchPrefix.length).replace(/%/g, '|');
|
const sharedAttrs = {
|
||||||
|
'data-vc-order': 'prependQueue',
|
||||||
|
'data-vc-priority': `${order}`,
|
||||||
|
};
|
||||||
|
|
||||||
const [styleStr, tokenKey, styleId, effectStyle, clientOnly, order]: [
|
keyStyleText = toStyleStr(styleStr, tokenKey, styleId, sharedAttrs, plain);
|
||||||
string,
|
|
||||||
string,
|
|
||||||
string,
|
|
||||||
Record<string, string>,
|
|
||||||
boolean,
|
|
||||||
number,
|
|
||||||
] = cache.cache.get(key)![1];
|
|
||||||
|
|
||||||
// Skip client only style
|
// =============== Create effect style ===============
|
||||||
if (clientOnly) {
|
if (effectStyle) {
|
||||||
return null! as OrderStyle;
|
Object.keys(effectStyle).forEach(effectKey => {
|
||||||
|
// Effect style can be reused
|
||||||
|
if (!effectStyles[effectKey]) {
|
||||||
|
effectStyles[effectKey] = true;
|
||||||
|
const effectStyleStr = normalizeStyle(effectStyle[effectKey]);
|
||||||
|
keyStyleText += toStyleStr(
|
||||||
|
effectStyleStr,
|
||||||
|
tokenKey,
|
||||||
|
`_effect-${effectKey}`,
|
||||||
|
sharedAttrs,
|
||||||
|
plain,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================== Style ======================
|
|
||||||
// Used for vc-util
|
|
||||||
const sharedAttrs = {
|
|
||||||
'data-vc-order': 'prependQueue',
|
|
||||||
'data-vc-priority': `${order}`,
|
|
||||||
};
|
|
||||||
|
|
||||||
let keyStyleText = toStyleStr(styleStr, tokenKey, styleId, sharedAttrs);
|
|
||||||
|
|
||||||
// Save cache path with hash mapping
|
|
||||||
cachePathMap[cachePath] = styleId;
|
|
||||||
|
|
||||||
// =============== Create effect style ===============
|
|
||||||
if (effectStyle) {
|
|
||||||
Object.keys(effectStyle).forEach(effectKey => {
|
|
||||||
// Effect style can be reused
|
|
||||||
if (!effectStyles[effectKey]) {
|
|
||||||
effectStyles[effectKey] = true;
|
|
||||||
keyStyleText += toStyleStr(
|
|
||||||
normalizeStyle(effectStyle[effectKey]),
|
|
||||||
tokenKey,
|
|
||||||
`_effect-${effectKey}`,
|
|
||||||
sharedAttrs,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const ret: OrderStyle = [order, keyStyleText];
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
})
|
|
||||||
.filter(o => o);
|
|
||||||
|
|
||||||
orderStyles
|
|
||||||
.sort((o1, o2) => o1[0] - o2[0])
|
|
||||||
.forEach(([, style]) => {
|
|
||||||
styleText += style;
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== Fill Cache Path ====================
|
return [order, styleId, keyStyleText];
|
||||||
styleText += toStyleStr(
|
};
|
||||||
`.${ATTR_CACHE_MAP}{content:"${serializeCacheMap(cachePathMap)}";}`,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
[ATTR_CACHE_MAP]: ATTR_CACHE_MAP,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return styleText;
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +1,37 @@
|
||||||
import useCacheToken from './hooks/useCacheToken';
|
import extractStyle from './extractStyle';
|
||||||
|
import useCacheToken, { getComputedToken } from './hooks/useCacheToken';
|
||||||
|
import useCSSVarRegister from './hooks/useCSSVarRegister';
|
||||||
import type { CSSInterpolation, CSSObject } from './hooks/useStyleRegister';
|
import type { CSSInterpolation, CSSObject } from './hooks/useStyleRegister';
|
||||||
import useStyleRegister, { extractStyle } from './hooks/useStyleRegister';
|
import useStyleRegister from './hooks/useStyleRegister';
|
||||||
import Keyframes from './Keyframes';
|
import Keyframes from './Keyframes';
|
||||||
import type { Linter } from './linters';
|
import type { Linter } from './linters';
|
||||||
import { legacyNotSelectorLinter, logicalPropertiesLinter, parentSelectorLinter } from './linters';
|
import {
|
||||||
import type { StyleContextProps, StyleProviderProps } from './StyleContext';
|
legacyNotSelectorLinter,
|
||||||
import { createCache, useStyleInject, useStyleProvider, StyleProvider } from './StyleContext';
|
logicalPropertiesLinter,
|
||||||
|
NaNLinter,
|
||||||
|
parentSelectorLinter,
|
||||||
|
} from './linters';
|
||||||
|
import type { StyleProviderProps } from './StyleContext';
|
||||||
|
import { createCache, StyleProvider } from './StyleContext';
|
||||||
import type { DerivativeFunc, TokenType } from './theme';
|
import type { DerivativeFunc, TokenType } from './theme';
|
||||||
import { createTheme, Theme } from './theme';
|
import { createTheme, Theme } from './theme';
|
||||||
import type { Transformer } from './transformers/interface';
|
import type { Transformer } from './transformers/interface';
|
||||||
import legacyLogicalPropertiesTransformer from './transformers/legacyLogicalProperties';
|
import legacyLogicalPropertiesTransformer from './transformers/legacyLogicalProperties';
|
||||||
import px2remTransformer from './transformers/px2rem';
|
import px2remTransformer from './transformers/px2rem';
|
||||||
import { supportLogicProps, supportWhere } from './util';
|
import { supportLogicProps, supportWhere, unit } from './util';
|
||||||
|
import { token2CSSVar } from './util/css-variables';
|
||||||
|
|
||||||
const cssinjs = {
|
|
||||||
Theme,
|
|
||||||
createTheme,
|
|
||||||
useStyleRegister,
|
|
||||||
useCacheToken,
|
|
||||||
createCache,
|
|
||||||
useStyleInject,
|
|
||||||
useStyleProvider,
|
|
||||||
Keyframes,
|
|
||||||
extractStyle,
|
|
||||||
|
|
||||||
// Transformer
|
|
||||||
legacyLogicalPropertiesTransformer,
|
|
||||||
px2remTransformer,
|
|
||||||
|
|
||||||
// Linters
|
|
||||||
logicalPropertiesLinter,
|
|
||||||
legacyNotSelectorLinter,
|
|
||||||
parentSelectorLinter,
|
|
||||||
|
|
||||||
// cssinjs
|
|
||||||
StyleProvider,
|
|
||||||
};
|
|
||||||
export {
|
export {
|
||||||
Theme,
|
Theme,
|
||||||
createTheme,
|
createTheme,
|
||||||
useStyleRegister,
|
useStyleRegister,
|
||||||
|
useCSSVarRegister,
|
||||||
useCacheToken,
|
useCacheToken,
|
||||||
createCache,
|
createCache,
|
||||||
useStyleInject,
|
StyleProvider,
|
||||||
useStyleProvider,
|
|
||||||
Keyframes,
|
Keyframes,
|
||||||
extractStyle,
|
extractStyle,
|
||||||
|
getComputedToken,
|
||||||
|
|
||||||
// Transformer
|
// Transformer
|
||||||
legacyLogicalPropertiesTransformer,
|
legacyLogicalPropertiesTransformer,
|
||||||
|
|
@ -55,9 +41,11 @@ export {
|
||||||
logicalPropertiesLinter,
|
logicalPropertiesLinter,
|
||||||
legacyNotSelectorLinter,
|
legacyNotSelectorLinter,
|
||||||
parentSelectorLinter,
|
parentSelectorLinter,
|
||||||
|
NaNLinter,
|
||||||
|
|
||||||
// cssinjs
|
// util
|
||||||
StyleProvider,
|
token2CSSVar,
|
||||||
|
unit,
|
||||||
};
|
};
|
||||||
export type {
|
export type {
|
||||||
TokenType,
|
TokenType,
|
||||||
|
|
@ -66,12 +54,9 @@ export type {
|
||||||
DerivativeFunc,
|
DerivativeFunc,
|
||||||
Transformer,
|
Transformer,
|
||||||
Linter,
|
Linter,
|
||||||
StyleContextProps,
|
|
||||||
StyleProviderProps,
|
StyleProviderProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const _experimental = {
|
export const _experimental = {
|
||||||
supportModernCSS: () => supportWhere() && supportLogicProps(),
|
supportModernCSS: () => supportWhere() && supportLogicProps(),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default cssinjs;
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import type { Linter } from './interface';
|
||||||
|
import { lintWarning } from './utils';
|
||||||
|
|
||||||
|
const linter: Linter = (key, value, info) => {
|
||||||
|
if ((typeof value === 'string' && /NaN/g.test(value)) || Number.isNaN(value)) {
|
||||||
|
lintWarning(`Unexpected 'NaN' in property '${key}: ${value}'.`, info);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default linter;
|
||||||
|
|
@ -3,4 +3,5 @@ export { default as hashedAnimationLinter } from './hashedAnimationLinter';
|
||||||
export type { Linter } from './interface';
|
export type { Linter } from './interface';
|
||||||
export { default as legacyNotSelectorLinter } from './legacyNotSelectorLinter';
|
export { default as legacyNotSelectorLinter } from './legacyNotSelectorLinter';
|
||||||
export { default as logicalPropertiesLinter } from './logicalPropertiesLinter';
|
export { default as logicalPropertiesLinter } from './logicalPropertiesLinter';
|
||||||
|
export { default as NaNLinter } from './NaNLinter';
|
||||||
export { default as parentSelectorLinter } from './parentSelectorLinter';
|
export { default as parentSelectorLinter } from './parentSelectorLinter';
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ export function lintWarning(message: string, info: LinterInfo) {
|
||||||
|
|
||||||
devWarning(
|
devWarning(
|
||||||
false,
|
false,
|
||||||
`[Ant Design Vue CSS-in-JS] ${path ? `Error in '${path}': ` : ''}${message}${
|
`[Ant Design Vue CSS-in-JS] ${path ? `Error in ${path}: ` : ''}${message}${
|
||||||
parentSelectors.length ? ` Selector info: ${parentSelectors.join(' -> ')}` : ''
|
parentSelectors.length ? ` Selector: ${parentSelectors.join(' | ')}` : ''
|
||||||
}`,
|
}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,36 @@
|
||||||
import type { CSSObject } from '..';
|
import type { CSSObject } from '..';
|
||||||
import type { Transformer } from './interface';
|
import type { Transformer } from './interface';
|
||||||
|
|
||||||
function splitValues(value: string | number) {
|
function splitValues(value: string | number): [values: (string | number)[], important: boolean] {
|
||||||
if (typeof value === 'number') {
|
if (typeof value === 'number') {
|
||||||
return [value];
|
return [[value], false];
|
||||||
}
|
}
|
||||||
|
|
||||||
const splitStyle = String(value).split(/\s+/);
|
const rawStyle = String(value).trim();
|
||||||
|
const importantCells = rawStyle.match(/(.*)(!important)/);
|
||||||
|
|
||||||
|
const splitStyle = (importantCells ? importantCells[1] : rawStyle).trim().split(/\s+/);
|
||||||
|
|
||||||
// Combine styles split in brackets, like `calc(1px + 2px)`
|
// Combine styles split in brackets, like `calc(1px + 2px)`
|
||||||
let temp = '';
|
let temp = '';
|
||||||
let brackets = 0;
|
let brackets = 0;
|
||||||
return splitStyle.reduce<string[]>((list, item) => {
|
return [
|
||||||
if (item.includes('(')) {
|
splitStyle.reduce<string[]>((list, item) => {
|
||||||
temp += item;
|
if (item.includes('(') || item.includes(')')) {
|
||||||
brackets += item.split('(').length - 1;
|
const left = item.split('(').length - 1;
|
||||||
} else if (item.includes(')')) {
|
const right = item.split(')').length - 1;
|
||||||
temp += ` ${item}`;
|
brackets += left - right;
|
||||||
brackets -= item.split(')').length - 1;
|
|
||||||
if (brackets === 0) {
|
|
||||||
list.push(temp);
|
|
||||||
temp = '';
|
|
||||||
}
|
}
|
||||||
} else if (brackets > 0) {
|
if (brackets === 0) {
|
||||||
temp += ` ${item}`;
|
list.push(temp + item);
|
||||||
} else {
|
temp = '';
|
||||||
list.push(item);
|
} else if (brackets > 0) {
|
||||||
}
|
temp += item;
|
||||||
return list;
|
}
|
||||||
}, []);
|
return list;
|
||||||
|
}, []),
|
||||||
|
!!importantCells,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
type MatchValue = string[] & {
|
type MatchValue = string[] & {
|
||||||
|
|
@ -105,8 +107,14 @@ const keyMap: Record<string, MatchValue> = {
|
||||||
borderEndEndRadius: ['borderBottomRightRadius'],
|
borderEndEndRadius: ['borderBottomRightRadius'],
|
||||||
};
|
};
|
||||||
|
|
||||||
function skipCheck(value: string | number) {
|
function wrapImportantAndSkipCheck(value: string | number, important: boolean) {
|
||||||
return { _skip_check_: true, value };
|
let parsedValue = value;
|
||||||
|
|
||||||
|
if (important) {
|
||||||
|
parsedValue = `${parsedValue} !important`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { _skip_check_: true, value: parsedValue };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -127,25 +135,28 @@ const transform: Transformer = {
|
||||||
const matchValue = keyMap[key];
|
const matchValue = keyMap[key];
|
||||||
|
|
||||||
if (matchValue && (typeof value === 'number' || typeof value === 'string')) {
|
if (matchValue && (typeof value === 'number' || typeof value === 'string')) {
|
||||||
const values = splitValues(value);
|
const [values, important] = splitValues(value);
|
||||||
|
|
||||||
if (matchValue.length && matchValue.notSplit) {
|
if (matchValue.length && matchValue.notSplit) {
|
||||||
// not split means always give same value like border
|
// not split means always give same value like border
|
||||||
matchValue.forEach(matchKey => {
|
matchValue.forEach(matchKey => {
|
||||||
clone[matchKey] = skipCheck(value);
|
clone[matchKey] = wrapImportantAndSkipCheck(value, important);
|
||||||
});
|
});
|
||||||
} else if (matchValue.length === 1) {
|
} else if (matchValue.length === 1) {
|
||||||
// Handle like `marginBlockStart` => `marginTop`
|
// Handle like `marginBlockStart` => `marginTop`
|
||||||
clone[matchValue[0]] = skipCheck(value);
|
clone[matchValue[0]] = wrapImportantAndSkipCheck(value, important);
|
||||||
} else if (matchValue.length === 2) {
|
} else if (matchValue.length === 2) {
|
||||||
// Handle like `marginBlock` => `marginTop` & `marginBottom`
|
// Handle like `marginBlock` => `marginTop` & `marginBottom`
|
||||||
matchValue.forEach((matchKey, index) => {
|
matchValue.forEach((matchKey, index) => {
|
||||||
clone[matchKey] = skipCheck(values[index] ?? values[0]);
|
clone[matchKey] = wrapImportantAndSkipCheck(values[index] ?? values[0], important);
|
||||||
});
|
});
|
||||||
} else if (matchValue.length === 4) {
|
} else if (matchValue.length === 4) {
|
||||||
// Handle like `inset` => `top` & `right` & `bottom` & `left`
|
// Handle like `inset` => `top` & `right` & `bottom` & `left`
|
||||||
matchValue.forEach((matchKey, index) => {
|
matchValue.forEach((matchKey, index) => {
|
||||||
clone[matchKey] = skipCheck(values[index] ?? values[index - 2] ?? values[0]);
|
clone[matchKey] = wrapImportantAndSkipCheck(
|
||||||
|
values[index] ?? values[index - 2] ?? values[0],
|
||||||
|
important,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
clone[key] = value;
|
clone[key] = value;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* respect https://github.com/cuth/postcss-pxtorem
|
* respect https://github.com/cuth/postcss-pxtorem
|
||||||
*/
|
*/
|
||||||
|
// @ts-ignore
|
||||||
import unitless from '@emotion/unitless';
|
import unitless from '@emotion/unitless';
|
||||||
import type { CSSObject } from '..';
|
import type { CSSObject } from '..';
|
||||||
import type { Transformer } from './interface';
|
import type { Transformer } from './interface';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import canUseDom from '../../../../_util/canUseDom';
|
import canUseDom from '../../canUseDom';
|
||||||
import { ATTR_MARK } from '../../StyleContext';
|
import { ATTR_MARK } from '../StyleContext';
|
||||||
|
|
||||||
export const ATTR_CACHE_MAP = 'data-ant-cssinjs-cache-path';
|
export const ATTR_CACHE_MAP = 'data-ant-cssinjs-cache-path';
|
||||||
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
export const token2CSSVar = (token: string, prefix = '') => {
|
||||||
|
return `--${prefix ? `${prefix}-` : ''}${token}`
|
||||||
|
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
||||||
|
.replace(/([A-Z]+)([A-Z][a-z0-9]+)/g, '$1-$2')
|
||||||
|
.replace(/([a-z])([A-Z0-9])/g, '$1-$2')
|
||||||
|
.toLowerCase();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const serializeCSSVar = <T extends Record<string, any>>(
|
||||||
|
cssVars: T,
|
||||||
|
hashId: string,
|
||||||
|
options?: {
|
||||||
|
scope?: string;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
if (!Object.keys(cssVars).length) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return `.${hashId}${options?.scope ? `.${options.scope}` : ''}{${Object.entries(cssVars)
|
||||||
|
.map(([key, value]) => `${key}:${value};`)
|
||||||
|
.join('')}}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TokenWithCSSVar<V, T extends Record<string, V> = Record<string, V>> = {
|
||||||
|
[key in keyof T]?: string | V;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const transformToken = <V, T extends Record<string, V> = Record<string, V>>(
|
||||||
|
token: T,
|
||||||
|
themeKey: string,
|
||||||
|
config?: {
|
||||||
|
prefix?: string;
|
||||||
|
ignore?: {
|
||||||
|
[key in keyof T]?: boolean;
|
||||||
|
};
|
||||||
|
unitless?: {
|
||||||
|
[key in keyof T]?: boolean;
|
||||||
|
};
|
||||||
|
preserve?: {
|
||||||
|
[key in keyof T]?: boolean;
|
||||||
|
};
|
||||||
|
scope?: string;
|
||||||
|
},
|
||||||
|
): [TokenWithCSSVar<V, T>, string] => {
|
||||||
|
const cssVars: Record<string, string> = {};
|
||||||
|
const result: TokenWithCSSVar<V, T> = {};
|
||||||
|
Object.entries(token).forEach(([key, value]) => {
|
||||||
|
if (config?.preserve?.[key]) {
|
||||||
|
result[key as keyof T] = value;
|
||||||
|
} else if ((typeof value === 'string' || typeof value === 'number') && !config?.ignore?.[key]) {
|
||||||
|
const cssVar = token2CSSVar(key, config?.prefix);
|
||||||
|
cssVars[cssVar] =
|
||||||
|
typeof value === 'number' && !config?.unitless?.[key] ? `${value}px` : String(value);
|
||||||
|
result[key as keyof T] = `var(${cssVar})`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return [result, serializeCSSVar(cssVars, themeKey, { scope: config?.scope })];
|
||||||
|
};
|
||||||
|
|
@ -1,12 +1,37 @@
|
||||||
import hash from '@emotion/hash';
|
import hash from '@emotion/hash';
|
||||||
import { removeCSS, updateCSS } from '../../vc-util/Dom/dynamicCSS';
|
import canUseDom from '../../canUseDom';
|
||||||
import canUseDom from '../canUseDom';
|
import { removeCSS, updateCSS } from '../../../vc-util/Dom/dynamicCSS';
|
||||||
|
import { ATTR_MARK, ATTR_TOKEN } from '../StyleContext';
|
||||||
|
import { Theme } from '../theme';
|
||||||
|
|
||||||
import { Theme } from './theme';
|
// Create a cache for memo concat
|
||||||
|
type NestWeakMap<T> = WeakMap<object, NestWeakMap<T> | T>;
|
||||||
|
const resultCache: NestWeakMap<object> = new WeakMap();
|
||||||
|
const RESULT_VALUE = {};
|
||||||
|
|
||||||
|
export function memoResult<T extends object, R>(callback: () => R, deps: T[]): R {
|
||||||
|
let current: WeakMap<any, any> = resultCache;
|
||||||
|
for (let i = 0; i < deps.length; i += 1) {
|
||||||
|
const dep = deps[i];
|
||||||
|
if (!current.has(dep)) {
|
||||||
|
current.set(dep, new WeakMap());
|
||||||
|
}
|
||||||
|
current = current.get(dep)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current.has(RESULT_VALUE)) {
|
||||||
|
current.set(RESULT_VALUE, callback());
|
||||||
|
}
|
||||||
|
|
||||||
|
return current.get(RESULT_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
// Create a cache here to avoid always loop generate
|
// Create a cache here to avoid always loop generate
|
||||||
const flattenTokenCache = new WeakMap<any, string>();
|
const flattenTokenCache = new WeakMap<any, string>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flatten token to string, this will auto cache the result when token not change
|
||||||
|
*/
|
||||||
export function flattenToken(token: any) {
|
export function flattenToken(token: any) {
|
||||||
let str = flattenTokenCache.get(token) || '';
|
let str = flattenTokenCache.get(token) || '';
|
||||||
|
|
||||||
|
|
@ -116,3 +141,39 @@ export function supportLogicProps(): boolean {
|
||||||
|
|
||||||
return canLogic!;
|
return canLogic!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isClientSide = canUseDom();
|
||||||
|
|
||||||
|
export function unit(num: string | number) {
|
||||||
|
if (typeof num === 'number') {
|
||||||
|
return `${num}px`;
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toStyleStr(
|
||||||
|
style: string,
|
||||||
|
tokenKey?: string,
|
||||||
|
styleId?: string,
|
||||||
|
customizeAttrs: Record<string, string> = {},
|
||||||
|
plain = false,
|
||||||
|
) {
|
||||||
|
if (plain) {
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
const attrs: Record<string, string | undefined> = {
|
||||||
|
...customizeAttrs,
|
||||||
|
[ATTR_TOKEN]: tokenKey,
|
||||||
|
[ATTR_MARK]: styleId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const attrStr = Object.keys(attrs)
|
||||||
|
.map(attr => {
|
||||||
|
const val = attrs[attr];
|
||||||
|
return val ? `${attr}="${val}"` : null;
|
||||||
|
})
|
||||||
|
.filter(v => v)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
|
return `<style ${attrStr}>${style}</style>`;
|
||||||
|
}
|
||||||
|
|
@ -2,32 +2,31 @@ export function isWindow(obj: any): obj is Window {
|
||||||
return obj !== null && obj !== undefined && obj === obj.window;
|
return obj !== null && obj !== undefined && obj === obj.window;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function getScroll(
|
const getScroll = (target: HTMLElement | Window | Document | null): number => {
|
||||||
target: HTMLElement | Window | Document | null,
|
|
||||||
top: boolean,
|
|
||||||
): number {
|
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const method = top ? 'scrollTop' : 'scrollLeft';
|
|
||||||
let result = 0;
|
let result = 0;
|
||||||
if (isWindow(target)) {
|
if (isWindow(target)) {
|
||||||
result = target[top ? 'scrollY' : 'scrollX'];
|
result = target.pageYOffset;
|
||||||
} else if (target instanceof Document) {
|
} else if (target instanceof Document) {
|
||||||
result = target.documentElement[method];
|
result = target.documentElement.scrollTop;
|
||||||
} else if (target instanceof HTMLElement) {
|
} else if (target instanceof HTMLElement) {
|
||||||
result = target[method];
|
result = target.scrollTop;
|
||||||
} else if (target) {
|
} else if (target) {
|
||||||
// According to the type inference, the `target` is `never` type.
|
// According to the type inference, the `target` is `never` type.
|
||||||
// Since we configured the loose mode type checking, and supports mocking the target with such shape below::
|
// Since we configured the loose mode type checking, and supports mocking the target with such shape below::
|
||||||
// `{ documentElement: { scrollLeft: 200, scrollTop: 400 } }`,
|
// `{ documentElement: { scrollLeft: 200, scrollTop: 400 } }`,
|
||||||
// the program may falls into this branch.
|
// the program may falls into this branch.
|
||||||
// Check the corresponding tests for details. Don't sure what is the real scenario this happens.
|
// Check the corresponding tests for details. Don't sure what is the real scenario this happens.
|
||||||
result = target[method];
|
/* biome-ignore lint/complexity/useLiteralKeys: target is a never type */ /* eslint-disable-next-line dot-notation */
|
||||||
|
result = target['scrollTop'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target && !isWindow(target) && typeof result !== 'number') {
|
if (target && !isWindow(target) && typeof result !== 'number') {
|
||||||
result = ((target.ownerDocument ?? target) as any).documentElement?.[method];
|
result = (target.ownerDocument ?? (target as Document)).documentElement?.scrollTop;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default getScroll;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import type { Ref, ShallowRef } from 'vue';
|
||||||
|
|
||||||
|
import { shallowRef, ref, watch, nextTick, onMounted, onUnmounted } from 'vue';
|
||||||
|
|
||||||
|
function useLayoutEffect(
|
||||||
|
fn: (mount: boolean) => void | VoidFunction,
|
||||||
|
deps?: Ref<any> | Ref<any>[] | ShallowRef<any> | ShallowRef<any>[],
|
||||||
|
) {
|
||||||
|
const firstMount = shallowRef(true);
|
||||||
|
const cleanupFn = ref(null);
|
||||||
|
let stopWatch = null;
|
||||||
|
|
||||||
|
stopWatch = watch(
|
||||||
|
deps,
|
||||||
|
() => {
|
||||||
|
nextTick(() => {
|
||||||
|
if (cleanupFn.value) {
|
||||||
|
cleanupFn.value();
|
||||||
|
}
|
||||||
|
cleanupFn.value = fn(firstMount.value);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ immediate: true, flush: 'post' },
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
firstMount.value = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (cleanupFn.value) {
|
||||||
|
cleanupFn.value();
|
||||||
|
}
|
||||||
|
if (stopWatch) {
|
||||||
|
stopWatch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useLayoutUpdateEffect = (callback, deps) => {
|
||||||
|
useLayoutEffect(firstMount => {
|
||||||
|
if (!firstMount) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
}, deps);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useLayoutEffect;
|
||||||
|
|
@ -14,7 +14,7 @@ interface ScrollToOptions {
|
||||||
export default function scrollTo(y: number, options: ScrollToOptions = {}) {
|
export default function scrollTo(y: number, options: ScrollToOptions = {}) {
|
||||||
const { getContainer = () => window, callback, duration = 450 } = options;
|
const { getContainer = () => window, callback, duration = 450 } = options;
|
||||||
const container = getContainer();
|
const container = getContainer();
|
||||||
const scrollTop = getScroll(container, true);
|
const scrollTop = getScroll(container);
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
|
|
||||||
const frameFunc = () => {
|
const frameFunc = () => {
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,11 @@ import useStyle from './style';
|
||||||
function getDefaultTarget() {
|
function getDefaultTarget() {
|
||||||
return typeof window !== 'undefined' ? window : null;
|
return typeof window !== 'undefined' ? window : null;
|
||||||
}
|
}
|
||||||
enum AffixStatus {
|
const AFFIX_STATUS_NONE = 0;
|
||||||
None,
|
const AFFIX_STATUS_PREPARE = 1;
|
||||||
Prepare,
|
|
||||||
}
|
type AffixStatus = typeof AFFIX_STATUS_NONE | typeof AFFIX_STATUS_PREPARE;
|
||||||
|
|
||||||
export interface AffixState {
|
export interface AffixState {
|
||||||
affixStyle?: CSSProperties;
|
affixStyle?: CSSProperties;
|
||||||
placeholderStyle?: CSSProperties;
|
placeholderStyle?: CSSProperties;
|
||||||
|
|
@ -82,7 +83,7 @@ const Affix = defineComponent({
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
affixStyle: undefined,
|
affixStyle: undefined,
|
||||||
placeholderStyle: undefined,
|
placeholderStyle: undefined,
|
||||||
status: AffixStatus.None,
|
status: AFFIX_STATUS_NONE,
|
||||||
lastAffix: false,
|
lastAffix: false,
|
||||||
prevTarget: null,
|
prevTarget: null,
|
||||||
timeout: null,
|
timeout: null,
|
||||||
|
|
@ -98,7 +99,12 @@ const Affix = defineComponent({
|
||||||
const measure = () => {
|
const measure = () => {
|
||||||
const { status, lastAffix } = state;
|
const { status, lastAffix } = state;
|
||||||
const { target } = props;
|
const { target } = props;
|
||||||
if (status !== AffixStatus.Prepare || !fixedNode.value || !placeholderNode.value || !target) {
|
if (
|
||||||
|
status !== AFFIX_STATUS_PREPARE ||
|
||||||
|
!fixedNode.value ||
|
||||||
|
!placeholderNode.value ||
|
||||||
|
!target
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,7 +114,7 @@ const Affix = defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
const newState = {
|
const newState = {
|
||||||
status: AffixStatus.None,
|
status: AFFIX_STATUS_NONE,
|
||||||
} as AffixState;
|
} as AffixState;
|
||||||
const placeholderRect = getTargetRect(placeholderNode.value as HTMLElement);
|
const placeholderRect = getTargetRect(placeholderNode.value as HTMLElement);
|
||||||
|
|
||||||
|
|
@ -172,7 +178,7 @@ const Affix = defineComponent({
|
||||||
};
|
};
|
||||||
const prepareMeasure = () => {
|
const prepareMeasure = () => {
|
||||||
Object.assign(state, {
|
Object.assign(state, {
|
||||||
status: AffixStatus.Prepare,
|
status: AFFIX_STATUS_PREPARE,
|
||||||
affixStyle: undefined,
|
affixStyle: undefined,
|
||||||
placeholderStyle: undefined,
|
placeholderStyle: undefined,
|
||||||
});
|
});
|
||||||
|
|
@ -253,12 +259,13 @@ const Affix = defineComponent({
|
||||||
});
|
});
|
||||||
|
|
||||||
const { prefixCls } = useConfigInject('affix', props);
|
const { prefixCls } = useConfigInject('affix', props);
|
||||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
const [wrapSSR, hashId, cssVarCls] = useStyle(prefixCls);
|
||||||
return () => {
|
return () => {
|
||||||
const { affixStyle, placeholderStyle, status } = state;
|
const { affixStyle, placeholderStyle, status } = state;
|
||||||
const className = classNames({
|
const className = classNames({
|
||||||
[prefixCls.value]: affixStyle,
|
[prefixCls.value]: affixStyle,
|
||||||
[hashId.value]: true,
|
[hashId.value]: true,
|
||||||
|
[cssVarCls.value]: true,
|
||||||
});
|
});
|
||||||
const restProps = omit(props, [
|
const restProps = omit(props, [
|
||||||
'prefixCls',
|
'prefixCls',
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,21 @@
|
||||||
import type { CSSObject } from '../../_util/cssinjs';
|
import type { CSSObject } from '../../_util/cssinjs';
|
||||||
import type { FullToken, GenerateStyle } from '../../theme/internal';
|
import { FullToken, GenerateStyle, genStyleHooks, GetDefaultToken } from '../../theme/internal';
|
||||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
|
||||||
|
export interface ComponentToken {
|
||||||
|
/**
|
||||||
|
* @desc 弹出层的 z-index
|
||||||
|
* @descEN z-index of popup
|
||||||
|
*/
|
||||||
|
zIndexPopup: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface AffixToken extends FullToken<'Affix'> {
|
interface AffixToken extends FullToken<'Affix'> {
|
||||||
zIndexPopup: number;
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================== Shared ==============================
|
// ============================== Shared ==============================
|
||||||
const genSharedAffixStyle: GenerateStyle<AffixToken> = (token): CSSObject => {
|
const genSharedAffixStyle: GenerateStyle<AffixToken> = (token): CSSObject => {
|
||||||
const { componentCls } = token;
|
const { componentCls } = token;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[componentCls]: {
|
[componentCls]: {
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
|
|
@ -18,10 +24,9 @@ const genSharedAffixStyle: GenerateStyle<AffixToken> = (token): CSSObject => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================== Export ==============================
|
export const prepareComponentToken: GetDefaultToken<'Affix'> = token => ({
|
||||||
export default genComponentStyleHook('Affix', token => {
|
zIndexPopup: token.zIndexBase + 10,
|
||||||
const affixToken = mergeToken<AffixToken>(token, {
|
|
||||||
zIndexPopup: token.zIndexBase + 10,
|
|
||||||
});
|
|
||||||
return [genSharedAffixStyle(affixToken)];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ============================== Export ==============================
|
||||||
|
export default genStyleHooks('Affix', genSharedAffixStyle, prepareComponentToken);
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,11 @@ export function getTargetRect(target: BindElement): DOMRect {
|
||||||
: ({ top: 0, bottom: window.innerHeight } as DOMRect);
|
: ({ top: 0, bottom: window.innerHeight } as DOMRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFixedTop(placeholderRect: DOMRect, targetRect: DOMRect, offsetTop: number) {
|
export function getFixedTop(placeholderRect: DOMRect, targetRect: DOMRect, offsetTop?: number) {
|
||||||
if (offsetTop !== undefined && targetRect.top > placeholderRect.top - offsetTop) {
|
if (
|
||||||
|
offsetTop !== undefined &&
|
||||||
|
Math.round(targetRect.top) > Math.round(placeholderRect.top) - offsetTop
|
||||||
|
) {
|
||||||
return `${offsetTop + targetRect.top}px`;
|
return `${offsetTop + targetRect.top}px`;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
@ -19,9 +22,12 @@ export function getFixedTop(placeholderRect: DOMRect, targetRect: DOMRect, offse
|
||||||
export function getFixedBottom(
|
export function getFixedBottom(
|
||||||
placeholderRect: DOMRect,
|
placeholderRect: DOMRect,
|
||||||
targetRect: DOMRect,
|
targetRect: DOMRect,
|
||||||
offsetBottom: number,
|
offsetBottom?: number,
|
||||||
) {
|
) {
|
||||||
if (offsetBottom !== undefined && targetRect.bottom < placeholderRect.bottom + offsetBottom) {
|
if (
|
||||||
|
offsetBottom !== undefined &&
|
||||||
|
Math.round(targetRect.bottom) < Math.round(placeholderRect.bottom) + offsetBottom
|
||||||
|
) {
|
||||||
const targetBottomOffset = window.innerHeight - targetRect.bottom;
|
const targetBottomOffset = window.innerHeight - targetRect.bottom;
|
||||||
return `${offsetBottom + targetBottomOffset}px`;
|
return `${offsetBottom + targetBottomOffset}px`;
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +35,7 @@ export function getFixedBottom(
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== Observer ========================
|
// ======================== Observer ========================
|
||||||
const TRIGGER_EVENTS = [
|
const TRIGGER_EVENTS: (keyof WindowEventMap)[] = [
|
||||||
'resize',
|
'resize',
|
||||||
'scroll',
|
'scroll',
|
||||||
'touchstart',
|
'touchstart',
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ const Alert = defineComponent({
|
||||||
props: alertProps(),
|
props: alertProps(),
|
||||||
setup(props, { slots, emit, attrs, expose }) {
|
setup(props, { slots, emit, attrs, expose }) {
|
||||||
const { prefixCls, direction } = useConfigInject('alert', props);
|
const { prefixCls, direction } = useConfigInject('alert', props);
|
||||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
const [wrapSSR, hashId, cssVarCls] = useStyle(prefixCls);
|
||||||
const closing = shallowRef(false);
|
const closing = shallowRef(false);
|
||||||
const closed = shallowRef(false);
|
const closed = shallowRef(false);
|
||||||
const alertNode = shallowRef();
|
const alertNode = shallowRef();
|
||||||
|
|
@ -134,6 +134,7 @@ const Alert = defineComponent({
|
||||||
[`${prefixClsValue}-closable`]: closable,
|
[`${prefixClsValue}-closable`]: closable,
|
||||||
[`${prefixClsValue}-rtl`]: direction.value === 'rtl',
|
[`${prefixClsValue}-rtl`]: direction.value === 'rtl',
|
||||||
[hashId.value]: true,
|
[hashId.value]: true,
|
||||||
|
[cssVarCls.value]: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const closeIcon = closable ? (
|
const closeIcon = closable ? (
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,29 @@
|
||||||
import type { CSSInterpolation, CSSObject } from '../../_util/cssinjs';
|
import { CSSObject, unit } from '../../_util/cssinjs';
|
||||||
import type { FullToken, GenerateStyle } from '../../theme/internal';
|
import { FullToken, GenerateStyle, genStyleHooks, GetDefaultToken } from '../../theme/internal';
|
||||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
|
||||||
import { resetComponent } from '../../style';
|
import { resetComponent } from '../../style';
|
||||||
|
import { CSSProperties } from 'vue';
|
||||||
|
|
||||||
export interface ComponentToken {}
|
export interface ComponentToken {
|
||||||
|
// Component token here
|
||||||
|
/**
|
||||||
|
* @desc 默认内间距
|
||||||
|
* @descEN Default padding
|
||||||
|
*/
|
||||||
|
defaultPadding: CSSProperties['padding'];
|
||||||
|
/**
|
||||||
|
* @desc 带有描述的内间距
|
||||||
|
* @descEN Padding with description
|
||||||
|
*/
|
||||||
|
withDescriptionPadding: CSSProperties['padding'];
|
||||||
|
/**
|
||||||
|
* @desc 带有描述时的图标尺寸
|
||||||
|
* @descEN Icon size with description
|
||||||
|
*/
|
||||||
|
withDescriptionIconSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
type AlertToken = FullToken<'Alert'> & {
|
type AlertToken = FullToken<'Alert'> & {
|
||||||
alertIconSizeLG: number;
|
// Custom token here
|
||||||
alertPaddingHorizontal: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const genAlertTypeStyle = (
|
const genAlertTypeStyle = (
|
||||||
|
|
@ -17,8 +33,8 @@ const genAlertTypeStyle = (
|
||||||
token: AlertToken,
|
token: AlertToken,
|
||||||
alertCls: string,
|
alertCls: string,
|
||||||
): CSSObject => ({
|
): CSSObject => ({
|
||||||
backgroundColor: bgColor,
|
background: bgColor,
|
||||||
border: `${token.lineWidth}px ${token.lineType} ${borderColor}`,
|
border: `${unit(token.lineWidth)} ${token.lineType} ${borderColor}`,
|
||||||
[`${alertCls}-icon`]: {
|
[`${alertCls}-icon`]: {
|
||||||
color: iconColor,
|
color: iconColor,
|
||||||
},
|
},
|
||||||
|
|
@ -35,12 +51,11 @@ export const genBaseStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSO
|
||||||
lineHeight,
|
lineHeight,
|
||||||
borderRadiusLG: borderRadius,
|
borderRadiusLG: borderRadius,
|
||||||
motionEaseInOutCirc,
|
motionEaseInOutCirc,
|
||||||
alertIconSizeLG,
|
withDescriptionIconSize,
|
||||||
colorText,
|
colorText,
|
||||||
paddingContentVerticalSM,
|
colorTextHeading,
|
||||||
alertPaddingHorizontal,
|
withDescriptionPadding,
|
||||||
paddingMD,
|
defaultPadding,
|
||||||
paddingContentHorizontalLG,
|
|
||||||
} = token;
|
} = token;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -49,7 +64,7 @@ export const genBaseStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSO
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
padding: `${paddingContentVerticalSM}px ${alertPaddingHorizontal}px`, // Fixed horizontal padding here.
|
padding: defaultPadding,
|
||||||
wordWrap: 'break-word',
|
wordWrap: 'break-word',
|
||||||
borderRadius,
|
borderRadius,
|
||||||
|
|
||||||
|
|
@ -67,14 +82,14 @@ export const genBaseStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSO
|
||||||
lineHeight: 0,
|
lineHeight: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
[`&-description`]: {
|
'&-description': {
|
||||||
display: 'none',
|
display: 'none',
|
||||||
fontSize,
|
fontSize,
|
||||||
lineHeight,
|
lineHeight,
|
||||||
},
|
},
|
||||||
|
|
||||||
'&-message': {
|
'&-message': {
|
||||||
color: colorText,
|
color: colorTextHeading,
|
||||||
},
|
},
|
||||||
|
|
||||||
[`&${componentCls}-motion-leave`]: {
|
[`&${componentCls}-motion-leave`]: {
|
||||||
|
|
@ -96,24 +111,23 @@ export const genBaseStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSO
|
||||||
|
|
||||||
[`${componentCls}-with-description`]: {
|
[`${componentCls}-with-description`]: {
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
paddingInline: paddingContentHorizontalLG,
|
padding: withDescriptionPadding,
|
||||||
paddingBlock: paddingMD,
|
|
||||||
|
|
||||||
[`${componentCls}-icon`]: {
|
[`${componentCls}-icon`]: {
|
||||||
marginInlineEnd: marginSM,
|
marginInlineEnd: marginSM,
|
||||||
fontSize: alertIconSizeLG,
|
fontSize: withDescriptionIconSize,
|
||||||
lineHeight: 0,
|
lineHeight: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
[`${componentCls}-message`]: {
|
[`${componentCls}-message`]: {
|
||||||
display: 'block',
|
display: 'block',
|
||||||
marginBottom: marginXS,
|
marginBottom: marginXS,
|
||||||
color: colorText,
|
color: colorTextHeading,
|
||||||
fontSize: fontSizeLG,
|
fontSize: fontSizeLG,
|
||||||
},
|
},
|
||||||
|
|
||||||
[`${componentCls}-description`]: {
|
[`${componentCls}-description`]: {
|
||||||
display: 'block',
|
display: 'block',
|
||||||
|
color: colorText,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -187,7 +201,7 @@ export const genActionStyle: GenerateStyle<AlertToken> = (token: AlertToken): CS
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[componentCls]: {
|
[componentCls]: {
|
||||||
[`&-action`]: {
|
'&-action': {
|
||||||
marginInlineStart: marginXS,
|
marginInlineStart: marginXS,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -196,7 +210,7 @@ export const genActionStyle: GenerateStyle<AlertToken> = (token: AlertToken): CS
|
||||||
padding: 0,
|
padding: 0,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
fontSize: fontSizeIcon,
|
fontSize: fontSizeIcon,
|
||||||
lineHeight: `${fontSizeIcon}px`,
|
lineHeight: unit(fontSizeIcon),
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
|
|
@ -222,19 +236,17 @@ export const genActionStyle: GenerateStyle<AlertToken> = (token: AlertToken): CS
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const genAlertStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSInterpolation => [
|
export const prepareComponentToken: GetDefaultToken<'Alert'> = token => {
|
||||||
genBaseStyle(token),
|
const paddingHorizontal = 12; // Fixed value here.
|
||||||
genTypeStyle(token),
|
return {
|
||||||
genActionStyle(token),
|
withDescriptionIconSize: token.fontSizeHeading3,
|
||||||
];
|
defaultPadding: `${token.paddingContentVerticalSM}px ${paddingHorizontal}px`,
|
||||||
|
withDescriptionPadding: `${token.paddingMD}px ${token.paddingContentHorizontalLG}px`,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default genComponentStyleHook('Alert', token => {
|
export default genStyleHooks(
|
||||||
const { fontSizeHeading3 } = token;
|
'Alert',
|
||||||
|
token => [genBaseStyle(token), genTypeStyle(token), genActionStyle(token)],
|
||||||
const alertToken = mergeToken<AlertToken>(token, {
|
prepareComponentToken,
|
||||||
alertIconSizeLG: fontSizeHeading3,
|
);
|
||||||
alertPaddingHorizontal: 12, // Fixed value here.
|
|
||||||
});
|
|
||||||
|
|
||||||
return [genAlertStyle(alertToken)];
|
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import AnchorLink from './AnchorLink';
|
||||||
import PropTypes from '../_util/vue-types';
|
import PropTypes from '../_util/vue-types';
|
||||||
import devWarning from '../vc-util/devWarning';
|
import devWarning from '../vc-util/devWarning';
|
||||||
import { arrayType } from '../_util/type';
|
import { arrayType } from '../_util/type';
|
||||||
|
import useCSSVarCls from '../config-provider/hooks/useCssVarCls';
|
||||||
|
|
||||||
export type AnchorDirection = 'vertical' | 'horizontal';
|
export type AnchorDirection = 'vertical' | 'horizontal';
|
||||||
|
|
||||||
|
|
@ -39,8 +40,7 @@ function getOffsetTop(element: HTMLElement, container: AnchorContainer): number
|
||||||
|
|
||||||
if (rect.width || rect.height) {
|
if (rect.width || rect.height) {
|
||||||
if (container === window) {
|
if (container === window) {
|
||||||
container = element.ownerDocument!.documentElement!;
|
return rect.top - element.ownerDocument!.documentElement!.clientTop;
|
||||||
return rect.top - container.clientTop;
|
|
||||||
}
|
}
|
||||||
return rect.top - (container as HTMLElement).getBoundingClientRect().top;
|
return rect.top - (container as HTMLElement).getBoundingClientRect().top;
|
||||||
}
|
}
|
||||||
|
|
@ -70,6 +70,7 @@ export const anchorProps = () => ({
|
||||||
targetOffset: Number,
|
targetOffset: Number,
|
||||||
items: arrayType<AnchorLinkItemProps[]>(),
|
items: arrayType<AnchorLinkItemProps[]>(),
|
||||||
direction: PropTypes.oneOf(['vertical', 'horizontal'] as AnchorDirection[]).def('vertical'),
|
direction: PropTypes.oneOf(['vertical', 'horizontal'] as AnchorDirection[]).def('vertical'),
|
||||||
|
replace: Boolean,
|
||||||
onChange: Function as PropType<(currentActiveLink: string) => void>,
|
onChange: Function as PropType<(currentActiveLink: string) => void>,
|
||||||
onClick: Function as PropType<(e: MouseEvent, link: { title: any; href: string }) => void>,
|
onClick: Function as PropType<(e: MouseEvent, link: { title: any; href: string }) => void>,
|
||||||
});
|
});
|
||||||
|
|
@ -91,7 +92,7 @@ export default defineComponent({
|
||||||
setup(props, { emit, attrs, slots, expose }) {
|
setup(props, { emit, attrs, slots, expose }) {
|
||||||
const { prefixCls, getTargetContainer, direction } = useConfigInject('anchor', props);
|
const { prefixCls, getTargetContainer, direction } = useConfigInject('anchor', props);
|
||||||
const anchorDirection = computed(() => props.direction ?? 'vertical');
|
const anchorDirection = computed(() => props.direction ?? 'vertical');
|
||||||
|
const rootCls = useCSSVarCls(prefixCls);
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
devWarning(
|
devWarning(
|
||||||
props.items && typeof slots.default !== 'function',
|
props.items && typeof slots.default !== 'function',
|
||||||
|
|
@ -133,7 +134,7 @@ export default defineComponent({
|
||||||
const target = document.getElementById(sharpLinkMatch[1]);
|
const target = document.getElementById(sharpLinkMatch[1]);
|
||||||
if (target) {
|
if (target) {
|
||||||
const top = getOffsetTop(target, container);
|
const top = getOffsetTop(target, container);
|
||||||
if (top < offsetTop + bounds) {
|
if (top <= offsetTop + bounds) {
|
||||||
linkSections.push({
|
linkSections.push({
|
||||||
link,
|
link,
|
||||||
top,
|
top,
|
||||||
|
|
@ -170,7 +171,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = getContainer.value();
|
const container = getContainer.value();
|
||||||
const scrollTop = getScroll(container, true);
|
const scrollTop = getScroll(container);
|
||||||
const eleOffsetTop = getOffsetTop(targetElement, container);
|
const eleOffsetTop = getOffsetTop(targetElement, container);
|
||||||
let y = scrollTop + eleOffsetTop;
|
let y = scrollTop + eleOffsetTop;
|
||||||
y -= targetOffset !== undefined ? targetOffset : offsetTop || 0;
|
y -= targetOffset !== undefined ? targetOffset : offsetTop || 0;
|
||||||
|
|
@ -277,6 +278,7 @@ export default defineComponent({
|
||||||
title={title}
|
title={title}
|
||||||
customTitleProps={option}
|
customTitleProps={option}
|
||||||
v-slots={{ customTitle: slots.customTitle }}
|
v-slots={{ customTitle: slots.customTitle }}
|
||||||
|
replace={props.replace}
|
||||||
>
|
>
|
||||||
{anchorDirection.value === 'vertical' ? createNestedLink(children) : null}
|
{anchorDirection.value === 'vertical' ? createNestedLink(children) : null}
|
||||||
</AnchorLink>
|
</AnchorLink>
|
||||||
|
|
@ -284,7 +286,7 @@ export default defineComponent({
|
||||||
})
|
})
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
const [wrapSSR, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const { offsetTop, affix, showInkInFixed } = props;
|
const { offsetTop, affix, showInkInFixed } = props;
|
||||||
|
|
@ -296,6 +298,8 @@ export default defineComponent({
|
||||||
const wrapperClass = classNames(hashId.value, props.wrapperClass, `${pre}-wrapper`, {
|
const wrapperClass = classNames(hashId.value, props.wrapperClass, `${pre}-wrapper`, {
|
||||||
[`${pre}-wrapper-horizontal`]: anchorDirection.value === 'horizontal',
|
[`${pre}-wrapper-horizontal`]: anchorDirection.value === 'horizontal',
|
||||||
[`${pre}-rtl`]: direction.value === 'rtl',
|
[`${pre}-rtl`]: direction.value === 'rtl',
|
||||||
|
[rootCls.value]: true,
|
||||||
|
[cssVarCls.value]: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const anchorClass = classNames(pre, {
|
const anchorClass = classNames(pre, {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ export const anchorLinkProps = () => ({
|
||||||
href: String,
|
href: String,
|
||||||
title: anyType<VueNode | ((item: any) => VueNode)>(),
|
title: anyType<VueNode | ((item: any) => VueNode)>(),
|
||||||
target: String,
|
target: String,
|
||||||
|
replace: Boolean,
|
||||||
/* private use */
|
/* private use */
|
||||||
customTitleProps: objectType<AnchorLinkItemProps>(),
|
customTitleProps: objectType<AnchorLinkItemProps>(),
|
||||||
});
|
});
|
||||||
|
|
@ -53,6 +54,10 @@ export default defineComponent({
|
||||||
const { href } = props;
|
const { href } = props;
|
||||||
contextHandleClick(e, { title: mergedTitle, href });
|
contextHandleClick(e, { title: mergedTitle, href });
|
||||||
scrollTo(href);
|
scrollTo(href);
|
||||||
|
if (props.replace) {
|
||||||
|
e.preventDefault();
|
||||||
|
window.location.replace(href);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,55 @@
|
||||||
import type { CSSObject } from '../../_util/cssinjs';
|
import { unit } from '../../_util/cssinjs';
|
||||||
import type { FullToken, GenerateStyle } from '../../theme/internal';
|
import {
|
||||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
FullToken,
|
||||||
|
GenerateStyle,
|
||||||
|
genStyleHooks,
|
||||||
|
GetDefaultToken,
|
||||||
|
mergeToken,
|
||||||
|
} from '../../theme/internal';
|
||||||
import { resetComponent, textEllipsis } from '../../style';
|
import { resetComponent, textEllipsis } from '../../style';
|
||||||
|
|
||||||
export interface ComponentToken {}
|
export interface ComponentToken {
|
||||||
|
/**
|
||||||
|
* @desc 链接纵向内间距
|
||||||
|
* @descEN Vertical padding of link
|
||||||
|
*/
|
||||||
|
linkPaddingBlock: number;
|
||||||
|
/**
|
||||||
|
* @desc 链接横向内间距
|
||||||
|
* @descEN Horizontal padding of link
|
||||||
|
*/
|
||||||
|
linkPaddingInlineStart: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Anchor 组件的 Token
|
||||||
|
* @descEN Token for Anchor component
|
||||||
|
*/
|
||||||
interface AnchorToken extends FullToken<'Anchor'> {
|
interface AnchorToken extends FullToken<'Anchor'> {
|
||||||
|
/**
|
||||||
|
* @desc 容器块偏移量
|
||||||
|
* @descEN Holder block offset
|
||||||
|
*/
|
||||||
holderOffsetBlock: number;
|
holderOffsetBlock: number;
|
||||||
anchorPaddingBlock: number;
|
/**
|
||||||
anchorPaddingBlockSecondary: number;
|
* @desc 次级锚点块内间距
|
||||||
anchorPaddingInline: number;
|
* @descEN Secondary anchor block padding
|
||||||
anchorBallSize: number;
|
*/
|
||||||
anchorTitleBlock: number;
|
anchorPaddingBlockSecondary: number | string;
|
||||||
|
/**
|
||||||
|
* @desc 锚点球大小
|
||||||
|
* @descEN Anchor ball size
|
||||||
|
*/
|
||||||
|
anchorBallSize: number | string;
|
||||||
|
/**
|
||||||
|
* @desc 锚点标题块
|
||||||
|
* @descEN Anchor title block
|
||||||
|
*/
|
||||||
|
anchorTitleBlock: number | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================== Shared ==============================
|
// ============================== Shared ==============================
|
||||||
const genSharedAnchorStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
|
const genSharedAnchorStyle: GenerateStyle<AnchorToken> = token => {
|
||||||
const {
|
const {
|
||||||
componentCls,
|
componentCls,
|
||||||
holderOffsetBlock,
|
holderOffsetBlock,
|
||||||
|
|
@ -24,26 +58,25 @@ const genSharedAnchorStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
|
||||||
colorPrimary,
|
colorPrimary,
|
||||||
lineType,
|
lineType,
|
||||||
colorSplit,
|
colorSplit,
|
||||||
|
calc,
|
||||||
} = token;
|
} = token;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[`${componentCls}-wrapper`]: {
|
[`${componentCls}-wrapper`]: {
|
||||||
marginBlockStart: -holderOffsetBlock,
|
marginBlockStart: calc(holderOffsetBlock).mul(-1).equal(),
|
||||||
paddingBlockStart: holderOffsetBlock,
|
paddingBlockStart: holderOffsetBlock,
|
||||||
|
|
||||||
// delete overflow: auto
|
// delete overflow: auto
|
||||||
// overflow: 'auto',
|
// overflow: 'auto',
|
||||||
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
|
|
||||||
[componentCls]: {
|
[componentCls]: {
|
||||||
...resetComponent(token),
|
...resetComponent(token),
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
paddingInlineStart: lineWidthBold,
|
paddingInlineStart: lineWidthBold,
|
||||||
|
|
||||||
[`${componentCls}-link`]: {
|
[`${componentCls}-link`]: {
|
||||||
paddingBlock: token.anchorPaddingBlock,
|
paddingBlock: token.linkPaddingBlock,
|
||||||
paddingInline: `${token.anchorPaddingInline}px 0`,
|
paddingInline: `${unit(token.linkPaddingInlineStart)} 0`,
|
||||||
|
|
||||||
'&-title': {
|
'&-title': {
|
||||||
...textEllipsis,
|
...textEllipsis,
|
||||||
|
|
@ -73,28 +106,21 @@ const genSharedAnchorStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
|
||||||
[componentCls]: {
|
[componentCls]: {
|
||||||
'&::before': {
|
'&::before': {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: {
|
insetInlineStart: 0,
|
||||||
_skip_check_: true,
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
top: 0,
|
top: 0,
|
||||||
height: '100%',
|
height: '100%',
|
||||||
borderInlineStart: `${lineWidthBold}px ${lineType} ${colorSplit}`,
|
borderInlineStart: `${unit(lineWidthBold)} ${lineType} ${colorSplit}`,
|
||||||
content: '" "',
|
content: '" "',
|
||||||
},
|
},
|
||||||
|
|
||||||
[`${componentCls}-ink`]: {
|
[`${componentCls}-ink`]: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: {
|
insetInlineStart: 0,
|
||||||
_skip_check_: true,
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
display: 'none',
|
display: 'none',
|
||||||
transform: 'translateY(-50%)',
|
transform: 'translateY(-50%)',
|
||||||
transition: `top ${motionDurationSlow} ease-in-out`,
|
transition: `top ${motionDurationSlow} ease-in-out`,
|
||||||
width: lineWidthBold,
|
width: lineWidthBold,
|
||||||
backgroundColor: colorPrimary,
|
backgroundColor: colorPrimary,
|
||||||
|
|
||||||
[`&${componentCls}-ink-visible`]: {
|
[`&${componentCls}-ink-visible`]: {
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
},
|
},
|
||||||
|
|
@ -109,7 +135,7 @@ const genSharedAnchorStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const genSharedAnchorHorizontalStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
|
const genSharedAnchorHorizontalStyle: GenerateStyle<AnchorToken> = token => {
|
||||||
const { componentCls, motionDurationSlow, lineWidthBold, colorPrimary } = token;
|
const { componentCls, motionDurationSlow, lineWidthBold, colorPrimary } = token;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -127,7 +153,7 @@ const genSharedAnchorHorizontalStyle: GenerateStyle<AnchorToken> = (token): CSSO
|
||||||
value: 0,
|
value: 0,
|
||||||
},
|
},
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
borderBottom: `1px ${token.lineType} ${token.colorSplit}`,
|
borderBottom: `${unit(token.lineWidth)} ${token.lineType} ${token.colorSplit}`,
|
||||||
content: '" "',
|
content: '" "',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -157,17 +183,23 @@ const genSharedAnchorHorizontalStyle: GenerateStyle<AnchorToken> = (token): CSSO
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================== Export ==============================
|
export const prepareComponentToken: GetDefaultToken<'Anchor'> = token => ({
|
||||||
export default genComponentStyleHook('Anchor', token => {
|
linkPaddingBlock: token.paddingXXS,
|
||||||
const { fontSize, fontSizeLG, padding, paddingXXS } = token;
|
linkPaddingInlineStart: token.padding,
|
||||||
|
|
||||||
const anchorToken = mergeToken<AnchorToken>(token, {
|
|
||||||
holderOffsetBlock: paddingXXS,
|
|
||||||
anchorPaddingBlock: paddingXXS,
|
|
||||||
anchorPaddingBlockSecondary: paddingXXS / 2,
|
|
||||||
anchorPaddingInline: padding,
|
|
||||||
anchorTitleBlock: (fontSize / 14) * 3,
|
|
||||||
anchorBallSize: fontSizeLG / 2,
|
|
||||||
});
|
|
||||||
return [genSharedAnchorStyle(anchorToken), genSharedAnchorHorizontalStyle(anchorToken)];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ============================== Export ==============================
|
||||||
|
export default genStyleHooks(
|
||||||
|
'Anchor',
|
||||||
|
token => {
|
||||||
|
const { fontSize, fontSizeLG, paddingXXS, calc } = token;
|
||||||
|
const anchorToken = mergeToken<AnchorToken>(token, {
|
||||||
|
holderOffsetBlock: paddingXXS,
|
||||||
|
anchorPaddingBlockSecondary: calc(paddingXXS).div(2).equal(),
|
||||||
|
anchorTitleBlock: calc(fontSize).div(14).mul(3).equal(),
|
||||||
|
anchorBallSize: calc(fontSizeLG).div(2).equal(),
|
||||||
|
});
|
||||||
|
return [genSharedAnchorStyle(anchorToken), genSharedAnchorHorizontalStyle(anchorToken)];
|
||||||
|
},
|
||||||
|
prepareComponentToken,
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import type { FullToken, GenerateStyle } from '../../theme/internal';
|
||||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||||
import { genPresetColor, resetComponent } from '../../style';
|
import { genPresetColor, resetComponent } from '../../style';
|
||||||
|
|
||||||
|
export interface ComponentToken {}
|
||||||
|
|
||||||
interface BadgeToken extends FullToken<'Badge'> {
|
interface BadgeToken extends FullToken<'Badge'> {
|
||||||
badgeFontHeight: number;
|
badgeFontHeight: number;
|
||||||
badgeZIndex: number | string;
|
badgeZIndex: number | string;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import type { FullToken, GenerateStyle } from '../../theme/internal';
|
||||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||||
import { genFocusStyle, resetComponent } from '../../style';
|
import { genFocusStyle, resetComponent } from '../../style';
|
||||||
|
|
||||||
|
export interface ComponentToken {}
|
||||||
|
|
||||||
interface BreadcrumbToken extends FullToken<'Breadcrumb'> {
|
interface BreadcrumbToken extends FullToken<'Breadcrumb'> {
|
||||||
breadcrumbBaseColor: string;
|
breadcrumbBaseColor: string;
|
||||||
breadcrumbFontSize: number;
|
breadcrumbFontSize: number;
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,11 @@ export default defineComponent({
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
devWarning(!size, 'Button.Group', 'Invalid prop `size`.');
|
devWarning(
|
||||||
|
!size || ['large', 'small', 'middle'].includes(size),
|
||||||
|
'Button.Group',
|
||||||
|
'Invalid prop `size`.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
[`${prefixCls.value}`]: true,
|
[`${prefixCls.value}`]: true,
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ export default defineComponent({
|
||||||
// emits: ['click', 'mousedown'],
|
// emits: ['click', 'mousedown'],
|
||||||
setup(props, { slots, attrs, emit, expose }) {
|
setup(props, { slots, attrs, emit, expose }) {
|
||||||
const { prefixCls, autoInsertSpaceInButton, direction, size } = useConfigInject('btn', props);
|
const { prefixCls, autoInsertSpaceInButton, direction, size } = useConfigInject('btn', props);
|
||||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);
|
||||||
const groupSizeContext = GroupSizeContext.useInject();
|
const groupSizeContext = GroupSizeContext.useInject();
|
||||||
const disabledContext = useInjectDisabled();
|
const disabledContext = useInjectDisabled();
|
||||||
const mergedDisabled = computed(() => props.disabled ?? disabledContext.value);
|
const mergedDisabled = computed(() => props.disabled ?? disabledContext.value);
|
||||||
|
|
@ -95,6 +95,7 @@ export default defineComponent({
|
||||||
compactItemClassnames.value,
|
compactItemClassnames.value,
|
||||||
{
|
{
|
||||||
[hashId.value]: true,
|
[hashId.value]: true,
|
||||||
|
[cssVarCls.value]: true,
|
||||||
[`${pre}`]: true,
|
[`${pre}`]: true,
|
||||||
[`${pre}-${shape}`]: shape !== 'default' && shape,
|
[`${pre}-${shape}`]: shape !== 'default' && shape,
|
||||||
[`${pre}-${type}`]: type,
|
[`${pre}-${type}`]: type,
|
||||||
|
|
@ -216,7 +217,7 @@ export default defineComponent({
|
||||||
);
|
);
|
||||||
|
|
||||||
if (href !== undefined) {
|
if (href !== undefined) {
|
||||||
return wrapSSR(
|
return wrapCSSVar(
|
||||||
<a {...buttonProps} href={href} target={target} ref={buttonNodeRef}>
|
<a {...buttonProps} href={href} target={target} ref={buttonNodeRef}>
|
||||||
{iconNode}
|
{iconNode}
|
||||||
{kids}
|
{kids}
|
||||||
|
|
@ -239,7 +240,7 @@ export default defineComponent({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return wrapSSR(buttonNode);
|
return wrapCSSVar(buttonNode);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Style as inline component
|
||||||
|
import type { ButtonToken } from './token';
|
||||||
|
import { prepareComponentToken, prepareToken } from './token';
|
||||||
|
import { genCompactItemStyle } from '../../style/compact-item';
|
||||||
|
import { genCompactItemVerticalStyle } from '../../style/compact-item-vertical';
|
||||||
|
import type { GenerateStyle } from '../../theme/internal';
|
||||||
|
import { genSubStyleComponent } from '../../theme/internal';
|
||||||
|
import type { CSSObject } from '../../_util/cssinjs';
|
||||||
|
import { unit } from '../../_util/cssinjs';
|
||||||
|
|
||||||
|
const genButtonCompactStyle: GenerateStyle<ButtonToken, CSSObject> = token => {
|
||||||
|
const { componentCls, calc } = token;
|
||||||
|
|
||||||
|
return {
|
||||||
|
[componentCls]: {
|
||||||
|
// Special styles for Primary Button
|
||||||
|
[`&-compact-item${componentCls}-primary`]: {
|
||||||
|
[`&:not([disabled]) + ${componentCls}-compact-item${componentCls}-primary:not([disabled])`]:
|
||||||
|
{
|
||||||
|
position: 'relative',
|
||||||
|
|
||||||
|
'&:before': {
|
||||||
|
position: 'absolute',
|
||||||
|
top: calc(token.lineWidth).mul(-1).equal(),
|
||||||
|
insetInlineStart: calc(token.lineWidth).mul(-1).equal(),
|
||||||
|
display: 'inline-block',
|
||||||
|
width: token.lineWidth,
|
||||||
|
height: `calc(100% + ${unit(token.lineWidth)} * 2)`,
|
||||||
|
backgroundColor: token.colorPrimaryHover,
|
||||||
|
content: '""',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Special styles for Primary Button
|
||||||
|
'&-compact-vertical-item': {
|
||||||
|
[`&${componentCls}-primary`]: {
|
||||||
|
[`&:not([disabled]) + ${componentCls}-compact-vertical-item${componentCls}-primary:not([disabled])`]:
|
||||||
|
{
|
||||||
|
position: 'relative',
|
||||||
|
|
||||||
|
'&:before': {
|
||||||
|
position: 'absolute',
|
||||||
|
top: calc(token.lineWidth).mul(-1).equal(),
|
||||||
|
insetInlineStart: calc(token.lineWidth).mul(-1).equal(),
|
||||||
|
display: 'inline-block',
|
||||||
|
width: `calc(100% + ${unit(token.lineWidth)} * 2)`,
|
||||||
|
height: token.lineWidth,
|
||||||
|
backgroundColor: token.colorPrimaryHover,
|
||||||
|
content: '""',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================== Export ==============================
|
||||||
|
export default genSubStyleComponent(
|
||||||
|
['Button', 'compact'],
|
||||||
|
token => {
|
||||||
|
const buttonToken = prepareToken(token);
|
||||||
|
|
||||||
|
return [
|
||||||
|
// Space Compact
|
||||||
|
genCompactItemStyle(buttonToken),
|
||||||
|
genCompactItemVerticalStyle(buttonToken),
|
||||||
|
genButtonCompactStyle(buttonToken),
|
||||||
|
] as CSSObject[];
|
||||||
|
},
|
||||||
|
prepareComponentToken,
|
||||||
|
);
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import type { ButtonToken } from '.';
|
import type { CSSObject } from '../../_util/cssinjs';
|
||||||
|
import type { ButtonToken } from './token';
|
||||||
import type { GenerateStyle } from '../../theme/internal';
|
import type { GenerateStyle } from '../../theme/internal';
|
||||||
|
|
||||||
const genButtonBorderStyle = (buttonTypeCls: string, borderColor: string) => ({
|
const genButtonBorderStyle = (buttonTypeCls: string, borderColor: string) => ({
|
||||||
|
|
@ -22,8 +23,8 @@ const genButtonBorderStyle = (buttonTypeCls: string, borderColor: string) => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const genGroupStyle: GenerateStyle<ButtonToken> = token => {
|
const genGroupStyle: GenerateStyle<ButtonToken, CSSObject> = token => {
|
||||||
const { componentCls, fontSize, lineWidth, colorPrimaryHover, colorErrorHover } = token;
|
const { componentCls, fontSize, lineWidth, groupBorderColor, colorErrorHover } = token;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[`${componentCls}-group`]: [
|
[`${componentCls}-group`]: [
|
||||||
|
|
@ -41,7 +42,7 @@ const genGroupStyle: GenerateStyle<ButtonToken> = token => {
|
||||||
},
|
},
|
||||||
|
|
||||||
'&:not(:first-child)': {
|
'&:not(:first-child)': {
|
||||||
marginInlineStart: -lineWidth,
|
marginInlineStart: token.calc(lineWidth).mul(-1).equal(),
|
||||||
|
|
||||||
[`&, & > ${componentCls}`]: {
|
[`&, & > ${componentCls}`]: {
|
||||||
borderStartStartRadius: 0,
|
borderStartStartRadius: 0,
|
||||||
|
|
@ -71,7 +72,7 @@ const genGroupStyle: GenerateStyle<ButtonToken> = token => {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Border Color
|
// Border Color
|
||||||
genButtonBorderStyle(`${componentCls}-primary`, colorPrimaryHover),
|
genButtonBorderStyle(`${componentCls}-primary`, groupBorderColor),
|
||||||
genButtonBorderStyle(`${componentCls}-danger`, colorErrorHover),
|
genButtonBorderStyle(`${componentCls}-danger`, colorErrorHover),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,59 @@
|
||||||
import type { CSSInterpolation, CSSObject } from '../../_util/cssinjs';
|
import type { CSSObject } from '../../_util/cssinjs';
|
||||||
import type { FullToken, GenerateStyle } from '../../theme/internal';
|
import { unit } from '../../_util/cssinjs';
|
||||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
|
||||||
import genGroupStyle from './group';
|
|
||||||
import { genFocusStyle } from '../../style';
|
import { genFocusStyle } from '../../style';
|
||||||
import { genCompactItemStyle } from '../../style/compact-item';
|
import type { GenerateStyle } from '../../theme/internal';
|
||||||
import { genCompactItemVerticalStyle } from '../../style/compact-item-vertical';
|
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
||||||
|
import genGroupStyle from './group';
|
||||||
|
import type { ButtonToken, ComponentToken } from './token';
|
||||||
|
import { prepareComponentToken, prepareToken } from './token';
|
||||||
|
|
||||||
/** Component only token. Which will handle additional calculation of alias token */
|
export type { ComponentToken };
|
||||||
export interface ComponentToken {}
|
|
||||||
|
|
||||||
export interface ButtonToken extends FullToken<'Button'> {
|
|
||||||
// FIXME: should be removed
|
|
||||||
colorOutlineDefault: string;
|
|
||||||
buttonPaddingHorizontal: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================== Shared ==============================
|
// ============================== Shared ==============================
|
||||||
const genSharedButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token): CSSObject => {
|
const genSharedButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token): CSSObject => {
|
||||||
const { componentCls, iconCls } = token;
|
const { componentCls, iconCls, fontWeight } = token;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[componentCls]: {
|
[componentCls]: {
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
fontWeight: 400,
|
fontWeight,
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
backgroundImage: 'none',
|
backgroundImage: 'none',
|
||||||
backgroundColor: 'transparent',
|
background: 'transparent',
|
||||||
border: `${token.lineWidth}px ${token.lineType} transparent`,
|
border: `${unit(token.lineWidth)} ${token.lineType} transparent`,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
transition: `all ${token.motionDurationMid} ${token.motionEaseInOut}`,
|
transition: `all ${token.motionDurationMid} ${token.motionEaseInOut}`,
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
touchAction: 'manipulation',
|
touchAction: 'manipulation',
|
||||||
lineHeight: token.lineHeight,
|
|
||||||
color: token.colorText,
|
color: token.colorText,
|
||||||
|
|
||||||
|
'&:disabled > *': {
|
||||||
|
pointerEvents: 'none',
|
||||||
|
},
|
||||||
|
|
||||||
'> span': {
|
'> span': {
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[`${componentCls}-icon`]: {
|
||||||
|
lineHeight: 0,
|
||||||
|
},
|
||||||
|
|
||||||
// Leave a space between icon and text.
|
// Leave a space between icon and text.
|
||||||
[`> ${iconCls} + span, > span + ${iconCls}`]: {
|
[`> ${iconCls} + span, > span + ${iconCls}`]: {
|
||||||
marginInlineStart: token.marginXS,
|
marginInlineStart: token.marginXS,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[`&:not(${componentCls}-icon-only) > ${componentCls}-icon`]: {
|
||||||
|
[`&${componentCls}-loading-icon, &:not(:last-child)`]: {
|
||||||
|
marginInlineEnd: token.marginXS,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
'> a': {
|
'> a': {
|
||||||
color: 'currentColor',
|
color: 'currentColor',
|
||||||
},
|
},
|
||||||
|
|
@ -54,54 +62,29 @@ const genSharedButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token): CSS
|
||||||
...genFocusStyle(token),
|
...genFocusStyle(token),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[`&${componentCls}-two-chinese-chars::first-letter`]: {
|
||||||
|
letterSpacing: '0.34em',
|
||||||
|
},
|
||||||
|
|
||||||
|
[`&${componentCls}-two-chinese-chars > *:not(${iconCls})`]: {
|
||||||
|
marginInlineEnd: '-0.34em',
|
||||||
|
letterSpacing: '0.34em',
|
||||||
|
},
|
||||||
|
|
||||||
// make `btn-icon-only` not too narrow
|
// make `btn-icon-only` not too narrow
|
||||||
[`&-icon-only${componentCls}-compact-item`]: {
|
[`&-icon-only${componentCls}-compact-item`]: {
|
||||||
flex: 'none',
|
flex: 'none',
|
||||||
},
|
},
|
||||||
// Special styles for Primary Button
|
|
||||||
[`&-compact-item${componentCls}-primary`]: {
|
|
||||||
[`&:not([disabled]) + ${componentCls}-compact-item${componentCls}-primary:not([disabled])`]:
|
|
||||||
{
|
|
||||||
position: 'relative',
|
|
||||||
|
|
||||||
'&:before': {
|
|
||||||
position: 'absolute',
|
|
||||||
top: -token.lineWidth,
|
|
||||||
insetInlineStart: -token.lineWidth,
|
|
||||||
display: 'inline-block',
|
|
||||||
width: token.lineWidth,
|
|
||||||
height: `calc(100% + ${token.lineWidth * 2}px)`,
|
|
||||||
backgroundColor: token.colorPrimaryHover,
|
|
||||||
content: '""',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Special styles for Primary Button
|
|
||||||
'&-compact-vertical-item': {
|
|
||||||
[`&${componentCls}-primary`]: {
|
|
||||||
[`&:not([disabled]) + ${componentCls}-compact-vertical-item${componentCls}-primary:not([disabled])`]:
|
|
||||||
{
|
|
||||||
position: 'relative',
|
|
||||||
|
|
||||||
'&:before': {
|
|
||||||
position: 'absolute',
|
|
||||||
top: -token.lineWidth,
|
|
||||||
insetInlineStart: -token.lineWidth,
|
|
||||||
display: 'inline-block',
|
|
||||||
width: `calc(100% + ${token.lineWidth * 2}px)`,
|
|
||||||
height: token.lineWidth,
|
|
||||||
backgroundColor: token.colorPrimaryHover,
|
|
||||||
content: '""',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
} as CSSObject;
|
||||||
};
|
};
|
||||||
|
|
||||||
const genHoverActiveButtonStyle = (hoverStyle: CSSObject, activeStyle: CSSObject): CSSObject => ({
|
const genHoverActiveButtonStyle = (
|
||||||
'&:not(:disabled)': {
|
btnCls: string,
|
||||||
|
hoverStyle: CSSObject,
|
||||||
|
activeStyle: CSSObject,
|
||||||
|
): CSSObject => ({
|
||||||
|
[`&:not(:disabled):not(${btnCls}-disabled)`]: {
|
||||||
'&:hover': hoverStyle,
|
'&:hover': hoverStyle,
|
||||||
'&:active': activeStyle,
|
'&:active': activeStyle,
|
||||||
},
|
},
|
||||||
|
|
@ -117,21 +100,22 @@ const genCircleButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
|
|
||||||
const genRoundButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
const genRoundButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
borderRadius: token.controlHeight,
|
borderRadius: token.controlHeight,
|
||||||
paddingInlineStart: token.controlHeight / 2,
|
paddingInlineStart: token.calc(token.controlHeight).div(2).equal(),
|
||||||
paddingInlineEnd: token.controlHeight / 2,
|
paddingInlineEnd: token.calc(token.controlHeight).div(2).equal(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// =============================== Type ===============================
|
// =============================== Type ===============================
|
||||||
const genDisabledStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
const genDisabledStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
cursor: 'not-allowed',
|
cursor: 'not-allowed',
|
||||||
borderColor: token.colorBorder,
|
borderColor: token.borderColorDisabled,
|
||||||
color: token.colorTextDisabled,
|
color: token.colorTextDisabled,
|
||||||
backgroundColor: token.colorBgContainerDisabled,
|
background: token.colorBgContainerDisabled,
|
||||||
boxShadow: 'none',
|
boxShadow: 'none',
|
||||||
});
|
});
|
||||||
|
|
||||||
const genGhostButtonStyle = (
|
const genGhostButtonStyle = (
|
||||||
btnCls: string,
|
btnCls: string,
|
||||||
|
background: string,
|
||||||
textColor: string | false,
|
textColor: string | false,
|
||||||
borderColor: string | false,
|
borderColor: string | false,
|
||||||
textColorDisabled: string | false,
|
textColorDisabled: string | false,
|
||||||
|
|
@ -141,17 +125,18 @@ const genGhostButtonStyle = (
|
||||||
): CSSObject => ({
|
): CSSObject => ({
|
||||||
[`&${btnCls}-background-ghost`]: {
|
[`&${btnCls}-background-ghost`]: {
|
||||||
color: textColor || undefined,
|
color: textColor || undefined,
|
||||||
backgroundColor: 'transparent',
|
background,
|
||||||
borderColor: borderColor || undefined,
|
borderColor: borderColor || undefined,
|
||||||
boxShadow: 'none',
|
boxShadow: 'none',
|
||||||
|
|
||||||
...genHoverActiveButtonStyle(
|
...genHoverActiveButtonStyle(
|
||||||
|
btnCls,
|
||||||
{
|
{
|
||||||
backgroundColor: 'transparent',
|
background,
|
||||||
...hoverStyle,
|
...hoverStyle,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
backgroundColor: 'transparent',
|
background,
|
||||||
...activeStyle,
|
...activeStyle,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -165,7 +150,7 @@ const genGhostButtonStyle = (
|
||||||
});
|
});
|
||||||
|
|
||||||
const genSolidDisabledButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
const genSolidDisabledButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
'&:disabled': {
|
[`&:disabled, &${token.componentCls}-disabled`]: {
|
||||||
...genDisabledStyle(token),
|
...genDisabledStyle(token),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -175,7 +160,7 @@ const genSolidButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const genPureDisabledButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
const genPureDisabledButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
'&:disabled': {
|
[`&:disabled, &${token.componentCls}-disabled`]: {
|
||||||
cursor: 'not-allowed',
|
cursor: 'not-allowed',
|
||||||
color: token.colorTextDisabled,
|
color: token.colorTextDisabled,
|
||||||
},
|
},
|
||||||
|
|
@ -185,12 +170,14 @@ const genPureDisabledButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token
|
||||||
const genDefaultButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
const genDefaultButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
...genSolidButtonStyle(token),
|
...genSolidButtonStyle(token),
|
||||||
|
|
||||||
backgroundColor: token.colorBgContainer,
|
background: token.defaultBg,
|
||||||
borderColor: token.colorBorder,
|
borderColor: token.defaultBorderColor,
|
||||||
|
color: token.defaultColor,
|
||||||
|
|
||||||
boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlTmpOutline}`,
|
boxShadow: token.defaultShadow,
|
||||||
|
|
||||||
...genHoverActiveButtonStyle(
|
...genHoverActiveButtonStyle(
|
||||||
|
token.componentCls,
|
||||||
{
|
{
|
||||||
color: token.colorPrimaryHover,
|
color: token.colorPrimaryHover,
|
||||||
borderColor: token.colorPrimaryHover,
|
borderColor: token.colorPrimaryHover,
|
||||||
|
|
@ -203,8 +190,9 @@ const genDefaultButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
|
|
||||||
...genGhostButtonStyle(
|
...genGhostButtonStyle(
|
||||||
token.componentCls,
|
token.componentCls,
|
||||||
token.colorBgContainer,
|
token.ghostBg,
|
||||||
token.colorBgContainer,
|
token.defaultGhostColor,
|
||||||
|
token.defaultGhostBorderColor,
|
||||||
token.colorTextDisabled,
|
token.colorTextDisabled,
|
||||||
token.colorBorder,
|
token.colorBorder,
|
||||||
),
|
),
|
||||||
|
|
@ -214,6 +202,7 @@ const genDefaultButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
borderColor: token.colorError,
|
borderColor: token.colorError,
|
||||||
|
|
||||||
...genHoverActiveButtonStyle(
|
...genHoverActiveButtonStyle(
|
||||||
|
token.componentCls,
|
||||||
{
|
{
|
||||||
color: token.colorErrorHover,
|
color: token.colorErrorHover,
|
||||||
borderColor: token.colorErrorBorderHover,
|
borderColor: token.colorErrorBorderHover,
|
||||||
|
|
@ -226,6 +215,7 @@ const genDefaultButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
|
|
||||||
...genGhostButtonStyle(
|
...genGhostButtonStyle(
|
||||||
token.componentCls,
|
token.componentCls,
|
||||||
|
token.ghostBg,
|
||||||
token.colorError,
|
token.colorError,
|
||||||
token.colorError,
|
token.colorError,
|
||||||
token.colorTextDisabled,
|
token.colorTextDisabled,
|
||||||
|
|
@ -239,24 +229,26 @@ const genDefaultButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
const genPrimaryButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
const genPrimaryButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
...genSolidButtonStyle(token),
|
...genSolidButtonStyle(token),
|
||||||
|
|
||||||
color: token.colorTextLightSolid,
|
color: token.primaryColor,
|
||||||
backgroundColor: token.colorPrimary,
|
background: token.colorPrimary,
|
||||||
|
|
||||||
boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlOutline}`,
|
boxShadow: token.primaryShadow,
|
||||||
|
|
||||||
...genHoverActiveButtonStyle(
|
...genHoverActiveButtonStyle(
|
||||||
|
token.componentCls,
|
||||||
{
|
{
|
||||||
color: token.colorTextLightSolid,
|
color: token.colorTextLightSolid,
|
||||||
backgroundColor: token.colorPrimaryHover,
|
background: token.colorPrimaryHover,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color: token.colorTextLightSolid,
|
color: token.colorTextLightSolid,
|
||||||
backgroundColor: token.colorPrimaryActive,
|
background: token.colorPrimaryActive,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
...genGhostButtonStyle(
|
...genGhostButtonStyle(
|
||||||
token.componentCls,
|
token.componentCls,
|
||||||
|
token.ghostBg,
|
||||||
token.colorPrimary,
|
token.colorPrimary,
|
||||||
token.colorPrimary,
|
token.colorPrimary,
|
||||||
token.colorTextDisabled,
|
token.colorTextDisabled,
|
||||||
|
|
@ -272,20 +264,23 @@ const genPrimaryButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
),
|
),
|
||||||
|
|
||||||
[`&${token.componentCls}-dangerous`]: {
|
[`&${token.componentCls}-dangerous`]: {
|
||||||
backgroundColor: token.colorError,
|
background: token.colorError,
|
||||||
boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.colorErrorOutline}`,
|
boxShadow: token.dangerShadow,
|
||||||
|
color: token.dangerColor,
|
||||||
|
|
||||||
...genHoverActiveButtonStyle(
|
...genHoverActiveButtonStyle(
|
||||||
|
token.componentCls,
|
||||||
{
|
{
|
||||||
backgroundColor: token.colorErrorHover,
|
background: token.colorErrorHover,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
backgroundColor: token.colorErrorActive,
|
background: token.colorErrorActive,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
...genGhostButtonStyle(
|
...genGhostButtonStyle(
|
||||||
token.componentCls,
|
token.componentCls,
|
||||||
|
token.ghostBg,
|
||||||
token.colorError,
|
token.colorError,
|
||||||
token.colorError,
|
token.colorError,
|
||||||
token.colorTextDisabled,
|
token.colorTextDisabled,
|
||||||
|
|
@ -314,8 +309,10 @@ const genLinkButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
color: token.colorLink,
|
color: token.colorLink,
|
||||||
|
|
||||||
...genHoverActiveButtonStyle(
|
...genHoverActiveButtonStyle(
|
||||||
|
token.componentCls,
|
||||||
{
|
{
|
||||||
color: token.colorLinkHover,
|
color: token.colorLinkHover,
|
||||||
|
background: token.linkHoverBg,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color: token.colorLinkActive,
|
color: token.colorLinkActive,
|
||||||
|
|
@ -328,6 +325,7 @@ const genLinkButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
color: token.colorError,
|
color: token.colorError,
|
||||||
|
|
||||||
...genHoverActiveButtonStyle(
|
...genHoverActiveButtonStyle(
|
||||||
|
token.componentCls,
|
||||||
{
|
{
|
||||||
color: token.colorErrorHover,
|
color: token.colorErrorHover,
|
||||||
},
|
},
|
||||||
|
|
@ -343,13 +341,14 @@ const genLinkButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
// Type: Text
|
// Type: Text
|
||||||
const genTextButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
const genTextButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
...genHoverActiveButtonStyle(
|
...genHoverActiveButtonStyle(
|
||||||
|
token.componentCls,
|
||||||
{
|
{
|
||||||
color: token.colorText,
|
color: token.colorText,
|
||||||
backgroundColor: token.colorBgTextHover,
|
background: token.textHoverBg,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color: token.colorText,
|
color: token.colorText,
|
||||||
backgroundColor: token.colorBgTextActive,
|
background: token.colorBgTextActive,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
@ -360,26 +359,19 @@ const genTextButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||||
|
|
||||||
...genPureDisabledButtonStyle(token),
|
...genPureDisabledButtonStyle(token),
|
||||||
...genHoverActiveButtonStyle(
|
...genHoverActiveButtonStyle(
|
||||||
|
token.componentCls,
|
||||||
{
|
{
|
||||||
color: token.colorErrorHover,
|
color: token.colorErrorHover,
|
||||||
backgroundColor: token.colorErrorBg,
|
background: token.colorErrorBg,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color: token.colorErrorHover,
|
color: token.colorErrorHover,
|
||||||
backgroundColor: token.colorErrorBg,
|
background: token.colorErrorBg,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Href and Disabled
|
|
||||||
const genDisabledButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
|
||||||
...genDisabledStyle(token),
|
|
||||||
[`&${token.componentCls}:hover`]: {
|
|
||||||
...genDisabledStyle(token),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const genTypeButtonStyle: GenerateStyle<ButtonToken> = token => {
|
const genTypeButtonStyle: GenerateStyle<ButtonToken> = token => {
|
||||||
const { componentCls } = token;
|
const { componentCls } = token;
|
||||||
|
|
||||||
|
|
@ -389,26 +381,30 @@ const genTypeButtonStyle: GenerateStyle<ButtonToken> = token => {
|
||||||
[`${componentCls}-dashed`]: genDashedButtonStyle(token),
|
[`${componentCls}-dashed`]: genDashedButtonStyle(token),
|
||||||
[`${componentCls}-link`]: genLinkButtonStyle(token),
|
[`${componentCls}-link`]: genLinkButtonStyle(token),
|
||||||
[`${componentCls}-text`]: genTextButtonStyle(token),
|
[`${componentCls}-text`]: genTextButtonStyle(token),
|
||||||
[`${componentCls}-disabled`]: genDisabledButtonStyle(token),
|
[`${componentCls}-ghost`]: genGhostButtonStyle(
|
||||||
|
token.componentCls,
|
||||||
|
token.ghostBg,
|
||||||
|
token.colorBgContainer,
|
||||||
|
token.colorBgContainer,
|
||||||
|
token.colorTextDisabled,
|
||||||
|
token.colorBorder,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// =============================== Size ===============================
|
// =============================== Size ===============================
|
||||||
const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls: string = ''): CSSInterpolation => {
|
const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls: string = '') => {
|
||||||
const {
|
const {
|
||||||
componentCls,
|
componentCls,
|
||||||
iconCls,
|
|
||||||
controlHeight,
|
controlHeight,
|
||||||
fontSize,
|
fontSize,
|
||||||
lineHeight,
|
lineHeight,
|
||||||
lineWidth,
|
|
||||||
borderRadius,
|
borderRadius,
|
||||||
buttonPaddingHorizontal,
|
buttonPaddingHorizontal,
|
||||||
|
iconCls,
|
||||||
|
buttonPaddingVertical,
|
||||||
} = token;
|
} = token;
|
||||||
|
|
||||||
const paddingVertical = Math.max(0, (controlHeight - fontSize * lineHeight) / 2 - lineWidth);
|
|
||||||
const paddingHorizontal = buttonPaddingHorizontal - lineWidth;
|
|
||||||
|
|
||||||
const iconOnlyCls = `${componentCls}-icon-only`;
|
const iconOnlyCls = `${componentCls}-icon-only`;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
@ -416,8 +412,9 @@ const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls: string = ''): CSS
|
||||||
{
|
{
|
||||||
[`${componentCls}${sizePrefixCls}`]: {
|
[`${componentCls}${sizePrefixCls}`]: {
|
||||||
fontSize,
|
fontSize,
|
||||||
|
lineHeight,
|
||||||
height: controlHeight,
|
height: controlHeight,
|
||||||
padding: `${paddingVertical}px ${paddingHorizontal}px`,
|
padding: `${unit(buttonPaddingVertical!)} ${unit(buttonPaddingHorizontal!)}`,
|
||||||
borderRadius,
|
borderRadius,
|
||||||
|
|
||||||
[`&${iconOnlyCls}`]: {
|
[`&${iconOnlyCls}`]: {
|
||||||
|
|
@ -427,8 +424,8 @@ const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls: string = ''): CSS
|
||||||
[`&${componentCls}-round`]: {
|
[`&${componentCls}-round`]: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
},
|
},
|
||||||
'> span': {
|
[iconCls]: {
|
||||||
transform: 'scale(1.143)', // 14px -> 16px
|
fontSize: token.buttonIconOnlyFontSize,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -441,10 +438,6 @@ const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls: string = ''): CSS
|
||||||
[`${componentCls}-loading-icon`]: {
|
[`${componentCls}-loading-icon`]: {
|
||||||
transition: `width ${token.motionDurationSlow} ${token.motionEaseInOut}, opacity ${token.motionDurationSlow} ${token.motionEaseInOut}`,
|
transition: `width ${token.motionDurationSlow} ${token.motionEaseInOut}, opacity ${token.motionDurationSlow} ${token.motionEaseInOut}`,
|
||||||
},
|
},
|
||||||
|
|
||||||
[`&:not(${iconOnlyCls}) ${componentCls}-loading-icon > ${iconCls}`]: {
|
|
||||||
marginInlineEnd: token.marginXS,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -458,14 +451,24 @@ const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls: string = ''): CSS
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
const genSizeBaseButtonStyle: GenerateStyle<ButtonToken> = token => genSizeButtonStyle(token);
|
const genSizeBaseButtonStyle: GenerateStyle<ButtonToken> = token =>
|
||||||
|
genSizeButtonStyle(
|
||||||
|
mergeToken<ButtonToken>(token, {
|
||||||
|
fontSize: token.contentFontSize,
|
||||||
|
lineHeight: token.contentLineHeight,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const genSizeSmallButtonStyle: GenerateStyle<ButtonToken> = token => {
|
const genSizeSmallButtonStyle: GenerateStyle<ButtonToken> = token => {
|
||||||
const smallToken = mergeToken<ButtonToken>(token, {
|
const smallToken = mergeToken<ButtonToken>(token, {
|
||||||
controlHeight: token.controlHeightSM,
|
controlHeight: token.controlHeightSM,
|
||||||
|
fontSize: token.contentFontSizeSM,
|
||||||
|
lineHeight: token.contentLineHeightSM,
|
||||||
padding: token.paddingXS,
|
padding: token.paddingXS,
|
||||||
buttonPaddingHorizontal: 8, // Fixed padding
|
buttonPaddingHorizontal: token.paddingInlineSM,
|
||||||
|
buttonPaddingVertical: token.paddingBlockSM,
|
||||||
borderRadius: token.borderRadiusSM,
|
borderRadius: token.borderRadiusSM,
|
||||||
|
buttonIconOnlyFontSize: token.onlyIconSizeSM,
|
||||||
});
|
});
|
||||||
|
|
||||||
return genSizeButtonStyle(smallToken, `${token.componentCls}-sm`);
|
return genSizeButtonStyle(smallToken, `${token.componentCls}-sm`);
|
||||||
|
|
@ -474,8 +477,12 @@ const genSizeSmallButtonStyle: GenerateStyle<ButtonToken> = token => {
|
||||||
const genSizeLargeButtonStyle: GenerateStyle<ButtonToken> = token => {
|
const genSizeLargeButtonStyle: GenerateStyle<ButtonToken> = token => {
|
||||||
const largeToken = mergeToken<ButtonToken>(token, {
|
const largeToken = mergeToken<ButtonToken>(token, {
|
||||||
controlHeight: token.controlHeightLG,
|
controlHeight: token.controlHeightLG,
|
||||||
fontSize: token.fontSizeLG,
|
fontSize: token.contentFontSizeLG,
|
||||||
|
lineHeight: token.contentLineHeightLG,
|
||||||
|
buttonPaddingHorizontal: token.paddingInlineLG,
|
||||||
|
buttonPaddingVertical: token.paddingBlockLG,
|
||||||
borderRadius: token.borderRadiusLG,
|
borderRadius: token.borderRadiusLG,
|
||||||
|
buttonIconOnlyFontSize: token.onlyIconSizeLG,
|
||||||
});
|
});
|
||||||
|
|
||||||
return genSizeButtonStyle(largeToken, `${token.componentCls}-lg`);
|
return genSizeButtonStyle(largeToken, `${token.componentCls}-lg`);
|
||||||
|
|
@ -493,33 +500,37 @@ const genBlockButtonStyle: GenerateStyle<ButtonToken> = token => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================== Export ==============================
|
// ============================== Export ==============================
|
||||||
export default genComponentStyleHook('Button', token => {
|
export default genStyleHooks(
|
||||||
const { controlTmpOutline, paddingContentHorizontal } = token;
|
'Button',
|
||||||
const buttonToken = mergeToken<ButtonToken>(token, {
|
token => {
|
||||||
colorOutlineDefault: controlTmpOutline,
|
const buttonToken = prepareToken(token);
|
||||||
buttonPaddingHorizontal: paddingContentHorizontal,
|
|
||||||
});
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
// Shared
|
// Shared
|
||||||
genSharedButtonStyle(buttonToken),
|
genSharedButtonStyle(buttonToken),
|
||||||
|
|
||||||
// Size
|
// Size
|
||||||
genSizeSmallButtonStyle(buttonToken),
|
genSizeSmallButtonStyle(buttonToken),
|
||||||
genSizeBaseButtonStyle(buttonToken),
|
genSizeBaseButtonStyle(buttonToken),
|
||||||
genSizeLargeButtonStyle(buttonToken),
|
genSizeLargeButtonStyle(buttonToken),
|
||||||
|
|
||||||
// Block
|
// Block
|
||||||
genBlockButtonStyle(buttonToken),
|
genBlockButtonStyle(buttonToken),
|
||||||
|
|
||||||
// Group (type, ghost, danger, disabled, loading)
|
// Group (type, ghost, danger, loading)
|
||||||
genTypeButtonStyle(buttonToken),
|
genTypeButtonStyle(buttonToken),
|
||||||
|
|
||||||
// Button Group
|
// Button Group
|
||||||
genGroupStyle(buttonToken),
|
genGroupStyle(buttonToken),
|
||||||
|
];
|
||||||
// Space Compact
|
},
|
||||||
genCompactItemStyle(token, { focus: false }),
|
prepareComponentToken,
|
||||||
genCompactItemVerticalStyle(token),
|
{
|
||||||
];
|
unitless: {
|
||||||
});
|
fontWeight: true,
|
||||||
|
contentLineHeight: true,
|
||||||
|
contentLineHeightSM: true,
|
||||||
|
contentLineHeightLG: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,234 @@
|
||||||
|
import type { CSSProperties } from 'vue';
|
||||||
|
import type { FullToken, GetDefaultToken } from '../../theme/internal';
|
||||||
|
import { getLineHeight, mergeToken } from '../../theme/internal';
|
||||||
|
import type { GenStyleFn } from '../../theme/util/genComponentStyleHook';
|
||||||
|
|
||||||
|
/** Component only token. Which will handle additional calculation of alias token */
|
||||||
|
export interface ComponentToken {
|
||||||
|
/**
|
||||||
|
* @desc 文字字重
|
||||||
|
* @descEN Font weight of text
|
||||||
|
*/
|
||||||
|
fontWeight: CSSProperties['fontWeight'];
|
||||||
|
/**
|
||||||
|
* @desc 默认按钮阴影
|
||||||
|
* @descEN Shadow of default button
|
||||||
|
*/
|
||||||
|
defaultShadow: string;
|
||||||
|
/**
|
||||||
|
* @desc 主要按钮阴影
|
||||||
|
* @descEN Shadow of primary button
|
||||||
|
*/
|
||||||
|
primaryShadow: string;
|
||||||
|
/**
|
||||||
|
* @desc 危险按钮阴影
|
||||||
|
* @descEN Shadow of danger button
|
||||||
|
*/
|
||||||
|
dangerShadow: string;
|
||||||
|
/**
|
||||||
|
* @desc 主要按钮文本颜色
|
||||||
|
* @descEN Text color of primary button
|
||||||
|
*/
|
||||||
|
primaryColor: string;
|
||||||
|
/**
|
||||||
|
* @desc 默认按钮文本颜色
|
||||||
|
* @descEN Text color of default button
|
||||||
|
*/
|
||||||
|
defaultColor: string;
|
||||||
|
/**
|
||||||
|
* @desc 默认按钮背景色
|
||||||
|
* @descEN Background color of default button
|
||||||
|
*/
|
||||||
|
defaultBg: string;
|
||||||
|
/**
|
||||||
|
* @desc 默认按钮边框颜色
|
||||||
|
* @descEN Border color of default button
|
||||||
|
*/
|
||||||
|
defaultBorderColor: string;
|
||||||
|
/**
|
||||||
|
* @desc 危险按钮文本颜色
|
||||||
|
* @descEN Text color of danger button
|
||||||
|
*/
|
||||||
|
dangerColor: string;
|
||||||
|
/**
|
||||||
|
* @desc 禁用状态边框颜色
|
||||||
|
* @descEN Border color of disabled button
|
||||||
|
*/
|
||||||
|
borderColorDisabled: string;
|
||||||
|
/**
|
||||||
|
* @desc 默认幽灵按钮文本颜色
|
||||||
|
* @descEN Text color of default ghost button
|
||||||
|
*/
|
||||||
|
defaultGhostColor: string;
|
||||||
|
/**
|
||||||
|
* @desc 幽灵按钮背景色
|
||||||
|
* @descEN Background color of ghost button
|
||||||
|
*/
|
||||||
|
ghostBg: string;
|
||||||
|
/**
|
||||||
|
* @desc 默认幽灵按钮边框颜色
|
||||||
|
* @descEN Border color of default ghost button
|
||||||
|
*/
|
||||||
|
defaultGhostBorderColor: string;
|
||||||
|
/**
|
||||||
|
* @desc 按钮横向内间距
|
||||||
|
* @descEN Horizontal padding of button
|
||||||
|
*/
|
||||||
|
paddingInline: CSSProperties['paddingInline'];
|
||||||
|
/**
|
||||||
|
* @desc 大号按钮横向内间距
|
||||||
|
* @descEN Horizontal padding of large button
|
||||||
|
*/
|
||||||
|
paddingInlineLG: CSSProperties['paddingInline'];
|
||||||
|
/**
|
||||||
|
* @desc 小号按钮横向内间距
|
||||||
|
* @descEN Horizontal padding of small button
|
||||||
|
*/
|
||||||
|
paddingInlineSM: CSSProperties['paddingInline'];
|
||||||
|
/**
|
||||||
|
* @desc 按钮横向内间距
|
||||||
|
* @descEN Horizontal padding of button
|
||||||
|
*/
|
||||||
|
paddingBlock: CSSProperties['paddingInline'];
|
||||||
|
/**
|
||||||
|
* @desc 大号按钮横向内间距
|
||||||
|
* @descEN Horizontal padding of large button
|
||||||
|
*/
|
||||||
|
paddingBlockLG: CSSProperties['paddingInline'];
|
||||||
|
/**
|
||||||
|
* @desc 小号按钮横向内间距
|
||||||
|
* @descEN Horizontal padding of small button
|
||||||
|
*/
|
||||||
|
paddingBlockSM: CSSProperties['paddingInline'];
|
||||||
|
/**
|
||||||
|
* @desc 只有图标的按钮图标尺寸
|
||||||
|
* @descEN Icon size of button which only contains icon
|
||||||
|
*/
|
||||||
|
onlyIconSize: number;
|
||||||
|
/**
|
||||||
|
* @desc 大号只有图标的按钮图标尺寸
|
||||||
|
* @descEN Icon size of large button which only contains icon
|
||||||
|
*/
|
||||||
|
onlyIconSizeLG: number;
|
||||||
|
/**
|
||||||
|
* @desc 小号只有图标的按钮图标尺寸
|
||||||
|
* @descEN Icon size of small button which only contains icon
|
||||||
|
*/
|
||||||
|
onlyIconSizeSM: number;
|
||||||
|
/**
|
||||||
|
* @desc 按钮组边框颜色
|
||||||
|
* @descEN Border color of button group
|
||||||
|
*/
|
||||||
|
groupBorderColor: string;
|
||||||
|
/**
|
||||||
|
* @desc 链接按钮悬浮态背景色
|
||||||
|
* @descEN Background color of link button when hover
|
||||||
|
*/
|
||||||
|
linkHoverBg: string;
|
||||||
|
/**
|
||||||
|
* @desc 文本按钮悬浮态背景色
|
||||||
|
* @descEN Background color of text button when hover
|
||||||
|
*/
|
||||||
|
textHoverBg: string;
|
||||||
|
/**
|
||||||
|
* @desc 按钮内容字体大小
|
||||||
|
* @descEN Font size of button content
|
||||||
|
*/
|
||||||
|
contentFontSize: number;
|
||||||
|
/**
|
||||||
|
* @desc 大号按钮内容字体大小
|
||||||
|
* @descEN Font size of large button content
|
||||||
|
*/
|
||||||
|
contentFontSizeLG: number;
|
||||||
|
/**
|
||||||
|
* @desc 小号按钮内容字体大小
|
||||||
|
* @descEN Font size of small button content
|
||||||
|
*/
|
||||||
|
contentFontSizeSM: number;
|
||||||
|
/**
|
||||||
|
* @desc 按钮内容字体行高
|
||||||
|
* @descEN Line height of button content
|
||||||
|
*/
|
||||||
|
contentLineHeight: number;
|
||||||
|
/**
|
||||||
|
* @desc 大号按钮内容字体行高
|
||||||
|
* @descEN Line height of large button content
|
||||||
|
*/
|
||||||
|
contentLineHeightLG: number;
|
||||||
|
/**
|
||||||
|
* @desc 小号按钮内容字体行高
|
||||||
|
* @descEN Line height of small button content
|
||||||
|
*/
|
||||||
|
contentLineHeightSM: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ButtonToken extends FullToken<'Button'> {
|
||||||
|
buttonPaddingHorizontal: CSSProperties['paddingInline'];
|
||||||
|
buttonPaddingVertical: CSSProperties['paddingBlock'];
|
||||||
|
buttonIconOnlyFontSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const prepareToken: (token: Parameters<GenStyleFn<'Button'>>[0]) => ButtonToken = token => {
|
||||||
|
const { paddingInline, onlyIconSize, paddingBlock } = token;
|
||||||
|
|
||||||
|
const buttonToken = mergeToken<ButtonToken>(token, {
|
||||||
|
buttonPaddingHorizontal: paddingInline,
|
||||||
|
buttonPaddingVertical: paddingBlock,
|
||||||
|
buttonIconOnlyFontSize: onlyIconSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
return buttonToken;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const prepareComponentToken: GetDefaultToken<'Button'> = token => {
|
||||||
|
const contentFontSize = token.contentFontSize ?? token.fontSize;
|
||||||
|
const contentFontSizeSM = token.contentFontSizeSM ?? token.fontSize;
|
||||||
|
const contentFontSizeLG = token.contentFontSizeLG ?? token.fontSizeLG;
|
||||||
|
const contentLineHeight = token.contentLineHeight ?? getLineHeight(contentFontSize);
|
||||||
|
const contentLineHeightSM = token.contentLineHeightSM ?? getLineHeight(contentFontSizeSM);
|
||||||
|
const contentLineHeightLG = token.contentLineHeightLG ?? getLineHeight(contentFontSizeLG);
|
||||||
|
|
||||||
|
return {
|
||||||
|
fontWeight: 400,
|
||||||
|
defaultShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlTmpOutline}`,
|
||||||
|
primaryShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlOutline}`,
|
||||||
|
dangerShadow: `0 ${token.controlOutlineWidth}px 0 ${token.colorErrorOutline}`,
|
||||||
|
primaryColor: token.colorTextLightSolid,
|
||||||
|
dangerColor: token.colorTextLightSolid,
|
||||||
|
borderColorDisabled: token.colorBorder,
|
||||||
|
defaultGhostColor: token.colorBgContainer,
|
||||||
|
ghostBg: 'transparent',
|
||||||
|
defaultGhostBorderColor: token.colorBgContainer,
|
||||||
|
paddingInline: token.paddingContentHorizontal - token.lineWidth,
|
||||||
|
paddingInlineLG: token.paddingContentHorizontal - token.lineWidth,
|
||||||
|
paddingInlineSM: 8 - token.lineWidth,
|
||||||
|
onlyIconSize: token.fontSizeLG,
|
||||||
|
onlyIconSizeSM: token.fontSizeLG - 2,
|
||||||
|
onlyIconSizeLG: token.fontSizeLG + 2,
|
||||||
|
groupBorderColor: token.colorPrimaryHover,
|
||||||
|
linkHoverBg: 'transparent',
|
||||||
|
textHoverBg: token.colorBgTextHover,
|
||||||
|
defaultColor: token.colorText,
|
||||||
|
defaultBg: token.colorBgContainer,
|
||||||
|
defaultBorderColor: token.colorBorder,
|
||||||
|
defaultBorderColorDisabled: token.colorBorder,
|
||||||
|
contentFontSize,
|
||||||
|
contentFontSizeSM,
|
||||||
|
contentFontSizeLG,
|
||||||
|
contentLineHeight,
|
||||||
|
contentLineHeightSM,
|
||||||
|
contentLineHeightLG,
|
||||||
|
paddingBlock: Math.max(
|
||||||
|
(token.controlHeight - contentFontSize * contentLineHeight) / 2 - token.lineWidth,
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
paddingBlockSM: Math.max(
|
||||||
|
(token.controlHeightSM - contentFontSizeSM * contentLineHeightSM) / 2 - token.lineWidth,
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
paddingBlockLG: Math.max(
|
||||||
|
(token.controlHeightLG - contentFontSizeLG * contentLineHeightLG) / 2 - token.lineWidth,
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -57,6 +57,18 @@ export interface ThemeConfig {
|
||||||
algorithm?: MappingAlgorithm | MappingAlgorithm[];
|
algorithm?: MappingAlgorithm | MappingAlgorithm[];
|
||||||
hashed?: boolean;
|
hashed?: boolean;
|
||||||
inherit?: boolean;
|
inherit?: boolean;
|
||||||
|
cssVar?:
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* Prefix for css variable, default to `antd`.
|
||||||
|
*/
|
||||||
|
prefix?: string;
|
||||||
|
/**
|
||||||
|
* Unique key for theme, should be set manually < react@18.
|
||||||
|
*/
|
||||||
|
key?: string;
|
||||||
|
}
|
||||||
|
| boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const configProviderProps = () => ({
|
export const configProviderProps = () => ({
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { useToken } from '../../theme/internal';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This hook is only for cssVar to add root className for components.
|
||||||
|
* If root ClassName is needed, this hook could be refactored with `-root`
|
||||||
|
* @param prefixCls
|
||||||
|
*/
|
||||||
|
const useCSSVarCls = (prefixCls: Ref<string>) => {
|
||||||
|
const [, , , , cssVar] = useToken();
|
||||||
|
|
||||||
|
return computed(() => (cssVar.value ? `${prefixCls.value}-css-var` : ''));
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useCSSVarCls;
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
import type { SizeType } from '../SizeContext';
|
||||||
|
import { useInjectSize } from '../SizeContext';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
import { computed, shallowRef, watch } from 'vue';
|
||||||
|
|
||||||
|
const useSize = <T>(customSize?: T | ((ctxSize: SizeType) => T)): Ref<T> => {
|
||||||
|
const size = useInjectSize();
|
||||||
|
|
||||||
|
const mergedSize = shallowRef(null);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
computed(() => {
|
||||||
|
return [customSize, size.value];
|
||||||
|
}),
|
||||||
|
() => {
|
||||||
|
if (!customSize) {
|
||||||
|
mergedSize.value = size.value as T;
|
||||||
|
}
|
||||||
|
if (typeof customSize === 'string') {
|
||||||
|
mergedSize.value = customSize ?? (size.value as T);
|
||||||
|
}
|
||||||
|
if (customSize instanceof Function) {
|
||||||
|
mergedSize.value = customSize(size.value) as T;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
return mergedSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useSize;
|
||||||
|
|
@ -2,13 +2,26 @@ import type { ThemeConfig } from '../context';
|
||||||
import { defaultConfig } from '../../theme/internal';
|
import { defaultConfig } from '../../theme/internal';
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
import devWarning from '../../vc-util/warning';
|
||||||
|
const themeKey = 'antdvtheme';
|
||||||
export default function useTheme(theme?: Ref<ThemeConfig>, parentTheme?: Ref<ThemeConfig>) {
|
export default function useTheme(theme?: Ref<ThemeConfig>, parentTheme?: Ref<ThemeConfig>) {
|
||||||
const themeConfig = computed(() => theme?.value || {});
|
const themeConfig = computed(() => theme?.value || {});
|
||||||
const parentThemeConfig = computed<ThemeConfig>(() =>
|
const parentThemeConfig = computed<ThemeConfig>(() =>
|
||||||
themeConfig.value.inherit === false || !parentTheme?.value ? defaultConfig : parentTheme.value,
|
themeConfig.value.inherit === false || !parentTheme?.value ? defaultConfig : parentTheme.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
const cssVarEnabled = themeConfig.value.cssVar || parentThemeConfig.value.cssVar;
|
||||||
|
const validKey = !!(
|
||||||
|
(typeof themeConfig.value.cssVar === 'object' && themeConfig.value.cssVar?.key) ||
|
||||||
|
themeKey
|
||||||
|
);
|
||||||
|
devWarning(
|
||||||
|
!cssVarEnabled || validKey,
|
||||||
|
'[Ant Design Vue ConfigProvider] Missing key in `cssVar` config. Please set `cssVar.key` manually in each ConfigProvider inside `cssVar` enabled ConfigProvider.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const mergedTheme = computed(() => {
|
const mergedTheme = computed(() => {
|
||||||
if (!theme?.value) {
|
if (!theme?.value) {
|
||||||
return parentTheme?.value;
|
return parentTheme?.value;
|
||||||
|
|
@ -26,6 +39,17 @@ export default function useTheme(theme?: Ref<ThemeConfig>, parentTheme?: Ref<The
|
||||||
} as any;
|
} as any;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const cssVarKey = `css-var-${themeKey.replace(/:/g, '')}`;
|
||||||
|
|
||||||
|
const mergedCssVar = (themeConfig.value.cssVar ?? parentThemeConfig.value.cssVar) && {
|
||||||
|
prefix: 'ant', // Default to ant
|
||||||
|
...(typeof parentThemeConfig.value.cssVar === 'object' ? parentThemeConfig.value.cssVar : {}),
|
||||||
|
...(typeof themeConfig.value.cssVar === 'object' ? themeConfig.value.cssVar : {}),
|
||||||
|
key:
|
||||||
|
(typeof themeConfig.value.cssVar === 'object' && themeConfig.value.cssVar?.key) ||
|
||||||
|
cssVarKey,
|
||||||
|
};
|
||||||
|
|
||||||
// Base token
|
// Base token
|
||||||
return {
|
return {
|
||||||
...parentThemeConfig.value,
|
...parentThemeConfig.value,
|
||||||
|
|
@ -36,6 +60,7 @@ export default function useTheme(theme?: Ref<ThemeConfig>, parentTheme?: Ref<The
|
||||||
...themeConfig.value.token,
|
...themeConfig.value.token,
|
||||||
},
|
},
|
||||||
components: mergedComponents,
|
components: mergedComponents,
|
||||||
|
cssVar: mergedCssVar,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
let uid = 0;
|
||||||
|
const useThemeKey = () => {
|
||||||
|
return 'themekey' + uid++;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useThemeKey;
|
||||||
|
|
@ -15,7 +15,7 @@ import type { ValidateMessages } from '../form/interface';
|
||||||
import useStyle from './style';
|
import useStyle from './style';
|
||||||
import useTheme from './hooks/useTheme';
|
import useTheme from './hooks/useTheme';
|
||||||
import defaultSeedToken from '../theme/themes/seed';
|
import defaultSeedToken from '../theme/themes/seed';
|
||||||
import type { ConfigProviderInnerProps, ConfigProviderProps, Theme } from './context';
|
import type { ConfigProviderInnerProps, ConfigProviderProps, Theme, ThemeConfig } from './context';
|
||||||
import {
|
import {
|
||||||
useConfigContextProvider,
|
useConfigContextProvider,
|
||||||
useConfigContextInject,
|
useConfigContextInject,
|
||||||
|
|
@ -26,7 +26,7 @@ import {
|
||||||
import { useProviderSize } from './SizeContext';
|
import { useProviderSize } from './SizeContext';
|
||||||
import { useProviderDisabled } from './DisabledContext';
|
import { useProviderDisabled } from './DisabledContext';
|
||||||
import { createTheme } from '../_util/cssinjs';
|
import { createTheme } from '../_util/cssinjs';
|
||||||
import { DesignTokenProvider } from '../theme/internal';
|
import { defaultTheme, DesignTokenProvider } from '../theme/context';
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
ConfigProviderProps,
|
ConfigProviderProps,
|
||||||
|
|
@ -226,19 +226,47 @@ const ConfigProvider = defineComponent({
|
||||||
|
|
||||||
// ================================ Dynamic theme ================================
|
// ================================ Dynamic theme ================================
|
||||||
const memoTheme = computed(() => {
|
const memoTheme = computed(() => {
|
||||||
const { algorithm, token, ...rest } = mergedTheme.value || {};
|
const { algorithm, token, components, cssVar, ...rest } = mergedTheme.value || {};
|
||||||
const themeObj =
|
const themeObj =
|
||||||
algorithm && (!Array.isArray(algorithm) || algorithm.length > 0)
|
algorithm && (!Array.isArray(algorithm) || algorithm.length > 0)
|
||||||
? createTheme(algorithm)
|
? createTheme(algorithm)
|
||||||
: undefined;
|
: defaultTheme;
|
||||||
|
|
||||||
|
const parsedComponents: any = {};
|
||||||
|
Object.entries(components || {}).forEach(([componentName, componentToken]) => {
|
||||||
|
const parsedToken: typeof componentToken & { theme?: typeof defaultTheme } = {
|
||||||
|
...componentToken,
|
||||||
|
};
|
||||||
|
if ('algorithm' in parsedToken) {
|
||||||
|
if (parsedToken.algorithm === true) {
|
||||||
|
parsedToken.theme = themeObj;
|
||||||
|
} else if (
|
||||||
|
Array.isArray(parsedToken.algorithm) ||
|
||||||
|
typeof parsedToken.algorithm === 'function'
|
||||||
|
) {
|
||||||
|
parsedToken.theme = createTheme(parsedToken.algorithm as any);
|
||||||
|
}
|
||||||
|
delete parsedToken.algorithm;
|
||||||
|
}
|
||||||
|
parsedComponents[componentName] = parsedToken;
|
||||||
|
});
|
||||||
|
|
||||||
|
const mergedToken = {
|
||||||
|
...defaultSeedToken,
|
||||||
|
...token,
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...rest,
|
...rest,
|
||||||
theme: themeObj,
|
theme: themeObj,
|
||||||
|
|
||||||
token: {
|
token: mergedToken,
|
||||||
...defaultSeedToken,
|
components: parsedComponents,
|
||||||
...token,
|
override: {
|
||||||
|
override: mergedToken,
|
||||||
|
...parsedComponents,
|
||||||
},
|
},
|
||||||
|
cssVar: cssVar as Exclude<ThemeConfig['cssVar'], boolean>,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const validateMessagesRef = computed(() => {
|
const validateMessagesRef = computed(() => {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { CSSObject } from '../../_util/cssinjs';
|
||||||
import { useStyleRegister } from '../../_util/cssinjs';
|
import { useStyleRegister } from '../../_util/cssinjs';
|
||||||
import { resetIcon } from '../../style';
|
import { resetIcon } from '../../style';
|
||||||
import { useToken } from '../../theme/internal';
|
import { useToken } from '../../theme/internal';
|
||||||
|
|
@ -13,16 +14,17 @@ const useStyle = (iconPrefixCls: Ref<string>) => {
|
||||||
hashId: '',
|
hashId: '',
|
||||||
path: ['ant-design-icons', iconPrefixCls.value],
|
path: ['ant-design-icons', iconPrefixCls.value],
|
||||||
})),
|
})),
|
||||||
() => [
|
() =>
|
||||||
{
|
[
|
||||||
[`.${iconPrefixCls.value}`]: {
|
{
|
||||||
...resetIcon(),
|
[`.${iconPrefixCls.value}`]: {
|
||||||
[`.${iconPrefixCls.value} .${iconPrefixCls.value}-icon`]: {
|
...resetIcon(),
|
||||||
display: 'block',
|
[`.${iconPrefixCls.value} .${iconPrefixCls.value}-icon`]: {
|
||||||
|
display: 'block',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
] as CSSObject[],
|
||||||
],
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ import type { TokenWithCommonCls } from '../../theme/util/genComponentStyleHook'
|
||||||
import { resetComponent, roundedArrow, textEllipsis } from '../../style';
|
import { resetComponent, roundedArrow, textEllipsis } from '../../style';
|
||||||
import { genCompactItemStyle } from '../../style/compact-item';
|
import { genCompactItemStyle } from '../../style/compact-item';
|
||||||
|
|
||||||
|
export interface ComponentToken {}
|
||||||
|
|
||||||
export interface ComponentToken {
|
export interface ComponentToken {
|
||||||
presetsWidth: number;
|
presetsWidth: number;
|
||||||
presetsMaxWidth: number;
|
presetsMaxWidth: number;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import type { FullToken, GenerateStyle } from '../../theme/internal';
|
||||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||||
import { resetComponent, textEllipsis } from '../../style';
|
import { resetComponent, textEllipsis } from '../../style';
|
||||||
|
|
||||||
|
export interface ComponentToken {}
|
||||||
|
|
||||||
interface DescriptionsToken extends FullToken<'Descriptions'> {
|
interface DescriptionsToken extends FullToken<'Descriptions'> {
|
||||||
descriptionsTitleMarginBottom: number;
|
descriptionsTitleMarginBottom: number;
|
||||||
descriptionsExtraColor: string;
|
descriptionsExtraColor: string;
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ const BackTop = defineComponent({
|
||||||
|
|
||||||
const handleScroll = throttleByAnimationFrame((e: Event | { target: any }) => {
|
const handleScroll = throttleByAnimationFrame((e: Event | { target: any }) => {
|
||||||
const { visibilityHeight } = props;
|
const { visibilityHeight } = props;
|
||||||
const scrollTop = getScroll(e.target, true);
|
const scrollTop = getScroll(e.target);
|
||||||
state.visible = scrollTop >= visibilityHeight;
|
state.visible = scrollTop >= visibilityHeight;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||||
import { resetComponent } from '../../style';
|
import { resetComponent } from '../../style';
|
||||||
import genFormValidateMotionStyle from './explain';
|
import genFormValidateMotionStyle from './explain';
|
||||||
|
|
||||||
|
export interface ComponentToken {}
|
||||||
|
|
||||||
export interface FormToken extends FullToken<'Form'> {
|
export interface FormToken extends FullToken<'Form'> {
|
||||||
formItemCls: string;
|
formItemCls: string;
|
||||||
rootPrefixCls: string;
|
rootPrefixCls: string;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ import type { CSSObject } from '../../_util/cssinjs';
|
||||||
import type { FullToken, GenerateStyle } from '../../theme/internal';
|
import type { FullToken, GenerateStyle } from '../../theme/internal';
|
||||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||||
|
|
||||||
|
export interface ComponentToken {}
|
||||||
|
|
||||||
interface GridRowToken extends FullToken<'Grid'> {}
|
interface GridRowToken extends FullToken<'Grid'> {}
|
||||||
|
|
||||||
interface GridColToken extends FullToken<'Grid'> {
|
interface GridColToken extends FullToken<'Grid'> {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import type { App } from 'vue';
|
||||||
|
|
||||||
import * as components from './components';
|
import * as components from './components';
|
||||||
import { default as version } from './version';
|
import { default as version } from './version';
|
||||||
import cssinjs from './_util/cssinjs';
|
import * as cssinjs from './_util/cssinjs';
|
||||||
export * from './components';
|
export * from './components';
|
||||||
export * from './_util/cssinjs';
|
export * from './_util/cssinjs';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import type { GlobalToken } from '../../theme/interface';
|
||||||
import { clearFix, resetComponent } from '../../style';
|
import { clearFix, resetComponent } from '../../style';
|
||||||
import { genCompactItemStyle } from '../../style/compact-item';
|
import { genCompactItemStyle } from '../../style/compact-item';
|
||||||
|
|
||||||
|
export interface ComponentToken {}
|
||||||
|
|
||||||
export type InputToken<T extends GlobalToken = FullToken<'Input'>> = T & {
|
export type InputToken<T extends GlobalToken = FullToken<'Input'>> = T & {
|
||||||
inputAffixPadding: number;
|
inputAffixPadding: number;
|
||||||
inputPaddingVertical: number;
|
inputPaddingVertical: number;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||||
import { resetComponent, textEllipsis } from '../../style';
|
import { resetComponent, textEllipsis } from '../../style';
|
||||||
import { operationUnit } from '../../style';
|
import { operationUnit } from '../../style';
|
||||||
|
|
||||||
|
export interface ComponentToken {}
|
||||||
|
|
||||||
interface PageHeaderToken extends FullToken<'PageHeader'> {
|
interface PageHeaderToken extends FullToken<'PageHeader'> {
|
||||||
pageHeaderPadding: number;
|
pageHeaderPadding: number;
|
||||||
pageHeaderPaddingVertical: number;
|
pageHeaderPaddingVertical: number;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ import type { FullToken, GenerateStyle } from '../../theme/internal';
|
||||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||||
import { genFocusOutline, genFocusStyle, resetComponent } from '../../style';
|
import { genFocusOutline, genFocusStyle, resetComponent } from '../../style';
|
||||||
|
|
||||||
|
export interface ComponentToken {}
|
||||||
|
|
||||||
interface PaginationToken extends InputToken<FullToken<'Pagination'>> {
|
interface PaginationToken extends InputToken<FullToken<'Pagination'>> {
|
||||||
paginationItemSize: number;
|
paginationItemSize: number;
|
||||||
paginationFontFamily: string;
|
paginationFontFamily: string;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import type { FullToken, GenerateStyle } from '../../theme/internal';
|
||||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||||
import { resetComponent } from '../../style';
|
import { resetComponent } from '../../style';
|
||||||
|
|
||||||
|
export interface ComponentToken {}
|
||||||
|
|
||||||
interface StatisticToken extends FullToken<'Statistic'> {
|
interface StatisticToken extends FullToken<'Statistic'> {
|
||||||
statisticTitleFontSize: number;
|
statisticTitleFontSize: number;
|
||||||
statisticContentFontSize: number;
|
statisticContentFontSize: number;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@ The most basic usage.
|
||||||
<template>
|
<template>
|
||||||
<a-switch v-model:checked="checked" />
|
<a-switch v-model:checked="checked" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
const checked = ref<boolean>(false);
|
const checked = ref<boolean>(true);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,6 @@ title:
|
||||||
import { reactive } from 'vue';
|
import { reactive } from 'vue';
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
checked1: true,
|
checked1: true,
|
||||||
checked2: false,
|
checked2: true,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ With text and icon.
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-space direction="vertical">
|
<a-space direction="vertical">
|
||||||
<a-switch v-model:checked="state.checked1" checked-children="开" un-checked-children="关" />
|
<a-switch v-model:checked="state.checked1" checked-children="开启" un-checked-children="关闭" />
|
||||||
<a-switch v-model:checked="state.checked2" checked-children="1" un-checked-children="0" />
|
<a-switch v-model:checked="state.checked2" checked-children="1" un-checked-children="0" />
|
||||||
<a-switch v-model:checked="state.checked3">
|
<a-switch v-model:checked="state.checked3">
|
||||||
<template #checkedChildren><check-outlined /></template>
|
<template #checkedChildren><check-outlined /></template>
|
||||||
|
|
@ -32,6 +32,6 @@ import { CheckOutlined, CloseOutlined } from '@ant-design/icons-vue';
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
checked1: true,
|
checked1: true,
|
||||||
checked2: false,
|
checked2: false,
|
||||||
checked3: false,
|
checked3: true,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ const Switch = defineComponent({
|
||||||
);
|
);
|
||||||
|
|
||||||
const { prefixCls, direction, size } = useConfigInject('switch', props);
|
const { prefixCls, direction, size } = useConfigInject('switch', props);
|
||||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);
|
||||||
const refSwitchNode = ref();
|
const refSwitchNode = ref();
|
||||||
const focus = () => {
|
const focus = () => {
|
||||||
refSwitchNode.value?.focus();
|
refSwitchNode.value?.focus();
|
||||||
|
|
@ -159,10 +159,11 @@ const Switch = defineComponent({
|
||||||
[prefixCls.value]: true,
|
[prefixCls.value]: true,
|
||||||
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
||||||
[hashId.value]: true,
|
[hashId.value]: true,
|
||||||
|
[cssVarCls.value]: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return () =>
|
return () =>
|
||||||
wrapSSR(
|
wrapCSSVar(
|
||||||
<Wave>
|
<Wave>
|
||||||
<button
|
<button
|
||||||
{...omit(props, [
|
{...omit(props, [
|
||||||
|
|
|
||||||
|
|
@ -1,107 +1,172 @@
|
||||||
import type { CSSObject } from '../../_util/cssinjs';
|
import type { CSSObject } from '../../_util/cssinjs';
|
||||||
|
import { unit } from '../../_util/cssinjs';
|
||||||
import { TinyColor } from '@ctrl/tinycolor';
|
import { TinyColor } from '@ctrl/tinycolor';
|
||||||
import type { FullToken, GenerateStyle } from '../../theme/internal';
|
|
||||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
|
||||||
import { genFocusStyle, resetComponent } from '../../style';
|
import { genFocusStyle, resetComponent } from '../../style';
|
||||||
|
import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/internal';
|
||||||
|
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
||||||
|
|
||||||
|
export interface ComponentToken {
|
||||||
|
/**
|
||||||
|
* @desc 开关高度
|
||||||
|
* @descEN Height of Switch
|
||||||
|
*/
|
||||||
|
trackHeight: number | string;
|
||||||
|
/**
|
||||||
|
* @desc 小号开关高度
|
||||||
|
* @descEN Height of small Switch
|
||||||
|
*/
|
||||||
|
trackHeightSM: number | string;
|
||||||
|
/**
|
||||||
|
* @desc 开关最小宽度
|
||||||
|
* @descEN Minimum width of Switch
|
||||||
|
*/
|
||||||
|
trackMinWidth: number | string;
|
||||||
|
/**
|
||||||
|
* @desc 小号开关最小宽度
|
||||||
|
* @descEN Minimum width of small Switch
|
||||||
|
*/
|
||||||
|
trackMinWidthSM: number | string;
|
||||||
|
/**
|
||||||
|
* @desc 开关内边距
|
||||||
|
* @descEN Padding of Switch
|
||||||
|
*/
|
||||||
|
trackPadding: number;
|
||||||
|
/**
|
||||||
|
* @desc 开关把手背景色
|
||||||
|
* @descEN Background color of Switch handle
|
||||||
|
*/
|
||||||
|
handleBg: string;
|
||||||
|
/**
|
||||||
|
* @desc 开关把手阴影
|
||||||
|
* @descEN Shadow of Switch handle
|
||||||
|
*/
|
||||||
|
handleShadow: string;
|
||||||
|
/**
|
||||||
|
* @desc 开关把手大小
|
||||||
|
* @descEN Size of Switch handle
|
||||||
|
*/
|
||||||
|
handleSize: number;
|
||||||
|
/**
|
||||||
|
* @desc 小号开关把手大小
|
||||||
|
* @descEN Size of small Switch handle
|
||||||
|
*/
|
||||||
|
handleSizeSM: number;
|
||||||
|
/**
|
||||||
|
* @desc 内容区域最小边距
|
||||||
|
* @descEN Minimum margin of content area
|
||||||
|
*/
|
||||||
|
innerMinMargin: number;
|
||||||
|
/**
|
||||||
|
* @desc 内容区域最大边距
|
||||||
|
* @descEN Maximum margin of content area
|
||||||
|
*/
|
||||||
|
innerMaxMargin: number;
|
||||||
|
/**
|
||||||
|
* @desc 小号开关内容区域最小边距
|
||||||
|
* @descEN Minimum margin of content area of small Switch
|
||||||
|
*/
|
||||||
|
innerMinMarginSM: number;
|
||||||
|
/**
|
||||||
|
* @desc 小号开关内容区域最大边距
|
||||||
|
* @descEN Maximum margin of content area of small Switch
|
||||||
|
*/
|
||||||
|
innerMaxMarginSM: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface SwitchToken extends FullToken<'Switch'> {
|
interface SwitchToken extends FullToken<'Switch'> {
|
||||||
switchMinWidth: number;
|
|
||||||
switchHeight: number;
|
|
||||||
switchDuration: string;
|
switchDuration: string;
|
||||||
switchColor: string;
|
switchColor: string;
|
||||||
switchDisabledOpacity: number;
|
switchDisabledOpacity: number;
|
||||||
switchInnerMarginMin: number;
|
switchLoadingIconSize: number | string;
|
||||||
switchInnerMarginMax: number;
|
|
||||||
switchPadding: number;
|
|
||||||
switchPinSize: number;
|
|
||||||
switchBg: string;
|
|
||||||
switchMinWidthSM: number;
|
|
||||||
switchHeightSM: number;
|
|
||||||
switchInnerMarginMinSM: number;
|
|
||||||
switchInnerMarginMaxSM: number;
|
|
||||||
switchPinSizeSM: number;
|
|
||||||
switchHandleShadow: string;
|
|
||||||
switchLoadingIconSize: number;
|
|
||||||
switchLoadingIconColor: string;
|
switchLoadingIconColor: string;
|
||||||
switchHandleActiveInset: string;
|
switchHandleActiveInset: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const genSwitchSmallStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
const genSwitchSmallStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
||||||
const { componentCls } = token;
|
const {
|
||||||
|
componentCls,
|
||||||
|
trackHeightSM,
|
||||||
|
trackPadding,
|
||||||
|
trackMinWidthSM,
|
||||||
|
innerMinMarginSM,
|
||||||
|
innerMaxMarginSM,
|
||||||
|
handleSizeSM,
|
||||||
|
calc,
|
||||||
|
} = token;
|
||||||
const switchInnerCls = `${componentCls}-inner`;
|
const switchInnerCls = `${componentCls}-inner`;
|
||||||
|
|
||||||
|
const trackPaddingCalc = unit(calc(handleSizeSM).add(calc(trackPadding).mul(2)).equal());
|
||||||
|
const innerMaxMarginCalc = unit(calc(innerMaxMarginSM).mul(2).equal());
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[componentCls]: {
|
[componentCls]: {
|
||||||
[`&${componentCls}-small`]: {
|
[`&${componentCls}-small`]: {
|
||||||
minWidth: token.switchMinWidthSM,
|
minWidth: trackMinWidthSM,
|
||||||
height: token.switchHeightSM,
|
height: trackHeightSM,
|
||||||
lineHeight: `${token.switchHeightSM}px`,
|
lineHeight: unit(trackHeightSM),
|
||||||
|
|
||||||
[`${componentCls}-inner`]: {
|
[`${componentCls}-inner`]: {
|
||||||
paddingInlineStart: token.switchInnerMarginMaxSM,
|
paddingInlineStart: innerMaxMarginSM,
|
||||||
paddingInlineEnd: token.switchInnerMarginMinSM,
|
paddingInlineEnd: innerMinMarginSM,
|
||||||
|
|
||||||
|
[`${switchInnerCls}-checked, ${switchInnerCls}-unchecked`]: {
|
||||||
|
minHeight: trackHeightSM,
|
||||||
|
},
|
||||||
|
|
||||||
[`${switchInnerCls}-checked`]: {
|
[`${switchInnerCls}-checked`]: {
|
||||||
marginInlineStart: `calc(-100% + ${
|
marginInlineStart: `calc(-100% + ${trackPaddingCalc} - ${innerMaxMarginCalc})`,
|
||||||
token.switchPinSizeSM + token.switchPadding * 2
|
marginInlineEnd: `calc(100% - ${trackPaddingCalc} + ${innerMaxMarginCalc})`,
|
||||||
}px - ${token.switchInnerMarginMaxSM * 2}px)`,
|
|
||||||
marginInlineEnd: `calc(100% - ${token.switchPinSizeSM + token.switchPadding * 2}px + ${
|
|
||||||
token.switchInnerMarginMaxSM * 2
|
|
||||||
}px)`,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
[`${switchInnerCls}-unchecked`]: {
|
[`${switchInnerCls}-unchecked`]: {
|
||||||
marginTop: -token.switchHeightSM,
|
marginTop: calc(trackHeightSM).mul(-1).equal(),
|
||||||
marginInlineStart: 0,
|
marginInlineStart: 0,
|
||||||
marginInlineEnd: 0,
|
marginInlineEnd: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
[`${componentCls}-handle`]: {
|
[`${componentCls}-handle`]: {
|
||||||
width: token.switchPinSizeSM,
|
width: handleSizeSM,
|
||||||
height: token.switchPinSizeSM,
|
height: handleSizeSM,
|
||||||
},
|
},
|
||||||
|
|
||||||
[`${componentCls}-loading-icon`]: {
|
[`${componentCls}-loading-icon`]: {
|
||||||
top: (token.switchPinSizeSM - token.switchLoadingIconSize) / 2,
|
top: calc(calc(handleSizeSM).sub(token.switchLoadingIconSize)).div(2).equal(),
|
||||||
fontSize: token.switchLoadingIconSize,
|
fontSize: token.switchLoadingIconSize,
|
||||||
},
|
},
|
||||||
|
|
||||||
[`&${componentCls}-checked`]: {
|
[`&${componentCls}-checked`]: {
|
||||||
[`${componentCls}-inner`]: {
|
[`${componentCls}-inner`]: {
|
||||||
paddingInlineStart: token.switchInnerMarginMinSM,
|
paddingInlineStart: innerMinMarginSM,
|
||||||
paddingInlineEnd: token.switchInnerMarginMaxSM,
|
paddingInlineEnd: innerMaxMarginSM,
|
||||||
[`${switchInnerCls}-checked`]: {
|
[`${switchInnerCls}-checked`]: {
|
||||||
marginInlineStart: 0,
|
marginInlineStart: 0,
|
||||||
marginInlineEnd: 0,
|
marginInlineEnd: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
[`${switchInnerCls}-unchecked`]: {
|
[`${switchInnerCls}-unchecked`]: {
|
||||||
marginInlineStart: `calc(100% - ${
|
marginInlineStart: `calc(100% - ${trackPaddingCalc} + ${innerMaxMarginCalc})`,
|
||||||
token.switchPinSizeSM + token.switchPadding * 2
|
marginInlineEnd: `calc(-100% + ${trackPaddingCalc} - ${innerMaxMarginCalc})`,
|
||||||
}px + ${token.switchInnerMarginMaxSM * 2}px)`,
|
|
||||||
marginInlineEnd: `calc(-100% + ${
|
|
||||||
token.switchPinSizeSM + token.switchPadding * 2
|
|
||||||
}px - ${token.switchInnerMarginMaxSM * 2}px)`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
[`${componentCls}-handle`]: {
|
[`${componentCls}-handle`]: {
|
||||||
insetInlineStart: `calc(100% - ${token.switchPinSizeSM + token.switchPadding}px)`,
|
insetInlineStart: `calc(100% - ${unit(calc(handleSizeSM).add(trackPadding).equal())})`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
[`&:not(${componentCls}-disabled):active`]: {
|
[`&:not(${componentCls}-disabled):active`]: {
|
||||||
[`&:not(${componentCls}-checked) ${switchInnerCls}`]: {
|
[`&:not(${componentCls}-checked) ${switchInnerCls}`]: {
|
||||||
[`${switchInnerCls}-unchecked`]: {
|
[`${switchInnerCls}-unchecked`]: {
|
||||||
marginInlineStart: token.marginXXS / 2,
|
marginInlineStart: calc(token.marginXXS).div(2).equal(),
|
||||||
marginInlineEnd: -token.marginXXS / 2,
|
marginInlineEnd: calc(token.marginXXS).mul(-1).div(2).equal(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
[`&${componentCls}-checked ${switchInnerCls}`]: {
|
[`&${componentCls}-checked ${switchInnerCls}`]: {
|
||||||
[`${switchInnerCls}-checked`]: {
|
[`${switchInnerCls}-checked`]: {
|
||||||
marginInlineStart: -token.marginXXS / 2,
|
marginInlineStart: calc(token.marginXXS).mul(-1).div(2).equal(),
|
||||||
marginInlineEnd: token.marginXXS / 2,
|
marginInlineEnd: calc(token.marginXXS).div(2).equal(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -111,13 +176,13 @@ const genSwitchSmallStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const genSwitchLoadingStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
const genSwitchLoadingStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
||||||
const { componentCls } = token;
|
const { componentCls, handleSize, calc } = token;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[componentCls]: {
|
[componentCls]: {
|
||||||
[`${componentCls}-loading-icon${token.iconCls}`]: {
|
[`${componentCls}-loading-icon${token.iconCls}`]: {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
top: (token.switchPinSize - token.fontSize) / 2,
|
top: calc(calc(handleSize).sub(token.fontSize)).div(2).equal(),
|
||||||
color: token.switchLoadingIconColor,
|
color: token.switchLoadingIconColor,
|
||||||
verticalAlign: 'top',
|
verticalAlign: 'top',
|
||||||
},
|
},
|
||||||
|
|
@ -130,17 +195,17 @@ const genSwitchLoadingStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const genSwitchHandleStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
const genSwitchHandleStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
||||||
const { componentCls } = token;
|
const { componentCls, trackPadding, handleBg, handleShadow, handleSize, calc } = token;
|
||||||
const switchHandleCls = `${componentCls}-handle`;
|
const switchHandleCls = `${componentCls}-handle`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[componentCls]: {
|
[componentCls]: {
|
||||||
[switchHandleCls]: {
|
[switchHandleCls]: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: token.switchPadding,
|
top: trackPadding,
|
||||||
insetInlineStart: token.switchPadding,
|
insetInlineStart: trackPadding,
|
||||||
width: token.switchPinSize,
|
width: handleSize,
|
||||||
height: token.switchPinSize,
|
height: handleSize,
|
||||||
transition: `all ${token.switchDuration} ease-in-out`,
|
transition: `all ${token.switchDuration} ease-in-out`,
|
||||||
|
|
||||||
'&::before': {
|
'&::before': {
|
||||||
|
|
@ -149,16 +214,16 @@ const genSwitchHandleStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
||||||
insetInlineEnd: 0,
|
insetInlineEnd: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
insetInlineStart: 0,
|
insetInlineStart: 0,
|
||||||
backgroundColor: token.colorWhite,
|
backgroundColor: handleBg,
|
||||||
borderRadius: token.switchPinSize / 2,
|
borderRadius: calc(handleSize).div(2).equal(),
|
||||||
boxShadow: token.switchHandleShadow,
|
boxShadow: handleShadow,
|
||||||
transition: `all ${token.switchDuration} ease-in-out`,
|
transition: `all ${token.switchDuration} ease-in-out`,
|
||||||
content: '""',
|
content: '""',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
[`&${componentCls}-checked ${switchHandleCls}`]: {
|
[`&${componentCls}-checked ${switchHandleCls}`]: {
|
||||||
insetInlineStart: `calc(100% - ${token.switchPinSize + token.switchPadding}px)`,
|
insetInlineStart: `calc(100% - ${unit(calc(handleSize).add(trackPadding).equal())})`,
|
||||||
},
|
},
|
||||||
|
|
||||||
[`&:not(${componentCls}-disabled):active`]: {
|
[`&:not(${componentCls}-disabled):active`]: {
|
||||||
|
|
@ -177,9 +242,20 @@ const genSwitchHandleStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const genSwitchInnerStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
const genSwitchInnerStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
||||||
const { componentCls } = token;
|
const {
|
||||||
|
componentCls,
|
||||||
|
trackHeight,
|
||||||
|
trackPadding,
|
||||||
|
innerMinMargin,
|
||||||
|
innerMaxMargin,
|
||||||
|
handleSize,
|
||||||
|
calc,
|
||||||
|
} = token;
|
||||||
const switchInnerCls = `${componentCls}-inner`;
|
const switchInnerCls = `${componentCls}-inner`;
|
||||||
|
|
||||||
|
const trackPaddingCalc = unit(calc(handleSize).add(calc(trackPadding).mul(2)).equal());
|
||||||
|
const innerMaxMarginCalc = unit(calc(innerMaxMargin).mul(2).equal());
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[componentCls]: {
|
[componentCls]: {
|
||||||
[switchInnerCls]: {
|
[switchInnerCls]: {
|
||||||
|
|
@ -187,8 +263,8 @@ const genSwitchInnerStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
borderRadius: 100,
|
borderRadius: 100,
|
||||||
height: '100%',
|
height: '100%',
|
||||||
paddingInlineStart: token.switchInnerMarginMax,
|
paddingInlineStart: innerMaxMargin,
|
||||||
paddingInlineEnd: token.switchInnerMarginMin,
|
paddingInlineEnd: innerMinMargin,
|
||||||
transition: `padding-inline-start ${token.switchDuration} ease-in-out, padding-inline-end ${token.switchDuration} ease-in-out`,
|
transition: `padding-inline-start ${token.switchDuration} ease-in-out, padding-inline-end ${token.switchDuration} ease-in-out`,
|
||||||
|
|
||||||
[`${switchInnerCls}-checked, ${switchInnerCls}-unchecked`]: {
|
[`${switchInnerCls}-checked, ${switchInnerCls}-unchecked`]: {
|
||||||
|
|
@ -197,54 +273,47 @@ const genSwitchInnerStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
||||||
fontSize: token.fontSizeSM,
|
fontSize: token.fontSizeSM,
|
||||||
transition: `margin-inline-start ${token.switchDuration} ease-in-out, margin-inline-end ${token.switchDuration} ease-in-out`,
|
transition: `margin-inline-start ${token.switchDuration} ease-in-out, margin-inline-end ${token.switchDuration} ease-in-out`,
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
|
minHeight: trackHeight,
|
||||||
},
|
},
|
||||||
|
|
||||||
[`${switchInnerCls}-checked`]: {
|
[`${switchInnerCls}-checked`]: {
|
||||||
marginInlineStart: `calc(-100% + ${token.switchPinSize + token.switchPadding * 2}px - ${
|
marginInlineStart: `calc(-100% + ${trackPaddingCalc} - ${innerMaxMarginCalc})`,
|
||||||
token.switchInnerMarginMax * 2
|
marginInlineEnd: `calc(100% - ${trackPaddingCalc} + ${innerMaxMarginCalc})`,
|
||||||
}px)`,
|
|
||||||
marginInlineEnd: `calc(100% - ${token.switchPinSize + token.switchPadding * 2}px + ${
|
|
||||||
token.switchInnerMarginMax * 2
|
|
||||||
}px)`,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
[`${switchInnerCls}-unchecked`]: {
|
[`${switchInnerCls}-unchecked`]: {
|
||||||
marginTop: -token.switchHeight,
|
marginTop: calc(trackHeight).mul(-1).equal(),
|
||||||
marginInlineStart: 0,
|
marginInlineStart: 0,
|
||||||
marginInlineEnd: 0,
|
marginInlineEnd: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
[`&${componentCls}-checked ${switchInnerCls}`]: {
|
[`&${componentCls}-checked ${switchInnerCls}`]: {
|
||||||
paddingInlineStart: token.switchInnerMarginMin,
|
paddingInlineStart: innerMinMargin,
|
||||||
paddingInlineEnd: token.switchInnerMarginMax,
|
paddingInlineEnd: innerMaxMargin,
|
||||||
[`${switchInnerCls}-checked`]: {
|
[`${switchInnerCls}-checked`]: {
|
||||||
marginInlineStart: 0,
|
marginInlineStart: 0,
|
||||||
marginInlineEnd: 0,
|
marginInlineEnd: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
[`${switchInnerCls}-unchecked`]: {
|
[`${switchInnerCls}-unchecked`]: {
|
||||||
marginInlineStart: `calc(100% - ${token.switchPinSize + token.switchPadding * 2}px + ${
|
marginInlineStart: `calc(100% - ${trackPaddingCalc} + ${innerMaxMarginCalc})`,
|
||||||
token.switchInnerMarginMax * 2
|
marginInlineEnd: `calc(-100% + ${trackPaddingCalc} - ${innerMaxMarginCalc})`,
|
||||||
}px)`,
|
|
||||||
marginInlineEnd: `calc(-100% + ${token.switchPinSize + token.switchPadding * 2}px - ${
|
|
||||||
token.switchInnerMarginMax * 2
|
|
||||||
}px)`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
[`&:not(${componentCls}-disabled):active`]: {
|
[`&:not(${componentCls}-disabled):active`]: {
|
||||||
[`&:not(${componentCls}-checked) ${switchInnerCls}`]: {
|
[`&:not(${componentCls}-checked) ${switchInnerCls}`]: {
|
||||||
[`${switchInnerCls}-unchecked`]: {
|
[`${switchInnerCls}-unchecked`]: {
|
||||||
marginInlineStart: token.switchPadding * 2,
|
marginInlineStart: calc(trackPadding).mul(2).equal(),
|
||||||
marginInlineEnd: -token.switchPadding * 2,
|
marginInlineEnd: calc(trackPadding).mul(-1).mul(2).equal(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
[`&${componentCls}-checked ${switchInnerCls}`]: {
|
[`&${componentCls}-checked ${switchInnerCls}`]: {
|
||||||
[`${switchInnerCls}-checked`]: {
|
[`${switchInnerCls}-checked`]: {
|
||||||
marginInlineStart: -token.switchPadding * 2,
|
marginInlineStart: calc(trackPadding).mul(-1).mul(2).equal(),
|
||||||
marginInlineEnd: token.switchPadding * 2,
|
marginInlineEnd: calc(trackPadding).mul(2).equal(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -253,7 +322,7 @@ const genSwitchInnerStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const genSwitchStyle = (token: SwitchToken): CSSObject => {
|
const genSwitchStyle = (token: SwitchToken): CSSObject => {
|
||||||
const { componentCls } = token;
|
const { componentCls, trackHeight, trackMinWidth } = token;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[componentCls]: {
|
[componentCls]: {
|
||||||
|
|
@ -262,9 +331,9 @@ const genSwitchStyle = (token: SwitchToken): CSSObject => {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
minWidth: token.switchMinWidth,
|
minWidth: trackMinWidth,
|
||||||
height: token.switchHeight,
|
height: trackHeight,
|
||||||
lineHeight: `${token.switchHeight}px`,
|
lineHeight: unit(trackHeight),
|
||||||
verticalAlign: 'middle',
|
verticalAlign: 'middle',
|
||||||
background: token.colorTextQuaternary,
|
background: token.colorTextQuaternary,
|
||||||
border: '0',
|
border: '0',
|
||||||
|
|
@ -306,48 +375,59 @@ const genSwitchStyle = (token: SwitchToken): CSSObject => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================== Export ==============================
|
// ============================== Export ==============================
|
||||||
export default genComponentStyleHook('Switch', token => {
|
export const prepareComponentToken: GetDefaultToken<'Switch'> = token => {
|
||||||
const switchHeight = token.fontSize * token.lineHeight;
|
const { fontSize, lineHeight, controlHeight, colorWhite } = token;
|
||||||
const switchHeightSM = token.controlHeight / 2;
|
|
||||||
const switchPadding = 2; // This is magic
|
|
||||||
const switchPinSize = switchHeight - switchPadding * 2;
|
|
||||||
const switchPinSizeSM = switchHeightSM - switchPadding * 2;
|
|
||||||
|
|
||||||
const switchToken = mergeToken<SwitchToken>(token, {
|
const height = fontSize * lineHeight;
|
||||||
switchMinWidth: switchPinSize * 2 + switchPadding * 4,
|
const heightSM = controlHeight / 2;
|
||||||
switchHeight,
|
const padding = 2; // Fixed value
|
||||||
switchDuration: token.motionDurationMid,
|
const handleSize = height - padding * 2;
|
||||||
switchColor: token.colorPrimary,
|
const handleSizeSM = heightSM - padding * 2;
|
||||||
switchDisabledOpacity: token.opacityLoading,
|
|
||||||
switchInnerMarginMin: switchPinSize / 2,
|
|
||||||
switchInnerMarginMax: switchPinSize + switchPadding + switchPadding * 2,
|
|
||||||
switchPadding,
|
|
||||||
switchPinSize,
|
|
||||||
switchBg: token.colorBgContainer,
|
|
||||||
switchMinWidthSM: switchPinSizeSM * 2 + switchPadding * 2,
|
|
||||||
switchHeightSM,
|
|
||||||
switchInnerMarginMinSM: switchPinSizeSM / 2,
|
|
||||||
switchInnerMarginMaxSM: switchPinSizeSM + switchPadding + switchPadding * 2,
|
|
||||||
switchPinSizeSM,
|
|
||||||
switchHandleShadow: `0 2px 4px 0 ${new TinyColor('#00230b').setAlpha(0.2).toRgbString()}`,
|
|
||||||
switchLoadingIconSize: token.fontSizeIcon * 0.75,
|
|
||||||
switchLoadingIconColor: `rgba(0, 0, 0, ${token.opacityLoading})`,
|
|
||||||
switchHandleActiveInset: '-30%',
|
|
||||||
});
|
|
||||||
|
|
||||||
return [
|
return {
|
||||||
genSwitchStyle(switchToken),
|
trackHeight: height,
|
||||||
|
trackHeightSM: heightSM,
|
||||||
|
trackMinWidth: handleSize * 2 + padding * 4,
|
||||||
|
trackMinWidthSM: handleSizeSM * 2 + padding * 2,
|
||||||
|
trackPadding: padding, // Fixed value
|
||||||
|
handleBg: colorWhite,
|
||||||
|
handleSize,
|
||||||
|
handleSizeSM,
|
||||||
|
handleShadow: `0 2px 4px 0 ${new TinyColor('#00230b').setAlpha(0.2).toRgbString()}`,
|
||||||
|
innerMinMargin: handleSize / 2,
|
||||||
|
innerMaxMargin: handleSize + padding + padding * 2,
|
||||||
|
innerMinMarginSM: handleSizeSM / 2,
|
||||||
|
innerMaxMarginSM: handleSizeSM + padding + padding * 2,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// inner style
|
export default genStyleHooks(
|
||||||
genSwitchInnerStyle(switchToken),
|
'Switch',
|
||||||
|
token => {
|
||||||
|
const switchToken = mergeToken<SwitchToken>(token, {
|
||||||
|
switchDuration: token.motionDurationMid,
|
||||||
|
switchColor: token.colorPrimary,
|
||||||
|
switchDisabledOpacity: token.opacityLoading,
|
||||||
|
switchLoadingIconSize: token.calc(token.fontSizeIcon).mul(0.75).equal(),
|
||||||
|
switchLoadingIconColor: `rgba(0, 0, 0, ${token.opacityLoading})`,
|
||||||
|
switchHandleActiveInset: '-30%',
|
||||||
|
});
|
||||||
|
|
||||||
// handle style
|
return [
|
||||||
genSwitchHandleStyle(switchToken),
|
genSwitchStyle(switchToken),
|
||||||
|
|
||||||
// loading style
|
// inner style
|
||||||
genSwitchLoadingStyle(switchToken),
|
genSwitchInnerStyle(switchToken),
|
||||||
|
|
||||||
// small style
|
// handle style
|
||||||
genSwitchSmallStyle(switchToken),
|
genSwitchHandleStyle(switchToken),
|
||||||
];
|
|
||||||
});
|
// loading style
|
||||||
|
genSwitchLoadingStyle(switchToken),
|
||||||
|
|
||||||
|
// small style
|
||||||
|
genSwitchSmallStyle(switchToken),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
prepareComponentToken,
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
import getAlphaColor from '../util/getAlphaColor';
|
|
||||||
|
|
||||||
describe('util', () => {
|
|
||||||
describe('getAlphaColor', () => {
|
|
||||||
it('should not process color with alpha', () => {
|
|
||||||
expect(getAlphaColor('rgba(0, 0, 0, 0.5)', 'rgba(255, 255, 255)')).toBe('rgba(0, 0, 0, 0.5)');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue