├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── docs ├── .vitepress │ ├── config.ts │ ├── runtime.d.ts │ ├── theme │ │ ├── components │ │ │ └── Outline │ │ │ │ ├── components │ │ │ │ └── VPDocOutlineItem.vue │ │ │ │ ├── index.vue │ │ │ │ └── utils │ │ │ │ ├── index.ts │ │ │ │ └── outline.js │ │ ├── index.codeeditor.files.ts │ │ ├── index.codeeditor.ts │ │ ├── index.ts │ │ ├── naive-ui.css │ │ └── style.css │ ├── uno.config.ts │ ├── vite-env.d.ts │ └── vite.config.ts ├── auto-imports.d.ts ├── components.d.ts ├── components │ ├── checkbox │ │ ├── demo │ │ │ ├── basic.vue │ │ │ ├── events.vue │ │ │ └── focus-blur.vue │ │ └── index.md │ ├── controls │ │ ├── demo │ │ │ ├── basic.vue │ │ │ ├── custom.vue │ │ │ ├── form.vue │ │ │ └── table.vue │ │ └── index.md │ ├── form │ │ ├── demo │ │ │ ├── array.vue │ │ │ ├── basic.vue │ │ │ ├── clone.vue │ │ │ ├── data-transform.vue │ │ │ ├── field-context.vue │ │ │ ├── field-render.vue │ │ │ ├── field.vue │ │ │ ├── formItemProps.vue │ │ │ ├── grid.vue │ │ │ ├── props.vue │ │ │ └── toolbars.vue │ │ └── index.md │ ├── globals │ │ ├── demo │ │ │ └── basic.vue │ │ └── index.md │ ├── radio │ │ ├── demo │ │ │ ├── basic.vue │ │ │ ├── buttons.vue │ │ │ └── sizes.vue │ │ └── index.md │ └── table │ │ ├── demo │ │ ├── basic.vue │ │ ├── form.vue │ │ ├── hooks.vue │ │ ├── pagination.vue │ │ ├── request.vue │ │ └── watcher.vue │ │ └── index.md ├── guide │ ├── faq.md │ ├── index.md │ └── install.md ├── index.md ├── package.json ├── public │ └── logo.svg ├── tsconfig.json └── zh-CN │ ├── components │ ├── checkbox │ │ ├── demo │ │ │ ├── basic.vue │ │ │ ├── events.vue │ │ │ └── focus-blur.vue │ │ └── index.md │ ├── controls │ │ ├── demo │ │ │ ├── basic.vue │ │ │ ├── custom.vue │ │ │ ├── form.vue │ │ │ └── table.vue │ │ └── index.md │ ├── form │ │ ├── demo │ │ │ ├── array.vue │ │ │ ├── basic.vue │ │ │ ├── clone.vue │ │ │ ├── data-transform.vue │ │ │ ├── field-context.vue │ │ │ ├── field-render.vue │ │ │ ├── field.vue │ │ │ ├── formItemProps.vue │ │ │ ├── grid.vue │ │ │ ├── props.vue │ │ │ └── toolbars.vue │ │ └── index.md │ ├── globals │ │ ├── demo │ │ │ └── basic.vue │ │ └── index.md │ ├── radio │ │ ├── demo │ │ │ ├── basic.vue │ │ │ ├── buttons.vue │ │ │ └── sizes.vue │ │ └── index.md │ └── table │ │ ├── demo │ │ ├── basic.vue │ │ ├── form.vue │ │ ├── hooks.vue │ │ ├── pagination.vue │ │ ├── request.vue │ │ └── watcher.vue │ │ └── index.md │ ├── guide │ ├── faq.md │ ├── index.md │ └── intro.md │ └── index.md ├── eslint.config.js ├── gloabl.d.ts ├── package.json ├── packages ├── components │ ├── checkbox │ │ ├── package.json │ │ ├── src │ │ │ ├── components │ │ │ │ ├── ProCheckboxGroup.tsx │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ └── tsup.config.ts │ ├── controls │ │ ├── package.json │ │ ├── src │ │ │ ├── components │ │ │ │ └── index.ts │ │ │ ├── composables │ │ │ │ └── index.ts │ │ │ ├── define │ │ │ │ └── index.tsx │ │ │ ├── index.ts │ │ │ └── types │ │ │ │ └── index.ts │ │ └── tsup.config.ts │ ├── dialog │ │ ├── package.json │ │ ├── src │ │ │ ├── components │ │ │ │ ├── ProDialog.tsx │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ └── tsup.config.ts │ ├── form │ │ ├── package.json │ │ ├── src │ │ │ ├── components │ │ │ │ └── index.tsx │ │ │ ├── composables │ │ │ │ ├── index.ts │ │ │ │ ├── useData.ts │ │ │ │ ├── useMetadata.ts │ │ │ │ ├── useRules.ts │ │ │ │ └── useValues.ts │ │ │ ├── config │ │ │ │ └── index.ts │ │ │ ├── define │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── types │ │ │ │ ├── config.ts │ │ │ │ ├── data.ts │ │ │ │ ├── index.ts │ │ │ │ └── instance.ts │ │ │ └── utils │ │ │ │ ├── fields.tsx │ │ │ │ ├── index.ts │ │ │ │ └── render.tsx │ │ └── tsup.config.ts │ ├── globals │ │ ├── package.json │ │ ├── src │ │ │ ├── components │ │ │ │ ├── NpGlobalDialog.tsx │ │ │ │ ├── NpGlobalLoadingBar.tsx │ │ │ │ ├── NpGlobalMessage.tsx │ │ │ │ ├── NpGlobalNotification.tsx │ │ │ │ ├── NpGlobalProvider.tsx │ │ │ │ ├── NpInstallProvider.tsx │ │ │ │ └── index.ts │ │ │ ├── global.naive.ts │ │ │ ├── global.ts │ │ │ ├── global.window.d.ts │ │ │ ├── index.ts │ │ │ └── utils │ │ │ │ └── index.ts │ │ └── tsup.config.ts │ ├── main │ │ ├── package.json │ │ ├── src │ │ │ ├── imports.ts │ │ │ ├── index.ts │ │ │ └── resolver.ts │ │ └── tsup.config.ts │ ├── radio │ │ ├── README.md │ │ ├── package.json │ │ ├── src │ │ │ ├── components │ │ │ │ ├── ProRadioGroup.tsx │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ └── tsup.config.ts │ └── table │ │ ├── README.md │ │ ├── package.json │ │ ├── src │ │ ├── components │ │ │ └── index.tsx │ │ ├── composables │ │ │ ├── index.ts │ │ │ ├── useColumnIndexes.ts │ │ │ ├── useColumnLink.ts │ │ │ ├── useColumns.ts │ │ │ ├── useMetadata.ts │ │ │ ├── useOffsetPagination.ts │ │ │ └── useTableMinWidth.ts │ │ ├── define │ │ │ └── index.ts │ │ ├── index.ts │ │ └── types │ │ │ └── index.ts │ │ └── tsup.config.ts ├── config │ ├── index.d.ts │ ├── index.js │ └── package.json └── utils │ ├── package.json │ ├── src │ ├── components │ │ └── index.ts │ ├── composables │ │ └── index.ts │ ├── index.ts │ ├── types │ │ └── index.ts │ └── utils │ │ └── index.ts │ └── tsup.config.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── test └── index.test.ts └── tsconfig.json /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | lint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: pnpm/action-setup@v4 18 | with: 19 | run_install: false 20 | - uses: actions/setup-node@v4 21 | with: 22 | node-version: lts/* 23 | cache: pnpm 24 | 25 | - run: pnpm i -g @antfu/ni 26 | - run: nci 27 | - run: nr lint 28 | - run: nr typecheck 29 | 30 | test: 31 | runs-on: ${{ matrix.os }} 32 | 33 | strategy: 34 | matrix: 35 | node: [lts/*] 36 | os: [ubuntu-latest, windows-latest, macos-latest] 37 | fail-fast: false 38 | 39 | steps: 40 | - uses: actions/checkout@v4 41 | - uses: pnpm/action-setup@v4 42 | with: 43 | run_install: false 44 | - name: Set node ${{ matrix.node }} 45 | uses: actions/setup-node@v4 46 | with: 47 | node-version: ${{ matrix.node }} 48 | cache: pnpm 49 | 50 | - run: pnpm i -g @antfu/ni 51 | - run: nci 52 | - run: nr test 53 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | permissions: 4 | id-token: write 5 | contents: write 6 | 7 | on: 8 | push: 9 | tags: 10 | - 'v*' 11 | 12 | jobs: 13 | release: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | - uses: pnpm/action-setup@v4 20 | - uses: actions/setup-node@v4 21 | with: 22 | node-version: lts/* 23 | registry-url: https://registry.npmjs.org/ 24 | 25 | - run: pnpm dlx changelogithub 26 | env: 27 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 28 | 29 | # # Uncomment the following lines to publish to npm on CI 30 | # 31 | # - run: pnpm install 32 | # - run: pnpm publish -r --access public 33 | # env: 34 | # NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 35 | # NPM_CONFIG_PROVENANCE: true 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | .DS_Store 3 | .idea 4 | *.log 5 | *.tgz 6 | coverage 7 | dist 8 | lib-cov 9 | logs 10 | node_modules 11 | temp 12 | cache 13 | .eslintcache 14 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | ignore-workspace-root-check=true 2 | shell-emulator=true 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Disable the default formatter, use eslint instead 3 | "prettier.enable": false, 4 | "editor.formatOnSave": false, 5 | 6 | // Auto fix 7 | "editor.codeActionsOnSave": { 8 | "source.fixAll.eslint": "explicit", 9 | "source.organizeImports": "never" 10 | }, 11 | 12 | // Silent the stylistic rules in you IDE, but still auto fix them 13 | "eslint.rules.customizations": [ 14 | { "rule": "style/*", "severity": "off" }, 15 | { "rule": "*-indent", "severity": "off" }, 16 | { "rule": "*-spacing", "severity": "off" }, 17 | { "rule": "*-spaces", "severity": "off" }, 18 | { "rule": "*-order", "severity": "off" }, 19 | { "rule": "*-dangle", "severity": "off" }, 20 | { "rule": "*-newline", "severity": "off" }, 21 | { "rule": "*quotes", "severity": "off" }, 22 | { "rule": "*semi", "severity": "off" } 23 | ], 24 | 25 | // Enable eslint for all supported languages 26 | "eslint.validate": [ 27 | "javascript", 28 | "javascriptreact", 29 | "typescript", 30 | "typescriptreact", 31 | "vue", 32 | "html", 33 | "markdown", 34 | "json", 35 | "jsonc", 36 | "yaml" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Please refer to https://github.com/antfu/contribute 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025-PRESENT Hairyf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Naive UI Pro Components 2 | 3 | [![npm version][npm-version-src]][npm-version-href] 4 | [![npm downloads][npm-downloads-src]][npm-downloads-href] 5 | [![bundle][bundle-src]][bundle-href] 6 | [![JSDocs][jsdocs-src]][jsdocs-href] 7 | [![License][license-src]][license-href] 8 | 9 | See our website [naive-ui-pro-components](https://naiveui-pro.vercel.app/) for more information. 10 | 11 | ## Install 12 | 13 | Using pnpm: 14 | 15 | ```bash 16 | pnpm install --save naive-ui-pro-components 17 | ``` 18 | 19 | or using yarn: 20 | 21 | ```bash 22 | yarn add naive-ui-pro-components 23 | ``` 24 | 25 | ## Usage 26 | 27 | ### Globals 28 | 29 | ```js 30 | import NaiveUIProComponents from 'naive-ui-pro-components' 31 | import { createApp } from 'vue' 32 | import App from './App.vue' 33 | 34 | const app = createApp(App) 35 | 36 | app.use(NaiveUIProComponents) 37 | ``` 38 | 39 | ## On-Demand Import (Recommended) 40 | 41 | Install the `unplugin-vue-components` and `unplugin-auto-import` plugins, which will automatically import all components and APIs from `naive-ui-pro-components`. 42 | 43 | ```sh 44 | npm install -D unplugin-vue-components unplugin-auto-import 45 | ``` 46 | 47 | ### Vite 48 | 49 | ```ts 50 | import NaiveUIProImports from 'naive-ui-pro-components/imports' 51 | import NaiveUIProResolver from 'naive-ui-pro-components/resolver' 52 | import AutoImport from 'unplugin-auto-import/vite' 53 | import Components from 'unplugin-vue-components/vite' 54 | // vite.config.ts 55 | import { defineConfig } from 'vite' 56 | 57 | export default defineConfig({ 58 | // ... 59 | plugins: [ 60 | // ... 61 | AutoImport({ 62 | imports: [NaiveUIProImports()], 63 | }), 64 | Components({ 65 | resolvers: [NaiveUIProResolver()], 66 | }), 67 | ], 68 | }) 69 | ``` 70 | 71 | ### Webpack 72 | 73 | ```js 74 | // webpack.config.js 75 | const NaiveUIProImports = require('naive-ui-pro-components/imports') 76 | const NaiveUIProResolver = require('naive-ui-pro-components/resolver') 77 | const AutoImport = require('unplugin-auto-import/webpack') 78 | const Components = require('unplugin-vue-components/webpack') 79 | 80 | module.exports = { 81 | // ... 82 | plugins: [ 83 | // ... 84 | AutoImport({ 85 | imports: [NaiveUIProImports()], 86 | }), 87 | Components({ 88 | resolvers: [NaiveUIProResolver()], 89 | }), 90 | ], 91 | } 92 | ``` 93 | 94 | ## License 95 | 96 | [MIT](./LICENSE) License © [Hairyf](https://github.com/hairyf) 97 | 98 | 99 | 100 | [npm-version-src]: https://img.shields.io/npm/v/naive-ui-pro-components?style=flat&colorA=080f12&colorB=1fa669 101 | [npm-version-href]: https://npmjs.com/package/naive-ui-pro-components 102 | [npm-downloads-src]: https://img.shields.io/npm/dm/naive-ui-pro-components?style=flat&colorA=080f12&colorB=1fa669 103 | [npm-downloads-href]: https://npmjs.com/package/naive-ui-pro-components 104 | [bundle-src]: https://img.shields.io/bundlephobia/minzip/naive-ui-pro-components?style=flat&colorA=080f12&colorB=1fa669&label=minzip 105 | [bundle-href]: https://bundlephobia.com/result?p=naive-ui-pro-components 106 | [license-src]: https://img.shields.io/github/license/hairyf/naive-ui-pro-components.svg?style=flat&colorA=080f12&colorB=1fa669 107 | [license-href]: https://github.com/hairyf/naive-ui-pro-components/blob/main/LICENSE 108 | [jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1fa669 109 | [jsdocs-href]: https://www.jsdocs.io/package/naive-ui-pro-components 110 | -------------------------------------------------------------------------------- /docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import type { DefaultTheme } from 'vitepress' 2 | import path from 'node:path' 3 | import process from 'node:process' 4 | import { transformerTwoslash } from '@shikijs/vitepress-twoslash' 5 | import { defineConfig } from 'vitepress' 6 | import { demoMdPlugin } from 'vitepress-plugin-demo' 7 | import { groupIconMdPlugin } from 'vitepress-plugin-group-icons' 8 | import { version } from '../../package.json' 9 | import vite from './vite.config' 10 | 11 | const VERSIONS: (DefaultTheme.NavItemWithLink | DefaultTheme.NavItemChildren)[] = [ 12 | { text: `v${version} (current)`, link: '/' }, 13 | { text: `Release Notes`, link: 'https://github.com/hairyf/naive-ui-pro-components/releases' }, 14 | { text: `Contributing`, link: 'https://github.com/hairyf/naive-ui-pro-components/blob/main/CONTRIBUTING.md' }, 15 | ] 16 | 17 | const types = [ 18 | 'auto-imports.d.ts', 19 | 'components.d.ts', 20 | ] 21 | 22 | export default defineConfig({ 23 | title: 'Naive Ultra', 24 | description: 'Make backend development easier', 25 | markdown: { 26 | theme: { 27 | light: 'vitesse-light', 28 | dark: 'vitesse-dark', 29 | }, 30 | codeTransformers: [ 31 | transformerTwoslash({ 32 | twoslashOptions: { 33 | compilerOptions: { 34 | types: types.map(type => path.resolve(process.cwd(), `./${type}`)), 35 | }, 36 | }, 37 | }), 38 | ], 39 | languages: ['js', 'jsx', 'ts', 'tsx'], 40 | config: (md) => { 41 | md.use(groupIconMdPlugin) 42 | md.use(demoMdPlugin) 43 | }, 44 | }, 45 | cleanUrls: true, 46 | vite, 47 | locales: { 48 | 'root': { 49 | label: 'English', 50 | lang: 'en-US', 51 | themeConfig: useThemeConfig('en-US'), 52 | }, 53 | 'zh-CN': { 54 | label: '简体中文', 55 | lang: 'zh-CN', 56 | themeConfig: useThemeConfig('zh-CN'), 57 | }, 58 | }, 59 | themeConfig: { 60 | search: { provider: 'local' }, 61 | }, 62 | 63 | head: [ 64 | ['meta', { name: 'theme-color', content: '#ffffff' }], 65 | ['link', { rel: 'icon', href: '/logo.svg', type: 'image/svg+xml' }], 66 | ['meta', { name: 'author', content: 'Hairyf' }], 67 | // ['meta', { property: 'og:title', content: '' }], 68 | // ['meta', { property: 'og:image', content: '' }], 69 | ['meta', { property: 'og:description', content: 'Make backend development easier' }], 70 | // ['meta', { name: 'twitter:card', content: 'summary_large_image' }], 71 | // ['meta', { name: 'twitter:image', content: '' }], 72 | ['meta', { name: 'viewport', content: 'width=device-width, initial-scale=1.0, viewport-fit=cover' }], 73 | ], 74 | }) 75 | 76 | function useThemeConfig(lang = 'en-US') { 77 | const prefix = lang = lang === 'en-US' ? '' : `/${lang}` 78 | 79 | const GUIDES: DefaultTheme.NavItemWithLink[] = [ 80 | { text: 'Getting Started', link: '/guide/' }, 81 | { text: 'Installation & Usage', link: '/guide/install' }, 82 | { text: 'FAQ', link: `${prefix}/guide/faq` }, 83 | ] 84 | const COMPONENTS: DefaultTheme.NavItemWithLink[] = [ 85 | { text: 'ProForm', link: `${prefix}/components/form/` }, 86 | { text: 'ProTable', link: `${prefix}/components/table/` }, 87 | { text: 'ProCheckbox', link: `${prefix}/components/checkbox/` }, 88 | { text: 'ProRadio', link: `${prefix}/components/radio/` }, 89 | { text: 'ProControls', link: `${prefix}/components/controls/` }, 90 | { text: 'ProGlobals', link: `${prefix}/components/globals/` }, 91 | ] 92 | const themeConfig: DefaultTheme.Config = { 93 | // https://vitepress.dev/reference/default-theme-config 94 | 95 | logo: '/logo.svg', 96 | nav: [ 97 | { text: 'Guide', items: [{ items: GUIDES }] }, 98 | { text: 'Components', items: [{ items: COMPONENTS }] }, 99 | { text: `v${version}`, items: VERSIONS }, 100 | ], 101 | sidebar: [ 102 | { text: 'Guide', items: [{ items: GUIDES }] }, 103 | { text: 'Components', items: [{ items: COMPONENTS }] }, 104 | ], 105 | 106 | editLink: { 107 | pattern: 'https://github.com/hairyf/naive-ui-pro-components/edit/main/docs/:path', 108 | text: 'Suggest changes to this page', 109 | }, 110 | search: { 111 | provider: 'local', 112 | }, 113 | 114 | socialLinks: [ 115 | { icon: 'github', link: 'https://github.com/hairyf/naive-ui-pro-components' }, 116 | ], 117 | 118 | footer: { 119 | message: 'Released under the MIT License.', 120 | copyright: 'Copyright © 2025-PRESENT Hairyf.', 121 | }, 122 | } 123 | return themeConfig 124 | } 125 | -------------------------------------------------------------------------------- /docs/.vitepress/runtime.d.ts: -------------------------------------------------------------------------------- 1 | import type { BundledLanguage, BundledTheme, CodeToHastOptions } from 'shiki' 2 | 3 | declare module '@vue/runtime-core' { 4 | interface ComponentCustomProperties { 5 | $highlighter: import('shiki').Highlighter 6 | $c2h: (code: string, options: CodeToHastOptions) => string 7 | } 8 | } 9 | export {} 10 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/components/Outline/components/VPDocOutlineItem.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 25 | 26 | 57 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/components/Outline/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 31 | 32 | 80 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/components/Outline/utils/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable ts/ban-ts-comment */ 2 | import { resolveHeaders } from './outline.js' 3 | 4 | export function getHeaders(range) { 5 | const headers = [...document.querySelectorAll('.VPDoc h2,h3,h4,h5,h6,.n-card')] 6 | .filter(el => el.id && el.hasChildNodes()) 7 | .map((el) => { 8 | const level = Number(el.tagName[1]) 9 | return { 10 | // @ts-expect-error 11 | title: serializeHeader(el), 12 | link: `#${el.id}`, 13 | level: level || 2, 14 | } 15 | }) 16 | return resolveHeaders(headers, range) 17 | } 18 | 19 | export function serializeHeader(h: HTMLDivElement) { 20 | let ret = '' 21 | for (const node of h.childNodes) { 22 | if (h?.classList?.contains('n-card')) 23 | return serializeHeader(h.querySelector('.n-card-header__main')!) 24 | 25 | if (node.nodeType === 1) { 26 | // @ts-expect-error 27 | if (node?.classList?.contains('VPBadge') || node?.classList?.contains('header-anchor')) { 28 | continue 29 | } 30 | 31 | ret += node.textContent 32 | } 33 | else if (node.nodeType === 3) { 34 | ret += node.textContent 35 | } 36 | } 37 | return ret.trim() 38 | } 39 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/components/Outline/utils/outline.js: -------------------------------------------------------------------------------- 1 | import { getScrollOffset } from 'vitepress' 2 | import { useAside } from 'vitepress/dist/client/theme-default/composables/aside.js' 3 | import { throttleAndDebounce } from 'vitepress/dist/client/theme-default/support/utils.js' 4 | import { onMounted, onUnmounted, onUpdated } from 'vue' 5 | 6 | const ignoreRE = /\b(?:VPBadge|header-anchor|footnote-ref|ignore-header)\b/ 7 | // cached list of anchor elements from resolveHeaders 8 | const resolvedHeaders = [] 9 | export function resolveTitle(theme) { 10 | return ((typeof theme.outline === 'object' 11 | && !Array.isArray(theme.outline) 12 | && theme.outline.label) 13 | || theme.outlineTitle 14 | || 'On this page') 15 | } 16 | export function getHeaders(range) { 17 | const headers = [ 18 | ...document.querySelectorAll('.VPDoc :where(h1,h2,h3,h4,h5,h6)'), 19 | ] 20 | .filter(el => el.id && el.hasChildNodes()) 21 | .map((el) => { 22 | const level = Number(el.tagName[1]) 23 | return { 24 | element: el, 25 | title: serializeHeader(el), 26 | link: `#${el.id}`, 27 | level, 28 | } 29 | }) 30 | return resolveHeaders(headers, range) 31 | } 32 | function serializeHeader(h) { 33 | let ret = '' 34 | for (const node of h.childNodes) { 35 | if (node.nodeType === 1) { 36 | if (ignoreRE.test(node.className)) 37 | continue 38 | ret += node.textContent 39 | } 40 | else if (node.nodeType === 3) { 41 | ret += node.textContent 42 | } 43 | } 44 | return ret.trim() 45 | } 46 | export function resolveHeaders(headers, range) { 47 | if (range === false) { 48 | return [] 49 | } 50 | const levelsRange = (typeof range === 'object' && !Array.isArray(range) 51 | ? range.level 52 | : range) || 2 53 | const [high, low] = typeof levelsRange === 'number' 54 | ? [levelsRange, levelsRange] 55 | : levelsRange === 'deep' 56 | ? [2, 6] 57 | : levelsRange 58 | return buildTree(headers, high, low) 59 | } 60 | export function useActiveAnchor(container, marker) { 61 | const { isAsideEnabled } = useAside() 62 | const onScroll = throttleAndDebounce(setActiveLink, 100) 63 | let prevActiveLink = null 64 | onMounted(() => { 65 | requestAnimationFrame(setActiveLink) 66 | window.addEventListener('scroll', onScroll) 67 | }) 68 | onUpdated(() => { 69 | // sidebar update means a route change 70 | activateLink(location.hash) 71 | }) 72 | onUnmounted(() => { 73 | window.removeEventListener('scroll', onScroll) 74 | }) 75 | function setActiveLink() { 76 | if (!isAsideEnabled.value) { 77 | return 78 | } 79 | const scrollY = window.scrollY 80 | const innerHeight = window.innerHeight 81 | const offsetHeight = document.body.offsetHeight 82 | const isBottom = Math.abs(scrollY + innerHeight - offsetHeight) < 1 83 | // resolvedHeaders may be repositioned, hidden or fix positioned 84 | const headers = resolvedHeaders 85 | .map(({ element, link }) => ({ 86 | link, 87 | top: getAbsoluteTop(element), 88 | })) 89 | .filter(({ top }) => !Number.isNaN(top)) 90 | .sort((a, b) => a.top - b.top) 91 | // no headers available for active link 92 | if (!headers.length) { 93 | activateLink(null) 94 | return 95 | } 96 | // page top 97 | if (scrollY < 1) { 98 | activateLink(null) 99 | return 100 | } 101 | // page bottom - highlight last link 102 | if (isBottom) { 103 | activateLink(headers[headers.length - 1].link) 104 | return 105 | } 106 | // find the last header above the top of viewport 107 | let activeLink = null 108 | for (const { link, top } of headers) { 109 | if (top > scrollY + getScrollOffset() + 4) { 110 | break 111 | } 112 | activeLink = link 113 | } 114 | activateLink(activeLink) 115 | } 116 | function activateLink(hash) { 117 | if (prevActiveLink) { 118 | prevActiveLink.classList.remove('active') 119 | } 120 | if (hash == null) { 121 | prevActiveLink = null 122 | } 123 | else { 124 | prevActiveLink = container.value.querySelector(`a[href="${decodeURIComponent(hash)}"]`) 125 | } 126 | const activeLink = prevActiveLink 127 | if (activeLink) { 128 | activeLink.classList.add('active') 129 | marker.value.style.top = `${activeLink.offsetTop + 39}px` 130 | marker.value.style.opacity = '1' 131 | } 132 | else { 133 | marker.value.style.top = '33px' 134 | marker.value.style.opacity = '0' 135 | } 136 | } 137 | } 138 | function getAbsoluteTop(element) { 139 | let offsetTop = 0 140 | while (element !== document.body) { 141 | if (element === null) { 142 | // child element is: 143 | // - not attached to the DOM (display: none) 144 | // - set to fixed position (not scrollable) 145 | // - body or html element (null offsetParent) 146 | return Number.NaN 147 | } 148 | offsetTop += element.offsetTop 149 | element = element.offsetParent 150 | } 151 | return offsetTop 152 | } 153 | function buildTree(data, min, max) { 154 | resolvedHeaders.length = 0 155 | const result = [] 156 | const stack = [] 157 | data.forEach((item) => { 158 | const node = { ...item, children: [] } 159 | let parent = stack[stack.length - 1] 160 | while (parent && parent.level >= node.level) { 161 | stack.pop() 162 | parent = stack[stack.length - 1] 163 | } 164 | if (node.element?.classList.contains('ignore-header') 165 | || (parent && 'shouldIgnore' in parent)) { 166 | stack.push({ level: node.level, shouldIgnore: true }) 167 | return 168 | } 169 | if (node.level > max || node.level < min) 170 | return 171 | resolvedHeaders.push({ element: node.element, link: node.link }) 172 | if (parent) 173 | parent.children.push(node) 174 | else 175 | result.push(node) 176 | stack.push(node) 177 | }) 178 | return result 179 | } 180 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.codeeditor.files.ts: -------------------------------------------------------------------------------- 1 | export const viteConfigJs = `\ 2 | import AutoImport from 'unplugin-auto-import/vite' 3 | import Components from 'unplugin-vue-components/vite' 4 | // import NaiveUIProResolver from 'naive-ui-pro-components/resolver' 5 | // import NaiveUIProImports from 'naive-ui-pro-components/imports' 6 | import { defineConfig } from 'vite' 7 | import Vue from '@vitejs/plugin-vue' 8 | 9 | export default defineConfig({ 10 | plugins: [ 11 | // AutoImport({ 12 | // imports: [NaiveUIProImports()], 13 | // }), 14 | // Components({ 15 | // resolvers: [NaiveUIProResolver()], 16 | // }), 17 | Vue() 18 | ], 19 | }) 20 | ` 21 | export const srcMainTs = `\ 22 | import { createApp } from 'vue'; 23 | import App from './App.vue'; 24 | 25 | createApp(App).mount('#app'); 26 | ` 27 | 28 | export const indexHtml = `\ 29 | 30 | 31 | 32 | 33 | 34 | 35 | Vite + Vue + TS 36 | 37 | 38 |
39 | 40 | 41 | 42 | ` 43 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.codeeditor.ts: -------------------------------------------------------------------------------- 1 | import type { NaiveUIContainerOptions } from 'vitepress-plugin-demo/client/naive-ui' 2 | import { indexHtml, srcMainTs, viteConfigJs } from './index.codeeditor.files' 3 | 4 | export const codeeditor: NaiveUIContainerOptions['codeeditor'] = { 5 | editors: ['stackblitz', 'codesandbox'], 6 | globals: { 7 | package: { 8 | scripts: { start: 'vite' }, 9 | dependencies: { 10 | 'naive-ui-pro-components': 'latest', 11 | 'vue': 'latest', 12 | }, 13 | devDependencies: { 14 | 'unplugin-vue-components': 'latest', 15 | 'unplugin-auto-import': 'latest', 16 | '@vitejs/plugin-vue': 'latest', 17 | 'typescript': 'latest', 18 | 'vite': 'latest', 19 | }, 20 | }, 21 | files: { 22 | 'vite.config.js': { content: viteConfigJs }, 23 | 'src/main.ts': { content: srcMainTs }, 24 | 'index.html': { content: indexHtml }, 25 | }, 26 | }, 27 | resolve(props) { 28 | const content = decodeURIComponent(props.tsCode || props.jsCode) 29 | return { 30 | opens: ['src/App.vue'], 31 | files: { 32 | 'src/App.vue': { content }, 33 | }, 34 | } 35 | }, 36 | } 37 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | // https://vitepress.dev/guide/custom-theme 2 | import type { EnhanceAppContext } from 'vitepress' 3 | import TwoslashFloating from '@shikijs/vitepress-twoslash/client' 4 | import { NpGlobalProvider } from 'naive-ui-pro-components' 5 | import NaiveUIContainer from 'vitepress-plugin-demo/client/naive-ui' 6 | import Theme from 'vitepress/theme' 7 | import { h } from 'vue' 8 | import Outline from './components/Outline/index.vue' 9 | 10 | import { codeeditor } from './index.codeeditor' 11 | import '@shikijs/vitepress-twoslash/style.css' 12 | import 'virtual:group-icons.css' 13 | import './style.css' 14 | import './naive-ui.css' 15 | // import 'uno.css' 16 | 17 | // @unocss-include 18 | export default { 19 | ...Theme, 20 | Layout: () => { 21 | return h(Theme.Layout, null, { 22 | // https://vitepress.dev/guide/extending-default-theme#layout-slots 23 | 'aside-top': () => h(Outline), 24 | }) 25 | }, 26 | async enhanceApp({ app }: EnhanceAppContext) { 27 | if (!import.meta.env.SSR) { 28 | const { default: NaiveUI } = await import('naive-ui') 29 | const { default: NaiveUIProComponents } = await import('naive-ui-pro-components') 30 | app.use(NaiveUI) 31 | app.use(NaiveUIProComponents) 32 | app.use(NaiveUIContainer, { 33 | github: 'https://github.com/hairyf/naive-ui-pro-components/tree/main', 34 | codeeditor, 35 | install: [ 36 | NpGlobalProvider, 37 | ], 38 | }) 39 | } 40 | 41 | app.use(TwoslashFloating) 42 | }, 43 | } 44 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/naive-ui.css: -------------------------------------------------------------------------------- 1 | .VPImage.logo { 2 | width: 32px; 3 | height: 32px; 4 | } 5 | .VPNavBarTitle .title span{ 6 | font-size: 18px; 7 | font-weight: normal; 8 | } 9 | 10 | button span { 11 | font-size: 14px; 12 | font-weight: 400; 13 | font-family: inherit; 14 | } -------------------------------------------------------------------------------- /docs/.vitepress/uno.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | defineConfig, 3 | presetAttributify, 4 | presetIcons, 5 | presetUno, 6 | } from 'unocss' 7 | 8 | export default defineConfig({ 9 | shortcuts: { 10 | 'button-action': 'flex flex-inline gap-2 items-center justify-center px-3 py-0.5 rounded hover:color-$vp-c-brand-2 hover:bg-$vp-c-default-soft', 11 | 'border-base': 'border-color-$vp-c-divider', 12 | 'text-brand': 'color-$vp-c-brand-1', 13 | 'text-brand-yellow': 'color-$vp-c-yellow-1', 14 | 'text-brand-red': 'color-$vp-c-red-1', 15 | }, 16 | blocklist: [ 17 | 'container', 18 | ], 19 | presets: [ 20 | presetUno(), 21 | presetAttributify(), 22 | presetIcons(), 23 | ], 24 | safelist: [ 25 | 'font-mono', 26 | 'mb0!', 27 | 'no-underline!', 28 | ], 29 | }) 30 | -------------------------------------------------------------------------------- /docs/.vitepress/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable ts/no-empty-object-type */ 2 | /// 3 | 4 | declare module '*.vue' { 5 | import type { DefineComponent } from 'vue' 6 | 7 | const component: DefineComponent<{}, {}, any> 8 | export default component 9 | } 10 | -------------------------------------------------------------------------------- /docs/.vitepress/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url' 2 | import VueJsx from '@vitejs/plugin-vue-jsx' 3 | import AutoImport from 'unplugin-auto-import/vite' 4 | import Components from 'unplugin-vue-components/vite' 5 | import { defineConfig } from 'vite' 6 | import Tsconfig from 'vite-tsconfig-paths' 7 | import { groupIconVitePlugin as GroupIconVitePlugin } from 'vitepress-plugin-group-icons' 8 | 9 | export default defineConfig({ 10 | plugins: [ 11 | Tsconfig({ projects: [fileURLToPath(new URL('../../tsconfig.json', import.meta.url))] }), 12 | // UnoCSS(fileURLToPath(new URL('./uno.config.ts', import.meta.url))), 13 | GroupIconVitePlugin(), 14 | VueJsx(), 15 | AutoImport({ 16 | imports: [{ 17 | 'naive-ui-pro-components': [ 18 | 'defineForm', 19 | 'defineTable', 20 | 'defineControls', 21 | 'field', 22 | 'useColumnIndexes', 23 | 'useColumnLink', 24 | 'useColumns', 25 | ], 26 | }], 27 | }), 28 | Components({ 29 | resolvers: [{ 30 | type: 'component', 31 | resolve: (name: string) => { 32 | if (name.match(/^Np.+/)) 33 | return { name, from: 'naive-ui-pro-components' } 34 | if (name.match(/^N.+/)) 35 | return { name, from: 'naive-ui' } 36 | }, 37 | }], 38 | }), 39 | ], 40 | ssr: { 41 | noExternal: ['naive-ui', 'vueuc', 'date-fns'], 42 | }, 43 | }) 44 | -------------------------------------------------------------------------------- /docs/auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // noinspection JSUnusedGlobalSymbols 5 | // Generated by unplugin-auto-import 6 | export {} 7 | declare global { 8 | const defineControls: typeof import('naive-ui-pro-components')['defineControls'] 9 | const defineForm: typeof import('naive-ui-pro-components')['defineForm'] 10 | const defineTable: typeof import('naive-ui-pro-components')['defineTable'] 11 | const field: typeof import('naive-ui-pro-components')['field'] 12 | const useColumnIndexes: typeof import('naive-ui-pro-components')['useColumnIndexes'] 13 | const useColumnLink: typeof import('naive-ui-pro-components')['useColumnLink'] 14 | const useColumns: typeof import('naive-ui-pro-components')['useColumns'] 15 | } 16 | -------------------------------------------------------------------------------- /docs/components.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // @ts-nocheck 3 | // Generated by unplugin-vue-components 4 | // Read more: https://github.com/vuejs/core/pull/3399 5 | export {} 6 | 7 | /* prettier-ignore */ 8 | declare module 'vue' { 9 | export interface GlobalComponents { 10 | NButton: typeof import('naive-ui')['NButton'] 11 | NCheckbox: typeof import('naive-ui')['NCheckbox'] 12 | NpCheckboxGroup: typeof import('naive-ui-pro-components')['NpCheckboxGroup'] 13 | NpControls: typeof import('naive-ui-pro-components')['NpControls'] 14 | NpForm: typeof import('naive-ui-pro-components')['NpForm'] 15 | NpRadioGroup: typeof import('naive-ui-pro-components')['NpRadioGroup'] 16 | NpTable: typeof import('naive-ui-pro-components')['NpTable'] 17 | NSpace: typeof import('naive-ui')['NSpace'] 18 | NSwitch: typeof import('naive-ui')['NSwitch'] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /docs/components/checkbox/demo/basic.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 | -------------------------------------------------------------------------------- /docs/components/checkbox/demo/events.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 22 | -------------------------------------------------------------------------------- /docs/components/checkbox/demo/focus-blur.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 31 | -------------------------------------------------------------------------------- /docs/components/checkbox/index.md: -------------------------------------------------------------------------------- 1 | # ProCheckbox 2 | 3 | Creates a group of checkboxes based on configuration options. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | ## Props 12 | 13 | | Name | Type | Default | Description | 14 | | --- | --- | --- | --- | 15 | | options | `CheckboxMixedOption[]` | `-` | Checkbox configuration options | 16 | | space | `SpaceProps` | `-` | Spacing configuration | 17 | 18 | > For more parameters, refer to [checkbox-group](https://www.naiveui.com/zh-CN/os-theme/components/checkbox#CheckboxGroup-Props). 19 | 20 | ### CheckboxOption Properties 21 | 22 | | Name | Type | Description | 23 | | --- | --- | --- | 24 | | ref | `CheckboxInst \| Ref` | Used to bind a checkbox instance | 25 | | slots | `Record JSX.Element \| undefined>` | Checkbox Slots | 26 | 27 | > For more parameters, refer to [checkbox](https://www.naiveui.com/zh-CN/os-theme/components/checkbox#Checkbox-Props). 28 | -------------------------------------------------------------------------------- /docs/components/controls/demo/basic.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 28 | -------------------------------------------------------------------------------- /docs/components/controls/demo/custom.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 32 | -------------------------------------------------------------------------------- /docs/components/controls/demo/form.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 39 | -------------------------------------------------------------------------------- /docs/components/controls/demo/table.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 45 | -------------------------------------------------------------------------------- /docs/components/controls/index.md: -------------------------------------------------------------------------------- 1 | # Pro Controls 2 | 3 | Pro Controls are commonly used in Table Columns and Form Toolbars, and can also be rendered independently using ``. 4 | 5 | It returns a Function Component that renders the component based on the provided parameters. 6 | 7 | 8 | 9 | ::: demo twoslash src="./demo/form.vue" title="Form Toolbar" 10 | 11 | Used in conjunction with Form Toolbars, Pro Controls allow you to quickly create form toolbars. 12 | 13 | ::: 14 | 15 | 16 | 17 | ::: demo twoslash src="./demo/custom.vue" title="Custom Rendering" 18 | 19 | By default, Controls only render button controls. If you need to use other components, you can use the custom attribute. 20 | 21 | ::: 22 | 23 | ## Item Props 24 | 25 | | Name | Type | Default | Description | 26 | | --- | --- | --- | --- | 27 | | render | `string \| (...args) => string \| VNodeChild` | `-` | Render the button item content | 28 | | helper | `(...args) => void \| Promise` | `-` | Handle click events, if a `promise` is returned, it will automatically enable `loading` | 29 | | enable | `(...args) => void \| Promise` | `-` | Whether to enable the control, returning `false` will not render the control | 30 | | custom | `(...args) => VNodeChild` | `-` | custom render | 31 | 32 | > For more parameters, please refer to [n-button](https://www.naiveui.com/zh-CN/light/components/button). 33 | -------------------------------------------------------------------------------- /docs/components/form/demo/array.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 25 | -------------------------------------------------------------------------------- /docs/components/form/demo/basic.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 26 | -------------------------------------------------------------------------------- /docs/components/form/demo/clone.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 41 | -------------------------------------------------------------------------------- /docs/components/form/demo/data-transform.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 23 | -------------------------------------------------------------------------------- /docs/components/form/demo/field-context.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 53 | -------------------------------------------------------------------------------- /docs/components/form/demo/field-render.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 29 | -------------------------------------------------------------------------------- /docs/components/form/demo/field.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /docs/components/form/demo/formItemProps.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 | -------------------------------------------------------------------------------- /docs/components/form/demo/grid.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 31 | -------------------------------------------------------------------------------- /docs/components/form/demo/props.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 30 | -------------------------------------------------------------------------------- /docs/components/form/demo/toolbars.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 33 | -------------------------------------------------------------------------------- /docs/components/form/index.md: -------------------------------------------------------------------------------- 1 | # NProForm 2 | 3 | > Advanced form is used to quickly build form views. Using pro-form requires the use of defineForm functions. 4 | 5 | Advanced forms are based on the principles of functional programming and abstract the form data into a JavaScript object. By manipulating and encapsulating this object, the form data becomes more reusable and maintainable. 6 | 7 | ::: demo twoslash src="./demo/basic.vue" title="Basic" 8 | Advanced forms are defined using `defineForm`, which returns an instance of a form object. It restructures the form data and provides excellent TypeScript support. 9 | ::: 10 | 11 | ::: demo twoslash src="./demo/array.vue" title="Schema" 12 | `defineForm` also supports array/object schemas. When defining an array, the `key` is required and it merges the fields within the array. 13 | ::: 14 | 15 | ::: demo twoslash src="./demo/grid.vue" title="Grid" 16 | Different fields can be set with different `span` values to achieve different grid layouts. 17 | ::: 18 | 19 | ::: demo twoslash src="./demo/props.vue" title="Form Component Settings" 20 | The `props` attribute within the fields can be used to customize the form components. 21 | ::: 22 | 23 | ::: demo twoslash src="./demo/formItemProps.vue" title="Form Item Settings" 24 | The `formItemProps` attribute within the fields allows for customization of the form items. It inherits all the properties of [n-form-item](https://www.naiveui.com/en-US/os-theme/components/form#FormItem-Props) and [n-grid-item](https://www.naiveui.com/en-US/os-theme/components/grid#GridItem-Props). 25 | ::: 26 | 27 | ::: demo twoslash src="./demo/field.vue" title="Form Fields" 28 | `field` is an object description that can be a regular object. Fields can be separated from the form and provide additional capabilities through the `field` function. 29 | ::: 30 | 31 | ::: demo twoslash src="./demo/field-context.vue" title="Field Context" 32 | The `field|withConfig` method can accept the current form instance for validation combinations. 33 | ::: 34 | 35 | ::: demo twoslash src="./demo/field-render.vue" title="Field Rendering" 36 | The `renderItem` attribute allows for custom rendering of form items. For complex content, it is recommended to use tsx for writing. 37 | ::: 38 | 39 | ::: demo twoslash src="./demo/clone.vue" title="Field Cloning" 40 | The `clone` method can be used on fields returned by the `field` function to create a new cloned field, avoiding reference sharing of field data. 41 | ::: 42 | 43 | ::: demo twoslash src="./demo/toolbars.vue" title="Toolbars" 44 | By enabling the `toolbars` mode, the grid will be fixed as `0:24 742:6 1394:4`. You can customize the content of the right-side toolbar using the `toolbars` slot. 45 | ::: 46 | 47 | ## Props 48 | 49 | | Name | Type | Default | Description | 50 | | --- | --- | --- | --- | 51 | | is | `ProFormInstance` | `-` | The instance of the component | 52 | | grid | `boolean` | `true` | Enables grid layout | 53 | | cols | `number \| ResponsiveDescription` | `24` | The number of grid columns to display | 54 | | x-gap | `number \| ResponsiveDescription` | `0` | The horizontal gap between slots | 55 | | y-gap | `number \| ResponsiveDescription` | `0` | The vertical gap between slots | 56 | 57 | > For more parameters, please refer to the attributes of [n-form](https://www.naiveui.com/en-US/os-theme/components/form) and [n-grid](https://www.naiveui.com/en-US/os-theme/components/grid). 58 | 59 | ## Form Methods 60 | 61 | | Name | Type | Description | 62 | | --- | --- | --- | 63 | | validate | `(paths?: string[]) => Promise` | Validates the form items, passing `paths` 64 | 65 | filters the parameters to be validated | 66 | | resetValidate | `(paths?: string[]) => void` | Resets the validation, passing `paths` filters the parameters to be reset | 67 | | resetFields | `(paths?: string[]) => void` | Resets the form data to its initial values, passing `paths` filters the parameters to be reset | 68 | 69 | ## Field Methods 70 | 71 | | Name | Type | Description | 72 | | --- | --- | --- | 73 | | withConfig | `(config) => WithConfigField` | Carries and returns a new configuration | 74 | | preventDefault | `() => WithConfigField` | Sets the label and rules to empty | 75 | | preventRequired | `() => WithConfigField` | Removes the required rule from the rules | 76 | | preventAutofill | `() => WithConfigField` | Prevents browser autofill | 77 | | clone | `() => WithConfigField` | Shallow clones the `field` | 78 | | cloneDeep | `() => WithConfigField` | Deep clones the `field` | 79 | 80 | ## Field Types 81 | 82 | | Name | Description | 83 | | --- | --- | 84 | | date-picker | Date picker | 85 | | auto-complete | Auto-complete | 86 | | cascader | Cascading selector | 87 | | input | Input field | 88 | | input-number | Number input field | 89 | | rate | Rating | 90 | | time-picker | Time picker | 91 | | mention | Mention | 92 | | select | Selector | 93 | | switch | Switch | 94 | | slider | Slider | 95 | | radio | Radio button | 96 | | checkbox | Checkbox | 97 | | textarea | Textarea | 98 | 99 | ## Slots 100 | 101 | | Name | Type | Description | 102 | | --- | --- | --- | 103 | | toolbars | `()` | Content of the toolbar. This slot is automatically enabled when using the toolbar mode. | 104 | -------------------------------------------------------------------------------- /docs/components/globals/demo/basic.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 54 | -------------------------------------------------------------------------------- /docs/components/globals/index.md: -------------------------------------------------------------------------------- 1 | # Globals 2 | 3 | All global components provided by `naive-ui` cannot be used in the global scope because they need to be mounted to a component. We provide the `globals` component to use these feedback components in the global scope, and it will automatically mount the TypeScript types. 4 | 5 | ```html 6 | 7 | 8 | 9 | 10 | ``` 11 | 12 | Now you can use `$message`, `$dialog`, `$loadingBar`, `$notification` in the global scope. 13 | 14 | 15 | 16 | ```ts 17 | function onClick() { 18 | $message.info('Life is full of beautiful dreams') 19 | } 20 | ``` 21 | 22 | Furthermore, we have optimized the return results of `$message` and `$dialog`, so now you can directly use `await` to get the user's selection result: 23 | 24 | ```ts 25 | const ins = $dialog.warning({ 26 | content: glbI18n.t('merchant.store.deleteText'), 27 | // If the user clicks on confirmation, the postApiStoreDelete method will be executed, and the button will be in a loading state. 28 | onPositiveClick: () => postApiStoreDelete({ storeId: id }), 29 | }) 30 | 31 | // Destroy method 32 | ins.destroy 33 | 34 | // Promise methods 35 | ins.then 36 | ins.catch 37 | 38 | // Use await to wait for the result 39 | await ins 40 | // The user clicked on confirmation and waited for the request to finish 41 | ``` 42 | 43 | For `$message`, the promise becomes successful after the animation ends. This is very useful in scenarios where you need to wait for the animation to finish before executing further actions: 44 | 45 | ```ts 46 | await $message.error('Error') 47 | // Animation finished, component is destroyed 48 | ``` 49 | 50 | ## On-Demand Usage 51 | 52 | If you only need to use `$message`, you can achieve it by mounting the component separately: 53 | 54 | ```html 55 | 56 | 59 | 60 | 65 | ``` 66 | 67 | You can use `np-install-provider` to simplify the mounting process: 68 | 69 | ```html 70 | 71 | 74 | 84 | ``` 85 | -------------------------------------------------------------------------------- /docs/components/radio/demo/basic.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 21 | -------------------------------------------------------------------------------- /docs/components/radio/demo/buttons.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 30 | -------------------------------------------------------------------------------- /docs/components/radio/demo/sizes.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 31 | -------------------------------------------------------------------------------- /docs/components/radio/index.md: -------------------------------------------------------------------------------- 1 | # ProRadio 2 | 3 | Creates a group of radio buttons based on configuration options. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | ## Props 12 | 13 | | Name | Type | Default | Description | 14 | | --- | --- | --- | --- | 15 | | options | `CheckboxMixedOption[]` | `-` | Radio button configuration options | 16 | | space | `SpaceProps` | `-` | Spacing configuration | 17 | | type | `'default' \| 'button'` | `-` | Type of radio button group | 18 | 19 | > For more parameters, refer to [radio-group](https://www.naiveui.com/zh-CN/os-theme/components/radio#RadioGroup-Props). 20 | 21 | ### RadioOption Properties 22 | 23 | | Name | Type | Description | 24 | | --- | --- | --- | 25 | | slots | `Record JSX.Element \| undefined>` | Radio Slots | 26 | 27 | > For more parameters, refer to [radio, radio-button](https://www.naiveui.com/zh-CN/os-theme/components/radio#Radio-Props,-RadioButton-Props). 28 | -------------------------------------------------------------------------------- /docs/components/table/demo/basic.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 32 | -------------------------------------------------------------------------------- /docs/components/table/demo/form.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 47 | -------------------------------------------------------------------------------- /docs/components/table/demo/hooks.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 40 | -------------------------------------------------------------------------------- /docs/components/table/demo/pagination.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 32 | -------------------------------------------------------------------------------- /docs/components/table/demo/request.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 60 | -------------------------------------------------------------------------------- /docs/components/table/demo/watcher.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 49 | -------------------------------------------------------------------------------- /docs/components/table/index.md: -------------------------------------------------------------------------------- 1 | # NProTable 2 | 3 | The advanced table is designed to address the issue of writing repetitive boilerplate code for tables in projects. It encapsulates many common logic and behaviors. These encapsulations can be simply classified into preset actions and preset logic. 4 | 5 | If your table needs to interact with the server or requires different cell styles, ProTable is the perfect choice. However, if you just need to render a basic table, we recommend using [data-table](https://www.naiveui.com/en-US/os-theme/components/data-table) or [table](https://www.naiveui.com/en-US/os-theme/components/table). 6 | 7 | 8 | 9 | ::: demo twoslash src="./demo/request.vue" title="External Control" 10 | 11 | Use `defineTable` to control the request externally. 12 | 13 | ::: 14 | 15 | ::: demo twoslash src="./demo/form.vue" title="Form Integration" 16 | 17 | You can also use it with `defineForm` to create a complete search page: 18 | 19 | ::: 20 | 21 | ::: demo twoslash src="./demo/pagination.vue" title="Table Pagination" 22 | 23 | Pagination is enabled by default. If you don't need pagination, you can set the `pagination` parameter to `false` to disable it. You can also override the `pagination` parameter by providing your own `pagination` configuration. 24 | 25 | ::: 26 | 27 | ::: demo twoslash src="./demo/watcher.vue" title="Data Watcher" 28 | 29 | When the table data changes, you may need to perform additional operations. You can use the `watch` parameter to listen for data changes, and it will automatically reset the pagination and make a new request. 30 | 31 | ::: 32 | 33 | ::: demo twoslash src="./demo/hooks.vue" title="Hooks Functions" 34 | 35 | The table provides several utility functions to quickly create columns. 36 | 37 | ::: 38 | 39 | ## Props 40 | 41 | | Name | Type | Default | Description | 42 | | --- | --- | --- | --- | 43 | | is | `ProTableInstance` | `-` | The instance of the component | 44 | | pagination | `boolean \| PaginationProps` | `true` | Pagination configuration | 45 | 46 | > For more parameters, refer to [data-table](https://www.naiveui.com/en-US/os-theme/components/data-table). 47 | 48 | ## Methods (table) 49 | 50 | | Name | Type | Description | 51 | | --- | --- | --- | 52 | | pagination | `OffsetPagination` | Pagination configuration for the table | 53 | | next | `() => void` | Go to the next page and make the corresponding request | 54 | | prev | `() => void` | Go to the previous page and make the corresponding request | 55 | | search | `(pagination?: ServerPaginationResolve) => Promise` | Request content based on pagination information | 56 | | reset | `() => Promise` | Reset the page number and make a new request | 57 | | request | `(pagination: ServerPaginationResolve) => DataResolved \| Promise>` | Source request function | 58 | | requestAll | `() => Promise` | Request all data based on page-related information, usually used for combining `.csv` files | 59 | -------------------------------------------------------------------------------- /docs/guide/faq.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | ## Passing an Object Instead of an Array in defineForm 4 | 5 | You may be puzzled by the use of objects instead of arrays in defineForm to declare the order of components. While we did initially use arrays in the design of the components, we found that arrays had some issues in practical use. Through our long-term practice, we believe that using objects is completely viable. 6 | 7 | **Type inference doesn't work well with arrays:** 8 | 9 | ```ts 10 | const form = defineForm([ 11 | { 12 | type: 'input', 13 | key: 'name' as const, 14 | }, 15 | { 16 | type: 'input', 17 | key: 'age' as const, 18 | }, 19 | ]) 20 | 21 | // Cannot correctly infer the type of form 22 | form.data // { name: string } | { age: string } 23 | ``` 24 | 25 | On the other hand, using objects allows for easy type inference: 26 | 27 | ```ts 28 | const form = defineForm({ 29 | name: { 30 | type: 'input', 31 | }, 32 | age: { 33 | type: 'input', 34 | }, 35 | }) 36 | 37 | form.data // { name: string; age: string } 38 | ``` 39 | 40 | **Limited flexibility in reusing combined form fields:** 41 | 42 | ```ts 43 | const nameField = { 44 | type: 'input', 45 | key: 'name' as const, 46 | // ...other configurations 47 | } 48 | 49 | const form = defineForm([ 50 | nameField, 51 | { ...nameField, key: 'name2' as const } 52 | ]) 53 | ``` 54 | 55 | Using object reuse: 56 | 57 | ```ts 58 | const nameField = { 59 | type: 'input', 60 | // ...other configurations 61 | } 62 | const form = defineForm({ 63 | name: nameField, 64 | name2: nameField, 65 | }) 66 | ``` 67 | 68 | While arrays make it easy to manipulate the order and display of form fields through indexing, the reality is that indexing is not always intuitive. Objects can also achieve dynamic form display through composition: 69 | 70 | ```ts 71 | const formFields = {/* ... */} 72 | const formFields2 = {/* ... */} 73 | 74 | const form = defineForm(() => { 75 | return { ...formFields, ...formFields2 } 76 | }) 77 | ``` 78 | 79 | ## Separation of Pagination Configuration and Control in defineTable 80 | 81 | In `NProTable`, the `pagination` property of the component is only used to configure the display parameters of pagination, while `page` and `pageSize` are managed entirely by `defineTable`. You can configure the initial values of these two parameters through the arguments of `defineTable`. 82 | 83 | ```ts 84 | const table = defineTable({ 85 | // ...other configurations 86 | page: 2, 87 | pageSize: 15, 88 | }) 89 | 90 | // You can access the current page number and page size through table.pagination.page and table.pagination.pageSize 91 | ``` 92 | -------------------------------------------------------------------------------- /docs/guide/index.md: -------------------------------------------------------------------------------- 1 | # The concept of ProComponents 2 | 3 | Similar to Ant Design Pro Components, Naive UI Pro Components is an advanced component library based on Naive UI, providing higher-level abstractions and encapsulation for backend systems. 4 | 5 | We have made some adjustments to the design of Naive UI Pro Components to better align with the design style of Naive UI and provide the ability to quickly and efficiently build high-quality backend applications. 6 | 7 | ## Design Ideas 8 | 9 | > The following content is from Ant Pro Components 10 | 11 | For almost any business, we actually define a series of behaviors based on state. Taking a table as an example, first, we need a state `data` to store the data requested from the server. For a better user experience, we also need a `loading` state. So we have a series of behaviors: we need to set `loading=true` first, then make a network request, set `data` to the requested data and `loading=false` when the network request is completed. Although it's very simple, a business system has a considerable number of tables, and each table is defined only once, resulting in a significant amount of work. 12 | 13 | If we want to re-request the network, we need to encapsulate the above behaviors into a method. When clicking to reload the data, if there is pagination, we also need a new variable `page`. We need to determine whether to reset the page to the first page before re-requesting based on our needs, which introduces another variable. If your form also needs to control the number of items displayed per page, it will be even more cumbersome. This repetitive work will waste a lot of our time. 14 | 15 | ## A component !≈ a page 16 | 17 | We don't want you to see components as pages, but rather as a superset of functionality. This allows your code to remain flexible, which is also the difference between Naive UI Pro Components and Ant Design Pro Components. 18 | 19 | A list page can use the combination of `pro-form` and `pro-table`, while an edit page can utilize `pro-form` plus a `button` or other various components. This allows us to focus on implementing core business logic and page effects. 20 | 21 | Based on this reason, we are cautious when developing new components. We try to break down the functionality of components into smaller components as much as possible, so that you can better combine them. 22 | -------------------------------------------------------------------------------- /docs/guide/install.md: -------------------------------------------------------------------------------- 1 | # Naive UI Pro Components 2 | 3 | Pro Components is a set of advanced components developed based on Naive UI. It provides higher-level abstractions and encapsulation, with out-of-the-box usability, and significantly improves the efficiency of creating CRUD pages, focusing on page development. 4 | 5 | ## Features 6 | 7 | - [ProForm](/components/form/) - A form template component based on object-oriented functional programming, with preset common layouts and behaviors. 8 | - [ProTable](/components/table/) - Abstracts network requests and table formatting. 9 | - [ProControls](/components/controls/) - Form and table controls for multiple scenarios. 10 | - [ProGlobals](/components/globals/) - Mounting and optimization of global feedback components. 11 | - In development... 12 | 13 | ## Installation 14 | 15 | Each component in pro-components is a separate package. You can also install `naive-ui-pro-components` to use all components. 16 | 17 | ```sh 18 | pnpm add @naive-ui-pro/form 19 | pnpm add @naive-ui-pro/table 20 | 21 | # or 22 | 23 | pnpm add naive-ui-pro-components 24 | ``` 25 | 26 | ## Global Import 27 | 28 | ```ts 29 | import NaiveUIProComponents from 'naive-ui-pro-components' 30 | import { createApp } from 'vue' 31 | import App from './App.vue' 32 | 33 | const app = createApp(App) 34 | 35 | app.use(NaiveUIProComponents) 36 | ``` 37 | 38 | ## On-Demand Import (Recommended) 39 | 40 | Install the `unplugin-vue-components` and `unplugin-auto-import` plugins, which will automatically import all components and APIs from `naive-ui-pro-components`. 41 | 42 | ```sh 43 | npm install -D unplugin-vue-components unplugin-auto-import 44 | ``` 45 | 46 | ### Vite 47 | 48 | ```ts 49 | import NaiveUIProImports from 'naive-ui-pro-components/imports' 50 | import NaiveUIProResolver from 'naive-ui-pro-components/resolver' 51 | import AutoImport from 'unplugin-auto-import/vite' 52 | import Components from 'unplugin-vue-components/vite' 53 | // vite.config.ts 54 | import { defineConfig } from 'vite' 55 | 56 | export default defineConfig({ 57 | // ... 58 | plugins: [ 59 | // ... 60 | AutoImport({ 61 | imports: [NaiveUIProImports()], 62 | }), 63 | Components({ 64 | resolvers: [NaiveUIProResolver()], 65 | }), 66 | ], 67 | }) 68 | ``` 69 | 70 | ### Webpack 71 | 72 | ```js 73 | const NaiveUIProImports = require('naive-ui-pro-components/imports') 74 | const NaiveUIProResolver = require('naive-ui-pro-components/resolver') 75 | // webpack.config.js 76 | const AutoImport = require('unplugin-auto-import/webpack') 77 | const Components = require('unplugin-vue-components/webpack') 78 | 79 | module.exports = { 80 | // ... 81 | plugins: [ 82 | // ... 83 | AutoImport({ 84 | imports: [NaiveUIProImports()], 85 | }), 86 | Components({ 87 | resolvers: [NaiveUIProResolver()], 88 | }), 89 | ], 90 | } 91 | ``` 92 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: "Naive UI Pro Components" 7 | text: "Naive UI" 8 | tagline: Make the development of middle and back office easier 9 | image: /logo.svg 10 | actions: 11 | - theme: brand 12 | text: Get Started 13 | link: /guide/ 14 | - theme: alt 15 | text: View on GitHub 16 | link: https://github.com/hairyf/naive-ui-pro-components 17 | 18 | features: 19 | - title: Easy to use 20 | details: Wrap under Naive UI to quickly build middle and back office systems. 21 | - title: Naive UI 22 | details: Seamlessly integrate with Naive UI components and extend them further. 23 | - title: TypeScript 24 | details: Written in TypeScript and comes with complete TS documentation. 25 | --- 26 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "scripts": { 5 | "docs:dev": "vitepress dev", 6 | "docs:build": "vitepress build", 7 | "docs:preview": "vitepress preview" 8 | }, 9 | "dependencies": { 10 | "naive-ui": "catalog:share", 11 | "naive-ui-pro-components": "workspace:^" 12 | }, 13 | "devDependencies": { 14 | "@iconify-json/svg-spinners": "catalog:", 15 | "@shikijs/vitepress-twoslash": "catalog:", 16 | "@unocss/reset": "catalog:", 17 | "@vueuse/core": "catalog:", 18 | "floating-vue": "catalog:", 19 | "pinia": "catalog:", 20 | "unocss": "catalog:", 21 | "unplugin-auto-import": "^0.16.4", 22 | "unplugin-vue-components": "catalog:", 23 | "vite-tsconfig-paths": "catalog:", 24 | "vitepress": "catalog:", 25 | "vitepress-plugin-demo": "^0.8.0", 26 | "vitepress-plugin-group-icons": "catalog:", 27 | "vue": "catalog:share" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /docs/public/logo.svg: -------------------------------------------------------------------------------- 1 | Naive UI - LOGO -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "jsx": "preserve", 5 | "jsxImportSource": "vue", 6 | "lib": [ 7 | "esnext", 8 | "dom" 9 | ], 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "strict": true, 14 | "declaration": true, 15 | "downlevelIteration": true, 16 | "sourceMap": true, 17 | "esModuleInterop": true, 18 | "skipDefaultLibCheck": true, 19 | "skipLibCheck": true 20 | }, 21 | "include": [ 22 | "./**/*.ts", 23 | "./**/*.d.ts", 24 | "./**/*.tsx", 25 | "./**/*.vue", 26 | "types/**.d.ts", 27 | "types/**/**.d.ts" 28 | ], 29 | "exclude": [ 30 | "**/dist/*.ts", 31 | "**/dist/*.d.ts" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /docs/zh-CN/components/checkbox/demo/basic.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 | -------------------------------------------------------------------------------- /docs/zh-CN/components/checkbox/demo/events.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 22 | -------------------------------------------------------------------------------- /docs/zh-CN/components/checkbox/demo/focus-blur.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 31 | -------------------------------------------------------------------------------- /docs/zh-CN/components/checkbox/index.md: -------------------------------------------------------------------------------- 1 | # ProCheckbox 2 | 3 | 以配置项创建复选框组。 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | ## Props 12 | 13 | | 名称 | 类型 | 默认值 | 说明 | 14 | | --- | --- | --- | --- | 15 | | options | `CheckboxMixedOption[]` | `-` | 复选框配置 | 16 | | space | `SpaceProps` | `-` | 间隔配置 | 17 | 18 | > 更多参数参考 [checkbox-group](https://www.naiveui.com/zh-CN/os-theme/components/checkbox#CheckboxGroup-Props)。 19 | 20 | ### CheckboxOption Properties 21 | 22 | | 名称 | 类型 | 说明 | 23 | | --- | --- | --- | 24 | | ref | `CheckboxInst \| Ref` | 用于绑定某个 checkbox 实例 | 25 | | slots | `Record JSX.Element \| undefined>` | Checkbox Slots | 26 | 27 | > 更多参数参考 [checkbox](https://www.naiveui.com/zh-CN/os-theme/components/checkbox#Checkbox-Props)。 28 | -------------------------------------------------------------------------------- /docs/zh-CN/components/controls/demo/basic.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 28 | -------------------------------------------------------------------------------- /docs/zh-CN/components/controls/demo/custom.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 32 | -------------------------------------------------------------------------------- /docs/zh-CN/components/controls/demo/form.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 39 | -------------------------------------------------------------------------------- /docs/zh-CN/components/controls/demo/table.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 45 | -------------------------------------------------------------------------------- /docs/zh-CN/components/controls/index.md: -------------------------------------------------------------------------------- 1 | # Pro Controls 2 | 3 | Pro Controls 通常用于在 Table Columns 与 Form Toolbars 中,它也可以使用 `` 独立渲染。 4 | 5 | 它返回一个 Function Component,该 Function Component 会根据传入的参数渲染组件。 6 | 7 | 8 | 9 | ::: demo twoslash src="./demo/form.vue" title="表单工具栏" 10 | 11 | 与 Form Toolbars 配合使用,可以快速制作表单工具栏。 12 | 13 | ::: 14 | 15 | 16 | 17 | ::: demo twoslash src="./demo/custom.vue" title="自定义渲染" 18 | 19 | 默认情况下,Controls 仅渲染 button 控件,如果需要使用其他组件,可以使用 `custom` 属性。 20 | 21 | ::: 22 | 23 | ## Item Props 24 | 25 | | 名称 | 类型 | 默认值 | 说明 | 26 | | --- | --- | --- | --- | 27 | | render | `string \| (...args) => string \| VNodeChild` | `-` | 渲染按钮控件项目内容 | 28 | | helper | `(...args) => void \| Promise` | `-` | 处理点击事件,如果返回的是 `promise` 将自动开启 `loading` | 29 | | enable | `(...args) => void \| Promise` | `-` | 是否启用控件,返回 `false` 将不渲染该控件 | 30 | | custom | `(...args) => VNodeChild` | `-` | 自定义渲染 | 31 | 32 | > 更多参数请参考 [n-button](https://www.naiveui.com/zh-CN/light/components/button)。 33 | -------------------------------------------------------------------------------- /docs/zh-CN/components/form/demo/array.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 25 | -------------------------------------------------------------------------------- /docs/zh-CN/components/form/demo/basic.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 27 | -------------------------------------------------------------------------------- /docs/zh-CN/components/form/demo/clone.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 38 | -------------------------------------------------------------------------------- /docs/zh-CN/components/form/demo/data-transform.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 23 | -------------------------------------------------------------------------------- /docs/zh-CN/components/form/demo/field-context.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 53 | -------------------------------------------------------------------------------- /docs/zh-CN/components/form/demo/field-render.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 29 | -------------------------------------------------------------------------------- /docs/zh-CN/components/form/demo/field.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /docs/zh-CN/components/form/demo/formItemProps.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 | -------------------------------------------------------------------------------- /docs/zh-CN/components/form/demo/grid.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 31 | -------------------------------------------------------------------------------- /docs/zh-CN/components/form/demo/props.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 30 | -------------------------------------------------------------------------------- /docs/zh-CN/components/form/demo/toolbars.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 32 | -------------------------------------------------------------------------------- /docs/zh-CN/components/form/index.md: -------------------------------------------------------------------------------- 1 | # NProForm 2 | 3 | > 高级表单用于快速构建表单视图,使用 pro-form 需要使用 defineForm Functions。 4 | 5 | 高级表单基于对象的函数式编程(functional programming)思想。并将表单数据抽象为一个 JavaScript 对象,通过对这个对象进行操作和封装,让表单数据具有更好的可复用性和可维护性。 6 | 7 | ::: demo twoslash src="./demo/basic.vue" title="基础" 8 | 高级表单使用 `defineForm` 定义,返回一个表单对象的实例,它会将表单数据进行重组,它具有良好的 Typescript 支持。 9 | ::: 10 | 11 | ::: demo twoslash src="./demo/array.vue" title="模式" 12 | `defineForm` 同时支持数组/对象模式,定义数组时,`key` 是必填项,它会将数组中的字段进行合并。 13 | ::: 14 | 15 | ::: demo twoslash src="./demo/grid.vue" title="栅格" 16 | 对不同的字段的 `span` 进行设置,可以实现不同栅格布局。 17 | ::: 18 | 19 | ::: demo twoslash src="./demo/props.vue" title="表单组件设置" 20 | 字段中的 `props` 属性,可以对表单组件进行设置。 21 | ::: 22 | 23 | ::: demo twoslash src="./demo/formItemProps.vue" title="表单项设置" 24 | 字段的 `formItemProps` 可以对表单项进行设置,它会继承 [n-form-item](https://www.naiveui.com/zh-CN/os-theme/components/form#FormItem-Props) 和 [n-grid-item](https://www.naiveui.com/zh-CN/os-theme/components/grid#GridItem-Props) 的所有属性。 25 | ::: 26 | 27 | ::: demo twoslash src="./demo/field.vue" title="表单字段" 28 | `field` 是一个描述对象,他可以是普通的对象,field 可以与 form 分离,并且通过 `field` 函数创建具有额外的能力。 29 | ::: 30 | 31 | ::: demo twoslash src="./demo/field-context.vue" title="字段上下文" 32 | `field|withConfig` 传入函数可接收当前使用的 form 实例,用于组合校验。 33 | ::: 34 | 35 | ::: demo twoslash src="./demo/field-render.vue" title="字段渲染" 36 | 通过 `renderItem` 字段可以自定义渲染表单项,如果内容比较复杂,我们建议使用 tsx 编写。 37 | ::: 38 | 39 | ::: demo twoslash src="./demo/clone.vue" title="字段克隆" 40 | 通过 `field` 方法返回的字段,使用 `clone` 方法可以克隆一个新的字段,避免字段数据引用是同一个 value。 41 | ::: 42 | 43 | ::: demo twoslash src="./demo/toolbars.vue" title="工具栏" 44 | 开启 `toolbars` 模式,栅格将固定为 `0:24 742:6 1394:4`,你可以通过 `toolbars` 插槽自定义右侧工具栏内容。 45 | ::: 46 | 47 | ## Props 48 | 49 | | 名称 | 类型 | 默认值 | 说明 | 50 | | --- | --- | --- | --- | 51 | | is | `ProFormInstance` | `-` | 组件的实例 | 52 | | grid | `boolean` | `true` | 是否开启栅格布局 | 53 | | cols | `number \| ResponsiveDescription` | `24` | 显示的栅格数量 | 54 | | x-gap | `number \| ResponsiveDescription` | `0` | 横向间隔槽 | 55 | | y-gap | `number \| ResponsiveDescription` | `0` | 纵向间隔槽 | 56 | 57 | > 更多参数请参考 [n-form](https://www.naiveui.com/zh-CN/os-theme/components/form) 和 [n-grid](https://www.naiveui.com/zh-CN/os-theme/components/grid) 的属性。 58 | 59 | ## Form Methods 60 | 61 | | 名称 | 类型 | 说明 | 62 | | --- | --- | --- | 63 | | validate | `(paths?: string[]) => Promise` | 验证表项,传递 `paths` 过滤需要验证的参数 | 64 | | resetValidate | `(paths?: string[]) => void` | 重置校验,传递 `paths` 过滤需要重置的参数 | 65 | | resetFields | `(paths?: string[]) => void` | 重置表单数据为初始值,传递 `paths` 过滤需要重置的参数 | 66 | 67 | ## Field Methods 68 | 69 | | 名称 | 类型 | 说明 | 70 | | --- | --- | --- | 71 | | withConfig | `(config) => WithConfigField` | 携带并返回新的配置 | 72 | | preventDefault | `() => WithConfigField` | 将 label、rules 置空 | 73 | | preventRequired | `() => WithConfigField` | rules 中的 required 被删除 | 74 | | preventAutofill | `() => WithConfigField` | 阻止浏览器自动填充 | 75 | | clone | `() => WithConfigField` | 浅拷贝 `field` | 76 | | cloneDeep | `() => WithConfigField` | 深拷贝 `field` | 77 | 78 | ## Field Types 79 | 80 | | 名称 | 说明 | 81 | | --- | --- | 82 | | date-picker | 日期选择器 | 83 | | auto-complete | 自动完成 | 84 | | cascader | 级联选择器 | 85 | | input | 输入框 | 86 | | input-number | 数字输入框 | 87 | | rate | 评分 | 88 | | time-picker | 时间选择器 | 89 | | mention | 提及 | 90 | | select | 选择器 | 91 | | switch | 开关 | 92 | | slider | 滑块 | 93 | | radio | 单选框 | 94 | | checkbox | 多选框 | 95 | | textarea | 多行输入框 | 96 | 97 | ## Slots 98 | 99 | | 名称 | 类型 | 说明 | 100 | | --- | --- | --- | 101 | | toolbars | `()` | 工具栏内容,使用该插槽默认开启工具栏模式 | 102 | -------------------------------------------------------------------------------- /docs/zh-CN/components/globals/demo/basic.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 54 | -------------------------------------------------------------------------------- /docs/zh-CN/components/globals/index.md: -------------------------------------------------------------------------------- 1 | # Globals 2 | 3 | `naive-ui` 所提供的所有全局组件,并不能在全局中使用,因为他们需要挂载到某个组件上,我们提供了 `globals` 组件,用于在全局中使用这些反馈组件,它将自动挂载 Typescript 类型。 4 | 5 | ```html 6 | 7 | 8 | 9 | 10 | ``` 11 | 12 | 现在你可以在全局中通过使用 `$message`、`$dialog`、`$loadingBar`、`$notification` 来使用这些方法。 13 | 14 | 15 | 16 | ```ts 17 | function onClick() { 18 | $message.info('人生处处是美梦') 19 | } 20 | ``` 21 | 22 | 另外,我们对 `$message`、`$dialog` 的返回结果进行了优化,现在你可以直接使用 `await` 来获取用户的选择结果: 23 | 24 | ```ts 25 | const ins = $dialog.warning({ 26 | content: glbI18n.t('merchant.store.deleteText'), 27 | // 如果用户点击了确认,则会执行 postApiStoreDelete 方法,此时 button 处于 loading 状态 28 | onPositiveClick: () => postApiStoreDelete({ storeId: id }), 29 | }) 30 | 31 | // 销毁方法 32 | ins.destroy 33 | 34 | // promise 方法 35 | ins.then 36 | ins.catch 37 | 38 | // 使用 await 等待结果 39 | await ins 40 | // 用户点击了确认,并等待请求结束 41 | ``` 42 | 43 | `$message` 则是动画结束后,promise 变为成功,这在一些需要等待动画结束后再执行的场景中非常有用: 44 | 45 | ```ts 46 | await $message.error('错误') 47 | // 动画执行结束,组件被销毁 48 | ``` 49 | 50 | ## 按需使用 51 | 52 | 如果你只需要使用 `$message`,你可以通过挂载单独的组件来实现: 53 | 54 | ```html 55 | 56 | 59 | 60 | 65 | ``` 66 | 67 | 你可以使用 `np-install-provider` 简化挂载操作: 68 | 69 | ```html 70 | 71 | 74 | 84 | ``` 85 | -------------------------------------------------------------------------------- /docs/zh-CN/components/radio/demo/basic.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 21 | -------------------------------------------------------------------------------- /docs/zh-CN/components/radio/demo/buttons.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 30 | -------------------------------------------------------------------------------- /docs/zh-CN/components/radio/demo/sizes.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 31 | -------------------------------------------------------------------------------- /docs/zh-CN/components/radio/index.md: -------------------------------------------------------------------------------- 1 | # ProRadio 2 | 3 | 以配置项创建单选框组。 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | ## Props 12 | 13 | | 名称 | 类型 | 默认值 | 说明 | 14 | | --- | --- | --- | --- | 15 | | options | `CheckboxMixedOption[]` | `-` | 复选框配置 | 16 | | space | `SpaceProps` | `-` | 间隔配置 | 17 | | type | `'default' \| 'button'` | `-` | 单选组类型 | 18 | 19 | > 更多参数参考 [radio-group](https://www.naiveui.com/zh-CN/os-theme/components/radio#RadioGroup-Props)。 20 | 21 | ### RadioOption Properties 22 | 23 | | 名称 | 类型 | 说明 | 24 | | --- | --- | --- | 25 | | slots | `Record JSX.Element \| undefined>` | Radio Slots | 26 | 27 | > 更多参数参考 [radio, radio-button](https://www.naiveui.com/zh-CN/os-theme/components/radio#Radio-Props,-RadioButton-Props)。 28 | -------------------------------------------------------------------------------- /docs/zh-CN/components/table/demo/basic.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 32 | -------------------------------------------------------------------------------- /docs/zh-CN/components/table/demo/form.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 47 | -------------------------------------------------------------------------------- /docs/zh-CN/components/table/demo/hooks.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 44 | -------------------------------------------------------------------------------- /docs/zh-CN/components/table/demo/pagination.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 32 | -------------------------------------------------------------------------------- /docs/zh-CN/components/table/demo/request.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 60 | -------------------------------------------------------------------------------- /docs/zh-CN/components/table/demo/watcher.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 49 | -------------------------------------------------------------------------------- /docs/zh-CN/components/table/index.md: -------------------------------------------------------------------------------- 1 | # NProTable 2 | 3 | NProTable 用于解决项目中需要写很多 table 的样板代码的问题,所以在其中做了封装了很多常用的逻辑。这些封装可以简单的分类为预设行为与预设逻辑。 4 | 5 | 当你的表格需要与服务端进行交互或者需要多种单元格样式时,ProTable 是不二选择,如果你只是想渲染一个表格,更建议你使用 [data-table](https://www.naiveui.com/zh-CN/os-theme/components/data-table) 或者 [table](https://www.naiveui.com/zh-CN/os-theme/components/table)。 6 | 7 | 8 | 9 | ::: demo twoslash src="./demo/request.vue" title="外部控制" 10 | 11 | 使用 `defineTable` 在外部控制请求。 12 | 13 | ::: 14 | 15 | ::: demo twoslash src="./demo/form.vue" title="表单组合" 16 | 17 | 你还可以和 `defineForm` 配合使用组成一个完整的搜索页: 18 | 19 | ::: 20 | 21 | ::: demo twoslash src="./demo/pagination.vue" title="表格分页" 22 | 23 | 分页默认开启,如果你不需要分页,可以通过 `pagination` 参数设置为 `false` 关闭,你也可以通过设置 `pagination` 覆盖 `pagination` 参数。 24 | 25 | ::: 26 | 27 | ::: demo twoslash src="./demo/watcher.vue" title="监听数据" 28 | 29 | 当表格数据变化时,你可能需要做一些额外的操作,这时你可以通过 `watch` 参数来监听数据变化,它将会自动重置分页并请求。 30 | 31 | ::: 32 | 33 | ::: demo twoslash src="./demo/hooks.vue" title="钩子函数" 34 | 35 | 表格存在一些工具函数,用于快速创建列。 36 | 37 | ::: 38 | 39 | ## Props 40 | 41 | | 名称 | 类型 | 默认值 | 说明 | 42 | | --- | --- | --- | --- | 43 | | is | `ProTableInstance` | `-` | 组件的实例 | 44 | | pagination | `boolean \| PaginationProps` | `true` | 分页配置 | 45 | 46 | > 更多参数参考 [data-table](https://www.naiveui.com/zh-CN/os-theme/components/data-table)。 47 | 48 | ## Methods(table) 49 | 50 | | 名称 | 类型 | 说明 | 51 | | --- | --- | --- | 52 | | pagination | `OffsetPagination` | 表单分页配置 | 53 | | next | `() => void` | 跳转下一页,并请求相关页面 | 54 | | prev | `() => void` | 跳转上一页,并请求相关页面 | 55 | | search | `(pagination?: ServerPaginationResolve) => Promise` | 根据分页信息重新请求内容 | 56 | | reset | `() => Promise` | 重置页码并重新请求 | 57 | | request | `(pagination: ServerPaginationResolve) => DataResolved \| Promise>` | 源请求函数 | 58 | | requestAll | `() => Promise` | 根据页码相关信息请求所有数据,一般可用于组合 `.csv` 文件 | 59 | -------------------------------------------------------------------------------- /docs/zh-CN/guide/faq.md: -------------------------------------------------------------------------------- 1 | # 常见问题 2 | 3 | ## defineForm 传递对象而不是数组 4 | 5 | 你可以会感到疑惑,defineForm 通过对象的形式声明组件的顺序,而不是数组,在组件设计之初我们我们的确是使用数组的形式,但是在实际使用中,我们发现数组的形式会有一些问题,且经过我们长期的实践,我们认为采用对象是完全可行的。 6 | 7 | **类型推断在数组上无法进行:** 8 | 9 | ```ts 10 | const form = defineForm([ 11 | { 12 | type: 'input', 13 | key: 'name' as const, 14 | }, 15 | { 16 | type: 'input', 17 | key: 'age' as const, 18 | }, 19 | ]) 20 | 21 | // 无法正确推断出 form 的类型 22 | form.data // { name: string } | { age: string } 23 | ``` 24 | 25 | 而通过对象的形式却能轻松的推断出类型: 26 | 27 | ```ts 28 | const form = defineForm({ 29 | name: { 30 | type: 'input', 31 | }, 32 | age: { 33 | type: 'input', 34 | }, 35 | }) 36 | 37 | form.data // { name: string; age: string } 38 | ``` 39 | 40 | **组合表单的复用形式不够灵活:** 41 | 42 | ```ts 43 | const nameField = { 44 | type: 'input', 45 | key: 'name' as const, 46 | // ...其他配置 47 | } 48 | 49 | const form = defineForm([ 50 | nameField, 51 | { ...nameField, key: 'name2' as const } 52 | ]) 53 | ``` 54 | 55 | 对象的复用形式: 56 | 57 | ```ts 58 | const nameField = { 59 | type: 'input', 60 | // ...其他配置 61 | } 62 | const form = defineForm({ 63 | name: nameField, 64 | name2: nameField, 65 | }) 66 | ``` 67 | 68 | 尽管通过数组可以很轻松的操作索引控制表单顺序和显示,然而现实情况是索引却并不直观,而对象也可以通过组合的方式实现动态的表单显示: 69 | 70 | ```ts 71 | const formFields = {/* ... */} 72 | const formFields2 = {/* ... */} 73 | 74 | const form = defineForm(() => { 75 | return { ...formFields, ...formFields2 } 76 | }) 77 | ``` 78 | 79 | ## defineTable 分页的配置与控制分离 80 | 81 | 在 `NProTable` 中,组件的 `pagination` 仅用于配置分页的显示参数,而 `page` 与 `pageSize` 由 `defineTable` 全程接管,你可以通过 `defineTable` 参数配置这两个参数的初始值。 82 | 83 | ```ts 84 | const table = defineTable({ 85 | // ...其他配置 86 | page: 2, 87 | pageSize: 15, 88 | }) 89 | 90 | // 你可以通过 table.pagination.page、table.pagination.pageSize 获取当前页码和当前页大小 91 | ``` 92 | -------------------------------------------------------------------------------- /docs/zh-CN/guide/index.md: -------------------------------------------------------------------------------- 1 | # Naive UI Pro Components 2 | 3 | Pro Components 是基于 Naive UI 开发的一套高级组件。它提供了更高层次的抽象和封装,具备开箱即用的可用性,并显著提高了创建 CRUD 页面的效率,专注于页面开发。 4 | 5 | ## Features 6 | 7 | - [ProForm](/zh-CN/components/form/) 基于对象的函数式编程的表单模板组件,预设常见布局和行为 8 | - [ProTable](/zh-CN/components/table/) 抽象网络请求和表格格式化 9 | - [ProControls](/zh-CN/components/controls/) 多场景的表单、表格控件 10 | - [ProGlobals](/zh-CN/components/globals/) 全局反馈组件挂载与优化 11 | - 开发中... 12 | 13 | ## 安装 14 | 15 | pro-components 中的每个组件都是一个独立的包。您也可以安装 `naive-ui-pro-components` 来使用所有组件。 16 | 17 | ```sh 18 | pnpm add @naive-ui-pro/form 19 | pnpm add @naive-ui-pro/table 20 | 21 | # or 22 | 23 | pnpm add naive-ui-pro-components 24 | ``` 25 | 26 | ## 完整引入 27 | 28 | ```ts 29 | import NaiveUIProComponents from 'naive-ui-pro-components' 30 | import { createApp } from 'vue' 31 | import App from './App.vue' 32 | 33 | const app = createApp(App) 34 | 35 | app.use(NaiveUIProComponents) 36 | ``` 37 | 38 | ## 按需导入(推荐) 39 | 40 | 安装 unplugin-vue-components 和 unplugin-auto-import 这两款插件,它们将自动导入 naive-ui-pro-components 的所有组件与 API。 41 | 42 | ```sh 43 | npm install -D unplugin-vue-components unplugin-auto-import 44 | ``` 45 | 46 | ### Vite 47 | 48 | ```ts 49 | import NaiveUIProImports from 'naive-ui-pro-components/imports' 50 | import NaiveUIProResolver from 'naive-ui-pro-components/resolver' 51 | import AutoImport from 'unplugin-auto-import/vite' 52 | import Components from 'unplugin-vue-components/vite' 53 | // vite.config.ts 54 | import { defineConfig } from 'vite' 55 | 56 | export default defineConfig({ 57 | // ... 58 | plugins: [ 59 | // ... 60 | AutoImport({ 61 | imports: [NaiveUIProImports()], 62 | }), 63 | Components({ 64 | resolvers: [NaiveUIProResolver()], 65 | }), 66 | ], 67 | }) 68 | ``` 69 | 70 | ### Webpack 71 | 72 | ```js 73 | const NaiveUIProImports = require('naive-ui-pro-components/imports') 74 | const NaiveUIProResolver = require('naive-ui-pro-components/resolver') 75 | // webpack.config.js 76 | const AutoImport = require('unplugin-auto-import/webpack') 77 | const Components = require('unplugin-vue-components/webpack') 78 | 79 | module.exports = { 80 | // ... 81 | plugins: [ 82 | // ... 83 | AutoImport({ 84 | imports: [NaiveUIProImports()], 85 | }), 86 | Components({ 87 | resolvers: [NaiveUIProResolver()], 88 | }), 89 | ], 90 | } 91 | ``` 92 | -------------------------------------------------------------------------------- /docs/zh-CN/guide/intro.md: -------------------------------------------------------------------------------- 1 | # The concept of ProComponents 2 | 3 | 类似于 Ant Design Pro Components,Naive UI Pro Components 是一个基于 Naive UI 的高级组件库,为后端系统提供更高级的抽象和封装。 4 | 5 | 我们对 Naive UI Pro Components 的设计进行了一些调整,以更好地与 Naive UI 的设计风格保持一致,并提供快速高效地构建高质量后端应用程序的能力。 6 | 7 | ## Design Ideas 8 | 9 | > 以下内容来自 Ant Pro Components 10 | 11 | 对于几乎任何业务来说,我们实际上是基于状态定义了一系列行为。以表格为例,首先我们需要一个状态 `data` 用于存储从服务器请求的数据,为了优化体验,我们还需要一个 `loading`。所以我们有了一系列行为,我们需要首先设置 `loading=true`,然后发起网络请求,网络请求完成后设置 `data` 为请求的数据,`loading=false`,一个网络请求完成了,虽然非常简单,但一个业务系统有相当数量的表格,每个表格只定义一次,工作量非常大。 12 | 13 | 如果要重新请求网络,我们需要将上述行为封装成一个方法,点击重新加载数据,如果有分页,那么还需要一个新的变量 page,我们需要根据需要在重新请求之前确定是否将页码重置为第一页,这引入了另一个变量。如果您的表单还需要控制每页显示的数量,那么将会更加繁琐。这种重复的工作会浪费我们大量的时间。 14 | 15 | ## A component !≈ a page 16 | 17 | 我们并不希望您将组件视为页面,而是将其视为功能的超集。这样可以使您的代码保持灵活性,这也是它与 Ant Design Pro Components 的区别之处。 18 | 19 | 列表页面可以使用 `pro-form` + `pro-table` 组合,而编辑页面可以利用 `pro-form` + `button` 或其他各种组件。这使我们能够专注于实现核心业务逻辑和页面效果。 20 | 21 | 基于此原因,我们对开发新的组件会尽可能的谨慎,我们会尽可能的将组件的功能拆分到更小的组件中,以便您可以更好的组合它们。 22 | -------------------------------------------------------------------------------- /docs/zh-CN/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: "Naive UI Pro Components" 7 | text: "Naive UI" 8 | tagline: 让中后台开发简单快捷 9 | image: /logo.svg 10 | actions: 11 | - theme: brand 12 | text: Get Started 13 | link: /zh-CN/guide/ 14 | - theme: alt 15 | text: View on GitHub 16 | link: https://github.com/hairyf/naive-ui-pro-components 17 | 18 | features: 19 | - title: 简单易用 20 | details: 在 Naive UI 下进行封装,快速搭建中后台系统。 21 | - title: Naive UI 22 | details: 无缝对接 Naive UI 的组件,在此基础上进行扩展。 23 | - title: TypeScript 24 | details: 当然使用 TypeScript 编写,带有完整的 TS 文档。 25 | --- 26 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import antfu from '@antfu/eslint-config' 3 | 4 | export default antfu( 5 | { 6 | type: 'app', 7 | vue: true, 8 | }, 9 | { 10 | rules: { 11 | 'vue/component-name-in-template-casing': 'off', 12 | 'ts/no-use-before-define': 'off', 13 | 'no-alert': 'off', 14 | }, 15 | }, 16 | ) 17 | -------------------------------------------------------------------------------- /gloabl.d.ts: -------------------------------------------------------------------------------- 1 | import 'vue/jsx-runtime' 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "version": "0.3.5", 4 | "private": true, 5 | "packageManager": "pnpm@10.4.0", 6 | "scripts": { 7 | "lint": "eslint --cache .", 8 | "build": "pnpm -r build", 9 | "docs": "pnpm -C docs run docs:dev", 10 | "docs:build": "pnpm -C docs run docs:build", 11 | "release": "bumpp -r && pnpm -r publish --access public", 12 | "test": "vitest", 13 | "typecheck": "tsc --noEmit", 14 | "prepare": "simple-git-hooks" 15 | }, 16 | "devDependencies": { 17 | "@antfu/eslint-config": "catalog:", 18 | "@antfu/ni": "catalog:", 19 | "@antfu/utils": "catalog:", 20 | "@shikijs/twoslash": "^3.2.1", 21 | "@types/node": "catalog:", 22 | "@vitejs/plugin-vue-jsx": "^4.1.2", 23 | "bumpp": "catalog:", 24 | "eslint": "catalog:", 25 | "lint-staged": "catalog:", 26 | "pnpm": "catalog:", 27 | "simple-git-hooks": "catalog:", 28 | "tsup": "6.6.0", 29 | "tsup-define": "workspace:*", 30 | "tsx": "catalog:", 31 | "typescript": "catalog:", 32 | "unbuild": "catalog:", 33 | "unplugin-vue-jsx": "^0.6.1", 34 | "vite": "catalog:", 35 | "vitest": "catalog:" 36 | }, 37 | 38 | "simple-git-hooks": { 39 | "pre-commit": "pnpm lint-staged" 40 | }, 41 | "lint-staged": { 42 | "*": "eslint --fix" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/components/checkbox/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@naiveui-pro/checkbox", 3 | "type": "module", 4 | "version": "0.3.5", 5 | "main": "./src/index.ts", 6 | "publishConfig": { 7 | "main": "./dist/index.cjs", 8 | "types": "./dist/index.d.ts", 9 | "module": "./dist/index.js", 10 | "unpkg": "./dist/index.iife.min.js", 11 | "jsdelivr": "./dist/index.iife.min.js", 12 | "exports": { 13 | ".": { 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.cjs" 16 | } 17 | } 18 | }, 19 | "files": ["dist"], 20 | "scripts": { 21 | "build": "tsup", 22 | "prepublish": "npm run build" 23 | }, 24 | "dependencies": { 25 | "@naiveui-pro/utils": "workspace:^", 26 | "@vueuse/core": "catalog:share", 27 | "naive-ui": "catalog:share", 28 | "vue": "catalog:share" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/components/checkbox/src/components/ProCheckboxGroup.tsx: -------------------------------------------------------------------------------- 1 | import type { CheckboxInst, CheckboxProps, SpaceProps } from 'naive-ui' 2 | import type { ExtractPropTypes, PropType, Ref } from 'vue' 3 | 4 | import type { JSX } from 'vue/jsx-runtime' 5 | import { reactiveOmit } from '@vueuse/core' 6 | import { checkboxGroupProps, NCheckbox, NCheckboxGroup, NSpace } from 'naive-ui' 7 | import { computed, defineComponent, isRef } from 'vue' 8 | 9 | export const proCheckboxGroupProps = { 10 | ...checkboxGroupProps, 11 | options: Array as PropType, 12 | space: Object as PropType, 13 | } 14 | 15 | export type ProCheckboxGroupProps = ExtractPropTypes 16 | 17 | export interface CheckboxMixedOption extends CheckboxProps { 18 | slots?: Record JSX.Element | undefined> 19 | ref?: CheckboxInst | undefined | Ref 20 | } 21 | 22 | export const NpCheckboxGroup = defineComponent({ 23 | name: 'ProCheckboxGroup', 24 | props: proCheckboxGroupProps, 25 | setup(props) { 26 | const options = computed(() => props.options || []) 27 | const groupProps = reactiveOmit(props, ['space', 'options']) 28 | function renderCheckbox(opts: CheckboxMixedOption, i: number) { 29 | return ( 30 | isRef(opts.ref) 35 | ? opts.ref.value = inst 36 | : opts.ref = inst 37 | } 38 | > 39 | {opts.label || opts.slots?.default?.()} 40 | 41 | ) 42 | } 43 | return () => ( 44 | 45 | 46 | {options.value.map(renderCheckbox)} 47 | 48 | 49 | ) 50 | }, 51 | }) 52 | 53 | export default NpCheckboxGroup 54 | -------------------------------------------------------------------------------- /packages/components/checkbox/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ProCheckboxGroup' 2 | -------------------------------------------------------------------------------- /packages/components/checkbox/src/index.ts: -------------------------------------------------------------------------------- 1 | import { NpCheckboxGroup } from './components' 2 | 3 | export * from './components' 4 | 5 | export default NpCheckboxGroup 6 | -------------------------------------------------------------------------------- /packages/components/checkbox/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'tsup-define' 2 | 3 | export default defineBuildConfig({ 4 | format: ['cjs', 'esm', 'iife', 'iife-min'], 5 | entry: ['src/index.ts'], 6 | name: 'pro-table', 7 | globalName: 'NaiveProTable', 8 | globals: { 9 | 'naive-ui': 'naive', 10 | 'vue': 'Vue', 11 | }, 12 | clean: true, 13 | }) 14 | -------------------------------------------------------------------------------- /packages/components/controls/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@naiveui-pro/controls", 3 | "type": "module", 4 | "version": "0.3.5", 5 | "main": "./src/index.ts", 6 | "publishConfig": { 7 | "main": "./dist/index.cjs", 8 | "types": "./dist/index.d.ts", 9 | "module": "./dist/index.js", 10 | "unpkg": "./dist/index.iife.min.js", 11 | "jsdelivr": "./dist/index.iife.min.js", 12 | "exports": { 13 | ".": { 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.cjs" 16 | } 17 | } 18 | }, 19 | "files": ["dist"], 20 | "scripts": { 21 | "build": "tsup", 22 | "prepublish": "npm run build" 23 | }, 24 | "dependencies": { 25 | "@naiveui-pro/utils": "workspace:^", 26 | "naive-ui": "catalog:share", 27 | "vue": "catalog:share" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/components/controls/src/components/index.ts: -------------------------------------------------------------------------------- 1 | import type { PropType } from 'vue' 2 | import type { ControlInstance } from '../types' 3 | import { defineComponent } from 'vue' 4 | 5 | export const NpControls = defineComponent({ 6 | name: 'NpControls', 7 | props: { 8 | is: { 9 | type: Object as PropType>, 10 | required: true, 11 | }, 12 | }, 13 | setup(props) { 14 | return () => props.is() 15 | }, 16 | }) 17 | -------------------------------------------------------------------------------- /packages/components/controls/src/composables/index.ts: -------------------------------------------------------------------------------- 1 | import type { Ref } from 'vue' 2 | import type { ControlParsedProps, ControlProps } from '../types' 3 | import { useAsyncCallback } from '@naiveui-pro/utils' 4 | import { computed, reactive, unref } from 'vue' 5 | 6 | export function useControlButtons(args: Ref, controls: Ref[]>) { 7 | const buttons = computed(() => { 8 | return unref(controls) 9 | .filter((control) => { 10 | const enable = typeof control.enable === 'function' 11 | ? control.enable(...unref(args)) 12 | : (control.enable || true) 13 | return enable 14 | }) 15 | .map((control) => { 16 | const { disabled: _, helper, render, ...props } = control 17 | const [onClick, loading] = useAsyncCallback(() => helper?.(...unref(args))) 18 | const defaultRender = () => typeof render === 'function' 19 | ? () => render(...unref(args)) 20 | : () => render 21 | const slots: any = { default: defaultRender, ...props.slots } 22 | for (const key in slots) 23 | slots[key] = slots[key]?.(...unref(args)) 24 | return reactive({ 25 | ...props, 26 | loading, 27 | slots, 28 | onClick, 29 | }) as ControlParsedProps 30 | }) 31 | }) 32 | 33 | return buttons 34 | } 35 | -------------------------------------------------------------------------------- /packages/components/controls/src/define/index.tsx: -------------------------------------------------------------------------------- 1 | import type { PropType } from 'vue' 2 | import type { ControlInstance, ControlParsedProps, ControlProps } from '../types' 3 | import { c, NButton } from 'naive-ui' 4 | import { defineComponent, h, toRefs } from 'vue' 5 | import { useControlButtons } from '../composables' 6 | 7 | export function defineControls(controls: ControlProps[]): ControlInstance { 8 | return (...args: T) => h(Component, { args, controls }) 9 | } 10 | 11 | const Component = defineComponent({ 12 | props: { 13 | args: { 14 | type: Array as PropType, 15 | default: () => [], 16 | }, 17 | controls: { 18 | type: Array as PropType, 19 | default: () => [], 20 | }, 21 | }, 22 | setup(props) { 23 | const { args, controls } = toRefs(props) 24 | const buttons = useControlButtons(args, controls) 25 | 26 | style.mount() 27 | 28 | function renderCustom(options: ControlParsedProps) { 29 | return
{options.custom?.(...props.args)}
30 | } 31 | function renderButton(options: ControlParsedProps) { 32 | const { slots, ...props } = options 33 | return ( 34 | 39 | {{ ...slots }} 40 | 41 | ) 42 | } 43 | 44 | return () => ( 45 |
46 | {buttons.value.map((options) => { 47 | return options.custom 48 | ? renderCustom(options) 49 | : renderButton(options) 50 | })} 51 |
52 | ) 53 | }, 54 | }) 55 | 56 | const style = c('.n-controls', { 57 | display: 'flex', 58 | alignItems: 'center', 59 | }, [ 60 | c('.n-controls__item:not(:first-child)', { 61 | marginLeft: '12px', 62 | }), 63 | ]) 64 | -------------------------------------------------------------------------------- /packages/components/controls/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components' 2 | export * from './define' 3 | export * from './types' 4 | -------------------------------------------------------------------------------- /packages/components/controls/src/types/index.ts: -------------------------------------------------------------------------------- 1 | import type { NestedRefs } from '@naiveui-pro/utils' 2 | import type { ButtonProps } from 'naive-ui' 3 | import type { VNodeChild } from 'vue' 4 | 5 | export interface ControlProps extends NestedRefs> { 6 | helper?: (...args: T) => Promise | void 7 | render?: string | ((...args: T) => VNodeChild) 8 | enable?: boolean | ((...args: T) => boolean) 9 | slots?: Record VNodeChild> 10 | custom?: (...args: T) => VNodeChild 11 | } 12 | 13 | export interface ControlInstance { 14 | (...args: T): VNodeChild 15 | } 16 | 17 | export interface ControlParsedProps extends ButtonProps { 18 | slots?: Record any> 19 | custom?: (...args: any[]) => VNodeChild | VNodeChild[] 20 | } 21 | -------------------------------------------------------------------------------- /packages/components/controls/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'tsup-define' 2 | 3 | export default defineBuildConfig({ 4 | format: ['cjs', 'esm', 'iife', 'iife-min'], 5 | entry: ['src/index.ts'], 6 | name: 'pro-controls', 7 | globalName: 'NaiveProControls', 8 | globals: { 9 | vue: 'Vue', 10 | }, 11 | clean: true, 12 | }) 13 | -------------------------------------------------------------------------------- /packages/components/dialog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@naiveui-pro/dialog", 3 | "type": "module", 4 | "version": "0.3.5", 5 | "private": true, 6 | "main": "./src/index.ts", 7 | "publishConfig": { 8 | "main": "./dist/index.cjs", 9 | "types": "./dist/index.d.ts", 10 | "module": "./dist/index.js", 11 | "unpkg": "./dist/index.iife.min.js", 12 | "jsdelivr": "./dist/index.iife.min.js", 13 | "exports": { 14 | ".": { 15 | "import": "./dist/index.js", 16 | "require": "./dist/index.cjs" 17 | } 18 | } 19 | }, 20 | "files": ["dist"], 21 | "scripts": { 22 | "build": "tsup", 23 | "prepublish": "npm run build" 24 | }, 25 | "dependencies": { 26 | "@naiveui-pro/utils": "workspace:^", 27 | "@vueuse/core": "catalog:share", 28 | "lodash-es": "^4.17.21", 29 | "naive-ui": "catalog:share", 30 | "vue": "catalog:share" 31 | }, 32 | "devDependencies": { 33 | "@types/lodash-es": "^4.17.12" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/components/dialog/src/components/ProDialog.tsx: -------------------------------------------------------------------------------- 1 | import type { ExtractPropTypes, PropType } from 'vue' 2 | import { useAsyncCallback } from '@naiveui-pro/utils' 3 | import { useVModel } from '@vueuse/core' 4 | import { debounce } from 'lodash-es' 5 | import { dialogProps, NDialog, NModal } from 'naive-ui' 6 | import { defineComponent } from 'vue' 7 | 8 | export const proDialogProps = { 9 | ...dialogProps, 10 | show: Boolean, 11 | width: String, 12 | onPositiveClick: Function as PropType<(e: MouseEvent) => void | Promise>, 13 | onNegativeClick: Function as PropType<(e: MouseEvent) => void | Promise>, 14 | disabled: Boolean, 15 | } 16 | 17 | export type ProDialogProps = ExtractPropTypes 18 | 19 | export const NpDialog = defineComponent({ 20 | name: 'ProDialog', 21 | props: proDialogProps, 22 | setup(props, { slots }) { 23 | const visible = useVModel(props, 'show') 24 | 25 | const [onPositiveClick, loading] = useAsyncCallback(async (e: MouseEvent) => { 26 | await props.onPositiveClick?.(e) 27 | }) 28 | 29 | const onDebouncedPositiveClick = debounce(onPositiveClick, 1000, { 30 | leading: true, 31 | trailing: false, 32 | }) 33 | 34 | return ( 35 | 36 | 41 | {slots.default?.()} 42 | 43 | 44 | ) 45 | }, 46 | }) 47 | -------------------------------------------------------------------------------- /packages/components/dialog/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ProDialog' 2 | -------------------------------------------------------------------------------- /packages/components/dialog/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components' 2 | -------------------------------------------------------------------------------- /packages/components/dialog/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'tsup-define' 2 | 3 | export default defineBuildConfig({ 4 | format: ['cjs', 'esm', 'iife', 'iife-min'], 5 | entry: ['src/index.ts'], 6 | name: 'pro-table', 7 | globalName: 'NaiveProTable', 8 | globals: { 9 | 'naive-ui': 'naive', 10 | 'vue': 'Vue', 11 | }, 12 | clean: true, 13 | }) 14 | -------------------------------------------------------------------------------- /packages/components/form/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@naiveui-pro/form", 3 | "type": "module", 4 | "version": "0.3.5", 5 | "main": "./src/index.ts", 6 | "publishConfig": { 7 | "main": "./dist/index.cjs", 8 | "types": "./dist/index.d.ts", 9 | "module": "./dist/index.js", 10 | "unpkg": "./dist/index.iife.min.js", 11 | "jsdelivr": "./dist/index.iife.min.js", 12 | "exports": { 13 | ".": { 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.cjs" 16 | } 17 | } 18 | }, 19 | "files": ["dist"], 20 | "scripts": { 21 | "build": "tsup", 22 | "prepublish": "npm run build" 23 | }, 24 | "dependencies": { 25 | "@naiveui-pro/checkbox": "workspace:*", 26 | "@naiveui-pro/radio": "workspace:*", 27 | "@naiveui-pro/utils": "workspace:^", 28 | "@vueuse/core": "catalog:share", 29 | "naive-ui": "catalog:share", 30 | "vue": "catalog:share" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/components/form/src/components/index.tsx: -------------------------------------------------------------------------------- 1 | import type { Component, FunctionalComponent, PropType, VNode } from 'vue' 2 | import type { FormItemConfig, ProFormInstance } from '../types' 3 | 4 | import { final, render } from '@naiveui-pro/utils' 5 | import { reactiveOmit, reactivePick } from '@vueuse/core' 6 | import { formProps, NForm, NFormItem, NFormItemGi, NGrid } from 'naive-ui' 7 | import { computed, defineComponent, toRefs } from 'vue' 8 | import { renderItemField } from '../utils' 9 | 10 | const { rules: _r, model: _m, ...extendsProps } = formProps 11 | 12 | export const proFormProps = { 13 | ...extendsProps, 14 | is: { 15 | type: Object as PropType>, 16 | required: true as const, 17 | }, 18 | cols: { 19 | type: Number, 20 | default: 24, 21 | }, 22 | xGap: { 23 | type: Number, 24 | default: 12, 25 | }, 26 | yGap: { 27 | type: Number, 28 | default: 0, 29 | }, 30 | grid: { 31 | type: Boolean, 32 | default: true, 33 | }, 34 | toolbars: [ 35 | String, 36 | Number, 37 | Object, 38 | Boolean, 39 | Function, 40 | ] as PropType< 41 | | Component 42 | | FunctionalComponent 43 | | string 44 | | number 45 | | VNode 46 | | boolean 47 | >, 48 | } 49 | 50 | export const NpForm = defineComponent({ 51 | name: 'ProForm', 52 | props: proFormProps, 53 | setup(_props, { slots }) { 54 | const customs = ['grid', 'xGap', 'yGap', 'cols', 'is', 'toolbars'] as const 55 | const props = reactivePick(_props, ...customs) 56 | const formProps = reactiveOmit(_props, ...customs) 57 | 58 | const { 59 | _formInstRef, 60 | _formItemInstRefs, 61 | _rules, 62 | data, 63 | values, 64 | } = toRefs(props.is) 65 | 66 | const gridProps = computed(() => ({ 67 | cols: props.cols, 68 | xGap: props.xGap, 69 | yGap: props.yGap, 70 | itemResponsive: true, 71 | })) 72 | 73 | const toolbars = computed(() => props.toolbars === '' || !!(props.toolbars || slots.toolbars)) 74 | 75 | function spawnItemProps(path: string, config: FormItemConfig) { 76 | return { 77 | label: final(config.label), 78 | showLabel: !!config.label, 79 | path, 80 | span: spawnSpan(config, toolbars.value), 81 | ...config.formItemProps, 82 | } 83 | } 84 | 85 | function renderForm(content: VNode | VNode[]) { 86 | return ( 87 | 88 | {props.grid 89 | ? {content} 90 | : content} 91 | 92 | ) 93 | } 94 | 95 | function renderFormItem(path: string, config: FormItemConfig) { 96 | const FormItem = props.grid ? NFormItemGi : NFormItem 97 | const modal = computed({ 98 | get: () => data.value[path], 99 | set: v => data.value[path] = v, 100 | }) 101 | return ( 102 | 107 | {{ 108 | default: () => renderItemField(modal, config, path), 109 | ...config.formItemSlots, 110 | }} 111 | 112 | ) 113 | } 114 | 115 | function renderToolbars() { 116 | const FormItem = props.grid ? NFormItemGi : NFormItem 117 | const content = render(props.toolbars) || slots.toolbars?.() 118 | 119 | if (!toolbars.value || !content) 120 | return null 121 | 122 | // TODO: https://github.com/tusen-ai/naive-ui/issues/4635 123 | return ( 124 | 125 |
126 | {content} 127 |
128 |
129 | ) 130 | } 131 | 132 | return () => renderForm( 133 | <> 134 | {Object 135 | .entries(values.value) 136 | .map(([path, config]) => renderFormItem(path, config))} 137 | {renderToolbars()} 138 | , 139 | ) 140 | }, 141 | }) 142 | 143 | function spawnSpan(config: FormItemConfig, toolbars = false) { 144 | if (!toolbars) 145 | return config.span || 24 146 | 147 | if (config.props?.type === 'datetimerange') 148 | return '0:24 742:12 1394:8' 149 | if (config.props?.type === 'daterange') 150 | return '0:24 742:12 1394:4' 151 | 152 | return '0:24 742:6 1394:4' 153 | } 154 | -------------------------------------------------------------------------------- /packages/components/form/src/composables/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useData' 2 | export * from './useMetadata' 3 | export * from './useRules' 4 | export * from './useValues' 5 | -------------------------------------------------------------------------------- /packages/components/form/src/composables/useData.ts: -------------------------------------------------------------------------------- 1 | import type { DeepReadonly } from 'vue' 2 | import type { Data, TransformData } from '../types' 3 | import type { Metadata } from './useMetadata' 4 | import { noop } from '@naiveui-pro/utils' 5 | import { syncRef } from '@vueuse/core' 6 | import { computed, reactive } from 'vue' 7 | 8 | export function useData(metadata: Metadata) { 9 | const { values, data, dataTrans } = metadata 10 | 11 | const dataRef = computed({ 12 | get: () => parse(values), 13 | set: noop, 14 | }) 15 | 16 | const dataTransRef = computed(() => parseTrans(values).value) 17 | 18 | const dataFirst = JSON.stringify(dataRef.value) 19 | 20 | function resetFields(paths?: string[]) { 21 | const cloneFirst = JSON.parse(dataFirst) 22 | paths ??= Object.keys(dataRef.value) 23 | for (const key of paths) 24 | dataRef.value[key] = cloneFirst[key] ?? null 25 | } 26 | 27 | syncRef(metadata.dataRef, dataRef, { direction: 'rtl' }) 28 | syncRef(metadata.dataTransRef, dataTransRef, { direction: 'rtl' }) 29 | metadata.resetFields = resetFields 30 | 31 | return { 32 | resetFields, 33 | data, 34 | dataTrans, 35 | dataRef, 36 | dataTransRef, 37 | } 38 | } 39 | 40 | function parse(values: Metadata['values']): Data { 41 | const entries = Object.entries(values.value) 42 | .filter(([_, i]) => i.receive !== false) 43 | .map(([key]) => key) 44 | .map((key) => { 45 | return [ 46 | key, 47 | computed({ 48 | get: () => values.value[key].value, 49 | set: v => (values.value[key].value = v), 50 | }), 51 | ] 52 | }) 53 | 54 | return reactive(Object.fromEntries(entries)) 55 | } 56 | 57 | function parseTrans(values: Metadata['values']) { 58 | return computed(() => { 59 | const target: Record = {} 60 | for (const key in values.value) { 61 | const { value, transform, receive } = values.value[key] 62 | if (receive === false) 63 | continue 64 | if (transform) 65 | target[key] = transform(value, key) 66 | else 67 | target[key] = value 68 | } 69 | return target as DeepReadonly> 70 | }) 71 | } 72 | -------------------------------------------------------------------------------- /packages/components/form/src/composables/useMetadata.ts: -------------------------------------------------------------------------------- 1 | import type { FormInst, FormItemInst } from 'naive-ui' 2 | import type { ShouldRuleBeApplied } from 'naive-ui/es/form/src/interface' 3 | import type { Ref } from 'vue' 4 | import type { FormItemConfig } from '../types' 5 | import { noop } from '@naiveui-pro/utils' 6 | import { reactiveComputed } from '@vueuse/core' 7 | import { ref } from 'vue' 8 | 9 | export interface Metadata { 10 | data: Record 11 | dataTrans: Record 12 | dataRef: Readonly> 13 | dataTransRef: Readonly> 14 | values: Ref> 15 | validate: (filters?: string[] | ShouldRuleBeApplied) => Promise 16 | resetValidate: (fields?: string[]) => void 17 | resetFields: (fields?: string[]) => void 18 | formInstRef: Ref 19 | formItemInstRefs: Ref 20 | } 21 | 22 | export function useMetadata(): Metadata { 23 | const dataRef = ref({}) 24 | const dataTransRef = ref({}) 25 | const values = ref({}) 26 | const formInstRef = ref() 27 | const formItemInstRefs = ref([]) 28 | 29 | function validate(filters?: string[] | ShouldRuleBeApplied) { 30 | if (!filters) 31 | return formInstRef.value?.validate() as any 32 | if (typeof filters === 'function') 33 | return formInstRef.value?.validate(undefined, filters) 34 | 35 | return formInstRef.value?.validate(undefined, rule => filters.includes(rule.key || '')) 36 | } 37 | 38 | function resetValidate(paths?: string[]) { 39 | if (!paths?.length) 40 | return formInstRef.value?.restoreValidation() 41 | for (const item of formItemInstRefs.value) { 42 | if (paths.includes(item.path || '')) 43 | item.restoreValidation() 44 | } 45 | } 46 | 47 | return { 48 | formInstRef, 49 | formItemInstRefs, 50 | data: reactiveComputed(() => dataRef.value), 51 | dataTrans: reactiveComputed(() => dataTransRef.value), 52 | dataRef, 53 | dataTransRef, 54 | validate, 55 | values, 56 | resetValidate, 57 | resetFields: noop, 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /packages/components/form/src/composables/useRules.ts: -------------------------------------------------------------------------------- 1 | import type { MaybeRef } from '@vueuse/core' 2 | import type { FormItemRule } from 'naive-ui' 3 | import type { FormItemConfig, FormItemConfigWithKey } from '../types' 4 | 5 | import { computed, unref } from 'vue' 6 | 7 | export function useRules(values: MaybeRef>) { 8 | return computed(() => { 9 | const entries = Object.keys(unref(values)) 10 | .map((key) => { 11 | const value = unref(values)[key] as FormItemConfigWithKey 12 | key = value.key || key 13 | return [value.key || key, parseRules(key, unref(value.rules), value.validate)] 14 | }) 15 | return Object.fromEntries(entries) 16 | }) 17 | } 18 | 19 | function parseRules(key: string, rules: FormItemRule | FormItemRule[] = [], validate?: MaybeRef) { 20 | if (unref(validate) === false) 21 | return [] 22 | if (!Array.isArray(rules)) 23 | rules = [rules] 24 | return rules.map(rule => ({ trigger: 'blur', key, ...rule })) 25 | } 26 | -------------------------------------------------------------------------------- /packages/components/form/src/composables/useValues.ts: -------------------------------------------------------------------------------- 1 | import type { MaybeRef } from '@vueuse/core' 2 | import type { FormItemConfig, WithFieldConfigExtends } from '../types' 3 | import type { Metadata } from './useMetadata' 4 | import { isFunction, isUndefined, noop } from '@naiveui-pro/utils' 5 | 6 | import { syncRef } from '@vueuse/core' 7 | import { computed, reactive, unref } from 'vue' 8 | 9 | export function useValues(metadata: Metadata, initialValues: MaybeRef) { 10 | function parse(): Record { 11 | const values = unref(initialValues) 12 | const entries = Object.keys(values).map((key) => { 13 | const target = Reflect.get(values, key) 14 | const option = isFunction(target) 15 | ? target(metadata) 16 | : target 17 | // 修复 naive-ui 无法处理 undefined 的情况 18 | if (isUndefined(option.value)) 19 | option.value = null 20 | return [option.key ?? key, option] 21 | }) 22 | return reactive(Object.fromEntries(entries)) 23 | } 24 | 25 | const values = computed({ 26 | get: parse, 27 | set: noop, 28 | }) 29 | 30 | syncRef(metadata.values, values.value, { direction: 'rtl' } as any) 31 | 32 | return values 33 | } 34 | -------------------------------------------------------------------------------- /packages/components/form/src/config/index.ts: -------------------------------------------------------------------------------- 1 | import { NpCheckboxGroup } from '@naiveui-pro/checkbox' 2 | import { NpRadioGroup } from '@naiveui-pro/radio' 3 | import { NAutoComplete, NButton, NCascader, NDatePicker, NInput, NInputNumber, NMention, NRate, NSelect, NSlider, NSwitch, NTimePicker } from 'naive-ui' 4 | 5 | export const FieldComponents: Record = { 6 | 'date-picker': NDatePicker, 7 | 'select': NSelect, 8 | 'auto-complete': NAutoComplete, 9 | 'cascader': NCascader, 10 | 'input': NInput, 11 | 'input-number': NInputNumber, 12 | 'button': NButton, 13 | 'switch': NSwitch, 14 | 'checkbox-group': NpCheckboxGroup, 15 | 'radio-group': NpRadioGroup, 16 | 'rate': NRate, 17 | 'slider': NSlider, 18 | 'time-picker': NTimePicker, 19 | 'mention': NMention, 20 | } 21 | -------------------------------------------------------------------------------- /packages/components/form/src/define/index.ts: -------------------------------------------------------------------------------- 1 | import type { MaybeRef } from '@vueuse/core' 2 | import type { FormExtendsConfig, ProFormInstance, RecordFormItemConfigExport } from '../types' 3 | 4 | import { reactive } from 'vue' 5 | import { useData, useMetadata, useRules, useValues } from '../composables' 6 | 7 | export type DefineFormReturn = T extends RecordFormItemConfigExport ? ProFormInstance : ProFormInstance 8 | 9 | export function defineForm(initialValues: MaybeRef): DefineFormReturn { 10 | const metadata = useMetadata() 11 | const values = useValues(metadata, initialValues) 12 | const formData = useData(metadata) 13 | const rules = useRules(values) 14 | 15 | const instance: ProFormInstance = reactive({ 16 | values, 17 | data: formData.data, 18 | dataTrans: formData.dataTrans, 19 | resetFields: formData.resetFields, 20 | validate: metadata.validate, 21 | resetValidate: metadata.resetValidate, 22 | _formInstRef: metadata.formInstRef, 23 | _formItemInstRefs: metadata.formItemInstRefs, 24 | _rules: rules, 25 | }) 26 | 27 | return instance as any 28 | } 29 | -------------------------------------------------------------------------------- /packages/components/form/src/index.ts: -------------------------------------------------------------------------------- 1 | import { NpForm } from './components' 2 | 3 | export * from './components' 4 | export * from './define' 5 | export * from './types' 6 | export * from './utils' 7 | 8 | export default NpForm 9 | -------------------------------------------------------------------------------- /packages/components/form/src/types/data.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable ts/no-empty-object-type */ 2 | import type { AnyFunction } from '@naiveui-pro/utils' 3 | import type { UnwrapRef } from 'vue' 4 | 5 | export type Data> = { 6 | [K in keyof T as FilterReceives]: UnwrapRef>; 7 | } & Record 8 | 9 | export type TransformData> = { 10 | [K in keyof T as FilterKeys>]: UnwrapRef>; 11 | } & Transform 12 | 13 | interface Field { value: T } 14 | type Transform = T extends { [K in keyof T]: FieldTransform } ? V : {} 15 | type FieldValue = T extends Field ? V : string 16 | type FilterKeys = T[K] extends { transform: AnyFunction } ? never : K 17 | type FilterReceives = T[K] extends { receive: false } ? never : K 18 | 19 | interface FieldTransform { 20 | transform?: (value: V) => R 21 | [key: string]: any 22 | } 23 | -------------------------------------------------------------------------------- /packages/components/form/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './config' 2 | export * from './data' 3 | export * from './instance' 4 | -------------------------------------------------------------------------------- /packages/components/form/src/types/instance.ts: -------------------------------------------------------------------------------- 1 | import type { FormInst, FormItemInst, FormRules } from 'naive-ui' 2 | import type { ShouldRuleBeApplied } from 'naive-ui/es/form/src/interface' 3 | import type { DecodeValues, RecordFormItemConfigExport } from './config' 4 | import type { Data, TransformData } from './data' 5 | 6 | export interface ProFormInstance { 7 | values: DecodeValues 8 | data: Data 9 | dataTrans: TransformData 10 | validate: (filters?: string[] | ShouldRuleBeApplied) => Promise 11 | resetValidate: (fields?: string[]) => void 12 | resetFields: (fields?: string[]) => void 13 | _formInstRef: FormInst | undefined 14 | _formItemInstRefs: FormItemInst[] 15 | _rules: FormRules 16 | } 17 | -------------------------------------------------------------------------------- /packages/components/form/src/utils/fields.tsx: -------------------------------------------------------------------------------- 1 | import type { FormItemFieldConfig, WithConfig, WithFieldConfig, WithFieldConfigExtends } from '../types' 2 | import { clone as _clone, cloneDeep as _cloneDeep, defu, final, isFunction, toArray } from '@naiveui-pro/utils' 3 | import { unref } from 'vue' 4 | 5 | export function field(item: WithFieldConfig) { 6 | const target = encase(item) 7 | 8 | function withConfig(config: WithFieldConfig) { 9 | if (isFunction(config)) 10 | return field((c: any) => defu((config as any)(c), { ...final(item as any, c) })) 11 | if (isFunction(item)) 12 | return field((c: any) => defu(config, { ...final(item, c) })) 13 | return field(defu(config, { ...item })) 14 | } 15 | 16 | target.withConfig = withConfig as unknown as WithConfig 17 | 18 | return target 19 | } 20 | 21 | function encase(config: WithFieldConfig) { 22 | const target = config as FormItemFieldConfig 23 | 24 | function clone() { 25 | return _clone(target) 26 | } 27 | function cloneDeep() { 28 | return _cloneDeep(target) 29 | } 30 | function noop() { 31 | return target 32 | } 33 | function preventDefault() { 34 | return target.withConfig({ rules: [], label: '' }) 35 | } 36 | function preventRequired() { 37 | return target.withConfig((inst) => { 38 | const source = final(target, inst) 39 | const rules = toArray(unref(source.rules) || []) 40 | .filter(v => !v?.required) 41 | return { ...source, rules } 42 | }) 43 | } 44 | 45 | function preventAutofill() { 46 | return target.withConfig({ 47 | renderItem(model, config, defaultRender) { 48 | return ( 49 | <> 50 | 51 | 52 | {defaultRender(model, config)} 53 | 54 | ) 55 | }, 56 | }) 57 | } 58 | target.clone = isFunction(target) ? noop : clone 59 | target.cloneDeep = isFunction(target) ? noop : cloneDeep 60 | target.preventDefault = preventDefault 61 | target.preventRequired = preventRequired 62 | target.preventAutofill = preventAutofill 63 | return target 64 | } 65 | -------------------------------------------------------------------------------- /packages/components/form/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './fields' 2 | export * from './render' 3 | -------------------------------------------------------------------------------- /packages/components/form/src/utils/render.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable ts/ban-ts-comment */ 2 | import type { Ref } from 'vue' 3 | import type { FormItemConfig } from '../types' 4 | 5 | import { final } from '@naiveui-pro/utils' 6 | import { reactive, unref } from 'vue' 7 | import { FieldComponents } from '../config' 8 | 9 | export function renderItemField(model: Ref, config: FormItemConfig, key?: string): any { 10 | const { renderItem, ..._config } = config 11 | return renderItem?.(model, _config, renderDefField) 12 | || renderDefField(model, _config, key) 13 | } 14 | 15 | export function renderDefField(model: Ref, config: FormItemConfig, _key?: string) { 16 | const placeholder = final(config.placeholder) 17 | const Component = config.type ? FieldComponents[config.type] : null 18 | 19 | if (!config.type || !Component) 20 | return null 21 | 22 | return ( 23 | 31 | {{ ...config.slots }} 32 | 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /packages/components/form/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'tsup-define' 2 | 3 | export default defineBuildConfig({ 4 | format: ['cjs', 'esm', 'iife', 'iife-min'], 5 | entry: ['src/index.ts'], 6 | name: 'pro-form', 7 | globalName: 'NaiveProForm', 8 | globals: { 9 | 'naive-ui': 'naive', 10 | 'vue': 'Vue', 11 | }, 12 | clean: true, 13 | }) 14 | -------------------------------------------------------------------------------- /packages/components/globals/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@naiveui-pro/globals", 3 | "type": "module", 4 | "version": "0.3.5", 5 | "main": "./src/index.ts", 6 | "publishConfig": { 7 | "main": "./dist/index.cjs", 8 | "types": "./dist/index.d.ts", 9 | "module": "./dist/index.js", 10 | "unpkg": "./dist/index.iife.min.js", 11 | "jsdelivr": "./dist/index.iife.min.js", 12 | "exports": { 13 | ".": { 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.cjs" 16 | } 17 | } 18 | }, 19 | "files": ["dist"], 20 | "scripts": { 21 | "build": "tsup", 22 | "prepublish": "npm run build" 23 | }, 24 | "dependencies": { 25 | "defu": "^6.1.2", 26 | "naive-ui": "catalog:share", 27 | "vue": "catalog:share" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/components/globals/src/components/NpGlobalDialog.tsx: -------------------------------------------------------------------------------- 1 | import type { XDialogProviderInst } from 'naive-ui' 2 | import { dialogProviderProps, NDialogProvider, useDialog } from 'naive-ui' 3 | import { defineComponent } from 'vue' 4 | import { createDeferred, packer } from '../utils' 5 | 6 | export const NpInstallDialog = defineComponent((_, { slots }) => { 7 | window.$dialog = useDialog() as XDialogProviderInst 8 | packer( 9 | window.$dialog, 10 | ['create', 'success', 'warning', 'error', 'info'], 11 | (source, key) => { 12 | window.$dialog[key] = function (options) { 13 | const deferred = createDeferred() 14 | const inst = source({ 15 | ...options, 16 | onPositiveClick(e) { 17 | const result = options.onPositiveClick?.(e) 18 | if (result instanceof Promise) { 19 | inst.loading = true 20 | result 21 | .finally(() => inst.loading = false) 22 | .then(deferred.resolve) 23 | .catch(deferred.reject) 24 | } 25 | else { 26 | deferred.resolve(result as any) 27 | } 28 | return result 29 | }, 30 | onClose() { 31 | deferred.reject() 32 | options.onClose?.() 33 | }, 34 | onNegativeClick(e) { 35 | deferred.reject() 36 | options.onNegativeClick?.(e) 37 | }, 38 | onMaskClick(e) { 39 | deferred.reject() 40 | options.onMaskClick?.(e) 41 | }, 42 | }) 43 | return Object.assign(deferred, inst) 44 | } 45 | }, 46 | ) 47 | return slots.default as any 48 | }) 49 | 50 | export const NpGlobalDialog = defineComponent({ 51 | name: 'NpGlobalDialog', 52 | props: dialogProviderProps, 53 | setup(props, { slots }) { 54 | return () => ( 55 | 56 | 57 | {slots} 58 | 59 | 60 | ) 61 | }, 62 | }) 63 | -------------------------------------------------------------------------------- /packages/components/globals/src/components/NpGlobalLoadingBar.tsx: -------------------------------------------------------------------------------- 1 | import { loadingBarProviderProps, NLoadingBarProvider, useLoadingBar } from 'naive-ui' 2 | import { defineComponent } from 'vue' 3 | 4 | export const NpInstallLoadingBar = defineComponent((_, { slots }) => { 5 | window.$loadingBar = useLoadingBar() 6 | return slots.default as any 7 | }) 8 | 9 | export const NpGlobalLoadingBar = defineComponent({ 10 | name: 'NpGlobalLoadingBar', 11 | props: loadingBarProviderProps, 12 | setup(props, { slots }) { 13 | return () => ( 14 | 15 | 16 | {slots} 17 | 18 | 19 | ) 20 | }, 21 | }) 22 | -------------------------------------------------------------------------------- /packages/components/globals/src/components/NpGlobalMessage.tsx: -------------------------------------------------------------------------------- 1 | import type { XMessageApiInjection } from 'naive-ui' 2 | import { messageProviderProps, NMessageProvider, useMessage } from 'naive-ui' 3 | 4 | import { defineComponent } from 'vue' 5 | import { createDeferred, packer } from '../utils' 6 | 7 | export const NpInstallMessage = defineComponent((props, { slots }) => { 8 | window.$message = useMessage() as XMessageApiInjection 9 | packer( 10 | window.$message, 11 | ['create', 'success', 'warning', 'error', 'info'], 12 | (source, key) => { 13 | window.$message[key] = function (content, options) { 14 | const deferred = createDeferred() 15 | const ins = source(content, { 16 | ...options, 17 | onAfterLeave() { 18 | options?.onAfterLeave?.() 19 | deferred.resolve() 20 | }, 21 | }) 22 | return Object.assign(deferred, ins) 23 | } 24 | }, 25 | ) 26 | return slots.default as any as any 27 | }) 28 | 29 | export const NpGlobalMessage = defineComponent({ 30 | name: 'NpGlobalMessage', 31 | props: messageProviderProps, 32 | setup(props, { slots }) { 33 | return () => ( 34 | 35 | 36 | {slots} 37 | 38 | 39 | ) 40 | }, 41 | }) 42 | -------------------------------------------------------------------------------- /packages/components/globals/src/components/NpGlobalNotification.tsx: -------------------------------------------------------------------------------- 1 | import { NNotificationProvider, notificationProviderProps, useNotification } from 'naive-ui' 2 | import { defineComponent } from 'vue' 3 | 4 | export const NpInstallNotification = defineComponent((_, { slots }) => { 5 | window.$notification = useNotification() 6 | return slots.default as any 7 | }) 8 | 9 | export const NpGlobalNotification = defineComponent({ 10 | name: 'NpGlobalNotification', 11 | props: notificationProviderProps, 12 | setup(props, { slots }) { 13 | return () => ( 14 | 15 | 16 | {slots} 17 | 18 | 19 | ) 20 | }, 21 | }) 22 | -------------------------------------------------------------------------------- /packages/components/globals/src/components/NpGlobalProvider.tsx: -------------------------------------------------------------------------------- 1 | import type { DialogProviderProps, LoadingBarProviderProps, MessageProviderProps, NotificationProviderProps } from 'naive-ui' 2 | import type { PropType } from 'vue' 3 | import { defineComponent } from 'vue' 4 | import { NpGlobalDialog } from './NpGlobalDialog' 5 | import { NpGlobalLoadingBar } from './NpGlobalLoadingBar' 6 | import { NpGlobalMessage } from './NpGlobalMessage' 7 | import { NpGlobalNotification } from './NpGlobalNotification' 8 | import { NpInstallProvider } from './NpInstallProvider' 9 | 10 | export const NpGlobalProvider = defineComponent({ 11 | name: 'NpGlobalProvider', 12 | props: { 13 | message: Object as PropType, 14 | loaderBar: Object as PropType, 15 | dialog: Object as PropType, 16 | notification: Object as PropType, 17 | }, 18 | setup(p, { slots }) { 19 | return () => ( 20 | 28 | {slots.default?.()} 29 | 30 | ) 31 | }, 32 | }) 33 | -------------------------------------------------------------------------------- /packages/components/globals/src/components/NpInstallProvider.tsx: -------------------------------------------------------------------------------- 1 | import type { Component, PropType, VNode } from 'vue' 2 | import { defineComponent, h } from 'vue' 3 | 4 | export interface InstallComponentOptions { component: Component, props: any } 5 | export type InstallComponentItem = Component | InstallComponentOptions 6 | 7 | export const NpInstallProvider = defineComponent({ 8 | props: { 9 | install: { 10 | type: Array as PropType, 11 | default: () => [], 12 | }, 13 | }, 14 | setup({ install }, { slots }) { 15 | function render(content: VNode, { component, props }: any) { 16 | return h(component, props, () => content) 17 | } 18 | return () => resolve(install).reduceRight(render, slots.default?.() as any) 19 | }, 20 | }) 21 | 22 | function resolve(components: InstallComponentItem[]): InstallComponentOptions[] { 23 | return components.map((item: any) => { 24 | if (!item.component) 25 | return { component: item as Component, props: {} } 26 | else 27 | return item 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /packages/components/globals/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './NpGlobalDialog' 2 | export * from './NpGlobalMessage' 3 | export * from './NpGlobalNotification' 4 | export * from './NpGlobalProvider' 5 | export * from './NpInstallProvider' 6 | -------------------------------------------------------------------------------- /packages/components/globals/src/global.naive.ts: -------------------------------------------------------------------------------- 1 | import type { DialogOptions, MessageOptions, MessageType } from 'naive-ui' 2 | import type { VNodeChild } from 'vue' 3 | 4 | declare module 'naive-ui' { 5 | type XDialogReactive = { 6 | readonly key: string 7 | readonly destroy: () => void 8 | } & DialogOptions & Promise 9 | 10 | export interface XDialogApiInjection { 11 | destroyAll: () => void 12 | create: (options: DialogOptions) => XDialogReactive 13 | success: (options: DialogOptions) => XDialogReactive 14 | warning: (options: DialogOptions) => XDialogReactive 15 | error: (options: DialogOptions) => XDialogReactive 16 | info: (options: DialogOptions) => XDialogReactive 17 | } 18 | 19 | export type XDialogProviderInst = XDialogApiInjection 20 | 21 | export type ContentType = string | (() => VNodeChild) 22 | 23 | export interface XMessageApiInjection { 24 | create: (content: ContentType, options?: MessageOptions) => XMessageReactive 25 | info: (content: ContentType, options?: MessageOptions) => XMessageReactive 26 | success: (content: ContentType, options?: MessageOptions) => XMessageReactive 27 | warning: (content: ContentType, options?: MessageOptions) => XMessageReactive 28 | error: (content: ContentType, options?: MessageOptions) => XMessageReactive 29 | loading: (content: ContentType, options?: MessageOptions) => XMessageReactive 30 | destroyAll: () => void 31 | } 32 | 33 | export type XMessageReactive = { 34 | content?: ContentType 35 | duration?: number 36 | closable?: boolean 37 | keepAliveOnHover?: boolean 38 | type: MessageType 39 | icon?: () => VNodeChild 40 | showIcon?: boolean 41 | onClose?: () => void 42 | destroy: () => void 43 | } & Promise 44 | 45 | export type XMessageProviderInst = XMessageApiInjection 46 | 47 | } 48 | 49 | export { } 50 | -------------------------------------------------------------------------------- /packages/components/globals/src/global.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | const $loadingBar: import('naive-ui').LoadingBarProviderInst 3 | const $dialog: import('naive-ui').XDialogProviderInst 4 | const $message: import('naive-ui').XMessageProviderInst 5 | const $notification: import('naive-ui').NotificationProviderInst 6 | } 7 | 8 | export {} 9 | -------------------------------------------------------------------------------- /packages/components/globals/src/global.window.d.ts: -------------------------------------------------------------------------------- 1 | interface Window { 2 | $loadingBar: import('naive-ui').LoadingBarProviderInst 3 | $dialog: import('naive-ui').XDialogProviderInst 4 | $message: import('naive-ui').XMessageProviderInst 5 | $notification: import('naive-ui').NotificationProviderInst 6 | } 7 | -------------------------------------------------------------------------------- /packages/components/globals/src/index.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { NpGlobalProvider } from './components' 4 | 5 | export * from './components' 6 | export * from './global' 7 | 8 | export * from './global.naive' 9 | 10 | export default NpGlobalProvider 11 | -------------------------------------------------------------------------------- /packages/components/globals/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export function packer(target: T, keys: K[], pack: (source: T[K], key: K) => void) { 2 | for (const key of keys) { 3 | const source = target[key] 4 | pack(source, key) 5 | } 6 | } 7 | 8 | export type Deferred = Promise & { resolve: (value: T) => void, reject: () => void } 9 | 10 | export function createDeferred(): Deferred { 11 | let resolve: any, reject: any 12 | 13 | const promise = new Promise((_resolve, _reject) => { 14 | resolve = _resolve 15 | reject = _reject 16 | }) as unknown as any 17 | 18 | promise.resolve = (v: any) => { 19 | resolve(v) 20 | return promise 21 | } 22 | promise.reject = reject 23 | 24 | return promise 25 | } 26 | -------------------------------------------------------------------------------- /packages/components/globals/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'tsup-define' 2 | 3 | export default defineBuildConfig({ 4 | format: ['cjs', 'esm', 'iife', 'iife-min'], 5 | entry: ['src/index.ts'], 6 | name: 'pro-globals', 7 | globalName: 'NaiveProGlobals', 8 | globals: { 9 | vue: 'Vue', 10 | }, 11 | clean: true, 12 | }) 13 | -------------------------------------------------------------------------------- /packages/components/main/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "naive-ui-pro-components", 3 | "type": "module", 4 | "version": "0.3.5", 5 | "exports": { 6 | ".": "./src/index.ts", 7 | "./resolver": "./src/resolver.ts", 8 | "./imports": "./src/imports.ts" 9 | }, 10 | "main": "./src/index.ts", 11 | "publishConfig": { 12 | "exports": { 13 | ".": { 14 | "types": "./dist/index.d.ts", 15 | "require": "./dist/index.cjs", 16 | "import": "./dist/index.js" 17 | }, 18 | "./resolver": { 19 | "types": "./dist/resolver.d.ts", 20 | "require": "./dist/resolver.cjs", 21 | "import": "./dist/resolver.js" 22 | }, 23 | "./imports": { 24 | "types": "./dist/imports.d.ts", 25 | "require": "./dist/imports.cjs", 26 | "import": "./dist/imports.js" 27 | }, 28 | "./*": "./*" 29 | }, 30 | "main": "./dist/index.cjs", 31 | "module": "./dist/index.js", 32 | "unpkg": "./dist/index.iife.min.js", 33 | "jsdelivr": "./dist/index.iife.min.js", 34 | "types": "./dist/index.d.ts" 35 | }, 36 | "typesVersions": { 37 | "*": { 38 | "*": [ 39 | "./dist/*", 40 | "./*" 41 | ] 42 | } 43 | }, 44 | "files": ["dist"], 45 | "engines": { 46 | "node": ">=14" 47 | }, 48 | "scripts": { 49 | "build": "tsup", 50 | "prepublish": "npm run build" 51 | }, 52 | "dependencies": { 53 | "@naiveui-pro/checkbox": "workspace:*", 54 | "@naiveui-pro/controls": "workspace:*", 55 | "@naiveui-pro/form": "workspace:*", 56 | "@naiveui-pro/globals": "workspace:*", 57 | "@naiveui-pro/radio": "workspace:*", 58 | "@naiveui-pro/table": "workspace:*", 59 | "naive-ui": "catalog:share", 60 | "vue": "catalog:share" 61 | }, 62 | "devDependencies": { 63 | "unplugin-auto-import": "^0.16.4", 64 | "unplugin-vue-components": "^0.25.0" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /packages/components/main/src/imports.ts: -------------------------------------------------------------------------------- 1 | import type { ImportsMap } from 'unplugin-auto-import/types' 2 | 3 | function NaiveUIProImports(): ImportsMap { 4 | return { 5 | 'naive-ui-pro-components': [ 6 | 'defineForm', 7 | 'defineTable', 8 | 'defineControls', 9 | 'field', 10 | 'useColumnIndexes', 11 | 'useColumnLink', 12 | 'useColumns', 13 | ], 14 | } 15 | } 16 | 17 | export default NaiveUIProImports 18 | -------------------------------------------------------------------------------- /packages/components/main/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue' 2 | import { NpCheckboxGroup } from '@naiveui-pro/checkbox' 3 | import { NpControls } from '@naiveui-pro/controls' 4 | import { NpForm } from '@naiveui-pro/form' 5 | import { NpGlobalDialog, NpGlobalMessage, NpGlobalNotification, NpGlobalProvider, NpInstallProvider } from '@naiveui-pro/globals' 6 | import { NpRadioGroup } from '@naiveui-pro/radio' 7 | import { NpTable } from '@naiveui-pro/table' 8 | 9 | export * from '@naiveui-pro/checkbox' 10 | export * from '@naiveui-pro/controls' 11 | export * from '@naiveui-pro/form' 12 | export * from '@naiveui-pro/globals' 13 | export * from '@naiveui-pro/radio' 14 | export * from '@naiveui-pro/table' 15 | 16 | function install(app: App): void { 17 | app.component('NpForm', NpForm) 18 | app.component('NpTable', NpTable) 19 | app.component('NpControls', NpControls) 20 | app.component('NpRadioGroup', NpRadioGroup) 21 | app.component('NpCheckboxGroup', NpCheckboxGroup) 22 | app.component('NpGlobalProvider', NpGlobalProvider) 23 | app.component('NpGlobalDialog', NpGlobalDialog) 24 | app.component('NpGlobalMessage', NpGlobalMessage) 25 | app.component('NpGlobalNotification', NpGlobalNotification) 26 | app.component('NpInstallProvider', NpInstallProvider) 27 | } 28 | 29 | export { install } 30 | 31 | export default install 32 | -------------------------------------------------------------------------------- /packages/components/main/src/resolver.ts: -------------------------------------------------------------------------------- 1 | import type { ComponentResolver } from 'unplugin-vue-components/types' 2 | 3 | function NaiveUIProResolver(): ComponentResolver { 4 | return { 5 | type: 'component', 6 | resolve: (name: string) => { 7 | if (name.match(/^Np.+/)) 8 | return { name, from: 'naive-ui-pro-components' } 9 | }, 10 | } 11 | } 12 | 13 | export default NaiveUIProResolver 14 | -------------------------------------------------------------------------------- /packages/components/main/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'tsup-define' 2 | 3 | export default [ 4 | ...defineBuildConfig({ 5 | format: ['cjs', 'esm', 'iife', 'iife-min'], 6 | entry: ['src/index.ts'], 7 | name: 'naive-ui-pro-components', 8 | globalName: 'NaiveUIProComponents', 9 | globals: { 10 | 'naive-ui': 'naive', 11 | 'vue': 'Vue', 12 | }, 13 | clean: true, 14 | 15 | }), 16 | { 17 | format: ['cjs', 'esm'], 18 | entry: ['src/resolver.ts', 'src/imports.ts'], 19 | name: 'naive-ui-pro-components', 20 | dts: true, 21 | }, 22 | ] 23 | -------------------------------------------------------------------------------- /packages/components/radio/README.md: -------------------------------------------------------------------------------- 1 | # Naive UI Pro Components 2 | 3 | See our website [naive-ui-pro-components](/https://naiveui-pro-components.vercel.app/) for more information. 4 | 5 | ## Install 6 | 7 | Using pnpm: 8 | 9 | ```bash 10 | pnpm install --save naive-ui-pro-components 11 | ``` 12 | 13 | or using yarn: 14 | 15 | ```bash 16 | yarn add naive-ui-pro-components 17 | ``` 18 | 19 | ## Usage 20 | 21 | ### Globals 22 | 23 | ```js 24 | import NaiveUIProComponents from 'naive-ui-pro-components' 25 | import { createApp } from 'vue' 26 | import App from './App.vue' 27 | 28 | const app = createApp(App) 29 | 30 | app.use(NaiveUIProComponents) 31 | ``` 32 | 33 | ## On-Demand Import (Recommended) 34 | 35 | Install the `unplugin-vue-components` and `unplugin-auto-import` plugins, which will automatically import all components and APIs from `naive-ui-pro-components`. 36 | 37 | ```sh 38 | npm install -D unplugin-vue-components unplugin-auto-import 39 | ``` 40 | 41 | ### Vite 42 | 43 | ```ts 44 | import NaiveUIProImports from 'naive-ui-pro-components/imports' 45 | import NaiveUIProResolver from 'naive-ui-pro-components/resolver' 46 | import AutoImport from 'unplugin-auto-import/vite' 47 | import Components from 'unplugin-vue-components/vite' 48 | // vite.config.ts 49 | import { defineConfig } from 'vite' 50 | 51 | export default defineConfig({ 52 | // ... 53 | plugins: [ 54 | // ... 55 | AutoImport({ 56 | imports: [NaiveUIProImports()], 57 | }), 58 | Components({ 59 | resolvers: [NaiveUIProResolver()], 60 | }), 61 | ], 62 | }) 63 | ``` 64 | 65 | ### Webpack 66 | 67 | ```js 68 | const NaiveUIProImports = require('naive-ui-pro-components/imports') 69 | const NaiveUIProResolver = require('naive-ui-pro-components/resolver') 70 | // webpack.config.js 71 | const AutoImport = require('unplugin-auto-import/webpack') 72 | const Components = require('unplugin-vue-components/webpack') 73 | 74 | module.exports = { 75 | // ... 76 | plugins: [ 77 | // ... 78 | AutoImport({ 79 | imports: [NaiveUIProImports()], 80 | }), 81 | Components({ 82 | resolvers: [NaiveUIProResolver()], 83 | }), 84 | ], 85 | } 86 | ``` 87 | -------------------------------------------------------------------------------- /packages/components/radio/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@naiveui-pro/radio", 3 | "type": "module", 4 | "version": "0.3.5", 5 | "main": "./src/index.ts", 6 | "publishConfig": { 7 | "main": "./dist/index.cjs", 8 | "types": "./dist/index.d.ts", 9 | "module": "./dist/index.js", 10 | "unpkg": "./dist/index.iife.min.js", 11 | "jsdelivr": "./dist/index.iife.min.js", 12 | "exports": { 13 | ".": { 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.cjs" 16 | } 17 | } 18 | }, 19 | "files": ["dist"], 20 | "scripts": { 21 | "build": "tsup", 22 | "prepublish": "npm run build" 23 | }, 24 | "dependencies": { 25 | "@naiveui-pro/utils": "workspace:^", 26 | "@vueuse/core": "catalog:share", 27 | "naive-ui": "catalog:share", 28 | "vue": "catalog:share" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/components/radio/src/components/ProRadioGroup.tsx: -------------------------------------------------------------------------------- 1 | import type { RadioProps, SpaceProps } from 'naive-ui' 2 | import type { ExtractPropTypes, PropType, VNodeChild } from 'vue' 3 | import type { JSX } from 'vue/jsx-runtime' 4 | import { reactiveOmit } from '@vueuse/core' 5 | import { NRadio, NRadioButton, NRadioGroup, NSpace, radioGroupProps } from 'naive-ui' 6 | import { computed, defineComponent } from 'vue' 7 | 8 | export const proRadioGroupProps = { 9 | ...radioGroupProps, 10 | type: { 11 | type: String as PropType<'button' | 'default'>, 12 | default: 'default', 13 | }, 14 | options: Array as PropType, 15 | space: Object as PropType, 16 | } 17 | 18 | export type ProRadioGroupProps = ExtractPropTypes 19 | 20 | export interface RadioMixedOption extends RadioProps { 21 | slots?: Record JSX.Element | undefined> 22 | } 23 | 24 | export const NpRadioGroup = defineComponent({ 25 | name: 'ProRadioGroup', 26 | props: proRadioGroupProps, 27 | setup(props) { 28 | const options = computed(() => props.options || []) 29 | const radioGroupProps = reactiveOmit(props, ['space', 'options', 'type']) 30 | function renderRadio({ slots, ...rest }: RadioMixedOption, i: number) { 31 | return props.type === 'default' 32 | ? {slots} 33 | : {slots} 34 | } 35 | function renderSpace(content: VNodeChild) { 36 | return props.type === 'default' 37 | ? {content} 38 | : content 39 | } 40 | return () => ( 41 | 42 | {renderSpace(options.value.map(renderRadio))} 43 | 44 | ) 45 | }, 46 | }) 47 | 48 | export default NpRadioGroup 49 | -------------------------------------------------------------------------------- /packages/components/radio/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ProRadioGroup' 2 | -------------------------------------------------------------------------------- /packages/components/radio/src/index.ts: -------------------------------------------------------------------------------- 1 | import { NpRadioGroup } from './components' 2 | 3 | export * from './components' 4 | export default NpRadioGroup 5 | -------------------------------------------------------------------------------- /packages/components/radio/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'tsup-define' 2 | 3 | export default defineBuildConfig({ 4 | format: ['cjs', 'esm', 'iife', 'iife-min'], 5 | entry: ['src/index.ts'], 6 | name: 'pro-table', 7 | globalName: 'NaiveProTable', 8 | globals: { 9 | 'naive-ui': 'naive', 10 | 'vue': 'Vue', 11 | }, 12 | clean: true, 13 | }) 14 | -------------------------------------------------------------------------------- /packages/components/table/README.md: -------------------------------------------------------------------------------- 1 | # Naive UI Pro Components 2 | 3 | See our website [naive-ui-pro-components](/https://naiveui-pro-components.vercel.app/) for more information. 4 | 5 | ## Install 6 | 7 | Using pnpm: 8 | 9 | ```bash 10 | pnpm install --save naive-ui-pro-components 11 | ``` 12 | 13 | or using yarn: 14 | 15 | ```bash 16 | yarn add naive-ui-pro-components 17 | ``` 18 | 19 | ## Usage 20 | 21 | ### Globals 22 | 23 | ```js 24 | import NaiveUIProComponents from 'naive-ui-pro-components' 25 | import { createApp } from 'vue' 26 | import App from './App.vue' 27 | 28 | const app = createApp(App) 29 | 30 | app.use(NaiveUIProComponents) 31 | ``` 32 | 33 | ## On-Demand Import (Recommended) 34 | 35 | Install the `unplugin-vue-components` and `unplugin-auto-import` plugins, which will automatically import all components and APIs from `naive-ui-pro-components`. 36 | 37 | ```sh 38 | npm install -D unplugin-vue-components unplugin-auto-import 39 | ``` 40 | 41 | ### Vite 42 | 43 | ```ts 44 | import NaiveUIProImports from 'naive-ui-pro-components/imports' 45 | import NaiveUIProResolver from 'naive-ui-pro-components/resolver' 46 | import AutoImport from 'unplugin-auto-import/vite' 47 | import Components from 'unplugin-vue-components/vite' 48 | // vite.config.ts 49 | import { defineConfig } from 'vite' 50 | 51 | export default defineConfig({ 52 | // ... 53 | plugins: [ 54 | // ... 55 | AutoImport({ 56 | imports: [NaiveUIProImports()], 57 | }), 58 | Components({ 59 | resolvers: [NaiveUIProResolver()], 60 | }), 61 | ], 62 | }) 63 | ``` 64 | 65 | ### Webpack 66 | 67 | ```js 68 | const NaiveUIProImports = require('naive-ui-pro-components/imports') 69 | const NaiveUIProResolver = require('naive-ui-pro-components/resolver') 70 | // webpack.config.js 71 | const AutoImport = require('unplugin-auto-import/webpack') 72 | const Components = require('unplugin-vue-components/webpack') 73 | 74 | module.exports = { 75 | // ... 76 | plugins: [ 77 | // ... 78 | AutoImport({ 79 | imports: [NaiveUIProImports()], 80 | }), 81 | Components({ 82 | resolvers: [NaiveUIProResolver()], 83 | }), 84 | ], 85 | } 86 | ``` 87 | -------------------------------------------------------------------------------- /packages/components/table/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@naiveui-pro/table", 3 | "type": "module", 4 | "version": "0.3.5", 5 | "main": "./src/index.ts", 6 | "publishConfig": { 7 | "main": "./dist/index.cjs", 8 | "types": "./dist/index.d.ts", 9 | "module": "./dist/index.js", 10 | "unpkg": "./dist/index.iife.min.js", 11 | "jsdelivr": "./dist/index.iife.min.js", 12 | "exports": { 13 | ".": { 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.cjs" 16 | } 17 | } 18 | }, 19 | "files": ["dist"], 20 | "scripts": { 21 | "build": "tsup", 22 | "prepublish": "npm run build" 23 | }, 24 | "dependencies": { 25 | "@naiveui-pro/utils": "workspace:^", 26 | "@vueuse/core": "catalog:share", 27 | "naive-ui": "catalog:share", 28 | "nanoid": "^5.1.5", 29 | "vue": "catalog:share" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/components/table/src/components/index.tsx: -------------------------------------------------------------------------------- 1 | import type { PaginationProps } from 'naive-ui' 2 | import type { PropType } from 'vue' 3 | 4 | import type { ProTableInstance } from '../types' 5 | import { If, isObject } from '@naiveui-pro/utils' 6 | 7 | import { reactiveOmit, reactivePick } from '@vueuse/core' 8 | import { c, dataTableProps, NDataTable, NPagination } from 'naive-ui' 9 | import { computed, defineComponent, reactive, toRefs } from 'vue' 10 | import { useTableMinWidth } from '../composables' 11 | 12 | const { pagination: _p, ...extendsProps } = dataTableProps 13 | 14 | export const proTableProps = { 15 | ...extendsProps, 16 | is: { 17 | type: Object as PropType, 18 | required: true as const, 19 | }, 20 | pagination: { 21 | type: [Boolean, Object] as PropType>, 22 | default: true as const, 23 | }, 24 | } 25 | 26 | export const NpTable = defineComponent({ 27 | name: 'ProTable', 28 | props: proTableProps, 29 | setup(_props, { slots }) { 30 | style.mount() 31 | 32 | const customs = ['is', 'pagination'] as const 33 | const props = reactivePick(_props, ...customs) 34 | const tableProps = reactiveOmit(_props, ...customs) 35 | 36 | const { 37 | _tableInstRef, 38 | data, 39 | loading, 40 | } = toRefs(props.is) 41 | 42 | const instance = computed(() => reactive(props.is)) 43 | const columns = computed(() => tableProps.columns ?? []) 44 | 45 | const showPagination = computed(() => props.pagination !== false) 46 | 47 | const pagination = computed(() => { 48 | return { 49 | showQuickJumper: instance.value.pagination.pageCount > 1, 50 | pageCount: instance.value.pagination.pageCount, 51 | showSizePicker: true, 52 | pageSizes: [10, 20, 30, 40], 53 | ...(isObject(props.pagination) ? props.pagination : {}), 54 | } as PaginationProps 55 | }) 56 | 57 | const minWidth = useTableMinWidth(columns) 58 | 59 | return () => ( 60 |
64 | 71 | {slots} 72 | 73 | 74 | 81 | 82 |
83 | ) 84 | }, 85 | }) 86 | 87 | const style = c([ 88 | c('.n-data-table-table', { 89 | minWidth: 'var(--n-table-min-width) !important', 90 | }), 91 | c('.np-table__pagination', { 92 | display: 'flex', 93 | justifyContent: 'flex-end', 94 | marginTop: '18px', 95 | userSelect: 'none', 96 | }), 97 | ]) 98 | -------------------------------------------------------------------------------- /packages/components/table/src/composables/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useColumnIndexes' 2 | export * from './useColumnLink' 3 | export * from './useColumns' 4 | export * from './useMetadata' 5 | export * from './useOffsetPagination' 6 | export * from './useTableMinWidth' 7 | -------------------------------------------------------------------------------- /packages/components/table/src/composables/useColumnIndexes.ts: -------------------------------------------------------------------------------- 1 | import type { UnwrapNestedRefs } from 'vue' 2 | import type { OffsetPagination } from './useOffsetPagination' 3 | import { nanoid } from 'nanoid' 4 | 5 | export function useColumnIndexes(pagination: UnwrapNestedRefs) { 6 | return { 7 | key: nanoid(5), 8 | title: '#', 9 | render: (_: any, rowIndex: number) => { 10 | return rowIndex + 1 + ((pagination.page - 1) * pagination.pageSize) 11 | }, 12 | fixed: 'left' as const, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/components/table/src/composables/useColumnLink.ts: -------------------------------------------------------------------------------- 1 | import type { TableBaseColumn, TableColumn } from './useColumns' 2 | import { NButton } from 'naive-ui' 3 | import { h } from 'vue' 4 | 5 | export function useColumnLink(linkKey: string, column: Partial>): TableColumn { 6 | return { 7 | key: linkKey, 8 | ...column, 9 | render: (row: any, index) => { 10 | const content = column.render?.(row, index) || row[column.key || linkKey] 11 | if (content === '-' || !content) 12 | return '-' 13 | if (!row[linkKey]) 14 | return content 15 | return h(NButton, { tag: 'a', href: row[linkKey], target: '_blank', text: true, type: 'primary' }, { default: () => content }) 16 | }, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/components/table/src/composables/useColumns.ts: -------------------------------------------------------------------------------- 1 | import type { MaybeRef } from '@vueuse/core' 2 | import type { TableBaseColumn as _TableBaseColumn, TableColumnGroup as _TableColumnGroup, InternalRowData, TableExpandColumn, TableSelectionColumn } from 'naive-ui/es/data-table/src/interface' 3 | import type { Ref } from 'vue' 4 | import { final } from '@naiveui-pro/utils' 5 | import { computed, unref } from 'vue' 6 | 7 | export interface TableBaseColumn extends _TableBaseColumn { 8 | disabled?: boolean | Ref | ((row: T, index: number) => boolean) 9 | text?: string | ((row: T, index: number) => string) 10 | } 11 | 12 | export type TableColumnGroup = _TableColumnGroup & { 13 | disabled?: TableBaseColumn['disabled'] 14 | text?: TableBaseColumn['text'] 15 | } 16 | 17 | export type TableColumn = TableColumnGroup | TableBaseColumn | TableSelectionColumn | TableExpandColumn 18 | export type TableColumns = Array> 19 | 20 | export function useColumns(columns: MaybeRef>) { 21 | const value = computed(() => { 22 | return unref(columns).filter(col => !final(col.disabled)).map(col => ({ minWidth: 120, ...col })) 23 | }) 24 | return value 25 | } 26 | 27 | export function useColumn(columns: MaybeRef>, key: string) { 28 | return unref(columns).find((v: any) => v.key === key)! 29 | } 30 | -------------------------------------------------------------------------------- /packages/components/table/src/composables/useMetadata.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable ts/ban-ts-comment */ 2 | import type { DataTableInst } from 'naive-ui' 3 | import type { ColumnKey, FilterState } from 'naive-ui/es/data-table/src/interface' 4 | import type { DefineTableOptions } from '../define' 5 | import { ref } from 'vue' 6 | import { useOffsetPagination } from './useOffsetPagination' 7 | 8 | export function useMetadata(options: DefineTableOptions) { 9 | const tableInstRef = ref() 10 | const firstPage = ref(options.pagination?.page || 1) 11 | const pagination = useOffsetPagination({ ...options.pagination }) 12 | 13 | function clearFilters() { 14 | tableInstRef.value?.clearFilters() 15 | } 16 | function clearSorter() { 17 | tableInstRef.value?.clearSorter() 18 | } 19 | function filters(filters: FilterState | null) { 20 | tableInstRef.value?.filters(filters) 21 | } 22 | function scrollTo(...args: any[]) { 23 | // @ts-expect-error 24 | tableInstRef.value?.scrollTo(...args) 25 | } 26 | function sort(columnKey: ColumnKey, order: 'ascend' | 'descend' | false) { 27 | tableInstRef.value?.sort(columnKey, order) 28 | } 29 | 30 | return { 31 | tableInstRef, 32 | pagination, 33 | clearFilters, 34 | clearSorter, 35 | filters, 36 | firstPage, 37 | scrollTo, 38 | sort, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/components/table/src/composables/useOffsetPagination.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | UseOffsetPaginationReturn as _OffsetPagination, 3 | UseOffsetPaginationOptions as _OffsetPaginationOptions, 4 | } from '@vueuse/core' 5 | import type { Ref, UnwrapNestedRefs } from 'vue' 6 | 7 | import { 8 | useOffsetPagination as _useOffsetPagination, 9 | } from '@vueuse/core' 10 | import { ref } from 'vue' 11 | 12 | export interface OffsetPaginationOptions extends Omit<_OffsetPaginationOptions, 'onPageChange' | 'onPageSizeChange' | 'onPageCountChange'> { 13 | } 14 | 15 | export interface OffsetPagination extends Omit<_OffsetPagination, 'currentPage' | 'currentPageSize'> { 16 | page: Ref 17 | pageSize: Ref 18 | total: Ref 19 | } 20 | export interface ServerPaginationResolve extends Partial> { 21 | page: number 22 | pageSize: number 23 | } 24 | 25 | export function useOffsetPagination(options: _OffsetPaginationOptions = {}): OffsetPagination { 26 | const total = ref(options.total || 0) 27 | const { 28 | currentPage, 29 | currentPageSize, 30 | pageCount, 31 | isFirstPage, 32 | isLastPage, 33 | prev, 34 | next, 35 | } = _useOffsetPagination({ 36 | total, 37 | ...options, 38 | }) as _OffsetPagination 39 | 40 | const returnValue = { 41 | page: currentPage, 42 | pageSize: currentPageSize, 43 | pageCount, 44 | isFirstPage, 45 | isLastPage, 46 | prev, 47 | next, 48 | total, 49 | } 50 | 51 | return returnValue as any 52 | } 53 | -------------------------------------------------------------------------------- /packages/components/table/src/composables/useTableMinWidth.ts: -------------------------------------------------------------------------------- 1 | import type { MaybeRef } from '@vueuse/core' 2 | import type { TableColumns } from './useColumns' 3 | import { useArrayMap, useArrayReduce } from '@vueuse/core' 4 | 5 | export function useTableMinWidth(columns: MaybeRef) { 6 | const columnWidths = useArrayMap(columns, col => Number(col.width || col.minWidth || 100)) 7 | return useArrayReduce(columnWidths, (acc, cur) => acc + cur, 0) 8 | } 9 | -------------------------------------------------------------------------------- /packages/components/table/src/define/index.ts: -------------------------------------------------------------------------------- 1 | import type { Ref } from 'vue' 2 | import type { OffsetPaginationOptions, ServerPaginationResolve } from '../composables' 3 | 4 | import type { ProTableInstance, ProTableRequest } from '../types' 5 | import { useAsyncCallback } from '@naiveui-pro/utils' 6 | import { useDebounceFn } from '@vueuse/core' 7 | import { reactive, ref, watch } from 'vue' 8 | import { useMetadata } from '../composables' 9 | 10 | export interface DefineTableOptions { 11 | request: ProTableRequest 12 | watch?: any[] 13 | immediate?: boolean 14 | pagination?: OffsetPaginationOptions 15 | } 16 | 17 | export function defineTable(options: DefineTableOptions): ProTableInstance { 18 | const { firstPage, pagination, tableInstRef } = useMetadata(options) 19 | const data = ref([]) as Ref 20 | const { immediate = true } = options 21 | 22 | const [request, loading] = useAsyncCallback( 23 | async (_pagination?: ServerPaginationResolve) => { 24 | if (typeof _pagination?.page === 'undefined') 25 | pagination.page.value = firstPage.value 26 | const result = await options.request(reactive(pagination)) 27 | pagination.total.value = result.total 28 | data.value = result.data 29 | }, 30 | ) 31 | 32 | const search = useDebounceFn(request, 50) 33 | const reset = useDebounceFn(() => request(), 50) 34 | const requestAll = () => requestAllData(options.request) 35 | 36 | watch(pagination.page, (newValue, oldValue) => { 37 | if (newValue !== oldValue) 38 | search(reactive(pagination)) 39 | }) 40 | 41 | watch(pagination.pageSize, () => { 42 | search(reactive(pagination)) 43 | }) 44 | 45 | if (options.watch) 46 | watch(options.watch, () => reset()) 47 | 48 | if (immediate) 49 | reset() 50 | 51 | return reactive({ 52 | _tableInstRef: tableInstRef, 53 | data, 54 | loading, 55 | search, 56 | request: options.request, 57 | reset, 58 | requestAll, 59 | pagination: reactive(pagination), 60 | prev: pagination.prev, 61 | next: pagination.next, 62 | }) 63 | } 64 | 65 | async function requestAllData(request: DefineTableOptions['request']) { 66 | const pagination = { page: 1, pageSize: 1000 } 67 | let length = Infinity 68 | const data: any[] = [] 69 | while (length > 1000) { 70 | const result = await request(pagination as any) 71 | const array = result.data as unknown as any[] || [] 72 | data.push(...array) 73 | length = array.length 74 | pagination.page++ 75 | } 76 | return data as unknown as T 77 | } 78 | -------------------------------------------------------------------------------- /packages/components/table/src/index.ts: -------------------------------------------------------------------------------- 1 | import { NpTable } from './components' 2 | 3 | export * from './components' 4 | export * from './composables' 5 | export * from './define' 6 | export * from './types' 7 | 8 | export default NpTable 9 | -------------------------------------------------------------------------------- /packages/components/table/src/types/index.ts: -------------------------------------------------------------------------------- 1 | import type { PromisifyFn } from '@vueuse/core' 2 | import type { DataTableInst } from 'naive-ui' 3 | import type { UnwrapNestedRefs } from 'vue' 4 | import type { OffsetPagination, ServerPaginationResolve } from '../composables' 5 | 6 | export interface ScrollTo { 7 | (x: number, y: number): void 8 | (options: { 9 | left?: number 10 | top?: number 11 | behavior?: ScrollBehavior 12 | }): void 13 | } 14 | 15 | export interface DataResolved { 16 | data: T 17 | total: number 18 | } 19 | 20 | export interface ProTableRequest { 21 | (pagination: ServerPaginationResolve): DataResolved | Promise> 22 | } 23 | 24 | export interface ProTableInstance { 25 | data: T 26 | loading: boolean 27 | pagination: UnwrapNestedRefs 28 | next: () => void 29 | prev: () => void 30 | request: ProTableRequest 31 | requestAll: () => Promise 32 | search: PromisifyFn<(pagination?: ServerPaginationResolve) => void> 33 | reset: PromisifyFn<() => void> 34 | _tableInstRef: DataTableInst | undefined 35 | } 36 | -------------------------------------------------------------------------------- /packages/components/table/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'tsup-define' 2 | 3 | export default defineBuildConfig({ 4 | format: ['cjs', 'esm', 'iife', 'iife-min'], 5 | entry: ['src/index.ts'], 6 | name: 'pro-table', 7 | globalName: 'NaiveProTable', 8 | globals: { 9 | 'naive-ui': 'naive', 10 | 'vue': 'Vue', 11 | }, 12 | clean: true, 13 | }) 14 | -------------------------------------------------------------------------------- /packages/config/index.d.ts: -------------------------------------------------------------------------------- 1 | import type Globals from 'esbuild-plugin-globals' 2 | import type { Options as _Options } from 'tsup' 3 | 4 | export interface Options extends Omit<_Options, 'format'> { 5 | globals?: Parameters[0] 6 | format?: ('cjs' | 'esm' | 'iife' | 'iife-min')[] 7 | } 8 | export declare function defineBuildConfig(options: Options): _Options[] 9 | -------------------------------------------------------------------------------- /packages/config/index.js: -------------------------------------------------------------------------------- 1 | const Globals = require('esbuild-plugin-globals') 2 | 3 | exports.defineBuildConfig = (options) => { 4 | const formats = options?.format || ['cjs', 'esm'] 5 | const iife = formats.findIndex(v => v === 'iife') 6 | formats.splice(iife, 1) 7 | const iifeMin = formats.findIndex(v => v === 'iifeMin') 8 | formats.splice(iifeMin, 1) 9 | 10 | const configs = [] 11 | 12 | options = { 13 | ...options, 14 | dts: { resolve: [/@naiveui-pro/, /naive-ui/, /@vueuse/] }, 15 | external: ['vue', 'naive-ui', '@vueuse/core', 'vue', /@naiveui-pro/], 16 | esbuildPlugins: [require('unplugin-vue-jsx/esbuild')()], 17 | } 18 | 19 | configs.push(options) 20 | 21 | if (iife) { 22 | configs.push({ 23 | ...options, 24 | format: 'iife', 25 | esbuildPlugins: [Globals(options?.globals), require('unplugin-vue-jsx/esbuild')()], 26 | outExtension({ format }) { 27 | return { js: `.${format}.js` } 28 | }, 29 | dts: false, 30 | }) 31 | } 32 | if (iifeMin) { 33 | configs.push({ 34 | ...options, 35 | format: 'iife', 36 | minify: true, 37 | esbuildPlugins: [Globals(options?.globals), require('unplugin-vue-jsx/esbuild')()], 38 | outExtension({ format }) { 39 | return { js: `.${format}.min.js` } 40 | }, 41 | dts: false, 42 | }) 43 | } 44 | 45 | return configs 46 | } 47 | -------------------------------------------------------------------------------- /packages/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tsup-define", 3 | "version": "0.3.5", 4 | "private": true, 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "devDependencies": { 8 | "esbuild-plugin-globals": "^0.2.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@naiveui-pro/utils", 3 | "type": "module", 4 | "version": "0.3.5", 5 | "main": "./src/index.ts", 6 | "publishConfig": { 7 | "main": "./dist/index.cjs", 8 | "types": "./dist/index.d.ts", 9 | "module": "./dist/index.js", 10 | "unpkg": "./dist/index.iife.min.js", 11 | "jsdelivr": "./dist/index.iife.min.js", 12 | "exports": { 13 | ".": { 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.cjs" 16 | } 17 | } 18 | }, 19 | "files": ["dist"], 20 | "scripts": { 21 | "build": "tsup", 22 | "prepublish": "npm run build" 23 | }, 24 | "dependencies": { 25 | "defu": "^6.1.2", 26 | "vue": "catalog:share" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/utils/src/components/index.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent, h } from 'vue' 2 | 3 | export const If = defineComponent({ 4 | name: 'If', 5 | props: { 6 | cond: { 7 | type: Boolean, 8 | default: true, 9 | }, 10 | tag: { 11 | type: String, 12 | }, 13 | }, 14 | setup(props, { slots }) { 15 | return () => props.cond 16 | ? (props.tag ? h(props.tag, {}, slots) : slots.default?.()) 17 | : null 18 | }, 19 | }) 20 | -------------------------------------------------------------------------------- /packages/utils/src/composables/index.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export function useAsyncCallback any>(fun: T) { 4 | const error = ref() 5 | const loading = ref(false) 6 | function execute(...args: any[]) { 7 | const result = fun(...args) 8 | if (result instanceof Promise) { 9 | loading.value = true 10 | result.finally(() => loading.value = false).catch() 11 | } 12 | return result 13 | } 14 | 15 | return [execute as unknown as T, loading, error] as const 16 | } 17 | -------------------------------------------------------------------------------- /packages/utils/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components' 2 | export * from './composables' 3 | export * from './types' 4 | export * from './utils' 5 | -------------------------------------------------------------------------------- /packages/utils/src/types/index.ts: -------------------------------------------------------------------------------- 1 | import type { Ref } from 'vue' 2 | 3 | export type NestedRefs = { [K in keyof T]: Ref | T[K] } & Record 4 | export type AnyFunction = (...args: any[]) => any 5 | -------------------------------------------------------------------------------- /packages/utils/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import type { Component, FunctionalComponent, VNode } from 'vue' 2 | import { createDefu } from 'defu' 3 | import { h } from 'vue' 4 | 5 | export function noop() { } 6 | 7 | // eslint-disable-next-line ts/no-unsafe-function-type 8 | export function isFunction(val: any): val is Function { 9 | return typeof val === 'function' 10 | } 11 | 12 | export function isUndefined(val: any): val is undefined { 13 | return typeof val === 'undefined' 14 | } 15 | 16 | export function toArray(val: T | T[]): T[] { 17 | return Array.isArray(val) ? val : [val].filter(Boolean) 18 | } 19 | 20 | export const defu = createDefu((obj, key, value) => { 21 | if (Array.isArray(obj[key]) && Array.isArray(value)) { 22 | obj[key] = value 23 | return true 24 | } 25 | }) 26 | 27 | export function final(value: ((...args: any[]) => T) | T, ...params: any[]) { 28 | return isFunction(value) ? value(...params) : value 29 | } 30 | 31 | export function isObject(val: any): val is Record { 32 | return val !== null && typeof val === 'object' && !Array.isArray(val) 33 | } 34 | 35 | export function isArray(val: any): val is T[] { 36 | return Array.isArray(val) 37 | } 38 | 39 | export function clone(value: T): T { 40 | if (isFunction(value)) 41 | return Object.assign((...args: any) => value(...args), value) 42 | if (Array.isArray(value)) 43 | return [...value] as any 44 | if (typeof value !== 'object' || value === null) 45 | return value 46 | return { ...value } 47 | } 48 | 49 | export function cloneDeep(value: T): T { 50 | if (isFunction(value)) 51 | return Object.assign((...args: any) => value(...args), cloneDeep({ ...value })) 52 | if (isArray(value)) 53 | return value.map(cloneDeep) as any 54 | if (!isObject(value) || value === null) 55 | return value 56 | const keys = Object.entries(value) 57 | const entries = keys.map(([key, value]) => [key, cloneDeep(value)]) 58 | return Object.fromEntries(entries) 59 | } 60 | 61 | export function render(is?: Component | FunctionalComponent | string | number | boolean | VNode | null) { 62 | if (typeof is === 'boolean') 63 | return null 64 | if (typeof is === 'string' || typeof is === 'number') 65 | return is 66 | return is ? h(is) : null 67 | } 68 | -------------------------------------------------------------------------------- /packages/utils/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'tsup-define' 2 | 3 | export default defineBuildConfig({ 4 | format: ['cjs', 'esm', 'iife', 'iife-min'], 5 | entry: ['src/index.ts'], 6 | name: 'pro-utils', 7 | globalName: 'NaiveProUtils', 8 | globals: { 9 | vue: 'Vue', 10 | }, 11 | clean: true, 12 | }) 13 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - playground 3 | - docs 4 | - packages/**/* 5 | - examples/**/* 6 | catalogs: 7 | share: 8 | vue: ^3.5.13 9 | naive-ui: ^2.41.0 10 | '@vueuse/core': ^13.0.0 11 | 12 | catalog: 13 | '@antfu/eslint-config': ^4.2.1 14 | '@antfu/ni': ^23.3.1 15 | '@antfu/utils': ^9.0.0 16 | '@iconify-json/svg-spinners': ^1.2.2 17 | '@shikijs/vitepress-twoslash': ^2.3.2 18 | '@types/node': ^22.13.4 19 | '@unocss/reset': ^65.5.0 20 | '@vueuse/core': ^12.7.0 21 | bumpp: ^10.0.3 22 | eslint: ^9.20.1 23 | floating-vue: ^5.2.2 24 | lint-staged: ^15.4.3 25 | pinia: ^3.0.1 26 | pnpm: ^10.4.0 27 | simple-git-hooks: ^2.11.1 28 | tsx: ^4.19.2 29 | typescript: ^5.7.3 30 | unbuild: ^3.3.1 31 | unocss: ^65.5.0 32 | unplugin-vue-components: ^28.1.0 33 | vite: ^6.1.0 34 | vite-tsconfig-paths: ^5.1.4 35 | vitepress: ^2.0.0-alpha.2 36 | vitepress-plugin-group-icons: ^1.3.5 37 | vitest: ^3.0.5 38 | -------------------------------------------------------------------------------- /test/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | 3 | describe('should', () => { 4 | it('exported', () => { 5 | expect(1).toEqual(1) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "jsx": "preserve", 5 | "jsxImportSource": "vue", 6 | "lib": [ 7 | "esnext", 8 | "dom" 9 | ], 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "strict": true, 14 | "declaration": true, 15 | "downlevelIteration": true, 16 | "sourceMap": true, 17 | "esModuleInterop": true, 18 | "skipDefaultLibCheck": true, 19 | "skipLibCheck": true 20 | }, 21 | "include": [ 22 | "./**/*.ts", 23 | "./**/*.d.ts", 24 | "./**/*.tsx", 25 | "./**/*.vue", 26 | "types/**.d.ts", 27 | "types/**/**.d.ts" 28 | ], 29 | "exclude": [ 30 | "**/dist/*.ts", 31 | "**/dist/*.d.ts" 32 | ] 33 | } 34 | --------------------------------------------------------------------------------