diff --git a/jest.config.js b/jest.config.js index 69b236475..36c930152 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,42 +1,42 @@ -module.exports = { - // u can change this option to a more specific folder for test single component or util when dev - // for example, ['/packages/components/button'] - roots: [''], - - testEnvironment: 'jsdom', - transform: { - '^.+\\.vue$': 'vue-jest', - '^.+\\.(t|j)sx?$': [ - 'babel-jest', - { - presets: [ - [ - '@babel/preset-env', - { - targets: { - node: true, - }, - }, - ], - '@babel/preset-typescript', - ], - plugins: ['@vue/babel-plugin-jsx'], - }, - ], - }, - moduleNameMapper: { - '^@idux(.*)$': '/packages$1', - }, - moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], - collectCoverage: true, - coverageReporters: ['json', 'lcov', 'text', 'cobertura'], - coverageThreshold: { - global: { - branches: 85, - functions: 85, - lines: 85, - statements: 85, - }, - }, - reporters: ['default', 'jest-junit'], -} +module.exports = { + // u can change this option to a more specific folder for test single component or util when dev + // for example, ['/packages/components/button'] + roots: [''], + + testEnvironment: 'jsdom', + transform: { + '^.+\\.vue$': 'vue-jest', + '^.+\\.(t|j)sx?$': [ + 'babel-jest', + { + presets: [ + [ + '@babel/preset-env', + { + targets: { + node: true, + }, + }, + ], + '@babel/preset-typescript', + ], + plugins: ['@vue/babel-plugin-jsx'], + }, + ], + }, + moduleNameMapper: { + '^@idux(.*)$': '/packages$1', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], + collectCoverage: true, + coverageReporters: ['json', 'lcov', 'text', 'cobertura'], + coverageThreshold: { + global: { + branches: 85, + functions: 85, + lines: 85, + statements: 85, + }, + }, + reporters: ['default', 'jest-junit'], +} \ No newline at end of file diff --git a/package.json b/package.json index e1034e223..bc4583bdd 100644 --- a/package.json +++ b/package.json @@ -1,121 +1,122 @@ -{ - "name": "idux", - "version": "0.0.0-NOT-USED", - "description": "A UI Component Library for Vue 3.x", - "private": true, - "scripts": { - "start": "gulp start:dev", - "icons": "gulp icons:start", - "cz": "git-cz", - "gen": "ts-node --project scripts/gen/tsconfig.json scripts/gen/index.ts", - "postinstall": "lerna bootstrap", - "codecov": "codecov -t", - "test": "jest", - "lint": "npm run ls-lint && npm run eslint && npm run stylelint && npm run markdownlint", - "lint-fix": "npm run ls-lint && npm run eslint-fix && npm run stylelint-fix && npm run markdownlint-fix", - "eslint": "eslint ./packages --ext .vue,.js,.ts", - "eslint-fix": "eslint --fix ./packages --ext .vue,.js,.ts", - "stylelint": "stylelint \"./packages/components/**/*.less\" --syntax less", - "stylelint-fix": "stylelint --fix \"./packages/components/**/*.less\" --syntax less", - "markdownlint": "markdownlint \"./packages/{cdk,components,pro}/**/*.md\"", - "markdownlint-fix": "markdownlint --fix \"./packages/{cdk,components,pro}/**/*.md\"", - "ls-lint": "ls-lint" - }, - "license": "MIT", - "devDependencies": { - "@babel/preset-env": "^7.12.0", - "@babel/preset-typescript": "^7.12.0", - "@commitlint/cli": "^11.0.0", - "@commitlint/config-angular": "^11.0.0", - "@ls-lint/ls-lint": "^1.9.0", - "@types/detect-port": "^1.3.0", - "@types/fs-extra": "^9.0.0", - "@types/gulp": "^4.0.0", - "@types/jest": "^26.0.0", - "@types/lodash": "^4.14.0", - "@types/marked": "^1.2.0", - "@types/parse5": "^5.0.0", - "@types/prismjs": "^1.16.0", - "@types/svgo": "^1.3.0", - "@types/yaml-front-matter": "^4.1.0", - "@typescript-eslint/eslint-plugin": "^4.8.0", - "@typescript-eslint/parser": "^4.8.0", - "@vitejs/plugin-vue": "^1.0.0", - "@vue/babel-plugin-jsx": "^1.0.0", - "@vue/compiler-sfc": "^3.0.0", - "@vue/eslint-config-prettier": "^6.0.0", - "@vue/eslint-config-typescript": "^7.0.0", - "@vue/test-utils": "^2.0.0-beta.12", - "babel-jest": "^26.6.0", - "chalk": "^4.1.0", - "codecov": "^3.8.0", - "commitizen": "^4.2.0", - "cz-conventional-changelog": "^3.3.0", - "detect-port": "^1.3.0", - "eslint": "^7.14.0", - "eslint-config-prettier": "^6.15.0", - "eslint-plugin-markdown": "^2.0.0-rc.0", - "eslint-plugin-prettier": "^3.1.0", - "eslint-plugin-vue": "^7.1.0", - "fs-extra": "^9.0.0", - "gulp": "^4.0.0", - "gulp-clean": "^0.4.0", - "husky": "^4.3.0", - "jest": "^26.6.0", - "jest-junit": "^12.0.0", - "lerna": "^3.22.0", - "less": "^3.12.2", - "lint-staged": "^10.5.0", - "lodash": "^4.17.0", - "markdownlint-cli": "^0.25.0", - "marked": "^1.2.0", - "parse5": "^6.0.0", - "prettier": "^2.0.0", - "prismjs": "^1.22.0", - "remark": "^13.0.0", - "resolve-bin": "^0.4.0", - "rimraf": "^3.0.0", - "stylelint": "^13.0.0", - "stylelint-config-prettier": "^8.0.0", - "stylelint-config-standard": "^20.0.0", - "svgo": "^1.3.0", - "ts-jest": "^26.4.0", - "ts-node": "^9.1.0", - "tslib": "^2.0.0", - "typescript": "^4.1.0", - "vite": "^v2.0.0-beta.2", - "vue": "^3.0.5", - "vue-jest": "^5.0.0-alpha.7", - "vue-router": "^4.0.0", - "vue-types": "^3.0.0", - "yaml-front-matter": "^4.1.0" - }, - "husky": { - "hooks": { - "pre-commit": "ls-lint && lint-staged", - "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" - } - }, - "lint-staged": { - "*.{js,ts,vue,json}": [ - "prettier --write" - ], - "*.{js,ts,vue}": [ - "eslint --fix" - ], - "*.less": [ - "stylelint --fix" - ], - "*.md": [ - "markdownlint --fix" - ] - }, - "config": { - "commitizen": { - "path": "./node_modules/cz-conventional-changelog" - } - }, - "engines": { - "node": ">11.5.0" - } -} +{ + "name": "idux", + "version": "0.0.0-NOT-USED", + "description": "A UI Component Library for Vue 3.x", + "private": true, + "scripts": { + "start": "gulp start:dev", + "icons": "gulp icons:start", + "cz": "git-cz", + "gen": "ts-node --project scripts/gen/tsconfig.json scripts/gen/index.ts", + "postinstall": "lerna bootstrap", + "codecov": "codecov -t", + "test": "jest", + "lint": "npm run ls-lint && npm run eslint && npm run stylelint && npm run markdownlint", + "lint-fix": "npm run ls-lint && npm run eslint-fix && npm run stylelint-fix && npm run markdownlint-fix", + "eslint": "eslint ./packages --ext .vue,.js,.ts", + "eslint-fix": "eslint --fix ./packages --ext .vue,.js,.ts", + "stylelint": "stylelint \"./packages/components/**/*.less\" --syntax less", + "stylelint-fix": "stylelint --fix \"./packages/components/**/*.less\" --syntax less", + "markdownlint": "markdownlint \"./packages/{cdk,components,pro}/**/*.md\"", + "markdownlint-fix": "markdownlint --fix \"./packages/{cdk,components,pro}/**/*.md\"", + "ls-lint": "ls-lint" + }, + "license": "MIT", + "devDependencies": { + "@babel/preset-env": "^7.12.0", + "@babel/preset-typescript": "^7.12.0", + "@commitlint/cli": "^11.0.0", + "@commitlint/config-angular": "^11.0.0", + "@ls-lint/ls-lint": "^1.9.0", + "@types/detect-port": "^1.3.0", + "@types/fs-extra": "^9.0.0", + "@types/gulp": "^4.0.0", + "@types/jest": "^26.0.0", + "@types/lodash": "^4.14.0", + "@types/marked": "^1.2.0", + "@types/parse5": "^5.0.0", + "@types/prismjs": "^1.16.0", + "@types/svgo": "^1.3.0", + "@types/yaml-front-matter": "^4.1.0", + "@typescript-eslint/eslint-plugin": "^4.8.0", + "@typescript-eslint/parser": "^4.8.0", + "@vitejs/plugin-vue": "^1.0.0", + "@vue/babel-plugin-jsx": "^1.0.0", + "@vue/compiler-sfc": "^3.0.0", + "@vue/eslint-config-prettier": "^6.0.0", + "@vue/eslint-config-typescript": "^7.0.0", + "@vue/test-utils": "^2.0.0-beta.12", + "babel-jest": "^26.6.0", + "chalk": "^4.1.0", + "codecov": "^3.8.0", + "commitizen": "^4.2.0", + "cz-conventional-changelog": "^3.3.0", + "detect-port": "^1.3.0", + "eslint": "^7.14.0", + "eslint-config-prettier": "^6.15.0", + "eslint-plugin-markdown": "^2.0.0-rc.0", + "eslint-plugin-prettier": "^3.1.0", + "eslint-plugin-vue": "^7.1.0", + "fs-extra": "^9.0.0", + "gulp": "^4.0.0", + "gulp-clean": "^0.4.0", + "husky": "^4.3.0", + "jest": "^26.6.0", + "jest-junit": "^12.0.0", + "lerna": "^3.22.0", + "less": "^3.12.2", + "lint-staged": "^10.5.0", + "lodash": "^4.17.0", + "markdownlint-cli": "^0.25.0", + "marked": "^1.2.0", + "mitt": "^2.1.0", + "parse5": "^6.0.0", + "prettier": "^2.0.0", + "prismjs": "^1.22.0", + "remark": "^13.0.0", + "resolve-bin": "^0.4.0", + "rimraf": "^3.0.0", + "stylelint": "^13.0.0", + "stylelint-config-prettier": "^8.0.0", + "stylelint-config-standard": "^20.0.0", + "svgo": "^1.3.0", + "ts-jest": "^26.4.0", + "ts-node": "^9.1.0", + "tslib": "^2.0.0", + "typescript": "^4.1.0", + "vite": "^v2.0.0-beta.2", + "vue": "^3.0.5", + "vue-jest": "^5.0.0-alpha.7", + "vue-router": "^4.0.0", + "vue-types": "^3.0.0", + "yaml-front-matter": "^4.1.0" + }, + "husky": { + "hooks": { + "pre-commit": "ls-lint && lint-staged", + "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" + } + }, + "lint-staged": { + "*.{js,ts,vue,json}": [ + "prettier --write" + ], + "*.{js,ts,vue}": [ + "eslint --fix" + ], + "*.less": [ + "stylelint --fix" + ], + "*.md": [ + "markdownlint --fix" + ] + }, + "config": { + "commitizen": { + "path": "./node_modules/cz-conventional-changelog" + } + }, + "engines": { + "node": ">11.5.0" + } +} \ No newline at end of file diff --git a/packages/components/checkbox/__tests__/__snapshots__/checkbox.spec.ts.snap b/packages/components/checkbox/__tests__/__snapshots__/checkbox.spec.ts.snap new file mode 100644 index 000000000..754e5346d --- /dev/null +++ b/packages/components/checkbox/__tests__/__snapshots__/checkbox.spec.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Checkbox.vue render work 1`] = `""`; diff --git a/packages/components/checkbox/__tests__/__snapshots__/checkboxGroup.spec.ts.snap b/packages/components/checkbox/__tests__/__snapshots__/checkboxGroup.spec.ts.snap new file mode 100644 index 000000000..39e0d9c1b --- /dev/null +++ b/packages/components/checkbox/__tests__/__snapshots__/checkboxGroup.spec.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CheckboxGroup.vue and Checkbox.vue render work 1`] = `"
"`; diff --git a/packages/components/checkbox/__tests__/checkbox.spec.ts b/packages/components/checkbox/__tests__/checkbox.spec.ts new file mode 100644 index 000000000..8ba0035b2 --- /dev/null +++ b/packages/components/checkbox/__tests__/checkbox.spec.ts @@ -0,0 +1,158 @@ +import { mount } from '@vue/test-utils' +import { ref, nextTick } from 'vue' +import IxCheckbox from '../src/Checkbox.vue' + +describe('Checkbox.vue', () => { + test('render work', () => { + const checked = ref(false) + const wrapper = mount({ + components: { IxCheckbox }, + template: `option`, + setup() { + return { checked } + }, + }) + expect(wrapper.html()).toMatchSnapshot() + }) + + test('checked(v-model) work', async () => { + const checked = ref(false) + const wrapper = mount({ + components: { IxCheckbox }, + template: `option`, + setup() { + return { checked } + }, + }) + + expect(wrapper.find('.ix-checkbox-checked').exists()).toBe(false) + + checked.value = true + + await nextTick() + expect(wrapper.find('.ix-checkbox-checked').exists()).toBe(true) + + await wrapper.trigger('click') + + expect(checked.value).toBe(false) + expect(wrapper.find('.ix-checkbox-checked').exists()).toBe(false) + }) + + test('no checked(v-model) work', async () => { + const wrapper = mount({ + components: { IxCheckbox }, + template: `option`, + }) + + expect(wrapper.find('.ix-checkbox-checked').exists()).toBe(false) + + await wrapper.trigger('click') + + expect(wrapper.find('.ix-checkbox-checked').exists()).toBe(true) + }) + + test('trueValue and falseValue work', async () => { + const checked = ref('yes') + const wrapper = mount({ + components: { IxCheckbox }, + template: `option`, + setup() { + return { checked } + }, + }) + + await wrapper.trigger('click') + + expect(checked.value).toBe('no') + }) + + test('disabled work', async () => { + const checked = ref(true) + const disabled = ref(true) + const mockFn = jest.fn() + const wrapper = mount({ + components: { IxCheckbox }, + template: `option`, + setup() { + return { checked, disabled, mockFn } + }, + }) + + expect(wrapper.classes()).toContain('ix-checkbox-disabled') + + await wrapper.trigger('click') + + expect(wrapper.classes()).toContain('ix-checkbox-disabled') + expect(checked.value).toBe(true) + expect(mockFn).toBeCalledTimes(0) + + disabled.value = false + + await nextTick() + + expect(wrapper.classes()).not.toContain('ix-checkbox-disabled') + + await wrapper.trigger('click') + + expect(checked.value).toBe(false) + expect(mockFn).toBeCalledTimes(1) + }) + + test('indeterminate work', async () => { + const checked = ref(true) + const indeterminate = ref(true) + const wrapper = mount({ + components: { IxCheckbox }, + template: `option`, + setup() { + return { checked, indeterminate } + }, + }) + + expect(wrapper.classes()).toContain('ix-checkbox-indeterminate') + expect(wrapper.classes()).not.toContain('ix-checkbox-checked') + + indeterminate.value = false + + await nextTick() + + expect(wrapper.classes()).not.toContain('ix-checkbox-indeterminate') + expect(wrapper.classes()).toContain('ix-checkbox-checked') + }) + + test('change event work', async () => { + const checked = ref(true) + const mockFn = jest.fn() + const wrapper = mount({ + components: { IxCheckbox }, + template: `option`, + setup() { + return { checked, mockFn } + }, + }) + + expect(mockFn).toBeCalledTimes(0) + + await wrapper.trigger('click') + + expect(mockFn).toBeCalledTimes(1) + }) + + test('original checkbox attributes work', async () => { + const wrapper = mount({ + components: { IxCheckbox }, + template: `option`, + }) + + const checkboxWrapper = wrapper.find('.ix-checkbox') + const originalInput = wrapper.find("input[type='checkbox']") + const input = wrapper.find('.ix-checkbox-inner') + + expect(checkboxWrapper.attributes()['name']).not.toBeDefined() + expect(checkboxWrapper.attributes()['value']).not.toBeDefined() + expect(checkboxWrapper.attributes()['tabindex']).not.toBeDefined() + expect(originalInput.attributes()['name']).toEqual('checkboxName') + expect(originalInput.attributes()['value']).toEqual('checkboxValue') + expect(input.attributes()['tabindex']).toEqual('1') + }) +}) diff --git a/packages/components/checkbox/__tests__/checkboxGroup.spec.ts b/packages/components/checkbox/__tests__/checkboxGroup.spec.ts new file mode 100644 index 000000000..1334c28d8 --- /dev/null +++ b/packages/components/checkbox/__tests__/checkboxGroup.spec.ts @@ -0,0 +1,148 @@ +import { mount } from '@vue/test-utils' +import CheckboxGroup from '../src/CheckboxGroup.vue' +import Checkbox from '../src/Checkbox.vue' +import { nextTick, Ref, ref } from 'vue' + +describe('CheckboxGroup.vue and Checkbox.vue', () => { + test('render work', () => { + const wrapper = mount({ + components: { CheckboxGroup, Checkbox }, + template: ` + + option1 + option2 + option3 + + `, + }) + expect(wrapper.findAll('.ix-checkbox').length).toBe(3) + expect(wrapper.html()).toMatchSnapshot() + }) + + test('value(v-model) work', async () => { + const value = ref([]) as Ref + const wrapper = mount({ + components: { CheckboxGroup, Checkbox }, + template: ` + + option1 + option2 + option3 + + `, + setup() { + return { value } + }, + }) + + expect(wrapper.findAll('.ix-checkbox-checked').length).toBe(0) + + value.value = ['option1'] + + await nextTick() + + expect(wrapper.findAllComponents({ name: 'IxCheckbox' })[0].classes()).toContain('ix-checkbox-checked') + + await wrapper.findAllComponents({ name: 'IxCheckbox' })[0].trigger('click') + + expect(value.value).toEqual([]) + }) + + test('no value(v-model) work', async () => { + const wrapper = mount({ + components: { CheckboxGroup, Checkbox }, + template: ` + + option1 + option2 + option3 + + `, + setup() { + return {} + }, + }) + + expect(wrapper.findAll('.ix-checkbox-checked').length).toBe(0) + + await wrapper.findAllComponents({ name: 'IxCheckbox' })[0].trigger('click') + + expect(wrapper.findAllComponents({ name: 'IxCheckbox' })[0].classes()).toContain('ix-checkbox-checked') + }) + + test('change event work', async () => { + const mockFn = jest.fn() + const wrapper = mount({ + components: { CheckboxGroup, Checkbox }, + template: ` + + option1 + option2 + option3 + + `, + setup() { + return { mockFn } + }, + }) + + expect(mockFn).toBeCalledTimes(0) + + await wrapper.findAllComponents({ name: 'IxCheckbox' })[0].trigger('click') + + expect(mockFn).toBeCalledTimes(1) + }) + + test('disabled work', async () => { + const value = ref([]) as Ref + const disabled = ref(true) + const mockFn = jest.fn() + const wrapper = mount({ + components: { CheckboxGroup, Checkbox }, + template: ` + + option1 + option2 + option3 + + `, + setup() { + return { value, disabled, mockFn } + }, + }) + + expect(wrapper.findAll('.ix-checkbox-disabled').length).toBe(3) + await wrapper.findAllComponents({ name: 'IxCheckbox' })[0].trigger('click') + expect(value.value).toEqual([]) + expect(mockFn).toBeCalledTimes(0) + + disabled.value = false + + await nextTick() + + expect(wrapper.findAll('.ix-checkbox-disabled').length).toBe(0) + + await wrapper.findAllComponents({ name: 'IxCheckbox' })[0].trigger('click') + expect(value.value).toEqual(['option1']) + expect(mockFn).toBeCalledTimes(1) + }) + + test('name work', async () => { + const wrapper = mount({ + components: { CheckboxGroup, Checkbox }, + template: ` + + option1 + option2 + option3 + + `, + setup() { + return {} + }, + }) + + expect(wrapper.findAll('input[name=group]').length).toBe(2) + expect(wrapper.findAll('input[name=child]').length).toBe(1) + }) +}) diff --git a/packages/components/checkbox/demo/Basic.vue b/packages/components/checkbox/demo/Basic.vue new file mode 100644 index 000000000..4ef8ab938 --- /dev/null +++ b/packages/components/checkbox/demo/Basic.vue @@ -0,0 +1,20 @@ + + + + diff --git a/packages/components/checkbox/demo/Disabled.vue b/packages/components/checkbox/demo/Disabled.vue new file mode 100644 index 000000000..79098c928 --- /dev/null +++ b/packages/components/checkbox/demo/Disabled.vue @@ -0,0 +1,43 @@ + + + + diff --git a/packages/components/checkbox/demo/Group.vue b/packages/components/checkbox/demo/Group.vue new file mode 100644 index 000000000..c1f1d671c --- /dev/null +++ b/packages/components/checkbox/demo/Group.vue @@ -0,0 +1,24 @@ + + + + diff --git a/packages/components/checkbox/demo/Indeterminate.vue b/packages/components/checkbox/demo/Indeterminate.vue new file mode 100644 index 000000000..cac0b42db --- /dev/null +++ b/packages/components/checkbox/demo/Indeterminate.vue @@ -0,0 +1,48 @@ + + + + diff --git a/packages/components/checkbox/demo/TrueFalse.vue b/packages/components/checkbox/demo/TrueFalse.vue new file mode 100644 index 000000000..08f8f42fe --- /dev/null +++ b/packages/components/checkbox/demo/TrueFalse.vue @@ -0,0 +1,20 @@ + + + + diff --git a/packages/components/checkbox/demo/basic.md b/packages/components/checkbox/demo/basic.md new file mode 100644 index 000000000..5063d6cda --- /dev/null +++ b/packages/components/checkbox/demo/basic.md @@ -0,0 +1,14 @@ +--- +order: 0 +title: + zh: 基本使用 + en: Basic usage +--- + +## zh + +`ix-checkbox` 的基础用法。 + +## en + +The basic usage of `ix-checkbox`. diff --git a/packages/components/checkbox/demo/disabled.md b/packages/components/checkbox/demo/disabled.md new file mode 100644 index 000000000..8e86a17c5 --- /dev/null +++ b/packages/components/checkbox/demo/disabled.md @@ -0,0 +1,14 @@ +--- +order: 2 +title: + zh: Disabled 基本使用 + en: Disabled usage +--- + +## zh + +用来控制`ix-checkbox`的禁用状态,与`checked`独立。 + +## en + +It is used to control the disabled state of the checkbox and is independent of checked. diff --git a/packages/components/checkbox/demo/group.md b/packages/components/checkbox/demo/group.md new file mode 100644 index 000000000..9bba4e998 --- /dev/null +++ b/packages/components/checkbox/demo/group.md @@ -0,0 +1,14 @@ +--- +order: 3 +title: + zh: CheckboxGroup 使用 + en: CheckboxGroup usage +--- + +## zh + +`ix-checkboxGroup` 基本使用 + +## en + +The basic usage of `ix-checkboxGroup`. diff --git a/packages/components/checkbox/demo/indeterminate.md b/packages/components/checkbox/demo/indeterminate.md new file mode 100644 index 000000000..dd733f64b --- /dev/null +++ b/packages/components/checkbox/demo/indeterminate.md @@ -0,0 +1,14 @@ +--- +order: 4 +title: + zh: indeterminate 使用 + en: indeterminate usage +--- + +## zh + +`indeterminate` 属性用以表示 `ix-checkbox` 的不确定状态,用于实现全选的效果。 + +## en + +The attribute `indeterminate` is used to represent the uncertain state of the `ix-checkbox` and to achieve the effect of selecting all. diff --git a/packages/components/checkbox/demo/trueFalse.md b/packages/components/checkbox/demo/trueFalse.md new file mode 100644 index 000000000..0885a642b --- /dev/null +++ b/packages/components/checkbox/demo/trueFalse.md @@ -0,0 +1,14 @@ +--- +order: 1 +title: + zh: trueValue 和 falseValue 使用 + en: trueValue and falseValue usage +--- + +## zh + +控制`ix-checkbox`是否选中的返回值。 + +## en + +The return value that controls whether the `ix-checkbox` is selected. diff --git a/packages/components/checkbox/docs/index.zh.md b/packages/components/checkbox/docs/index.zh.md new file mode 100644 index 000000000..ec1fe6ddb --- /dev/null +++ b/packages/components/checkbox/docs/index.zh.md @@ -0,0 +1,83 @@ +--- +category: components +type: 通用 +title: Checkbox +subtitle: 多选框 +cover: +--- + +## 何时使用 + +单独使用可以表示两种状态之间的切换 +在一组可选项中进行多项选择 + +## API + +### `ix-checkbox` + +#### Props + +除以下表格之外还支持原生 元素的所有属性。 +| 参数 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | +| --- | --- | --- | --- | --- | --- | +| `v-model:checked` | 指定当前勾选框是否选中 | `boolean` | `false` | - | - | +| `disabled` | 禁用状态 |`boolean`| `false` | - | - | +| `indeterminate` | 是否处于不确定状态 | `boolean` | `false`| - | 当值为true时,按钮样式处于半选状态,且不受checked影响 | +| `trueValue` | 勾选框选中时返回的值 | `string \| number` | `true`| - | - | +| `falseValue` | 勾选框不选中时返回的值 | `string \| number` | `false`| - | - | + +#### Slots + +|名称 | 说明 | 参数类型 | 备注 | +| --- | --- | --- | --- | +|`defalut` | 文本区域 | - | - | + +#### Emits + +| 名称 | 说明 | 参数类型 | 备注 | +| --- | --- | --- | --- | +| `change` | 点击勾选框时触发 | `Function(checkValue:boolean \| string \| number)` | - | + +### `ix-checkbox-group` + +#### Group Props + +| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | +| --- | --- | --- | --- | --- | --- |value +| `v-model:value` | 指定当前勾选框是否选中 | `boolean` | `false` | - | - | +| `disabled` | 子`ix-checkbox`禁用状态 | `boolean` | `false` | - |- | +| `name` | 子`ix-checkbox` 的 name 属性 | `string` | - | - |- | + +#### Group Slots + +|名称 | 说明 | 参数类型 | 备注 | +| --- | --- | --- | --- | +|`defalut` | `ix-checkbox`区域 | - | - | + +#### Group Emits + +| 名称 | 说明 | 参数类型 | 备注 | +| --- | --- | --- | --- | +| `change` | `value`变化时的回调函数 | `Function(checkValue:string[])` | - | + +### 主题变量 + +| 变量名 | default 主题| 说明 | +| --- | --- | --- | +|@checkbox-border-color| @grey-d10| -| +|@checkbox-bg-color| @white|-| +|@checkbox-tick-color| @white|-| +|@checkbox-bg-indeterminate-color| @primary-color|-| +|@checkbox-border-checked-color| @primary-color|-| +|@checkbox-bg-checked-color| @primary-color|-| +|@checkbox-border-disabled-color| @grey|-| +|@checkbox-tick-disabled-color| @grey|-| +|@checkbox-bg-disabled-color| @white|-| +|@checkbox-bg-indeterminate-disabled-color| @grey|-| +|@checkbox-label-disabled-color| @grey|-| +|@checkbox-font-size| @font-size-md|-| +|@checkbox-group-line-height| @line-height-base|-| +|@checkbox-height| 16px|-| +|@checkbox-width| 16px|-| + + diff --git a/packages/components/checkbox/index.ts b/packages/components/checkbox/index.ts new file mode 100644 index 000000000..fbd32e807 --- /dev/null +++ b/packages/components/checkbox/index.ts @@ -0,0 +1,9 @@ +import { installComponent } from '@idux/components/core/utils' +import IxCheckbox from './src/Checkbox.vue' +import IxCheckboxGroup from './src/CheckboxGroup.vue' + +IxCheckbox.install = installComponent(IxCheckbox) +IxCheckboxGroup.install = installComponent(IxCheckboxGroup) + +export { IxCheckbox, IxCheckboxGroup } +export * from './src/types' diff --git a/packages/components/checkbox/src/Checkbox.vue b/packages/components/checkbox/src/Checkbox.vue new file mode 100644 index 000000000..a704d65c0 --- /dev/null +++ b/packages/components/checkbox/src/Checkbox.vue @@ -0,0 +1,43 @@ + + diff --git a/packages/components/checkbox/src/CheckboxGroup.vue b/packages/components/checkbox/src/CheckboxGroup.vue new file mode 100644 index 000000000..7070e0c83 --- /dev/null +++ b/packages/components/checkbox/src/CheckboxGroup.vue @@ -0,0 +1,58 @@ + + + diff --git a/packages/components/checkbox/src/checkbox.ts b/packages/components/checkbox/src/checkbox.ts new file mode 100644 index 000000000..de1529b4f --- /dev/null +++ b/packages/components/checkbox/src/checkbox.ts @@ -0,0 +1,11 @@ +import type { CheckboxGroupProps } from './types' + +import { InjectionKey } from 'vue' + +import { Subject } from '@idux/cdk/subject' + +export const checkboxGroupInjectionKey: InjectionKey = Symbol() + +export type SubjectType = { value: string } + +export const subjectInjectKey: InjectionKey> = Symbol() diff --git a/packages/components/checkbox/src/setup.ts b/packages/components/checkbox/src/setup.ts new file mode 100644 index 000000000..f3f9a6b70 --- /dev/null +++ b/packages/components/checkbox/src/setup.ts @@ -0,0 +1,127 @@ +import { computed, ComputedRef, inject, Ref, ref, watch, getCurrentInstance, SetupContext } from 'vue' +import { checkboxGroupInjectionKey, subjectInjectKey } from './checkbox' +import { isArray, hasSlot } from '@idux/cdk/utils' +import type { CheckboxProps } from './types' +import { useAttrs } from '@idux/components/core/utils/useAttrs' + +type CheckValue = number | string | boolean + +export const setup = ( + props: CheckboxProps, + { slots }: SetupContext, +): { + isChecked: ComputedRef + isDisabled: ComputedRef + hasDefaultSlot: ComputedRef + handleChange: (e: Event) => void + classes: ComputedRef< + { 'ix-checkbox-disabled': boolean; 'ix-checkbox-indeterminate': boolean; 'ix-checkbox-checked': boolean }[] + > + inputName: ComputedRef + attrs: Ref> +} => { + const hasDefaultSlot = computed(() => hasSlot(slots)) + + const isDisabled = useDisabled() + + const { checkeValue, handleChange } = useCheckValue(isDisabled) + + const isChecked = useChecked(checkeValue as Ref) + + const classes = useClasses(isDisabled, isChecked) + + const inputName = useName() + + const attrs = useAttrs({ keys: ['type', 'tabindex'] }) + + return { + isChecked, + isDisabled, + hasDefaultSlot, + handleChange, + classes, + inputName, + attrs, + } +} + +const useCheckValue = (isDisabled: ComputedRef) => { + //eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const { props, attrs, emit } = getCurrentInstance()! + const groupSubject = inject(subjectInjectKey, null) + const checkeValue = ref(props.checked ?? props.falseValue) + + watch( + () => props.checked, + v => { + checkeValue.value = v + }, + ) + + const handleChange = (e: Event) => { + if (isDisabled.value) { + return + } + const targetChecked = (e.target as EventTarget).checked + const targetCheckValue = targetChecked ? props.trueValue : props.falseValue + // no value props passed + if (props.checked === void 0) { + checkeValue.value = targetCheckValue + } + emit('change', targetCheckValue) + emit('update:checked', targetCheckValue) + groupSubject?.dispatch({ value: (attrs.value ?? targetCheckValue) as string }) + } + + return { + checkeValue, + handleChange, + } +} + +const useChecked = (checkValue: Ref) => { + const groupProps = inject(checkboxGroupInjectionKey, {}) + //eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const { props, attrs } = getCurrentInstance()! + + return computed(() => { + const groupValue = groupProps.value + if (groupValue && isArray(groupValue)) { + return groupValue.includes((attrs.value as string) ?? checkValue.value) + } + + return checkValue.value === props.trueValue + }) +} + +const useDisabled = () => { + const groupProps = inject(checkboxGroupInjectionKey, {}) + //eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const { props } = getCurrentInstance()! + return computed(() => { + return !!(groupProps.disabled || props.disabled) + }) +} + +const useClasses = (isDisabled: ComputedRef, isChecked: ComputedRef) => { + //eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const { props } = getCurrentInstance()! + + return computed(() => { + const disabled = isDisabled.value + const checked = isChecked.value + const indeterminate = props.indeterminate + return [ + { + 'ix-checkbox-disabled': !!disabled, + 'ix-checkbox-indeterminate': !!indeterminate, + 'ix-checkbox-checked': !indeterminate && checked, + }, + ] + }) +} + +const useName = () => { + const groupProps = inject(checkboxGroupInjectionKey, {}) + return computed(() => groupProps.name ?? '') +} diff --git a/packages/components/checkbox/src/types.ts b/packages/components/checkbox/src/types.ts new file mode 100644 index 000000000..14749c841 --- /dev/null +++ b/packages/components/checkbox/src/types.ts @@ -0,0 +1,17 @@ +export interface CheckboxOriginalProps { + checked?: string | number | boolean + disabled?: boolean + indeterminate?: boolean + trueValue?: string | number | boolean + falseValue?: string | number | boolean +} + +export interface CheckboxGroupOriginalProps { + value?: string[] + disabled?: boolean + name?: string +} + +export type CheckboxProps = Readonly + +export type CheckboxGroupProps = Readonly diff --git a/packages/components/checkbox/style/index.less b/packages/components/checkbox/style/index.less new file mode 100644 index 000000000..f772dadf1 --- /dev/null +++ b/packages/components/checkbox/style/index.less @@ -0,0 +1,145 @@ +@import '../../style/default.less'; + +@checkbox-prefix: ~'@{idux-prefix}-checkbox'; +@checkbox-group-prefix: ~'@{idux-prefix}-checkbox-group'; + +.@{checkbox-prefix} { + box-sizing: border-box; + font-size: @checkbox-font-size; + line-height: 1; + position: relative; + display: inline-block; + white-space: nowrap; + vertical-align: middle; + outline: none; + cursor: pointer; + + &-input-wrapper { + box-sizing: border-box; + list-style: none; + position: relative; + display: inline-block; + white-space: nowrap; + vertical-align: middle; + outline: none; + cursor: pointer; + } + + &-inner { + display: inline-block; + position: relative; + border: 1px solid @checkbox-border-color; + border-radius: 2px; + box-sizing: border-box; + width: @checkbox-width; + height: @checkbox-height; + background-color: @checkbox-bg-color; + z-index: 1; + transition: background-color ease .25s; + + &::after { + position: absolute; + top: 50%; + left: 22%; + display: table; + width: 5px; + height: 8px; + border: 2px solid @checkbox-tick-color; + border-top: 0; + border-left: 0; + transform: rotate(45deg) scale(0) translate(-50%, -50%); + transition: transform .25s ease; + opacity: 0; + content: " "; + } + } + + &-input { + opacity: 0; + outline: none; + position: absolute; + margin: 0; + width: 0; + height: 0; + z-index: -1; + } + + &-label { + padding: 0 8px; + } + + &-checked { + .@{checkbox-prefix} { + &-inner { + background-color: @checkbox-bg-checked-color; + border-color: @checkbox-border-checked-color; + + &::after { + opacity: 1; + transform: rotate(45deg) scale(1) translate(-50%, -50%); + } + } + } + } + + &-indeterminate { + .@{checkbox-prefix} { + &-inner { + + &::after { + left: 50%; + width: 8px; + height: 8px; + background-color: @checkbox-bg-indeterminate-color; + border: 0; + transform: translate(-50%, -50%) scale(1); + opacity: 1; + content: " " + } + } + } + } + + &-disabled { + &.@{checkbox-prefix}-checked { + .@{checkbox-prefix}-inner::after { + border-color: @checkbox-tick-disabled-color; + } + } + + &.@{checkbox-prefix}-indeterminate { + .@{checkbox-prefix}-inner::after { + background-color: @checkbox-bg-indeterminate-disabled-color; + } + } + + .@{checkbox-prefix} { + &-inner { + background-color: @checkbox-bg-disabled-color; + border-color: @checkbox-border-disabled-color !important; + } + + &-input { + cursor: not-allowed; + } + + &-label { + color: @checkbox-label-disabled-color; + cursor: not-allowed; + } + } + } +} + +.@{checkbox-group-prefix} { + box-sizing: border-box; + margin: 0; + padding: 0; + line-height: @checkbox-group-line-height; + list-style: none; + display: inline-block; + + .@{checkbox-prefix} { + margin-right: 8px; + } +} diff --git a/packages/components/components.less b/packages/components/components.less index df3b059a9..ad7907eba 100644 --- a/packages/components/components.less +++ b/packages/components/components.less @@ -1,11 +1,12 @@ -@import './button/style/index.less'; -@import './icon/style/index.less'; -@import './badge/style/index.less'; -@import './divider/style/index.less'; -@import './image/style/index.less'; -@import './spin/style/index.less'; -@import './space/style/index.less'; -@import './empty/style/index.less'; -@import './result/style/index.less'; -@import "./typography/style/index.less"; -@import './rate/style/index.less'; +@import './button/style/index.less'; +@import './icon/style/index.less'; +@import './badge/style/index.less'; +@import './divider/style/index.less'; +@import './image/style/index.less'; +@import './spin/style/index.less'; +@import './space/style/index.less'; +@import './empty/style/index.less'; +@import './result/style/index.less'; +@import "./typography/style/index.less"; +@import './rate/style/index.less'; +@import './checkbox/style/index.less'; diff --git a/packages/components/index.ts b/packages/components/index.ts index 8352b13a4..a2b96bab5 100644 --- a/packages/components/index.ts +++ b/packages/components/index.ts @@ -10,6 +10,7 @@ import { IxEmpty } from './empty' import { IxResult } from './result' import { IxTypography } from './typography' import { IxRate } from './rate' +import { IxCheckbox, IxCheckboxGroup } from './checkbox' const components = [ IxButton, @@ -23,6 +24,8 @@ const components = [ IxEmpty, IxResult, IxRate, + IxCheckbox, + IxCheckboxGroup, ] const directives: Record = { @@ -59,3 +62,4 @@ export { IxEmpty } export { IxResult } export { IxTypography } export { IxRate } +export { IxCheckbox, IxCheckboxGroup } diff --git a/packages/components/style/default.less b/packages/components/style/default.less index 159118d88..c6e4f881d 100644 --- a/packages/components/style/default.less +++ b/packages/components/style/default.less @@ -1,167 +1,188 @@ -@import './mixins/index.less'; -@import './variable/index.less'; - -@theme: default; - -@idux-prefix: ix; - -/* height -------------------------- */ -@height-sm: 24px; -@height-md: 32px; -@height-lg: 36px; - -@line-height-base: 1.5715; - -/* color -------------------------- */ -@primary-color: @blue; -@info-color: @primary-color; -@success-color: @green; -@warning-color: @yellow; -@error-color: @red; - -@body-background: @white; -@component-background: @white; - -/* text -------------------------- */ -@text-color: @black; - -/* button -------------------------- */ -@button-font-weight: @font-weight-md; - -@button-line-height: @line-height-base; - -@button-height-sm: @height-sm; -@button-height-md: @height-md; -@button-height-lg: @height-lg; - -@button-font-size-sm: @font-size-sm; -@button-font-size-md: @font-size-sm; -@button-font-size-lg: @font-size-md; - -@button-padding-sm: @padding-sm; -@button-padding-md: @padding-md; -@button-padding-lg: @padding-lg; - -@button-icon-margin-left: @margin-xs; - -@button-border-style: @border-style; -@button-border-size: @border-size-sm; -@button-border-radius: @border-radius-sm; - -@button-disable-opacity: 0.4; - -@button-shadow: 0 2px 0 rgba(0, 0, 0, 0.015); - -@button-primary-color: @white; -@button-primary-bg: @primary-color; -@button-primary-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.15); - -@button-default-color: @text-color; -@button-default-bg: @component-background; -@button-default-border: @border-color; - -@button-danger-color: @error-color; -@button-danger-bg: @error-color; - -@button-ghost-color: @component-background; -@button-ghost-bg: transparent; -@button-ghost-border: @component-background; - -@button-link-color: @primary-color; -@button-link-hover-bg: transparent; - -@button-text-color: @text-color; -@button-text-hover-bg: rgba(0, 0, 0, 0.12); - -/* spin -------------------------- */ -@spin-tip-color: @primary-color; -@spin-icon-color: @primary-color; - -@spin-font-size-sm: @font-size-sm; -@spin-font-size-md: @font-size-md; -@spin-font-size-lg: @font-size-lg; - -/* space -------------------------- */ -@space-margin-sm: @margin-sm; -@space-margin-md: @margin-lg; -@space-margin-lg: @margin-xl; - -/* empty -------------------------- */ -@empty-margin-md: @margin-md; -@empty-line-height-md: @line-height-base; -@empty-font-size-md: @font-size-md; -@empty-icon-font-size-md: 64px; - -/* space -------------------------- */ -@result-padding: @spacing-gutter * 6 @spacing-gutter * 4; -@result-text-align: center; -@result-icon-margin: @margin-xl; -@result-icon-size: @font-size-lg * 4; -@result-title-font-size: @font-size-xl; -@result-title-color: @black-d20; -@result-title-line-height: 1.8; -@result-subtitle-font-size: @font-size-sm; -@result-subtitle-color: @black-l40; -@result-subtitle-line-height: 1.6; -@result-extra-margin: @margin-xl; -@result-extra-children-margin: @margin-sm; -@result-content-margin: @margin-xl; -@result-content-padding: @padding-xl @spacing-gutter * 5; -@result-content-background: @grey-l50; -@result-success-color: @success-color; -@result-warning-color: @warning-color; -@result-error-color: @error-color; -@result-info-color: @info-color; - -/* typography -------------------------- */ -@typography-default-color: @black; -@typography-secondary-color: @black-l20; -@typography-success-color: @success-color; -@typography-warning-color: @warning-color; -@typography-error-color: @error-color; -@typography-disabled-color: @black-l50; -@typography-mark-padding: 0; -@typography-mark-background-color: @yellow-l50; -@typography-code-margin: 0 0.2em; -@typography-code-padding: 0.2em 0.4em 0.1em; -@typography-code-font-size: 85%; -@typography-code-background: @grey; -@typography-code-border: @border-size-sm solid @grey-d30; -@typography-code-border-radius: @border-radius-md; -@typography-kbd-margin: 0 0.2em; -@typography-kbd-padding: 0.15em 0.4em 0.1em; -@typography-kbd-font-size: 90%; -@typography-kbd-background: @grey; -@typography-kbd-border-style: solid; -@typography-kbd-border-color: @grey-d30; -@typography-kbd-border-width: @border-size-sm @border-size-sm @border-size-md; -@typography-kbd-border-radius: @border-radius-md; -@typography-strong-font-weight: @font-weight-lg; -@typography-a-color: @primary-color; -@typography-a-outline: none; -@typography-a-cursor: pointer; -@typography-a-transition: color 0.3s; -@typography-a-color-hover-focus: @blue-l30; -@typography-a-color-active: @blue-d30; -@typography-title-margin-top: 1.2em; -@typography-title-margin-bottom: 0.5em; -@typography-title-font-weight: @font-weight-lg; -@typography-title-color: @black-l10; -@typography-title-1-font-size: ceil(@font-size-base * 2.71); -@typography-title-2-font-size: ceil(@font-size-base * 2.14); -@typography-title-3-font-size: ceil(@font-size-base * 1.71); -@typography-title-4-font-size: ceil(@font-size-base * 1.42); -@typography-title-5-font-size: ceil(@font-size-base * 1.14); -@typography-paragraph-margin-bottom: 1em; -@typography-list-margin: 0 0 1em 0; -@typography-list-padding: 0; -@typography-list-item-margin: 0 0 0 20px; -@typography-list-item-padding: 0 0 0 4px; -@typography-ul-list-style: circle; -@typography-sub-ul-list-style: disc; -@typography-ol-list-style: decimal; -/* rate -------------------------- */ -@rate-icon-size: 24px; -@rate-icon-margin: 8px; -@rate-stroke-color: #f7ba2a; -@rate-background-color: #c6d1de; +@import './mixins/index.less'; +@import './variable/index.less'; + +@theme: default; + +@idux-prefix: ix; + +/* height -------------------------- */ +@height-sm: 24px; +@height-md: 32px; +@height-lg: 36px; + +@line-height-base: 1.5715; + +/* color -------------------------- */ +@primary-color: @blue; +@info-color: @primary-color; +@success-color: @green; +@warning-color: @yellow; +@error-color: @red; + +@body-background: @white; +@component-background: @white; + +/* text -------------------------- */ +@text-color: @black; + +/* button -------------------------- */ +@button-font-weight: @font-weight-md; + +@button-line-height: @line-height-base; + +@button-height-sm: @height-sm; +@button-height-md: @height-md; +@button-height-lg: @height-lg; + +@button-font-size-sm: @font-size-sm; +@button-font-size-md: @font-size-sm; +@button-font-size-lg: @font-size-md; + +@button-padding-sm: @padding-sm; +@button-padding-md: @padding-md; +@button-padding-lg: @padding-lg; + +@button-icon-margin-left: @margin-xs; + +@button-border-style: @border-style; +@button-border-size: @border-size-sm; +@button-border-radius: @border-radius-sm; + +@button-disable-opacity: 0.4; + +@button-shadow: 0 2px 0 rgba(0, 0, 0, 0.015); + +@button-primary-color: @white; +@button-primary-bg: @primary-color; +@button-primary-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.15); + +@button-default-color: @text-color; +@button-default-bg: @component-background; +@button-default-border: @border-color; + +@button-danger-color: @error-color; +@button-danger-bg: @error-color; + +@button-ghost-color: @component-background; +@button-ghost-bg: transparent; +@button-ghost-border: @component-background; + +@button-link-color: @primary-color; +@button-link-hover-bg: transparent; + +@button-text-color: @text-color; +@button-text-hover-bg: rgba(0, 0, 0, 0.12); + +/* spin -------------------------- */ +@spin-tip-color: @primary-color; +@spin-icon-color: @primary-color; + +@spin-font-size-sm: @font-size-sm; +@spin-font-size-md: @font-size-md; +@spin-font-size-lg: @font-size-lg; + +/* space -------------------------- */ +@space-margin-sm: @margin-sm; +@space-margin-md: @margin-lg; +@space-margin-lg: @margin-xl; + +/* empty -------------------------- */ +@empty-margin-md: @margin-md; +@empty-line-height-md: @line-height-base; +@empty-font-size-md: @font-size-md; +@empty-icon-font-size-md: 64px; + +/* space -------------------------- */ +@result-padding: @spacing-gutter * 6 @spacing-gutter * 4; +@result-text-align: center; +@result-icon-margin: @margin-xl; +@result-icon-size: @font-size-lg * 4; +@result-title-font-size: @font-size-xl; +@result-title-color: @black-d20; +@result-title-line-height: 1.8; +@result-subtitle-font-size: @font-size-sm; +@result-subtitle-color: @black-l40; +@result-subtitle-line-height: 1.6; +@result-extra-margin: @margin-xl; +@result-extra-children-margin: @margin-sm; +@result-content-margin: @margin-xl; +@result-content-padding: @padding-xl @spacing-gutter * 5; +@result-content-background: @grey-l50; +@result-success-color: @success-color; +@result-warning-color: @warning-color; +@result-error-color: @error-color; +@result-info-color: @info-color; + +/* typography -------------------------- */ +@typography-default-color: @black; +@typography-secondary-color: @black-l20; +@typography-success-color: @success-color; +@typography-warning-color: @warning-color; +@typography-error-color: @error-color; +@typography-disabled-color: @black-l50; +@typography-mark-padding: 0; +@typography-mark-background-color: @yellow-l50; +@typography-code-margin: 0 0.2em; +@typography-code-padding: 0.2em 0.4em 0.1em; +@typography-code-font-size: 85%; +@typography-code-background: @grey; +@typography-code-border: @border-size-sm solid @grey-d30; +@typography-code-border-radius: @border-radius-md; +@typography-kbd-margin: 0 0.2em; +@typography-kbd-padding: 0.15em 0.4em 0.1em; +@typography-kbd-font-size: 90%; +@typography-kbd-background: @grey; +@typography-kbd-border-style: solid; +@typography-kbd-border-color: @grey-d30; +@typography-kbd-border-width: @border-size-sm @border-size-sm @border-size-md; +@typography-kbd-border-radius: @border-radius-md; +@typography-strong-font-weight: @font-weight-lg; +@typography-a-color: @primary-color; +@typography-a-outline: none; +@typography-a-cursor: pointer; +@typography-a-transition: color 0.3s; +@typography-a-color-hover-focus: @blue-l30; +@typography-a-color-active: @blue-d30; +@typography-title-margin-top: 1.2em; +@typography-title-margin-bottom: 0.5em; +@typography-title-font-weight: @font-weight-lg; +@typography-title-color: @black-l10; +@typography-title-1-font-size: ceil(@font-size-base * 2.71); +@typography-title-2-font-size: ceil(@font-size-base * 2.14); +@typography-title-3-font-size: ceil(@font-size-base * 1.71); +@typography-title-4-font-size: ceil(@font-size-base * 1.42); +@typography-title-5-font-size: ceil(@font-size-base * 1.14); +@typography-paragraph-margin-bottom: 1em; +@typography-list-margin: 0 0 1em 0; +@typography-list-padding: 0; +@typography-list-item-margin: 0 0 0 20px; +@typography-list-item-padding: 0 0 0 4px; +@typography-ul-list-style: circle; +@typography-sub-ul-list-style: disc; +@typography-ol-list-style: decimal; +/* rate -------------------------- */ +@rate-icon-size: 24px; +@rate-icon-margin: 8px; +@rate-stroke-color: #f7ba2a; +@rate-background-color: #c6d1de; +/* checkbox -------------------------- */ +@checkbox-border-color: @grey-d10; +@checkbox-bg-color: @white; +@checkbox-tick-color: @white; +@checkbox-bg-indeterminate-color: @primary-color; + +@checkbox-border-checked-color: @primary-color; +@checkbox-bg-checked-color: @primary-color; + +@checkbox-border-disabled-color: @grey; +@checkbox-tick-disabled-color: @grey; +@checkbox-bg-disabled-color: @white; +@checkbox-bg-indeterminate-disabled-color: @grey; +@checkbox-label-disabled-color: @grey; + +@checkbox-font-size: @font-size-md; + +@checkbox-height: 16px; +@checkbox-width: 16px; + +@checkbox-group-line-height: @line-height-base; diff --git a/scripts/gen/index.ts b/scripts/gen/index.ts index f5fb62a66..2b95d1637 100644 --- a/scripts/gen/index.ts +++ b/scripts/gen/index.ts @@ -64,7 +64,7 @@ if (moduleName === 'components') { let currIndexContent = readFileSync(indexFilePath, 'utf-8') currIndexContent = currIndexContent .replace('\n\n', `\nimport { Ix${upperFirstComponentName} } from './${compName}'\n\n`) - .replace(']', ` Ix${upperFirstComponentName},\n]`) + .replace(']', `, Ix${upperFirstComponentName}]`) currIndexContent += `export { Ix${upperFirstComponentName} }\n` writeFileSync(indexFilePath, currIndexContent) diff --git a/scripts/gulp/icons/index.ts b/scripts/gulp/icons/index.ts index 510328104..ccfaacbf3 100644 --- a/scripts/gulp/icons/index.ts +++ b/scripts/gulp/icons/index.ts @@ -1,64 +1,64 @@ -import { copySync, ensureDirSync, readdirSync, readFileSync, removeSync, writeFileSync } from 'fs-extra' -import { camelCase, upperFirst } from 'lodash' -import { join } from 'path' -import SVGO from 'svgo' -import { buildConfig } from '../buildConfig' - -const definitionTemplate = `export const {{definitionName}} = { - name: '{{name}}', - svgString: '{{svgString}}', -} -` -const { iconAssetsDir, iconDefinitionsDir, siteIconAssetsDir } = buildConfig -const outputDefinitionNames: string[] = [] - -const options: SVGO.Options = { - plugins: [{ removeAttrs: { attrs: ['fill', 'class'] } }, { sortAttrs: true }, { removeDimensions: true }], -} - -const svgo = new SVGO(options) - -export async function generateIcons(): Promise { - const iconDirname = join(iconAssetsDir) - const iconPaths = readdirSync(iconDirname) - - const outputIcons = iconPaths.map(async iconName => { - const iconFile = join(iconAssetsDir, iconName) - const iconFileContent = readFileSync(iconFile, 'utf8') - await output(iconFileContent, iconName) - }) - await Promise.all(outputIcons) - - const indexContent = - outputDefinitionNames.map(item => `export { ${upperFirst(item)} } from './${item}'`).join('\n') + '\n' - writeFileSync(join(iconDefinitionsDir, `index.ts`), indexContent, 'utf8') -} - -async function output(content: string, iconName: string) { - const { data } = await svgo.optimize(content) - outputIcons(iconName, data) - outputDefinitions(iconName, data) -} - -function outputIcons(iconName: string, data: string) { - ensureDirSync(iconAssetsDir) - writeFileSync(join(iconAssetsDir, iconName), data, 'utf8') -} - -function outputDefinitions(iconName: string, data: string) { - ensureDirSync(iconDefinitionsDir) - const _iconName = `${iconName.replace('.svg', '')}` - const camelCaseName = camelCase(_iconName) - const definitionName = upperFirst(camelCaseName) - const iconDefinition = definitionTemplate - .replace('{{definitionName}}', definitionName) - .replace('{{name}}', _iconName) - .replace('{{svgString}}', data) - writeFileSync(join(iconDefinitionsDir, `${camelCaseName}.ts`), iconDefinition, 'utf8') - outputDefinitionNames.push(`${camelCaseName}`) -} - -export function copyToSite(): void { - removeSync(siteIconAssetsDir) - copySync(iconAssetsDir, siteIconAssetsDir) -} +import { copySync, ensureDirSync, readdirSync, readFileSync, removeSync, writeFileSync } from 'fs-extra' +import { camelCase, upperFirst } from 'lodash' +import { join } from 'path' +import SVGO from 'svgo' +import { buildConfig } from '../buildConfig' + +const definitionTemplate = `export const {{definitionName}} = { + name: '{{name}}', + svgString: '{{svgString}}', +} +` +const { iconAssetsDir, iconDefinitionsDir, siteIconAssetsDir } = buildConfig +const outputDefinitionNames: string[] = [] + +const options: SVGO.Options = { + plugins: [{ removeAttrs: { attrs: ['fill', 'class'] } }, { sortAttrs: true }, { removeDimensions: true }], +} + +const svgo = new SVGO(options) + +export async function generateIcons(): Promise { + const iconDirname = join(iconAssetsDir) + const iconPaths = readdirSync(iconDirname) + + const outputIcons = iconPaths.map(async iconName => { + const iconFile = join(iconAssetsDir, iconName) + const iconFileContent = readFileSync(iconFile, 'utf8') + await output(iconFileContent, iconName) + }) + await Promise.all(outputIcons) + + const indexContent = + outputDefinitionNames.map(item => `export { ${upperFirst(item)} } from './${item}'`).join('\n') + '\n' + writeFileSync(join(iconDefinitionsDir, `index.ts`), indexContent, 'utf8') +} + +async function output(content: string, iconName: string) { + const { data } = await svgo.optimize(content) + outputIcons(iconName, data) + outputDefinitions(iconName, data) +} + +function outputIcons(iconName: string, data: string) { + ensureDirSync(iconAssetsDir) + writeFileSync(join(iconAssetsDir, iconName), data, 'utf8') +} + +function outputDefinitions(iconName: string, data: string) { + ensureDirSync(iconDefinitionsDir) + const _iconName = `${iconName.replace('.svg', '')}` + const camelCaseName = camelCase(_iconName) + const definitionName = upperFirst(camelCaseName) + const iconDefinition = definitionTemplate + .replace('{{definitionName}}', definitionName) + .replace('{{name}}', _iconName) + .replace('{{svgString}}', data) + writeFileSync(join(iconDefinitionsDir, `${camelCaseName}.ts`), iconDefinition, 'utf8') + outputDefinitionNames.push(`${camelCaseName}`) +} + +export function copyToSite(): void { + removeSync(siteIconAssetsDir) + copySync(iconAssetsDir, siteIconAssetsDir) +}