diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8e13df8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + +jobs: + verify: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 9 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - run: pnpm install --frozen-lockfile + - run: pnpm build + - run: pnpm --filter @s-form/core test --run + - run: pnpm --filter @s-form/core pack --dry-run + - run: pnpm --filter @s-form/preset-element-plus pack --dry-run diff --git a/.gitignore b/.gitignore index 50314e3..4bf84b0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules .pnpm-debug.log **/dist/* .DS_Store +coverage diff --git a/README.md b/README.md index 7878ed3..4ad0631 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ npm i @s-form/core @s-form/preset-element-plus element-plus ```ts import { SmartForm } from '@s-form/core' -import { useDefineSchema } from '@s-form/preset-element-plus' -//import base style -import '@s-form/core/style' +import { useEleSchema } from '@s-form/preset-element-plus' +// import base style +import '@s-form/core/style.css' -const schema = useDefineSchema()([ +const schema = useEleSchema()([ { label: '输入框', alias: 'input', diff --git a/package.json b/package.json index 85e5905..43fa61f 100644 --- a/package.json +++ b/package.json @@ -8,22 +8,7 @@ "build": "pnpm -r --filter=./packages/* run build", "precommit": "lint-staged" }, - "devDependencies": { - "@rollup/plugin-typescript": "^9.0.2", - "@rushstack/eslint-patch": "^1.2.0", - "@vue/eslint-config-prettier": "^7.0.0", - "@vue/eslint-config-typescript": "^11.0.2", - "async-validator": "^4.2.5", - "bumpp": "^8.2.1", - "eslint": "^8.34.0", - "eslint-plugin-vue": "^9.9.0", - "prettier": "^2.8.4", - "typescript": "4.9.5", - "vitest": "^0.23.4", - "vue": "^3.2.47" - }, "dependencies": { - "@s-form/core": "workspace:*", - "rollup": "^3.15.0" + "@s-form/core": "workspace:*" } } \ No newline at end of file diff --git a/packages/core/.vscode/settings.json b/packages/core/.vscode/settings.json new file mode 100644 index 0000000..3662b37 --- /dev/null +++ b/packages/core/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/packages/core/package.json b/packages/core/package.json index 932e493..de7d784 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,45 +1,54 @@ { "name": "@s-form/core", - "version": "0.0.11", + "version": "0.0.20", "license": "MIT", "type": "module", "typings": "dist/index.d.ts", + "files": [ + "dist" + ], "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" }, + "./style.css": "./dist/style.css", "./style": { "import": "./dist/style.css" } }, "scripts": { "build": "vite build && vue-tsc --emitDeclarationOnly", - "precommit": "lint-staged" + "precommit": "lint-staged", + "test": "vitest", + "coverage": "vitest run --coverage" }, "dependencies": { - "vue-composable": "^1.0.0-beta.24" + "vitest": "^1.6.0", + "vue-composable": "1.0.0-beta.24" }, "module": "dist/index.js", + "publishConfig": { + "access": "public" + }, "peerDependencies": { - "@vueuse/core": "^9.12.0", + "@vueuse/core": "^10.0.0", "async-validator": "^4.2.5", - "vue": "^3.2.41" + "vue": "^3.4.0" }, "devDependencies": { - "@types/uuid": "8.3.4", - "@typescript-eslint/eslint-plugin": "^5.38.0", - "@typescript-eslint/parser": "^5.38.0", - "@vitejs/plugin-vue": "^4.0.0", - "@vue-macros/volar": "^0.8.4", - "eslint": "^8.23.1", - "postcss": "^8.4.16", - "sass": "^1.55.0", - "typescript": "^4.9.5", - "unplugin-auto-import": "0.14.2", - "unplugin-vue-define-options": "^1.2.3", - "vite": "4.1.1", - "vite-plugin-windicss": "^1.8.10", - "vue-tsc": "^1.0.24" + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.62.0", + "@vitejs/plugin-vue": "^5.0.5", + "@vitest/coverage-v8": "^1.6.0", + "@vue-macros/volar": "^0.18.18", + "@vue/test-utils": "^2.4.6", + "eslint": "^8.57.0", + "postcss": "^8.4.38", + "sass": "^1.77.4", + "typescript": "^5.4.5", + "vite": "^5.2.12", + "vite-plugin-windicss": "^1.9.3", + "vue-tsc": "^2.0.19" } -} \ No newline at end of file +} diff --git a/packages/core/src/FormItem/FormItem.vue b/packages/core/src/FormItem/FormItem.vue index bb3e872..3d983c8 100644 --- a/packages/core/src/FormItem/FormItem.vue +++ b/packages/core/src/FormItem/FormItem.vue @@ -5,6 +5,7 @@ class="form-item" :class="labelClass" :style="state.item.itemStyle" + :id="state.item.key" >
-
- {{ data.validate.$message }} +
+ {{ errMsg }}
@@ -76,6 +77,20 @@ const labelClass = computed(() => { ].flatMap((e) => e.split(' ')) }) +const errMsg = computed(() => { + const error = props.data.$errors?.[0] + const toErrorString = (e: unknown): string => + typeof e === 'string' + ? e + : Array.isArray(e) + ? toErrorString(e[0]) + : e instanceof Error + ? e.message + : 'error' + + return toErrorString(error) +}) + defineExpose({ el: elRef, }) @@ -84,8 +99,9 @@ defineExpose({ .label-wrapper { @apply flex-shrink-0 whitespace-nowrap relative z-1 mr-3; } + .form-item { - @apply flex text-sm relative; + @apply flex text-sm relative; &.label-top { @apply block; @@ -102,6 +118,7 @@ defineExpose({ @apply text-red-400 text-xs; @apply absolute -bottom-1.3em left-0; } + .content-wrapper { @apply flex-1 relative; } diff --git a/packages/core/src/FormItem/type.ts b/packages/core/src/FormItem/type.ts index 6f12da7..69653c7 100644 --- a/packages/core/src/FormItem/type.ts +++ b/packages/core/src/FormItem/type.ts @@ -1,11 +1,18 @@ +import type { Ref } from 'vue' + export type ValidateModel = { $value: T $anyInvalid?: any + $anyDirty?: boolean $errors?: any $dirty: boolean $reset: () => void [customProp: `$${string}`]: any validate: { $message: string + $pending?: boolean + $promise: { value: Promise } } } + +export type ValidateModelObj = Record diff --git a/packages/core/src/ItemRender.vue b/packages/core/src/ItemRender.vue index b091b0f..3e22a84 100644 --- a/packages/core/src/ItemRender.vue +++ b/packages/core/src/ItemRender.vue @@ -1,26 +1,34 @@ + \ No newline at end of file diff --git a/packages/core/tests/BasicComp.vue b/packages/core/tests/BasicComp.vue new file mode 100644 index 0000000..1c7bb23 --- /dev/null +++ b/packages/core/tests/BasicComp.vue @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/packages/core/tests/CustomModelComp.vue b/packages/core/tests/CustomModelComp.vue new file mode 100644 index 0000000..b9bdd8f --- /dev/null +++ b/packages/core/tests/CustomModelComp.vue @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/packages/core/tests/basic.test.ts b/packages/core/tests/basic.test.ts new file mode 100644 index 0000000..51ce65a --- /dev/null +++ b/packages/core/tests/basic.test.ts @@ -0,0 +1,312 @@ +import { SmartForm, createSmartForm, useCustomItem } from '@s-form/core' +import { mount } from '@vue/test-utils' +import { expect, test } from 'vitest' +import CustomModelComp from './CustomModelComp.vue' +import BasicComp from './BasicComp.vue' +import { nextTick, reactive } from 'vue' + +test('CustomModelComp - use modelKeyMap', async () => { + const model = reactive({ name: '', age: 12 }) + const wrapper = mount(SmartForm, { + props: { + formSchema: [ + useCustomItem({ + component: CustomModelComp, + key: 'name', + modelKeyMap: { + model: 'name', + count: 'age', + }, + }), + ], + modelValue: model, + }, + global: { + plugins: [createSmartForm()], + }, + }) + + await wrapper.find('input').setValue('hi') + expect(model.name).toBe('hi') + expect( + wrapper.getComponent(SmartForm).vm.$.exposed!.getResult() + ).resolves.toStrictEqual({ name: 'hi', age: 12 }) + await wrapper.find('input[type="number"]').setValue('18') + expect(model.age).toBe(18) + expect( + wrapper.getComponent(SmartForm).vm.$.exposed!.getResult() + ).resolves.toStrictEqual({ name: 'hi', age: 18 }) +}) + +test('CustomModelComp - use modelValueKey', async () => { + const model = reactive({ name: '' }) + const wrapper = mount(SmartForm, { + props: { + formSchema: [ + useCustomItem({ + component: CustomModelComp, + key: 'name', + modelValueKey: 'model', + }), + ], + modelValue: model, + }, + global: { + plugins: [createSmartForm()], + }, + }) + + await wrapper.find('input').setValue('hi') + expect(model.name).toBe('hi') + expect( + wrapper.getComponent(SmartForm).vm.$.exposed!.getResult() + ).resolves.toStrictEqual({ name: 'hi' }) +}) + +test('BasicComp', async () => { + const model = reactive({ name: '' }) + const wrapper = mount(SmartForm, { + props: { + formSchema: [ + useCustomItem({ + component: BasicComp, + key: 'name', + }), + ], + modelValue: model, + }, + global: { + plugins: [createSmartForm()], + }, + }) + + await wrapper.find('input').setValue('hi') + expect(model.name).toBe('hi') + expect( + wrapper.getComponent(SmartForm).vm.$.exposed!.getResult() + ).resolves.toStrictEqual({ name: 'hi' }) +}) + +test('rules', async () => { + const model = reactive({ name: '', nickname: '', age: 12 }) + const wrapper = mount(SmartForm, { + props: { + formSchema: [ + useCustomItem({ + component: BasicComp, + key: 'name', + }), + useCustomItem({ + component: CustomModelComp, + key: 'custom', + modelKeyMap: { + model: 'nickname', + count: 'age', + }, + }), + ], + modelValue: model, + rules: { + name: [{ required: true }], + nickname: { required: true }, + age: { required: true, min: 18, type: 'number' }, + }, + }, + global: { + plugins: [createSmartForm()], + }, + }) + + const matchError = async (err: string) => { + await expect( + wrapper.getComponent(SmartForm).vm.$.exposed!.getResult() + ).rejects.toThrowError(err) + } + + await matchError('name is required') + + await wrapper.find('input').setValue('hello') + + await matchError('nickname is required') + + await wrapper.find('input.model').setValue('world') + + await matchError('age cannot be less than 18') + await wrapper.find('input.count').setValue('18') + + expect( + wrapper.getComponent(SmartForm).vm.$.exposed!.getResult() + ).resolves.toStrictEqual({ name: 'hello', nickname: 'world', age: 18 }) +}) + +test('rules in FormItem', async () => { + const model = reactive({ name: '', nickname: '', age: 12, tag: '' }) + const wrapper = mount(SmartForm, { + props: { + formSchema: [ + useCustomItem({ + component: BasicComp, + key: 'name', + rules: [{ required: true }], + }), + useCustomItem({ + component: BasicComp, + key: 'tag', + rules: [ + { + //callback style + asyncValidator(r, v, cb) { + setTimeout(() => { + if (!model.tag) { + cb('What kind of fan are you?') + } + cb() + }, 10) + }, + }, + ], + }), + useCustomItem({ + component: CustomModelComp, + key: 'custom', + modelKeyMap: { + model: 'nickname', + count: 'age', + }, + rules: { + // custom async rule + asyncValidator(r, v, cb, source, options) { + if (model.age < 18) { + throw new Error(`It's a grown-up's world, not a kid's.`) + } + return Promise.resolve() + }, + }, + }), + ], + modelValue: model, + }, + global: { + plugins: [createSmartForm()], + }, + }) + + const matchError = async (err: string, checkMsg = true) => { + await expect( + wrapper.getComponent(SmartForm).vm.$.exposed!.getResult() + ).rejects.toThrowError(err) + + if (checkMsg) { + const handler = wrapper.find('.error-msg') + expect(handler.exists()).true + expect(handler.text()).toBe(err) + } + } + + // 没操作不显示错误信息 + expect(wrapper.find('.error-msg').exists()).toBe(false) + await matchError('name is required') + + await wrapper.find('input').setValue('Trump') + + await matchError('What kind of fan are you?') + + await wrapper.find('#tag input').setValue('MAGA') + + await matchError(`It's a grown-up's world, not a kid's.`) + + await wrapper.find('input.count').setValue('18') + + expect( + wrapper.getComponent(SmartForm).vm.$.exposed!.getResult() + ).resolves.toStrictEqual({ + name: 'Trump', + nickname: '', + tag: 'MAGA', + age: 18, + }) +}) + +test('FormItem changes', async () => { + const model = reactive({ name: '', nickname: '', age: 12, tag: '' }) + const schema = reactive([ + useCustomItem({ + component: BasicComp, + key: 'name', + rules: [{ required: true }], + }), + useCustomItem({ + component: BasicComp, + key: 'tag', + rules: [ + { + //callback style + asyncValidator(r, v, cb) { + setTimeout(() => { + if (!model.tag) { + cb('What kind of fan are you?') + } + cb() + }, 10) + }, + }, + ], + }), + useCustomItem({ + component: CustomModelComp, + key: 'custom', + modelKeyMap: { + model: 'nickname', + count: 'age', + }, + rules: { + // custom async rule + asyncValidator(r, v, cb, source, options) { + if (model.age < 18) { + throw new Error(`It's a grown-up's world, not a kid's.`) + } + return Promise.resolve() + }, + }, + }), + ]) + + const wrapper = mount(SmartForm, { + props: { + formSchema: schema as any, + modelValue: model, + }, + global: { + plugins: [createSmartForm()], + }, + }) + + const matchError = (err: string) => + expect( + wrapper.getComponent(SmartForm).vm.$.exposed!.getResult() + ).rejects.toThrowError(err) + + await matchError('name is required') + + await wrapper.find('input').setValue('Trump') + + await matchError('What kind of fan are you?') + + await wrapper.find('#tag input').setValue('MAGA') + + await matchError(`It's a grown-up's world, not a kid's.`) + + await wrapper.find('input.count').setValue('18') + + schema.splice(0, 1) + + await nextTick() + + expect( + wrapper.getComponent(SmartForm).vm.$.exposed!.getResult() + ).resolves.toStrictEqual({ + nickname: '', + tag: 'MAGA', + age: 18, + }) +}) diff --git a/packages/core/tests/index.js b/packages/core/tests/index.js new file mode 100644 index 0000000..e69de29 diff --git a/packages/core/tests/predefine.test.ts b/packages/core/tests/predefine.test.ts new file mode 100644 index 0000000..c5c42f3 --- /dev/null +++ b/packages/core/tests/predefine.test.ts @@ -0,0 +1,15 @@ +import { SmartForm, createSmartForm } from '@s-form/core'; +import { mount } from '@vue/test-utils'; +import { test, expect } from 'vitest' +import App from './App.vue'; + +test('predefine comp', async () => { + const wrapper = mount(App, { + global: { + plugins: [createSmartForm()] + } + }) + + await wrapper.find('input[type="number"]').setValue('18') + expect(wrapper.getComponent(SmartForm).vm.$.exposed!.getResult()).resolves.toStrictEqual({ name: "", age: 18, b: null }) +}) \ No newline at end of file diff --git a/packages/core/tests/predefinedComp.ts b/packages/core/tests/predefinedComp.ts new file mode 100644 index 0000000..f792652 --- /dev/null +++ b/packages/core/tests/predefinedComp.ts @@ -0,0 +1,8 @@ +import { defineFormTypes } from '@s-form/core'; +import CustomModelComp from './CustomModelComp.vue' +import BasicComp from './BasicComp.vue' + +export const useSchema = defineFormTypes({ + basic: { component: BasicComp }, + custom: { component: CustomModelComp } +}) \ No newline at end of file diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index df5a15a..e894eb6 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -17,14 +17,14 @@ "preserveSymlinks": true, "declarationDir": "dist", "rootDir": "src", - "baseUrl": ".", "jsx": "preserve", "skipLibCheck": true, "types": [ - "unplugin-vue-define-options/macros-global" + "vite/client" ] }, "include": [ - "src/" + "src/index.ts", + "src/**/*.vue" ] } \ No newline at end of file diff --git a/packages/core/vite.config.ts b/packages/core/vite.config.ts index a29551b..6004852 100644 --- a/packages/core/vite.config.ts +++ b/packages/core/vite.config.ts @@ -1,10 +1,9 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import WindiCSS from 'vite-plugin-windicss' -import DefineOptions from 'unplugin-vue-define-options/vite' export default defineConfig({ - plugins: [vue(), WindiCSS(), DefineOptions()], + plugins: [vue(), WindiCSS()], resolve: { alias: [], }, diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts new file mode 100644 index 0000000..55cf0eb --- /dev/null +++ b/packages/core/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineConfig, mergeConfig } from 'vitest/config' +import { resolve } from 'node:path' +import viteConfig from './vite.config' + +export default mergeConfig(viteConfig, defineConfig({ + test: { + alias: { + '@s-form/core': resolve(__dirname, './src/index.ts'), + }, + environment: 'jsdom' + } +})) \ No newline at end of file diff --git a/packages/preset-element-plus/.gitignore b/packages/preset-element-plus/.gitignore new file mode 100644 index 0000000..9b1ee42 --- /dev/null +++ b/packages/preset-element-plus/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/packages/preset-element-plus/README.md b/packages/preset-element-plus/README.md new file mode 100644 index 0000000..912ea24 --- /dev/null +++ b/packages/preset-element-plus/README.md @@ -0,0 +1,11 @@ +# @s-form/preset-element-plus + +Element Plus preset components for `@s-form/core`. + +```bash +npm i @s-form/core @s-form/preset-element-plus element-plus +``` + +```ts +import { useEleSchema } from '@s-form/preset-element-plus' +``` diff --git a/packages/preset-element-plus/package.json b/packages/preset-element-plus/package.json index 6c48be5..63a74d9 100644 --- a/packages/preset-element-plus/package.json +++ b/packages/preset-element-plus/package.json @@ -1,20 +1,36 @@ { "name": "@s-form/preset-element-plus", - "version": "0.0.5", + "version": "0.0.6", "type": "module", "typings": "dist/index.d.ts", "module": "dist/index.mjs", + "files": [ + "dist", + "README.md" + ], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs" + } + }, + "publishConfig": { + "access": "public" + }, "peerDependencies": { - "@s-form/core": "^0.0.5", - "vue": "^3.2.0", + "@s-form/core": "^0.0.19", "element-plus": "^2.0.0", - "async-validator": "^4.0.0" + "vue": "^3.4.0" }, "devDependencies": { "@s-form/core": "workspace:*", - "unbuild": "1.1.1" + "element-plus": "^2.0.0", + "typescript": "^5.0.0", + "vue": "^3.4.0", + "unbuild": "1.1.1", + "@types/bun": "latest" }, "scripts": { "build": "unbuild" } -} \ No newline at end of file +} diff --git a/playground/package.json b/playground/package.json index 2cba335..13a62c9 100644 --- a/playground/package.json +++ b/playground/package.json @@ -14,20 +14,20 @@ "@element-plus/icons-vue": "^2.0.10", "@s-form/core": "workspace:*", "@s-form/preset-element-plus": "workspace:*", - "@vueuse/core": "^9.12.0", + "@vueuse/core": "^10.2.1", "async-validator": "^4.2.5", "element-plus": "^2.2.26", "pinia": "^2.0.21", - "vue": "^3.2.41", + "vue": "^3.4.0", "vue-router": "^4.1.5" }, "devDependencies": { "@rushstack/eslint-patch": "^1.1.4", "@types/jsdom": "^20.0.0", "@types/markdown-it": "^12.2.3", - "@types/node": "^16.11.56", "@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue-jsx": "^2.0.1", + "@vue-macros/volar": "^0.18.11", "@vue/eslint-config-prettier": "^7.0.0", "@vue/eslint-config-typescript": "^11.0.0", "@vue/test-utils": "^2.0.2", @@ -43,11 +43,12 @@ "prettier": "^2.7.1", "shiki": "^0.11.1", "stylus": "^0.59.0", - "typescript": "~4.7.4", - "vite": "4.1.1", + "typescript": "^5.3.3", + "unplugin-vue-macros": "^2.4.0", + "vite": "^5.0.12", "vite-plugin-md": "^0.20.4", "vite-plugin-windicss": "^1.8.8", - "vitest": "^0.23.0", - "vue-tsc": "^0.40.7" + "vitest": "^1.2.2", + "vue-tsc": "^1.8.6" } } \ No newline at end of file diff --git a/playground/src/App.vue b/playground/src/App.vue index 2fce7c5..96eb126 100644 --- a/playground/src/App.vue +++ b/playground/src/App.vue @@ -5,7 +5,7 @@ import { isDark } from './composable/dark' import { ElIcon } from 'element-plus' import { Moon, Sunny } from '@element-plus/icons-vue' import { useToggle } from '@vueuse/shared' -import { ref } from 'vue' +import { ref } from 'vue' const toggleDark = useToggle(isDark) const a = ref(0) @@ -13,6 +13,7 @@ const a = ref(0)