├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── support-question.md ├── renovate.json └── workflows │ ├── autoapprove.yml │ ├── codeql-analysis.yml │ ├── tests.yml │ └── update-generated-code.yml ├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── HOW_TO_BUILD.md ├── LICENSE ├── README.md ├── SECURITY.md ├── docs ├── using-vue-cli.md └── using-webpack.md ├── gulpfile.js ├── jest.config.base.js ├── jest.config.js ├── jest.tsconfig.json ├── lerna.json ├── package-lock.json ├── package.json ├── packages ├── devextreme-vue-generator │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── common-reexports-generator.ts │ │ ├── component-generator.test.ts │ │ ├── component-generator.ts │ │ ├── converter.test.ts │ │ ├── converter.ts │ │ ├── generator.test.ts │ │ ├── generator.ts │ │ ├── helpers.ts │ │ ├── index-generator.test.ts │ │ ├── index-generator.ts │ │ └── template.ts │ ├── tsconfig.json │ ├── tsconfig.local.json │ └── tslint.json ├── devextreme-vue │ ├── .gitignore │ ├── build.config.js │ ├── gulpfile.js │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── accordion.ts │ │ ├── action-sheet.ts │ │ ├── autocomplete.ts │ │ ├── bar-gauge.ts │ │ ├── box.ts │ │ ├── bullet.ts │ │ ├── button-group.ts │ │ ├── button.ts │ │ ├── calendar.ts │ │ ├── chart.ts │ │ ├── check-box.ts │ │ ├── circular-gauge.ts │ │ ├── color-box.ts │ │ ├── common │ │ │ ├── charts.ts │ │ │ ├── data │ │ │ │ └── custom-store.ts │ │ │ ├── grids.ts │ │ │ └── index.ts │ │ ├── context-menu.ts │ │ ├── core │ │ │ ├── __mocks__ │ │ │ │ └── templates-discovering.ts │ │ │ ├── __tests__ │ │ │ │ ├── button.test.ts │ │ │ │ ├── component.test.ts │ │ │ │ ├── configuration.test.ts │ │ │ │ ├── data-grid.test.ts │ │ │ │ ├── form.test.ts │ │ │ │ ├── helpers.test.ts │ │ │ │ ├── selectbox.test.ts │ │ │ │ ├── templates-discovering.test.ts │ │ │ │ ├── templates-manager.test.ts │ │ │ │ ├── textbox.test.ts │ │ │ │ ├── toolbar.test.ts │ │ │ │ └── vue-helper.ts │ │ │ ├── children-processing.ts │ │ │ ├── component.ts │ │ │ ├── config.ts │ │ │ ├── configuration-component.ts │ │ │ ├── configuration.ts │ │ │ ├── constants.ts │ │ │ ├── extension-component.ts │ │ │ ├── helpers.ts │ │ │ ├── index.ts │ │ │ ├── strategy │ │ │ │ ├── vue2 │ │ │ │ │ └── index.ts │ │ │ │ └── vue3 │ │ │ │ │ └── index.ts │ │ │ ├── templates-discovering.ts │ │ │ ├── templates-manager.ts │ │ │ ├── version.ts │ │ │ └── vue-helper.ts │ │ ├── data-grid.ts │ │ ├── date-box.ts │ │ ├── date-range-box.ts │ │ ├── defer-rendering.ts │ │ ├── diagram.ts │ │ ├── draggable.ts │ │ ├── drawer.ts │ │ ├── drop-down-box.ts │ │ ├── drop-down-button.ts │ │ ├── file-manager.ts │ │ ├── file-uploader.ts │ │ ├── filter-builder.ts │ │ ├── form.ts │ │ ├── funnel.ts │ │ ├── gallery.ts │ │ ├── gantt.ts │ │ ├── html-editor.ts │ │ ├── index.ts │ │ ├── linear-gauge.ts │ │ ├── list.ts │ │ ├── load-indicator.ts │ │ ├── load-panel.ts │ │ ├── lookup.ts │ │ ├── map.ts │ │ ├── menu.ts │ │ ├── multi-view.ts │ │ ├── number-box.ts │ │ ├── pie-chart.ts │ │ ├── pivot-grid-field-chooser.ts │ │ ├── pivot-grid.ts │ │ ├── polar-chart.ts │ │ ├── popover.ts │ │ ├── popup.ts │ │ ├── progress-bar.ts │ │ ├── radio-group.ts │ │ ├── range-selector.ts │ │ ├── range-slider.ts │ │ ├── recurrence-editor.ts │ │ ├── resizable.ts │ │ ├── responsive-box.ts │ │ ├── sankey.ts │ │ ├── scheduler.ts │ │ ├── scroll-view.ts │ │ ├── select-box.ts │ │ ├── slider.ts │ │ ├── sortable.ts │ │ ├── sparkline.ts │ │ ├── speed-dial-action.ts │ │ ├── switch.ts │ │ ├── tab-panel.ts │ │ ├── tabs.ts │ │ ├── tag-box.ts │ │ ├── text-area.ts │ │ ├── text-box.ts │ │ ├── tile-view.ts │ │ ├── toast.ts │ │ ├── toolbar.ts │ │ ├── tooltip.ts │ │ ├── tree-list.ts │ │ ├── tree-map.ts │ │ ├── tree-view.ts │ │ ├── validation-group.ts │ │ ├── validation-summary.ts │ │ ├── validator.ts │ │ └── vector-map.ts │ └── tsconfig.json ├── sandbox │ ├── app.vue │ ├── components │ │ ├── accessing-instance-example.vue │ │ ├── button-example.vue │ │ ├── chart-example.vue │ │ ├── example-block.vue │ │ ├── form-example.vue │ │ ├── grid-example.vue │ │ ├── list-example.vue │ │ ├── map-example.vue │ │ ├── menu-example.vue │ │ ├── number-box-example.vue │ │ ├── popup-example.vue │ │ ├── scheduler-example.vue │ │ ├── scroll-view-example.vue │ │ ├── tab-panel-example.vue │ │ ├── text-box-example.vue │ │ └── validation-example.vue │ ├── data.ts │ ├── main.ts │ ├── package.json │ ├── public │ │ ├── css │ │ │ ├── bootstrap.css │ │ │ └── index.css │ │ └── index.html │ ├── tsconfig.json │ ├── vue-shim.d.ts │ └── webpack.config.js └── vue2-strategy │ ├── .gitignore │ ├── LICENSE │ ├── build.config.js │ ├── gulpfile.js │ ├── jest.config.js │ ├── package.json │ ├── sandbox │ ├── app.vue │ ├── components │ │ ├── accessing-instance-example.vue │ │ ├── button-example.vue │ │ ├── chart-example.vue │ │ ├── example-block.vue │ │ ├── form-example.vue │ │ ├── grid-example.vue │ │ ├── list-example.vue │ │ ├── map-example.vue │ │ ├── menu-example.vue │ │ ├── number-box-example.vue │ │ ├── popup-example.vue │ │ ├── scheduler-example.vue │ │ ├── scroll-view-example.vue │ │ ├── tab-panel-example.vue │ │ ├── text-box-example.vue │ │ └── validation-example.vue │ ├── main.ts │ ├── public │ │ ├── css │ │ │ ├── bootstrap.css │ │ │ └── index.css │ │ └── index.html │ ├── tsconfig.json │ └── vue-shim.d.ts │ ├── src │ ├── accordion.ts │ ├── action-sheet.ts │ ├── autocomplete.ts │ ├── bar-gauge.ts │ ├── box.ts │ ├── bullet.ts │ ├── button-group.ts │ ├── button.ts │ ├── calendar.ts │ ├── chart.ts │ ├── check-box.ts │ ├── circular-gauge.ts │ ├── color-box.ts │ ├── common │ │ ├── charts.ts │ │ ├── data │ │ │ └── custom-store.ts │ │ ├── grids.ts │ │ └── index.ts │ ├── context-menu.ts │ ├── core │ │ ├── __mocks__ │ │ │ └── templates-discovering.ts │ │ ├── __tests__ │ │ │ ├── form.test.ts │ │ │ └── helper.test.ts │ │ ├── children-processing.ts │ │ ├── component.test.ts │ │ ├── component.ts │ │ ├── config.test.ts │ │ ├── config.ts │ │ ├── configuration-component.ts │ │ ├── configuration.test.ts │ │ ├── configuration.ts │ │ ├── constants.ts │ │ ├── errors.ts │ │ ├── extension-component.ts │ │ ├── helpers.ts │ │ ├── index.ts │ │ ├── templates-discovering.test.ts │ │ ├── templates-discovering.ts │ │ ├── templates-manager.test.ts │ │ └── templates-manager.ts │ ├── data-grid.ts │ ├── date-box.ts │ ├── date-range-box.ts │ ├── defer-rendering.ts │ ├── diagram.ts │ ├── draggable.ts │ ├── drawer.ts │ ├── drop-down-box.ts │ ├── drop-down-button.ts │ ├── file-manager.ts │ ├── file-uploader.ts │ ├── filter-builder.ts │ ├── form.ts │ ├── funnel.ts │ ├── gallery.ts │ ├── gantt.ts │ ├── html-editor.ts │ ├── index.ts │ ├── linear-gauge.ts │ ├── list.ts │ ├── load-indicator.ts │ ├── load-panel.ts │ ├── lookup.ts │ ├── map.ts │ ├── menu.ts │ ├── multi-view.ts │ ├── number-box.ts │ ├── pie-chart.ts │ ├── pivot-grid-field-chooser.ts │ ├── pivot-grid.ts │ ├── polar-chart.ts │ ├── popover.ts │ ├── popup.ts │ ├── progress-bar.ts │ ├── radio-group.ts │ ├── range-selector.ts │ ├── range-slider.ts │ ├── recurrence-editor.ts │ ├── resizable.ts │ ├── responsive-box.ts │ ├── sankey.ts │ ├── scheduler.ts │ ├── scroll-view.ts │ ├── select-box.ts │ ├── slider.ts │ ├── sortable.ts │ ├── sparkline.ts │ ├── speed-dial-action.ts │ ├── switch.ts │ ├── tab-panel.ts │ ├── tabs.ts │ ├── tag-box.ts │ ├── text-area.ts │ ├── text-box.ts │ ├── tile-view.ts │ ├── toast.ts │ ├── toolbar.ts │ ├── tooltip.ts │ ├── tree-list.ts │ ├── tree-map.ts │ ├── tree-view.ts │ ├── validation-group.ts │ ├── validation-summary.ts │ ├── validator.ts │ └── vector-map.ts │ ├── tsconfig.json │ └── webpack.config.js ├── tsconfig.json └── tslint.json /.gitattributes: -------------------------------------------------------------------------------- 1 | .circleci text eol=lf 2 | .github text eol=lf 3 | .gitattributes text eol=lf 4 | .gitignore text eol=lf 5 | *.js text eol=lf 6 | *.json text eol=lf 7 | *.md text eol=lf 8 | *.yml text eol=lf 9 | *.vue text eol=lf 10 | *.css text eol=lf 11 | *.ts text eol=lf 12 | *.html text eol=lf 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ### Issue Type 7 | 8 | 9 | ### Description 10 | 11 | The issue is related to ... 12 | 13 | #### Steps to Reproduce 14 | 15 | 16 | #### Current Behavior 17 | 18 | #### Expected Behavior 19 | 20 | #### Demo 21 | 25 | 26 | ### Environment Details 27 | 30 | - devextreme version: 31 | - devextreme-vue version: 32 | - vue version: 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug in devextreme-vue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 17 | 18 | # Bug Report 19 | 20 | 21 | 22 | **Package versions:** 23 | 24 | devexteme version: 25 | devextreme-vue version: 26 | 27 | **Steps to reproduce:** 28 | 32 | 33 | **Current behavior:** 34 | 35 | 36 | **Expected behavior:** 37 | 38 | 39 | **Screenshots:** 40 | 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest a feature for devextreme-vue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Feature request 11 | 12 | **Package versions you currently use:** 13 | 14 | devexteme version: 15 | devextreme-vue version: 16 | 17 | **Description:** 18 | 19 | 20 | **Preferred Solution:** 21 | 22 | 23 | **Alternatives:** 24 | 25 | 26 | **Additional Context:** 27 | 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support-question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Support Question 3 | about: Questions and requests for support. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Support Question 11 | 12 | Please do not submit support requests and "How to" questions here. Navigate to our Support Center (https://devexpress.com/sc) instead. 13 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "prConcurrentLimit": 2, 6 | "semanticCommits": "enabled", 7 | "rangeStrategy": "bump", 8 | "baseBranches": [ 9 | "master" 10 | ], 11 | "labels": [ 12 | "dependencies" 13 | ], 14 | "vulnerabilityAlerts": { 15 | "enabled": true, 16 | "automerge": true 17 | }, 18 | "packageRules": [ 19 | { 20 | "matchPackageNames": [ "typescript", "typescript-min" ], 21 | "matchUpdateTypes": [ "major", "minor" ], 22 | "enabled": false 23 | }, 24 | { 25 | "matchPackagePatterns": [ "*" ], 26 | "matchUpdateTypes": [ "minor", "patch" ], 27 | "automerge": true 28 | }, 29 | { 30 | "matchPackagePatterns": [ "*" ], 31 | "matchUpdateTypes": [ "major" ], 32 | "enabled": false 33 | } 34 | ], 35 | "reviewers": [ 36 | "team:devextreme-devops" 37 | ], 38 | "ignorePaths": [ 39 | ".github" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/autoapprove.yml: -------------------------------------------------------------------------------- 1 | name: Auto approve 2 | 3 | on: 4 | pull_request_target 5 | 6 | jobs: 7 | auto-approve: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: hmarr/auto-approve-action@v2 11 | if: github.actor == 'renovate-bot' || github.actor == 'renovate[bot]' 12 | with: 13 | github-token: "${{ secrets.GITHUB_TOKEN }}" 14 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master", "[0-9][0-9].[0-9]" ] 17 | pull_request: 18 | schedule: 19 | - cron: '0 0 * * 6' 20 | 21 | jobs: 22 | analyze: 23 | name: Analyze 24 | runs-on: ubuntu-latest 25 | permissions: 26 | actions: read 27 | contents: read 28 | security-events: write 29 | 30 | strategy: 31 | fail-fast: false 32 | matrix: 33 | language: [ 'javascript' ] 34 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 35 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 36 | 37 | steps: 38 | - name: Checkout repository 39 | uses: actions/checkout@v3 40 | 41 | # Initializes the CodeQL tools for scanning. 42 | - name: Initialize CodeQL 43 | uses: github/codeql-action/init@v2 44 | with: 45 | languages: ${{ matrix.language }} 46 | # If you wish to specify custom queries, you can do so here or in a config file. 47 | # By default, queries listed here will override any specified in a config file. 48 | # Prefix the list here with "+" to use these queries and those in the config file. 49 | 50 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 51 | # queries: security-extended,security-and-quality 52 | 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v2 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 61 | 62 | # If the Autobuild fails above, remove it and uncomment the following three lines. 63 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 64 | 65 | # - run: | 66 | # echo "Run, Build Application using script" 67 | # ./location_of_script_within_repo/buildscript.sh 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v2 71 | with: 72 | category: "/language:${{matrix.language}}" 73 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | - '[12][0-9].[12]' 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 60 14 | 15 | steps: 16 | - name: Get sources 17 | uses: actions/checkout@v3 18 | 19 | - name: Use Node.js 18 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: '18.16.1' 23 | 24 | - uses: actions/setup-dotnet@v3 25 | with: 26 | dotnet-version: '3.1.x' 27 | 28 | - name: Install npm packages 29 | run: npm i 30 | 31 | - name: Run lint 32 | run: npm run lint 33 | 34 | - name: Restore devextreme 35 | run: npm run restore-devextreme 36 | 37 | - name: Build devextreme repo 38 | working-directory: devextreme/packages/devextreme 39 | run: npm run build-npm-devextreme 40 | 41 | - name: Pack DevExtreme 42 | working-directory: devextreme/packages/devextreme/artifacts/npm/devextreme 43 | run: | 44 | npm pack 45 | mv devextreme*.tgz devextreme.tgz 46 | 47 | - name: Install devextreme package 48 | run: | 49 | npm i --save-dev ./devextreme/packages/devextreme/artifacts/npm/devextreme/devextreme.tgz --workspace=devextreme-vue --workspace=devextreme-vue2-strategy --no-audit --no-fund 50 | 51 | - name: Generate metadata 52 | run: npm run generate-metadata 53 | 54 | - name: Build packages 55 | run: npm run build:packages 56 | 57 | - name: Check generated code 58 | shell: bash 59 | run: | 60 | git add . -N 61 | changes=$(git diff --name-status HEAD -- packages/devextreme-vue/src packages/vue2-strategy/src) 62 | if [ -n "$changes" ]; then 63 | echo "Generated code is outdated. The following files have uncommitted changes:" 64 | echo "$changes"; 65 | echo "To update generated code, generate metadata, build packages and commit changes." 66 | exit 1 67 | fi 68 | 69 | - name: Run tests with Vue 70 | run: npm run test 71 | 72 | - name: Check packing 73 | run: npm run pack 74 | 75 | - name: Archive internal-tools artifacts 76 | uses: actions/upload-artifact@v3 77 | with: 78 | name: internal-tools-artifacts 79 | path: artifacts/internal-tools/ 80 | retention-days: 7 81 | -------------------------------------------------------------------------------- /.github/workflows/update-generated-code.yml: -------------------------------------------------------------------------------- 1 | name: Update generated code 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | on: 8 | workflow_dispatch: 9 | 10 | jobs: 11 | update-generated-code: 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 20 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.DX_ROBOT_PAT }} 16 | DX_ROBOT_EMAIL: ${{ secrets.DX_ROBOT_EMAIL }} 17 | branch_name: "robot/update-generated-code" 18 | paths: "packages/devextreme-vue/src packages/vue2-strategy/src" 19 | 20 | steps: 21 | - name: Get sources 22 | uses: actions/checkout@v3 23 | 24 | - name: Use Node.js 18 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: '18' 28 | 29 | - name: Setup DX Robot 30 | shell: bash 31 | run: | 32 | git config --global user.email $DX_ROBOT_EMAIL 33 | git config --global user.name "DX Robot" 34 | 35 | - name: Check if branch exists 36 | id: check_branch 37 | shell: bash 38 | continue-on-error: true 39 | run: | 40 | if [[ -n $(git ls-remote --heads origin $branch_name) ]]; then 41 | echo "branch_exists=true" >> $GITHUB_OUTPUT 42 | else 43 | echo "branch_exists=false" >> $GITHUB_OUTPUT 44 | fi 45 | 46 | - name: Pull branch 47 | if: steps.check_branch.outputs.branch_exists == 'true' 48 | shell: bash 49 | run: | 50 | git fetch 51 | git switch $branch_name --force 52 | 53 | - name: Install packages 54 | run: npm i 55 | 56 | - name: Update submodule 57 | run: npm run pull-devextreme 58 | 59 | - name: Generate metadata 60 | run: npm run generate-metadata 61 | 62 | - name: Generate code 63 | run: npm run build:packages 64 | 65 | - name: Commit changes and make PR, if necessary 66 | shell: bash 67 | run: | 68 | branch_exists=${{ steps.check_branch.outputs.branch_exists }} 69 | git add . -N 70 | if git diff --name-status --exit-code HEAD -- $paths; then 71 | echo "Generated code is up to date" 72 | else 73 | if ! $branch_exists; then 74 | git fetch 75 | git switch master 76 | git checkout -b $branch_name 77 | fi 78 | 79 | devextreme_sha=$(git ls-tree --object-only HEAD devextreme) 80 | git commit -a -m "Update devextreme submodule to $devextreme_sha. Update generated code." 81 | git push --set-upstream origin $branch_name --force 82 | 83 | if ! $branch_exists; then 84 | gh pr create --title "Update generated code" --body "Update generated code" --reviewer DevExpress/devextreme-essentials 85 | fi 86 | fi 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/launch.json 2 | .idea 3 | node_modules/ 4 | dist/ 5 | artifacts/ 6 | packages/devextreme-vue/metadata/integration-data.json 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "devextreme"] 2 | path = devextreme 3 | url = https://github.com/DevExpress/DevExtreme.git 4 | branch = 23_2 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 4, 3 | "editor.insertSpaces": true, 4 | "typescript.tsdk": "node_modules/typescript/lib" 5 | } -------------------------------------------------------------------------------- /HOW_TO_BUILD.md: -------------------------------------------------------------------------------- 1 | # DevExtreme Vue. How to Build an Application 2 | 3 | ## DevExtreme Submodule 4 | 5 | Since we store the generated code in this repository, you need to commit a version of `devextreme` that is compatible with it. 6 | We use `git-submodule` to keep the versions of `devextreme` and `devextreme-vue` in sync. 7 | 8 | After you clone the `devextreme-vue` repository, use the following command to load the `devextreme` submodule: 9 | 10 | npm run restore-devextreme 11 | 12 | This command also resets all changes in the submodule, if any occur. 13 | 14 | To get the latest `devextreme` commits, update the submodule: 15 | 16 | npm run pull-devextreme 17 | 18 | After that, generate the code (see the last paragraph) and commit the changes in the submodule file. 19 | 20 | ## Link DevExtreme Modules 21 | 22 | To install the latest version of `devextreme` in the `node_modules` folder, run the following command: 23 | 24 | npm run link-devextreme 25 | 26 | ## Local DevExtreme Fork 27 | 28 | If you need to use your fork of `devextreme` locally instead of a submodule, change the path to `devextreme` in `package.json`, section `config`: 29 | 30 | "config": { 31 | "devextreme": "./devextreme" 32 | } 33 | 34 | ## Generate Code 35 | 36 | Generate metadata before code generation: 37 | 38 | npm run generate-metadata 39 | 40 | Then, build the packages: 41 | 42 | npm run build:packages 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Developer Express Inc. 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 | # DevExtreme Vue UI and Visualization Components # 2 | 3 | [![Run Status](https://api.shippable.com/projects/5ab4c6354a24a207009ec636/badge?branch=master)](https://app.shippable.com/github/DevExpress/devextreme-vue) 4 | [![NPM](https://img.shields.io/npm/v/devextreme-vue.svg?maxAge=43200)](https://www.npmjs.com/package/devextreme-vue) 5 | 6 | This project allows you to use [DevExtreme components](http://js.devexpress.com/Demos/WidgetsGallery/) in [Vue](https://vuejs.org) applications. 7 | 8 | * [Documentation](https://js.devexpress.com/Documentation/Guide/Vue_Components/DevExtreme_Vue_Components/) 9 | * [Technical Demos](https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/Overview/Vue/Light/) 10 | * [Responsive UI Templates](https://js.devexpress.com/Vue/Templates/UITemplates/) 11 | * [Application Template](https://js.devexpress.com/Vue/Documentation/Guide/Vue_Components/Application_Template/) 12 | 13 | ## License ## 14 | 15 | **DevExtreme Vue UI Components are released as an MIT-licensed (free and open-source) add-on to DevExtreme.** 16 | 17 | Familiarize yourself with the [DevExtreme License](https://js.devexpress.com/Licensing/). [Free trial is available!](http://js.devexpress.com/Buy/) 18 | 19 | ## Support & Feedback ## 20 | 21 | If you have questions regarding Vue functionality, consult [Vue docs](https://vuejs.org/v2/guide/). 22 | 23 | If you want to report a bug, request a feature, or ask a question, submit an [issue](https://github.com/DevExpress/devextreme-vue/issues) to this repo. Alternatively, you can contact us at the [DevExpress Support Center](https://www.devexpress.com/Support/Center) if you own an active DevExtreme license. 24 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security 2 | 3 | Please refer to [DevExpress Security Policy](https://github.com/DevExpress/Shared/security/policy) 4 | -------------------------------------------------------------------------------- /docs/using-vue-cli.md: -------------------------------------------------------------------------------- 1 | # Using the DevExtreme Vue Components with Vue CLI 2 | 3 | ## Create a new Application ## 4 | 5 | Use [Vue CLI](https://cli.vuejs.org/) to create a new project. 6 | 7 | ## Add DevExtreme ## 8 | 9 | Follow the [installation](https://github.com/DevExpress/devextreme-vue#installation) section in our Readme. 10 | -------------------------------------------------------------------------------- /docs/using-webpack.md: -------------------------------------------------------------------------------- 1 | # Using the DevExtreme Vue Integration with Webpack 2 | 3 | ## Create a new Application ## 4 | 5 | You can use [the original Vue template on Webpack](https://github.com/vuejs-templates/webpack) or some other starter to create a new project based on Webpack. 6 | 7 | ## Add DevExtreme ## 8 | 9 | Follow the [installation](https://github.com/DevExpress/devextreme-vue#installation) section in our Readme. 10 | 11 | ## Configure Webpack Loaders for DevExtreme Stylesheets ## 12 | 13 | If you are using [the original Vue template on Webpack](https://github.com/vuejs-templates/webpack) 14 | you don't need any extra configuration, since all the required loaders are configured out-of-the-box. 15 | Otherwise, you need to make sure that all the necessary extensions are processed by the corresponding 16 | [loaders](https://webpack.github.io/docs/loaders.html) as described below. 17 | 18 | Go to *webpack.config.js* and configure loaders for css and fonts as follows: 19 | 20 | ```js 21 | ... 22 | loaders: [ 23 | ... 24 | { test: /\.css$/, loader: "style-loader!css-loader" }, 25 | { test: /\.(ttf|eot|woff)$/, loader: "file-loader?name=/[name].[ext]" } 26 | ] 27 | ... 28 | ``` 29 | 30 | Also, make sure that *style-loader*, *css-loader* and *file-loader* are installed into your project as npm dev dependencies. 31 | 32 | ## Import DevExtreme Stylesheets ## 33 | 34 | Having loaders configured, you need to import the required [DevExtreme css files](https://js.devexpress.com/Documentation/Guide/Themes/Predefined_Themes/). 35 | Go to your main .js file and import the stylesheets as follows: 36 | 37 | ```js 38 | ... 39 | import 'devextreme/dist/css/dx.common.css'; 40 | import 'devextreme/dist/css/dx.light.css'; 41 | ``` 42 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const tslint = require("gulp-tslint"); 3 | 4 | gulp.task('lint', () => gulp.src(['packages/*/src/**/*.ts', 'packages/**/sandbox/*/*.ts', '!**/node_modules/**/*']) 5 | .pipe(tslint({ 6 | formatter: "verbose" 7 | })) 8 | .pipe(tslint.report())); 9 | -------------------------------------------------------------------------------- /jest.config.base.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: [''], 3 | transform: { 4 | '.*': [ 5 | 'ts-jest', 6 | { 7 | diagnostics: false, 8 | }, 9 | ] 10 | }, 11 | testURL: 'http://localhost', 12 | testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", 13 | moduleFileExtensions: [ 14 | "ts", 15 | "tsx", 16 | "js", 17 | "jsx", 18 | "json", 19 | "node" 20 | ] 21 | }; 22 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('./jest.config.base.js'); 2 | 3 | module.exports = { 4 | ...base, 5 | projects: ['/packages/*/jest.config.js'], 6 | coverageDirectory: '/coverage/', 7 | }; 8 | -------------------------------------------------------------------------------- /jest.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "target": "ES2019", 5 | "emitDecoratorMetadata": false // coverage broke if true 6 | }, 7 | "include": [], 8 | "exclude": ["node_modules","npm"] 9 | } 10 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "independent", 6 | "npmClient": "npm", 7 | "stream": true, 8 | "nohoist": ["vue-loader", "vue-template-compiler", "@vue/test-utils", "typescript"] 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Developer Express Inc.", 3 | "name": "root", 4 | "private": true, 5 | "version": "0.0.0", 6 | "description": "DevExtreme Vue UI and Visualization Components", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/DevExpress/devextreme-vue.git" 10 | }, 11 | "config": { 12 | "devextreme": "./devextreme" 13 | }, 14 | "scripts": { 15 | "link-devextreme": "cross-env-shell npm run build-npm-devextreme --prefix $npm_package_config_devextreme && npm link $npm_package_config_devextreme/packages/devextreme/artifacts/npm/devextreme --legacy-peer-deps --workspace=devextreme-vue --workspace=devextreme-vue2-strategy", 16 | "restore-submodule": "git submodule init && git submodule update && cd devextreme && git add . && cd .. && git submodule update --force --checkout", 17 | "restore-devextreme": "npm run restore-submodule && npm i --prefix ./devextreme", 18 | "pull-devextreme": "npm run restore-submodule -- --remote && npm i --prefix ./devextreme", 19 | "generate-metadata:discover": "cross-env-shell npm run discover-declarations --prefix $npm_package_config_devextreme/packages/devextreme", 20 | "generate-metadata:integration": "cross-env-shell dx-tools integration-data-generator --js-scripts $npm_package_config_devextreme/packages/devextreme/js --artifacts $npm_package_config_devextreme/packages/devextreme/artifacts/internal-tools --output-path ./packages/devextreme-vue/metadata/integration-data.json", 21 | "generate-metadata": "npm run generate-metadata:discover && npm run generate-metadata:integration", 22 | "clean:packages": "npm run clean --yes --ws --if-present && npm run clean:root", 23 | "clean:root": "del-cli node_modules", 24 | "start:packages": "npn run start --ws --if-present", 25 | "build:packages": "npm run build --ws --if-present", 26 | "lint": "gulp lint", 27 | "test": "jest", 28 | "test-watch": "jest --watch", 29 | "pack": "npm run generate-metadata && npm run pack --ws --if-present" 30 | }, 31 | "keywords": [ 32 | "vue", 33 | "devextreme", 34 | "devexpress" 35 | ], 36 | "license": "MIT", 37 | "devDependencies": { 38 | "@types/jest": "^26.0.24", 39 | "cpy-cli": "^3.1.1", 40 | "cross-env": "^7.0.3", 41 | "del-cli": "^3.0.1", 42 | "devextreme": "23.2-next", 43 | "devextreme-internal-tools": "10.0.0-beta.17", 44 | "gulp": "^4.0.2", 45 | "gulp-tslint": "^8.1.4", 46 | "jest": "^26.6.3", 47 | "ts-jest": "^26.5.6", 48 | "tslint": "^5.11.0", 49 | "yocto-queue": "^1.0.0" 50 | }, 51 | "workspaces": [ 52 | "packages/*" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /packages/devextreme-vue-generator/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base.js'); 2 | const pack = require('./package'); 3 | 4 | const packageName = pack.name; 5 | 6 | module.exports = { 7 | ...base, 8 | name: packageName, 9 | displayName: packageName, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/devextreme-vue-generator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Developer Express Inc.", 3 | "name": "devextreme-vue-generator", 4 | "version": "3.0.4", 5 | "description": "DevExtreme Vue UI and Visualization Components", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/DevExpress/devextreme-vue.git" 9 | }, 10 | "main": "dist/generator", 11 | "types": "dist/generator", 12 | "files": [ 13 | "dist" 14 | ], 15 | "scripts": { 16 | "clean": "del-cli ./dist", 17 | "build": "npm run clean && tsc", 18 | "build:local": "npm run clean && tsc --project tsconfig.local.json", 19 | "pack": "npm run build && npm pack", 20 | "postpack": "cpy './*.tgz' dist && del-cli './*.tgz'", 21 | "test": "jest" 22 | }, 23 | "keywords": [ 24 | "vue", 25 | "devextreme", 26 | "devexpress" 27 | ], 28 | "license": "MIT", 29 | "dependencies": { 30 | "dasherize": "^2.0.0", 31 | "dot": "^1.1.3" 32 | }, 33 | "devDependencies": { 34 | "@types/dot": "^1.1.5", 35 | "@types/jest": "^26.0.24", 36 | "cpy-cli": "^4.0.0", 37 | "del-cli": "^3.0.1", 38 | "devextreme-internal-tools": "10.0.0-beta.17", 39 | "jest": "^26.6.3", 40 | "ts-jest": "^26.5.6", 41 | "typescript": "^4.7.4" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/devextreme-vue-generator/src/common-reexports-generator.ts: -------------------------------------------------------------------------------- 1 | import { createTempate } from "./template"; 2 | 3 | const render: (model: { module: string, reexports: string[] }) => string = createTempate(` 4 | export {<#~ it.reexports :reExport #> 5 | <#= reExport #>,<#~#> 6 | } from "devextreme/<#= it.module #>"; 7 | `.trimStart()); 8 | 9 | function generate(module: string, reexports: string[]): string { 10 | const result = render({ module, reexports }); 11 | return result; 12 | } 13 | 14 | export default generate; 15 | -------------------------------------------------------------------------------- /packages/devextreme-vue-generator/src/converter.test.ts: -------------------------------------------------------------------------------- 1 | import { convertTypes } from "./converter"; 2 | 3 | it("deduplicates", () => { 4 | expect(convertTypes([ 5 | { type: "String", isCustomType: false, acceptableValues: [], importPath: "importPath", isImportedType: false }, 6 | { type: "Number", isCustomType: false, acceptableValues: [], importPath: "importPath", isImportedType: false }, 7 | { type: "String", isCustomType: false, acceptableValues: [], importPath: "importPath", isImportedType: false } 8 | ])).toEqual(["String", "Number"]); 9 | }); 10 | 11 | it("returns undefined if finds Any", () => { 12 | expect(convertTypes([{ 13 | type: "Any", 14 | isCustomType: false, 15 | acceptableValues: [], 16 | importPath: "importPath", 17 | isImportedType: false 18 | }])).toBeUndefined(); 19 | expect(convertTypes([ 20 | { 21 | type: "String", 22 | isCustomType: false, 23 | acceptableValues: [], 24 | importPath: "importPath", 25 | isImportedType: false 26 | }, 27 | { 28 | type: "Number", 29 | isCustomType: false, 30 | acceptableValues: [], 31 | importPath: "importPath", 32 | isImportedType: false 33 | }, 34 | { type: "Any", isCustomType: false, acceptableValues: [], importPath: "importPath", isImportedType: false } 35 | ])).toBeUndefined(); 36 | }); 37 | 38 | it("returns Object if finds isCustomType", () => { 39 | expect(convertTypes([{ 40 | type: "CustomType", 41 | isCustomType: true, 42 | acceptableValues: [], 43 | importPath: "importPath", 44 | isImportedType: false 45 | }])).toEqual(["Object"]); 46 | }); 47 | 48 | it("returns undefined if array is empty", () => { 49 | expect(convertTypes([])).toBeUndefined(); 50 | }); 51 | 52 | it("returns undefined if array is undefined", () => { 53 | expect(convertTypes(undefined)).toBeUndefined(); 54 | }); 55 | 56 | it("returns undefined if array is null", () => { 57 | expect(convertTypes(null)).toBeUndefined(); 58 | }); 59 | 60 | it("expands custom types", () => { 61 | expect(convertTypes([{ 62 | type: "CustomType", 63 | isCustomType: true, 64 | acceptableValues: [], 65 | importPath: "importPath", 66 | isImportedType: false 67 | }], { 68 | CustomType: { 69 | name: "CustomType", 70 | module: "module", 71 | types: [ 72 | { 73 | type: "String", 74 | isCustomType: false, 75 | acceptableValues: [], 76 | importPath: "importPath", 77 | isImportedType: false 78 | }, 79 | { 80 | type: "Number", 81 | isCustomType: false, 82 | acceptableValues: [], 83 | importPath: "importPath", 84 | isImportedType: false 85 | } 86 | ], 87 | props: [], 88 | templates: [] 89 | } 90 | })).toEqual(["Object", "String", "Number"]); 91 | }); 92 | -------------------------------------------------------------------------------- /packages/devextreme-vue-generator/src/converter.ts: -------------------------------------------------------------------------------- 1 | import { ICustomType, ITypeDescr } from "devextreme-internal-tools/integration-data-model"; 2 | 3 | function convertTypes( 4 | types: ITypeDescr[] | undefined | null, 5 | customTypes?: Record 6 | ): string[] | undefined { 7 | if (types === undefined || types === null || types.length === 0) { 8 | return; 9 | } 10 | 11 | if (customTypes) { 12 | types = types.concat(expandTypes(types, customTypes)); 13 | } 14 | 15 | const convertedTypes = new Set(types.map(convertType)); 16 | if (convertedTypes.has("Any")) { 17 | return; 18 | } 19 | 20 | return Array.from(convertedTypes); 21 | } 22 | 23 | function expandTypes(types: ITypeDescr[], customTypes: Record): ITypeDescr[] { 24 | const expandedTypes: ITypeDescr[] = []; 25 | types.forEach((t) => { 26 | if (t.isCustomType) { 27 | const aliases = customTypes[t.type].types; 28 | if (aliases) { 29 | expandedTypes.push(...aliases); 30 | } 31 | } 32 | }); 33 | return expandedTypes; 34 | } 35 | function convertType(typeDescr: ITypeDescr): string { 36 | switch (typeDescr.type) { 37 | case "String": 38 | case "Number": 39 | case "Boolean": 40 | case "Array": 41 | case "Object": 42 | case "Function": 43 | return typeDescr.type; 44 | } 45 | 46 | if (typeDescr.isCustomType) { 47 | return "Object"; 48 | } 49 | 50 | return "Any"; 51 | } 52 | 53 | export { convertTypes }; 54 | -------------------------------------------------------------------------------- /packages/devextreme-vue-generator/src/generator.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { IWidget } from "devextreme-internal-tools/integration-data-model"; 3 | import { mapWidget } from "./generator"; 4 | 5 | const simpleWidget: IWidget = { 6 | complexOptions: [], 7 | exportPath: "", 8 | isEditor: false, 9 | isExtension: false, 10 | hasTranscludedContent: true, 11 | name: "dxTestWidget", 12 | nesteds: [], 13 | options: [], 14 | templates: [], 15 | optionsTypeParams: [], 16 | reexports: [] 17 | }; 18 | 19 | describe("mapWidget", () => { 20 | it("ignores 'key' option in props", () => { 21 | const result = mapWidget( 22 | { 23 | ...simpleWidget, 24 | options: [ 25 | { 26 | firedEvents: [], 27 | name: "key", 28 | types: [ 29 | { type: "Object" } as any, 30 | { type: "String" } as any 31 | ], 32 | isSubscribable: false, 33 | props: [], 34 | isDeprecated: false, 35 | } 36 | ] 37 | }, 38 | "baseComponentPath", 39 | "configComponentPath", 40 | [] 41 | ); 42 | expect(result.component.props).toEqual([]); 43 | }); 44 | 45 | it("does not ignore 'key' in nested components", () => { 46 | const result = mapWidget( 47 | { 48 | ...simpleWidget, 49 | complexOptions: [ 50 | { 51 | name: "key", 52 | optionName: "key", 53 | owners: [simpleWidget.name], 54 | props: [], 55 | isCollectionItem: false, 56 | nesteds: [], 57 | predefinedProps: [], 58 | templates: [] 59 | }, 60 | ] 61 | }, 62 | "baseComponentPath", 63 | "configComponentPath", 64 | [] 65 | ); 66 | expect(result.component.nestedComponents).toEqual( 67 | [{ name: "DxKey", optionName: "key", props: [], predefinedProps: [], isCollectionItem: false }] 68 | ); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /packages/devextreme-vue-generator/src/helpers.ts: -------------------------------------------------------------------------------- 1 | import dasherize from "dasherize"; 2 | import { extname as getPathExtension } from "path"; 3 | 4 | export function removeExtension(path: string) { 5 | return path.slice(0, - getPathExtension(path).length); 6 | } 7 | 8 | export function removePrefix(value: string, prefix: string): string { 9 | return new RegExp(`^${prefix}`, "i").test(value) ? value.substring(prefix.length) : value; 10 | } 11 | 12 | export function toKebabCase(value: string): string { 13 | return dasherize(value); 14 | } 15 | 16 | export function uppercaseFirst(value: string): string { 17 | return value[0].toUpperCase() + value.substr(1); 18 | } 19 | 20 | export function lowercaseFirst(value: string): string { 21 | return value[0].toLowerCase() + value.substr(1); 22 | } 23 | 24 | export function compareStrings(a: string, b: string) { 25 | return a.localeCompare(b, undefined, { caseFirst: "upper" }); 26 | } 27 | -------------------------------------------------------------------------------- /packages/devextreme-vue-generator/src/index-generator.test.ts: -------------------------------------------------------------------------------- 1 | import generate from "./index-generator"; 2 | 3 | it("generates", () => { 4 | expect( 5 | generate([ 6 | { name: "widget", path: "./path" }, 7 | { name: "anotherWidget", path: "./another/path" }, 8 | ]) 9 | ).toBe(EXPECTED_GENERATES); 10 | }); 11 | //#region EXPECTED_GENERATES 12 | const EXPECTED_GENERATES = ` 13 | export { widget } from "./path"; 14 | export { anotherWidget } from "./another/path"; 15 | `.trimLeft(); 16 | -------------------------------------------------------------------------------- /packages/devextreme-vue-generator/src/index-generator.ts: -------------------------------------------------------------------------------- 1 | import { createTempate } from "./template"; 2 | 3 | interface IReExport { 4 | name: string; 5 | path: string; 6 | } 7 | 8 | function generate(paths: IReExport[]): string { 9 | return render(paths); 10 | } 11 | 12 | const render: (model: IReExport[]) => string = createTempate(` 13 | <#~ it :reExport #>export { <#= reExport.name #> } from "<#= reExport.path #>"; 14 | <#~#> 15 | `.trim()); 16 | 17 | export default generate; 18 | export { 19 | IReExport 20 | }; 21 | -------------------------------------------------------------------------------- /packages/devextreme-vue-generator/src/template.ts: -------------------------------------------------------------------------------- 1 | import { template, templateSettings } from "dot"; 2 | 3 | const defaultSettings = { 4 | ...templateSettings, 5 | conditional: /\<#\?(\?)?\s*([\s\S]*?)\s*#\>/g, 6 | define: /\<###\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)##\>/g, 7 | encode: /\<#!([\s\S]+?)#\>/g, 8 | evaluate: /\<#([\s\S]+?)#\>/g, 9 | interpolate: /\<#=([\s\S]+?)#\>/g, 10 | iterate: /\<#~\s*(?:#\>|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*#\>)/g, 11 | use: /\<##([\s\S]+?)#\>/g, 12 | strip: false, 13 | varname: "it" 14 | }; 15 | 16 | const createTempate = (templateStr: string): ((model: any) => string) => { 17 | const templateFunc = template(templateStr, defaultSettings); 18 | 19 | return (model: any) => (templateFunc(model) as string) 20 | .replace(/[\s\S]{1}\x08{1}|[\s\S]{2}\x08{2}|[\s\S]{3}\x08{3}/g, "") 21 | .replace(/\x08/, ""); 22 | }; 23 | 24 | const L0: string = `\n`; 25 | const L1: string = `\n` + tab(1); 26 | const L2: string = `\n` + tab(2); 27 | const L3: string = `\n` + tab(3); 28 | const L4: string = `\n` + tab(4); 29 | 30 | const TAB1: string = tab(1); 31 | const TAB2: string = tab(2); 32 | const TAB3: string = tab(3); 33 | const TAB4: string = tab(4); 34 | 35 | function tab(i: number): string { 36 | return Array(i * 2 + 1).join(" "); 37 | } 38 | 39 | export { 40 | createTempate, 41 | L0, 42 | L1, 43 | L2, 44 | L3, 45 | L4, 46 | TAB1, 47 | TAB2, 48 | TAB3, 49 | TAB4 50 | }; 51 | -------------------------------------------------------------------------------- /packages/devextreme-vue-generator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "outDir": "./dist" 6 | }, 7 | 8 | "include": [ 9 | "src/**/*" 10 | ], 11 | "exclude": [ 12 | "src/**/*.test.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/devextreme-vue-generator/tsconfig.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist", 4 | "baseUrl": "./packages", 5 | "paths": { 6 | "devextreme-vue-generator": [ 7 | "devextreme-vue-generator/src" 8 | ], 9 | "devextreme-vue": [ 10 | "devextreme-vue/src" 11 | ], 12 | "devextreme-vue/*": [ 13 | "devextreme-vue/src/*" 14 | ], 15 | "devextreme-vue2-strategy": [ 16 | "vue2-strategy/src/core" 17 | ] 18 | }, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "target": "es5", 23 | "module": "commonjs", 24 | "noEmitOnError": true, 25 | "declaration": true, 26 | "experimentalDecorators": true, 27 | "forceConsistentCasingInFileNames": true, 28 | "noFallthroughCasesInSwitch": true, 29 | "noImplicitAny": false, 30 | "noImplicitReturns": true, 31 | "noImplicitThis": true, 32 | "noUnusedLocals": true, 33 | "noUnusedParameters": true, 34 | "strictNullChecks": true, 35 | "lib": [ 36 | "esnext", 37 | "dom" 38 | ] 39 | }, 40 | "include": [ 41 | "src/**/*" 42 | ], 43 | "exclude": [ 44 | "src/**/*.test.ts", 45 | "node_modules", 46 | "dist" 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /packages/devextreme-vue-generator/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "linterOptions": { 4 | "exclude": [ 5 | "dist" 6 | ] 7 | } 8 | } -------------------------------------------------------------------------------- /packages/devextreme-vue/.gitignore: -------------------------------------------------------------------------------- 1 | npm/ 2 | -------------------------------------------------------------------------------- /packages/devextreme-vue/build.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | src: './src/**/*.ts', 4 | ignoredGlobs: ['!./src/**/*.test.ts', '!./src/**/__mocks__/*'], 5 | npm: { 6 | dist: './npm/', 7 | strategySrc: '../vue2-strategy/npm/*', 8 | strategyDist: './npm/core/strategy/vue2', 9 | package: 'package.json', 10 | license: '../../LICENSE', 11 | readme: '../../README.md' 12 | }, 13 | metadataPath: './metadata/integration-data.json', 14 | generatedComponentsDir: './src', 15 | coreComponentsDir: './src/core', 16 | indexFileName: './src/index.ts', 17 | baseComponent: './core/index', 18 | configComponent: './core/index', 19 | widgetsPackage: 'devextreme' 20 | }; 21 | -------------------------------------------------------------------------------- /packages/devextreme-vue/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base.js'); 2 | const pack = require('./package'); 3 | 4 | const packageName = pack.name; 5 | 6 | module.exports = { 7 | ...base, 8 | name: packageName, 9 | displayName: packageName, 10 | moduleNameMapper: { 11 | "^vue$": "vue/dist/vue.cjs", 12 | "^@/(.*)$": "/src/$1" 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /packages/devextreme-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devextreme-vue", 3 | "version": "23.2.0", 4 | "description": "DevExtreme Vue UI and Visualization Components", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/DevExpress/devextreme-vue.git" 8 | }, 9 | "main": "index.js", 10 | "types": "index.d.ts", 11 | "scripts": { 12 | "clean": "gulp clean", 13 | "build": "npm run clean && gulp generate", 14 | "pack": "gulp npm.pack", 15 | "test": "jest" 16 | }, 17 | "keywords": [ 18 | "vue", 19 | "devextreme", 20 | "devexpress" 21 | ], 22 | "author": "Developer Express Inc.", 23 | "license": "MIT", 24 | "peerDependencies": { 25 | "devextreme": "23.2-next", 26 | "vue": "^2.5.16 || ^3.0.0" 27 | }, 28 | "devDependencies": { 29 | "@types/jest": "^26.0.24", 30 | "@vue/compiler-sfc": "^3.0.0", 31 | "@vue/test-utils": "2.0.0-beta.7", 32 | "del": "^3.0.0", 33 | "devextreme-vue-generator": "^3.0.3", 34 | "devextreme-vue2-strategy": "~23.2.0", 35 | "gulp": "^4.0.2", 36 | "gulp-header": "^2.0.9", 37 | "gulp-shell": "^0.6.5", 38 | "gulp-typescript": "^5.0.1", 39 | "jest": "^26.6.3", 40 | "ts-jest": "^26.5.6", 41 | "typescript": "^4.7.4", 42 | "vue": "3.0.0", 43 | "vue-router": "^4.0.16", 44 | "vue-template-compiler": "^2.6.3" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/button.ts: -------------------------------------------------------------------------------- 1 | import Button, { Properties } from "devextreme/ui/button"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 30 | 31 | interface DxButton extends AccessibleOptions { 32 | readonly instance?: Button; 33 | } 34 | const DxButton = createComponent({ 35 | props: { 36 | accessKey: String, 37 | activeStateEnabled: Boolean, 38 | disabled: Boolean, 39 | elementAttr: Object, 40 | focusStateEnabled: Boolean, 41 | height: [Function, Number, String], 42 | hint: String, 43 | hoverStateEnabled: Boolean, 44 | icon: String, 45 | onClick: Function, 46 | onContentReady: Function, 47 | onDisposing: Function, 48 | onInitialized: Function, 49 | onOptionChanged: Function, 50 | rtlEnabled: Boolean, 51 | stylingMode: String, 52 | tabIndex: Number, 53 | template: {}, 54 | text: String, 55 | type: String, 56 | useSubmitBehavior: Boolean, 57 | validationGroup: String, 58 | visible: Boolean, 59 | width: [Function, Number, String] 60 | }, 61 | emits: { 62 | "update:isActive": null, 63 | "update:hoveredElement": null, 64 | "update:accessKey": null, 65 | "update:activeStateEnabled": null, 66 | "update:disabled": null, 67 | "update:elementAttr": null, 68 | "update:focusStateEnabled": null, 69 | "update:height": null, 70 | "update:hint": null, 71 | "update:hoverStateEnabled": null, 72 | "update:icon": null, 73 | "update:onClick": null, 74 | "update:onContentReady": null, 75 | "update:onDisposing": null, 76 | "update:onInitialized": null, 77 | "update:onOptionChanged": null, 78 | "update:rtlEnabled": null, 79 | "update:stylingMode": null, 80 | "update:tabIndex": null, 81 | "update:template": null, 82 | "update:text": null, 83 | "update:type": null, 84 | "update:useSubmitBehavior": null, 85 | "update:validationGroup": null, 86 | "update:visible": null, 87 | "update:width": null, 88 | }, 89 | computed: { 90 | instance(): Button { 91 | return (this as any).$_instance; 92 | } 93 | }, 94 | beforeCreate() { 95 | (this as any).$_WidgetClass = Button; 96 | (this as any).$_hasAsyncTemplate = true; 97 | } 98 | }); 99 | 100 | export default DxButton; 101 | export { 102 | DxButton 103 | }; 104 | import type * as DxButtonTypes from "devextreme/ui/button_types"; 105 | export { DxButtonTypes }; 106 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/common/charts.ts: -------------------------------------------------------------------------------- 1 | export { 2 | AnimationEaseMode, 3 | AnnotationType, 4 | ArgumentAxisHoverMode, 5 | AxisScaleType, 6 | ChartsAxisLabelOverlap, 7 | ChartsColor, 8 | ChartsDataType, 9 | ChartsLabelOverlap, 10 | DashStyle, 11 | DiscreteAxisDivisionMode, 12 | GradientColor, 13 | HatchDirection, 14 | LabelOverlap, 15 | LabelPosition, 16 | LegendHoverMode, 17 | LegendMarkerState, 18 | Palette, 19 | PaletteColorSet, 20 | PaletteExtensionMode, 21 | PointInteractionMode, 22 | PointSymbol, 23 | registerGradient, 24 | registerPattern, 25 | RelativePosition, 26 | ScaleBreak, 27 | ScaleBreakLineStyle, 28 | SeriesHoverMode, 29 | SeriesSelectionMode, 30 | SeriesType, 31 | ShiftLabelOverlap, 32 | TextOverflow, 33 | Theme, 34 | TimeInterval, 35 | TimeIntervalConfig, 36 | ValueErrorBarDisplayMode, 37 | ValueErrorBarType, 38 | VisualRange, 39 | VisualRangeUpdateMode, 40 | WordWrap, 41 | ZoomPanAction, 42 | } from "devextreme/common/charts"; 43 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/common/data/custom-store.ts: -------------------------------------------------------------------------------- 1 | export { 2 | GroupItem, 3 | isGroupItemsArray, 4 | isItemsArray, 5 | isLoadResultObject, 6 | LoadResult, 7 | LoadResultObject, 8 | } from "devextreme/common/data/custom-store"; 9 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/common/grids.ts: -------------------------------------------------------------------------------- 1 | export { 2 | AdaptiveDetailRowPreparingInfo, 3 | ApplyChangesMode, 4 | ApplyFilterMode, 5 | ColumnBase, 6 | ColumnButtonBase, 7 | ColumnChooser, 8 | ColumnChooserMode, 9 | ColumnChooserSearchConfig, 10 | ColumnChooserSelectionConfig, 11 | ColumnCustomizeTextArg, 12 | ColumnFixing, 13 | ColumnFixingTexts, 14 | ColumnHeaderFilter, 15 | ColumnHeaderFilterSearchConfig, 16 | ColumnLookup, 17 | ColumnResizeMode, 18 | DataChange, 19 | DataChangeInfo, 20 | DataChangeType, 21 | DataErrorOccurredInfo, 22 | DataRenderMode, 23 | EditingBase, 24 | EditingTextsBase, 25 | EnterKeyAction, 26 | EnterKeyDirection, 27 | FilterOperation, 28 | FilterPanel, 29 | FilterPanelTexts, 30 | FilterRow, 31 | FilterRowOperationDescriptions, 32 | FilterType, 33 | GridBase, 34 | GridBaseOptions, 35 | GridsEditMode, 36 | GridsEditRefreshMode, 37 | GroupExpandMode, 38 | HeaderFilter, 39 | HeaderFilterGroupInterval, 40 | HeaderFilterSearchConfig, 41 | HeaderFilterTexts, 42 | KeyboardNavigation, 43 | KeyDownInfo, 44 | LoadPanel, 45 | NewRowInfo, 46 | NewRowPosition, 47 | Pager, 48 | PagerDisplayMode, 49 | PagerPageSize, 50 | PagingBase, 51 | RowDragging, 52 | RowDraggingTemplateData, 53 | RowInsertedInfo, 54 | RowInsertingInfo, 55 | RowKeyInfo, 56 | RowRemovedInfo, 57 | RowRemovingInfo, 58 | RowUpdatedInfo, 59 | RowUpdatingInfo, 60 | RowValidatingInfo, 61 | SavingInfo, 62 | ScrollingBase, 63 | SearchPanel, 64 | SelectedFilterOperation, 65 | SelectionBase, 66 | SelectionChangedInfo, 67 | SelectionColumnDisplayMode, 68 | Sorting, 69 | StartEditAction, 70 | StateStoreType, 71 | StateStoring, 72 | SummaryType, 73 | ToolbarPreparingInfo, 74 | } from "devextreme/common/grids"; 75 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/common/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | ApplyValueMode, 3 | AsyncRule, 4 | ButtonStyle, 5 | ButtonType, 6 | CompareRule, 7 | ComparisonOperator, 8 | CustomRule, 9 | DataStructure, 10 | DataType, 11 | Direction, 12 | DragDirection, 13 | Draggable, 14 | DragHighlight, 15 | EditorStyle, 16 | EmailRule, 17 | ExportFormat, 18 | FieldChooserLayout, 19 | FirstDayOfWeek, 20 | Format, 21 | GlobalConfig, 22 | HorizontalAlignment, 23 | HorizontalEdge, 24 | LabelMode, 25 | MaskMode, 26 | Mode, 27 | NumericRule, 28 | Orientation, 29 | PageLoadMode, 30 | PageOrientation, 31 | PatternRule, 32 | Position, 33 | PositionAlignment, 34 | RangeRule, 35 | RequiredRule, 36 | Scrollable, 37 | ScrollbarMode, 38 | ScrollDirection, 39 | ScrollMode, 40 | SearchMode, 41 | SelectAllMode, 42 | SimplifiedSearchMode, 43 | SingleMultipleAllOrNone, 44 | SingleMultipleOrNone, 45 | SingleOrMultiple, 46 | SingleOrNone, 47 | SliderValueChangeMode, 48 | Sortable, 49 | SortOrder, 50 | StoreType, 51 | StringLengthRule, 52 | SubmenuShowMode, 53 | TextBoxPredefinedButton, 54 | TextEditorButton, 55 | TextEditorButtonLocation, 56 | ToolbarItemComponent, 57 | ToolbarItemLocation, 58 | TooltipShowMode, 59 | ValidationCallbackData, 60 | ValidationMessageMode, 61 | ValidationRule, 62 | ValidationRuleType, 63 | ValidationStatus, 64 | VerticalAlignment, 65 | VerticalEdge, 66 | } from "devextreme/common"; 67 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/__mocks__/templates-discovering.ts: -------------------------------------------------------------------------------- 1 | export const discover = jest.fn(() => ({})); 2 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/__tests__/button.test.ts: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import { defineComponent } from "vue"; 3 | import { createRouter, createWebHistory } from "vue-router"; 4 | 5 | import DxButton from "../../button"; 6 | 7 | jest.setTimeout(1000); 8 | beforeEach(() => { 9 | jest.clearAllMocks(); 10 | }); 11 | 12 | describe("template rendering", () => { 13 | 14 | it("should render a template with child router-view", async (done) => { 15 | const appView = defineComponent({ 16 | template: 17 | ` 18 | 21 | `, 22 | components: { 23 | DxButton 24 | } 25 | }); 26 | const rootView = defineComponent({ 27 | template: ` 28 | 29 | ` 30 | }); 31 | 32 | const testView = defineComponent({ 33 | template: ` 34 |
text
35 | ` 36 | }); 37 | 38 | const router = createRouter({ 39 | routes: [ 40 | { 41 | name: "rootview", 42 | path: "/", 43 | component: appView, 44 | redirect: "/test", 45 | children: [ 46 | { 47 | name: "testview", 48 | path: "/test", 49 | components: { test: testView } 50 | } 51 | ] 52 | }, 53 | { 54 | path: "/:pathMatch(.*)*", 55 | redirect: "/" 56 | } 57 | ], 58 | history: createWebHistory() 59 | }); 60 | router.push("/"); 61 | await router.isReady(); 62 | const wrapper = mount(rootView, { 63 | global: { 64 | plugins: [router] 65 | } 66 | }); 67 | 68 | expect(wrapper.vm.$el.getElementsByClassName("test")).toHaveLength(1); 69 | done(); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/__tests__/data-grid.test.ts: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import { defineComponent, nextTick } from "vue"; 3 | 4 | import { DxColumn, DxDataGrid } from "../../data-grid"; 5 | 6 | jest.setTimeout(1000); 7 | beforeEach(() => { 8 | jest.clearAllMocks(); 9 | }); 10 | 11 | describe("data grid", () => { 12 | 13 | it("vmodel should work correctly for nested components", async (done) => { 14 | const vm = defineComponent({ 15 | template: 16 | ` 21 | 25 | 29 | `, 30 | components: { 31 | DxDataGrid, 32 | DxColumn 33 | }, 34 | data() { 35 | return { 36 | visible1: true, 37 | visible2: true 38 | }; 39 | }, 40 | props: { 41 | data: { 42 | type: Object, 43 | default: [ 44 | { prop: "test1", prop2: "test1"}, 45 | { prop: "test2", prop2: "test2" } 46 | ] 47 | } 48 | } 49 | }); 50 | 51 | const wrapper = mount(vm); 52 | const instance = (wrapper.getComponent("#grid").vm as any).$_instance; 53 | instance.option("columns[0].visible", false); 54 | 55 | nextTick(() => { 56 | instance.option("columns[1].visible", false); 57 | nextTick(() => { 58 | expect(wrapper.vm.visible1).toBe(false); 59 | expect(wrapper.vm.visible2).toBe(false); 60 | done(); 61 | }); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/__tests__/form.test.ts: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import { defineComponent, nextTick } from "vue"; 3 | 4 | import { DxForm, DxItem } from "../../form"; 5 | 6 | jest.setTimeout(1000); 7 | beforeEach(() => { 8 | jest.clearAllMocks(); 9 | }); 10 | 11 | describe("form", () => { 12 | 13 | it("should render config components by condition", async (done) => { 14 | const vm = defineComponent({ 15 | template: 16 | ` 20 | 24 | 27 | `, 28 | components: { 29 | DxItem, DxForm 30 | }, 31 | props: { 32 | show: { 33 | type: Boolean, 34 | default: true 35 | }, 36 | data: { 37 | type: Object, 38 | default: { 39 | FirstName: "name1", 40 | Position: "name2" 41 | } 42 | } 43 | } 44 | }); 45 | 46 | const wrapper = mount(vm); 47 | 48 | wrapper.setProps({ show: false }); 49 | 50 | nextTick(() => { 51 | wrapper.setProps({ show: true }); 52 | nextTick(() => { 53 | expect(wrapper.getComponent("#form").vm.$el 54 | .getElementsByClassName("dx-field-item-label-text")).toHaveLength(2); 55 | done(); 56 | }); 57 | }); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/__tests__/selectbox.test.ts: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import { defineComponent } from "vue"; 3 | 4 | import DxSelectBox from "../../select-box"; 5 | import DxTextBox from "../../text-box"; 6 | 7 | jest.setTimeout(1000); 8 | beforeEach(() => { 9 | jest.clearAllMocks(); 10 | }); 11 | 12 | describe("template rendering", () => { 13 | 14 | it("field template rendered", () => { 15 | const vm = defineComponent({ 16 | template: 17 | ` 18 | 21 | `, 22 | data() { 23 | return { 24 | dataSource: [{ ID: 1 }] 25 | }; 26 | }, 27 | components: { 28 | DxSelectBox, 29 | DxTextBox 30 | } 31 | }); 32 | const wrapper = mount(vm); 33 | expect(wrapper.getComponent("#component").vm.$el.children).toHaveLength(1); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/__tests__/templates-discovering.test.ts: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import { defineComponent } from "vue"; 3 | import { discover } from "../templates-discovering"; 4 | 5 | describe("templates-discovering (vue 3)", () => { 6 | 7 | it("discovers named scoped slot", () => { 8 | const template = "