├── .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 | [](https://app.shippable.com/github/DevExpress/devextreme-vue) 4 | [](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 | 19 | 20 | 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 | 19 | 20 | 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 = ""; 9 | expect(getDiscoveredTemplates(template)).toEqual(["slot_name"]); 10 | }); 11 | 12 | it("discovers named not-scoped slot", () => { 13 | const template = ""; 14 | expect(getDiscoveredTemplates(template)).toEqual(["slot_name"]); 15 | }); 16 | 17 | // Vue doesn't recognize this as slot 18 | it("doesn't discover not-scoped not-named slot", () => { 19 | const template = ""; 20 | expect(getDiscoveredTemplates(template)).toEqual([]); 21 | }); 22 | 23 | // to avoid creating templates from config-components 24 | it("doesn't discover implicit default slot", () => { 25 | const template = "abc"; 26 | expect(getDiscoveredTemplates(template)).toEqual([]); 27 | }); 28 | 29 | // to avoid creating templates from config-components 30 | it("doesn't discover custom component", () => { 31 | const template = ""; 32 | expect(getDiscoveredTemplates(template)).toEqual([]); 33 | }); 34 | }); 35 | 36 | function getDiscoveredTemplates(template: string): string[] { 37 | let actual; 38 | const vm = defineComponent({ 39 | template: `${template}`, 40 | components: { 41 | container: { 42 | render() { 43 | actual = discover(this); 44 | return null; 45 | } 46 | }, 47 | customComponent: { 48 | render() { 49 | return null; 50 | } 51 | } 52 | } 53 | }); 54 | 55 | mount(vm); 56 | return Object.keys(actual); 57 | } 58 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/__tests__/textbox.test.ts: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import { defineComponent, nextTick } from "vue"; 3 | 4 | import { IConfigurable } from "../configuration-component"; 5 | 6 | import DxTextBox from "../../text-box"; 7 | 8 | jest.setTimeout(1000); 9 | beforeEach(() => { 10 | jest.clearAllMocks(); 11 | }); 12 | 13 | describe("two-way binding", () => { 14 | 15 | it("v-model works correctly", async (done) => { 16 | const vm = defineComponent({ 17 | template: 18 | ` 19 | 20 | `, 21 | components: { 22 | DxTextBox 23 | }, 24 | data() { 25 | return { 26 | testValue: "value" 27 | }; 28 | } 29 | }); 30 | const wrapper = mount(vm); 31 | const component = wrapper.getComponent("#component2").vm as any as IConfigurable; 32 | component.$_config.updateValue = jest.fn(); 33 | wrapper.getComponent("#component1").vm.$emit("update:modelValue", "newValue"); 34 | nextTick(() => { 35 | expect(component.$_config.updateValue).toBeCalled(); 36 | done(); 37 | }); 38 | }); 39 | 40 | it("v-model with argument works correctly", async (done) => { 41 | const vm = defineComponent({ 42 | template: 43 | ` 44 | 45 | `, 46 | components: { 47 | DxTextBox 48 | }, 49 | props: { 50 | testValue: String 51 | } 52 | }); 53 | const wrapper = mount(vm); 54 | const component = wrapper.getComponent("#component2").vm as any as IConfigurable; 55 | component.$_config.updateValue = jest.fn(); 56 | wrapper.setProps({ testValue: "test" }); 57 | nextTick(() => { 58 | expect(component.$_config.updateValue).toBeCalled(); 59 | done(); 60 | }); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/__tests__/toolbar.test.ts: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import { defineComponent } from "vue"; 3 | 4 | import DxButton from "../../button"; 5 | import DxToolbar, { DxItem } from "../../toolbar"; 6 | 7 | jest.setTimeout(1000); 8 | beforeEach(() => { 9 | jest.clearAllMocks(); 10 | }); 11 | 12 | describe("template rendering", () => { 13 | 14 | it("should render a default template only for a configuration component where it is declared", () => { 15 | const vm = defineComponent({ 16 | template: 17 | ` 18 | 19 | 20 | 21 | 22 | 23 | 24 | `, 25 | components: { 26 | DxToolbar, 27 | DxItem, 28 | DxButton 29 | } 30 | }); 31 | const wrapper = mount(vm); 32 | expect(wrapper.vm.$el.getElementsByClassName("dx-button")).toHaveLength(1); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/__tests__/vue-helper.ts: -------------------------------------------------------------------------------- 1 | import { setCompatOptions } from "../vue-helper"; 2 | 3 | describe("setCompatOptions", () => { 4 | 5 | it("set mode", () => { 6 | const component = { 7 | compatConfig: {} 8 | }; 9 | setCompatOptions(component); 10 | expect(component.compatConfig).toStrictEqual({ MODE: 3 }); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/children-processing.ts: -------------------------------------------------------------------------------- 1 | import { PatchFlags } from "@vue/shared"; 2 | import { VNode } from "vue"; 3 | import Configuration from "./configuration"; 4 | import { IConfigurable, IConfigurationComponent } from "./configuration-component"; 5 | import { configurationChildren, getComponentInfo, getNormalizedProps } from "./vue-helper"; 6 | 7 | function pullAllChildren(directChildren: VNode[], allChildren: VNode[], config: Configuration): void { 8 | if (!directChildren || directChildren.length === 0) { return; } 9 | 10 | pullConfigComponents(directChildren, allChildren, config); 11 | } 12 | 13 | export function isFragment(node: any): boolean { 14 | const patchFlag = node.patchFlag; 15 | return patchFlag === PatchFlags.KEYED_FRAGMENT 16 | || patchFlag === PatchFlags.UNKEYED_FRAGMENT 17 | || patchFlag === PatchFlags.STABLE_FRAGMENT 18 | || patchFlag === PatchFlags.BAIL; 19 | } 20 | 21 | export function pullConfigComponents(children: VNode[], nodes: VNode[], ownerConfig: Configuration): void { 22 | children.forEach((node) => { 23 | if (isFragment(node) && Array.isArray(node.children)) { 24 | pullConfigComponents(node.children as any as VNode[], nodes, ownerConfig); 25 | } 26 | if (!isFragment(node)) { 27 | nodes.push(node); 28 | } 29 | if (!node) { return; } 30 | 31 | const componentInfo = getComponentInfo(node) as any as IConfigurationComponent; 32 | if (!componentInfo || !componentInfo.$_optionName) { return; } 33 | 34 | const componentChildren = configurationChildren(node); 35 | const initialValues = { 36 | ...componentInfo.$_predefinedProps, 37 | ...getNormalizedProps(node.props || {}) 38 | }; 39 | 40 | const config = ownerConfig.createNested( 41 | componentInfo.$_optionName, 42 | initialValues, 43 | componentInfo.$_isCollectionItem, 44 | componentInfo.$_expectedChildren 45 | ); 46 | 47 | (node as any as IConfigurable).$_config = config; 48 | (node as any as IConfigurable).$_innerChanges = {}; 49 | 50 | if (componentChildren) { 51 | pullConfigComponents(componentChildren as VNode[], nodes, config); 52 | } 53 | }); 54 | } 55 | 56 | export { 57 | pullAllChildren 58 | }; 59 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/config.ts: -------------------------------------------------------------------------------- 1 | interface IOptions { 2 | deepWatch: boolean; 3 | } 4 | 5 | let config: IOptions = { 6 | deepWatch: false 7 | }; 8 | 9 | function setOptions(options: Partial): void { 10 | config = { ...config, ...options }; 11 | } 12 | 13 | function getOption(optionName: TName): IOptions[TName] { 14 | return config[optionName]; 15 | } 16 | 17 | export default setOptions; 18 | export { getOption }; 19 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/constants.ts: -------------------------------------------------------------------------------- 1 | const DX_TEMPLATE_WRAPPER_CLASS = "dx-template-wrapper"; 2 | const DX_REMOVE_EVENT = "dxremove"; 3 | 4 | export { 5 | DX_TEMPLATE_WRAPPER_CLASS, 6 | DX_REMOVE_EVENT 7 | }; 8 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/extension-component.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent } from "vue"; 2 | import { IBaseComponent, initBaseComponent } from "./component"; 3 | import { getNodeOptions } from "./vue-helper"; 4 | 5 | interface IExtension { 6 | $_isExtension: boolean; 7 | $_attachTo(element: any); 8 | } 9 | 10 | interface IExtensionComponentNode { 11 | $_hasOwner: boolean; 12 | } 13 | 14 | function initDxExtensionComponent() { 15 | return defineComponent({ 16 | extends: initBaseComponent(), 17 | mounted() { 18 | this.$el.setAttribute("isExtension", "true"); 19 | const nodeOptions = getNodeOptions(this); 20 | (nodeOptions as any as IExtension).$_isExtension = true; 21 | (nodeOptions as any as IExtension).$_attachTo = this.attachTo.bind(this); 22 | if (nodeOptions && (nodeOptions as any as IExtensionComponentNode).$_hasOwner) { return; } 23 | 24 | this.attachTo(this.$el); 25 | }, 26 | 27 | methods: { 28 | attachTo(element: any) { 29 | (this as any as IBaseComponent).$_createWidget(element); 30 | } 31 | } 32 | }); 33 | } 34 | 35 | export { 36 | initDxExtensionComponent, 37 | IExtension, 38 | IExtensionComponentNode 39 | }; 40 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/helpers.ts: -------------------------------------------------------------------------------- 1 | export function getTemplatePropName(props: Record | null, templateName: string): string { 2 | for (const propName in props) { 3 | if (props[propName] === templateName) { 4 | return propName; 5 | } 6 | } 7 | 8 | return templateName; 9 | } 10 | 11 | export function uppercaseFirst(value: string): string { 12 | return value[0].toUpperCase() + value.substr(1); 13 | } 14 | 15 | export function lowercaseFirst(value: string): string { 16 | return value[0].toLowerCase() + value.substr(1); 17 | } 18 | 19 | export function camelize(value: string): string { 20 | return lowercaseFirst(value.split("-").map((v) => uppercaseFirst(v)).join("")); 21 | } 22 | 23 | export function toComparable(value: any): any { 24 | return value instanceof Date ? value.getTime() : value; 25 | } 26 | 27 | export function isEqual(value1, value2) { 28 | if (toComparable(value1) === toComparable(value2)) { 29 | return true; 30 | } 31 | 32 | if (Array.isArray(value1) && Array.isArray(value2)) { 33 | return value1.length === 0 && value2.length === 0; 34 | } 35 | 36 | return false; 37 | } 38 | 39 | export function forEachChildNode( 40 | el: Node, 41 | callback: (child: ReturnType) => void 42 | ) { 43 | Array.prototype.slice.call(el.childNodes).forEach(callback); 44 | } 45 | 46 | export function allKeysAreEqual(obj1: object, obj2: object) { 47 | const obj1Keys = Object.keys(obj1); 48 | 49 | if (obj1Keys.length !== Object.keys(obj2).length) { 50 | return false; 51 | } 52 | 53 | for (const key of obj1Keys) { 54 | if (!obj2.hasOwnProperty(key)) { 55 | return false; 56 | } 57 | } 58 | 59 | return true; 60 | } 61 | 62 | export function getOptionValue(options, optionPath) { 63 | let value = options; 64 | 65 | optionPath.split(".").forEach((p) => { 66 | const optionInfo = getOptionInfo(p); 67 | if (value) { 68 | value = optionInfo.isCollection ? 69 | value[optionInfo.name] && value[optionInfo.name][optionInfo.index] : 70 | value[optionInfo.name]; 71 | } 72 | }); 73 | 74 | return value; 75 | } 76 | 77 | export function getOptionInfo(name: string): IOptionInfo | ICollectionOptionInfo { 78 | const parts = name.split("["); 79 | 80 | if (parts.length === 1) { 81 | return { 82 | isCollection: false, 83 | name, 84 | fullName: name 85 | }; 86 | } 87 | 88 | return { 89 | isCollection: true, 90 | name: parts[0], 91 | fullName: name, 92 | index: Number(parts[1].slice(0, -1)) 93 | }; 94 | } 95 | 96 | interface IOptionInfo { 97 | isCollection: false; 98 | name: string; 99 | fullName: string; 100 | } 101 | 102 | interface ICollectionOptionInfo { 103 | isCollection: true; 104 | name: string; 105 | fullName: string; 106 | index: number; 107 | } 108 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/index.ts: -------------------------------------------------------------------------------- 1 | import * as vue2Stategy from "./strategy/vue2/index"; 2 | import * as vue3Stategy from "./strategy/vue3/index"; 3 | import { isVue3 } from "./version"; 4 | 5 | const strategy = isVue3() ? vue3Stategy : vue2Stategy; 6 | 7 | export const createComponent = strategy.createComponent; 8 | 9 | export const createConfigurationComponent = strategy.createConfigurationComponent; 10 | 11 | export const createExtensionComponent = strategy.createExtensionComponent; 12 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/strategy/vue2/index.ts: -------------------------------------------------------------------------------- 1 | export function createComponent(): any { 2 | return; 3 | } 4 | 5 | export function createConfigurationComponent(): any { 6 | return; 7 | } 8 | 9 | export function createExtensionComponent(): any { 10 | return; 11 | } 12 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/strategy/vue3/index.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent, DefineComponent } from "vue"; 2 | import { initDxComponent } from "../../component"; 3 | import { initDxConfiguration } from "../../configuration-component"; 4 | import { initDxExtensionComponent } from "../../extension-component"; 5 | import { setCompatOptions, setVModel } from "../../vue-helper"; 6 | 7 | export function createComponent(config: any): DefineComponent { 8 | config.extends = initDxComponent(); 9 | setCompatOptions(config); 10 | if (config.model) { 11 | setVModel(config); 12 | } 13 | return defineComponent(config); 14 | } 15 | 16 | export function createConfigurationComponent(config: any): DefineComponent { 17 | config.extends = initDxConfiguration(); 18 | setCompatOptions(config); 19 | return defineComponent(config); 20 | } 21 | 22 | export function createExtensionComponent(config: any): DefineComponent { 23 | config.extends = initDxExtensionComponent(); 24 | setCompatOptions(config); 25 | return defineComponent(config); 26 | } 27 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/core/version.ts: -------------------------------------------------------------------------------- 1 | import * as VueType from "vue"; 2 | const Vue = (VueType as any).default || VueType; 3 | 4 | export function getVueVersion() { 5 | const currentVersion = (Vue as any).version; 6 | return Number(currentVersion.split(".")[0]); 7 | } 8 | 9 | export function isVue3() { 10 | return getVueVersion() === 3; 11 | } 12 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/draggable.ts: -------------------------------------------------------------------------------- 1 | import Draggable, { Properties } from "devextreme/ui/draggable"; 2 | import { createComponent } from "./core/index"; 3 | import { createConfigurationComponent } from "./core/index"; 4 | 5 | type AccessibleOptions = Pick; 29 | 30 | interface DxDraggable extends AccessibleOptions { 31 | readonly instance?: Draggable; 32 | } 33 | const DxDraggable = createComponent({ 34 | props: { 35 | autoScroll: Boolean, 36 | boundary: {}, 37 | clone: Boolean, 38 | container: {}, 39 | cursorOffset: [Object, String], 40 | data: {}, 41 | dragDirection: String, 42 | dragTemplate: {}, 43 | elementAttr: Object, 44 | group: String, 45 | handle: String, 46 | height: [Function, Number, String], 47 | onDisposing: Function, 48 | onDragEnd: Function, 49 | onDragMove: Function, 50 | onDragStart: Function, 51 | onInitialized: Function, 52 | onOptionChanged: Function, 53 | rtlEnabled: Boolean, 54 | scrollSensitivity: Number, 55 | scrollSpeed: Number, 56 | width: [Function, Number, String] 57 | }, 58 | emits: { 59 | "update:isActive": null, 60 | "update:hoveredElement": null, 61 | "update:autoScroll": null, 62 | "update:boundary": null, 63 | "update:clone": null, 64 | "update:container": null, 65 | "update:cursorOffset": null, 66 | "update:data": null, 67 | "update:dragDirection": null, 68 | "update:dragTemplate": null, 69 | "update:elementAttr": null, 70 | "update:group": null, 71 | "update:handle": null, 72 | "update:height": null, 73 | "update:onDisposing": null, 74 | "update:onDragEnd": null, 75 | "update:onDragMove": null, 76 | "update:onDragStart": null, 77 | "update:onInitialized": null, 78 | "update:onOptionChanged": null, 79 | "update:rtlEnabled": null, 80 | "update:scrollSensitivity": null, 81 | "update:scrollSpeed": null, 82 | "update:width": null, 83 | }, 84 | computed: { 85 | instance(): Draggable { 86 | return (this as any).$_instance; 87 | } 88 | }, 89 | beforeCreate() { 90 | (this as any).$_WidgetClass = Draggable; 91 | (this as any).$_hasAsyncTemplate = true; 92 | (this as any).$_expectedChildren = { 93 | cursorOffset: { isCollectionItem: false, optionName: "cursorOffset" } 94 | }; 95 | } 96 | }); 97 | 98 | const DxCursorOffset = createConfigurationComponent({ 99 | emits: { 100 | "update:isActive": null, 101 | "update:hoveredElement": null, 102 | "update:x": null, 103 | "update:y": null, 104 | }, 105 | props: { 106 | x: Number, 107 | y: Number 108 | } 109 | }); 110 | (DxCursorOffset as any).$_optionName = "cursorOffset"; 111 | 112 | export default DxDraggable; 113 | export { 114 | DxDraggable, 115 | DxCursorOffset 116 | }; 117 | import type * as DxDraggableTypes from "devextreme/ui/draggable_types"; 118 | export { DxDraggableTypes }; 119 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/drawer.ts: -------------------------------------------------------------------------------- 1 | import Drawer, { Properties } from "devextreme/ui/drawer"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 29 | 30 | interface DxDrawer extends AccessibleOptions { 31 | readonly instance?: Drawer; 32 | } 33 | const DxDrawer = createComponent({ 34 | props: { 35 | activeStateEnabled: Boolean, 36 | animationDuration: Number, 37 | animationEnabled: Boolean, 38 | closeOnOutsideClick: [Boolean, Function], 39 | disabled: Boolean, 40 | elementAttr: Object, 41 | height: [Function, Number, String], 42 | hint: String, 43 | hoverStateEnabled: Boolean, 44 | maxSize: Number, 45 | minSize: Number, 46 | onDisposing: Function, 47 | onInitialized: Function, 48 | onOptionChanged: Function, 49 | opened: Boolean, 50 | openedStateMode: String, 51 | position: String, 52 | revealMode: String, 53 | rtlEnabled: Boolean, 54 | shading: Boolean, 55 | template: {}, 56 | visible: Boolean, 57 | width: [Function, Number, String] 58 | }, 59 | emits: { 60 | "update:isActive": null, 61 | "update:hoveredElement": null, 62 | "update:activeStateEnabled": null, 63 | "update:animationDuration": null, 64 | "update:animationEnabled": null, 65 | "update:closeOnOutsideClick": null, 66 | "update:disabled": null, 67 | "update:elementAttr": null, 68 | "update:height": null, 69 | "update:hint": null, 70 | "update:hoverStateEnabled": null, 71 | "update:maxSize": null, 72 | "update:minSize": null, 73 | "update:onDisposing": null, 74 | "update:onInitialized": null, 75 | "update:onOptionChanged": null, 76 | "update:opened": null, 77 | "update:openedStateMode": null, 78 | "update:position": null, 79 | "update:revealMode": null, 80 | "update:rtlEnabled": null, 81 | "update:shading": null, 82 | "update:template": null, 83 | "update:visible": null, 84 | "update:width": null, 85 | }, 86 | computed: { 87 | instance(): Drawer { 88 | return (this as any).$_instance; 89 | } 90 | }, 91 | beforeCreate() { 92 | (this as any).$_WidgetClass = Drawer; 93 | (this as any).$_hasAsyncTemplate = true; 94 | } 95 | }); 96 | 97 | export default DxDrawer; 98 | export { 99 | DxDrawer 100 | }; 101 | import type * as DxDrawerTypes from "devextreme/ui/drawer_types"; 102 | export { DxDrawerTypes }; 103 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/load-indicator.ts: -------------------------------------------------------------------------------- 1 | import LoadIndicator, { Properties } from "devextreme/ui/load_indicator"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 17 | 18 | interface DxLoadIndicator extends AccessibleOptions { 19 | readonly instance?: LoadIndicator; 20 | } 21 | const DxLoadIndicator = createComponent({ 22 | props: { 23 | elementAttr: Object, 24 | height: [Function, Number, String], 25 | hint: String, 26 | indicatorSrc: String, 27 | onContentReady: Function, 28 | onDisposing: Function, 29 | onInitialized: Function, 30 | onOptionChanged: Function, 31 | rtlEnabled: Boolean, 32 | visible: Boolean, 33 | width: [Function, Number, String] 34 | }, 35 | emits: { 36 | "update:isActive": null, 37 | "update:hoveredElement": null, 38 | "update:elementAttr": null, 39 | "update:height": null, 40 | "update:hint": null, 41 | "update:indicatorSrc": null, 42 | "update:onContentReady": null, 43 | "update:onDisposing": null, 44 | "update:onInitialized": null, 45 | "update:onOptionChanged": null, 46 | "update:rtlEnabled": null, 47 | "update:visible": null, 48 | "update:width": null, 49 | }, 50 | computed: { 51 | instance(): LoadIndicator { 52 | return (this as any).$_instance; 53 | } 54 | }, 55 | beforeCreate() { 56 | (this as any).$_WidgetClass = LoadIndicator; 57 | (this as any).$_hasAsyncTemplate = true; 58 | } 59 | }); 60 | 61 | export default DxLoadIndicator; 62 | export { 63 | DxLoadIndicator 64 | }; 65 | import type * as DxLoadIndicatorTypes from "devextreme/ui/load_indicator_types"; 66 | export { DxLoadIndicatorTypes }; 67 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/progress-bar.ts: -------------------------------------------------------------------------------- 1 | import ProgressBar, { Properties } from "devextreme/ui/progress_bar"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 33 | 34 | interface DxProgressBar extends AccessibleOptions { 35 | readonly instance?: ProgressBar; 36 | } 37 | const DxProgressBar = createComponent({ 38 | props: { 39 | disabled: Boolean, 40 | elementAttr: Object, 41 | height: [Function, Number, String], 42 | hint: String, 43 | hoverStateEnabled: Boolean, 44 | isDirty: Boolean, 45 | isValid: Boolean, 46 | max: Number, 47 | min: Number, 48 | onComplete: Function, 49 | onContentReady: Function, 50 | onDisposing: Function, 51 | onInitialized: Function, 52 | onOptionChanged: Function, 53 | onValueChanged: Function, 54 | readOnly: Boolean, 55 | rtlEnabled: Boolean, 56 | showStatus: Boolean, 57 | statusFormat: [Function, String], 58 | validationError: {}, 59 | validationErrors: Array, 60 | validationMessageMode: String, 61 | validationMessagePosition: String, 62 | validationStatus: String, 63 | value: {}, 64 | visible: Boolean, 65 | width: [Function, Number, String] 66 | }, 67 | emits: { 68 | "update:isActive": null, 69 | "update:hoveredElement": null, 70 | "update:disabled": null, 71 | "update:elementAttr": null, 72 | "update:height": null, 73 | "update:hint": null, 74 | "update:hoverStateEnabled": null, 75 | "update:isDirty": null, 76 | "update:isValid": null, 77 | "update:max": null, 78 | "update:min": null, 79 | "update:onComplete": null, 80 | "update:onContentReady": null, 81 | "update:onDisposing": null, 82 | "update:onInitialized": null, 83 | "update:onOptionChanged": null, 84 | "update:onValueChanged": null, 85 | "update:readOnly": null, 86 | "update:rtlEnabled": null, 87 | "update:showStatus": null, 88 | "update:statusFormat": null, 89 | "update:validationError": null, 90 | "update:validationErrors": null, 91 | "update:validationMessageMode": null, 92 | "update:validationMessagePosition": null, 93 | "update:validationStatus": null, 94 | "update:value": null, 95 | "update:visible": null, 96 | "update:width": null, 97 | }, 98 | computed: { 99 | instance(): ProgressBar { 100 | return (this as any).$_instance; 101 | } 102 | }, 103 | beforeCreate() { 104 | (this as any).$_WidgetClass = ProgressBar; 105 | (this as any).$_hasAsyncTemplate = true; 106 | } 107 | }); 108 | 109 | export default DxProgressBar; 110 | export { 111 | DxProgressBar 112 | }; 113 | import type * as DxProgressBarTypes from "devextreme/ui/progress_bar_types"; 114 | export { DxProgressBarTypes }; 115 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/resizable.ts: -------------------------------------------------------------------------------- 1 | import Resizable, { Properties } from "devextreme/ui/resizable"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 23 | 24 | interface DxResizable extends AccessibleOptions { 25 | readonly instance?: Resizable; 26 | } 27 | const DxResizable = createComponent({ 28 | props: { 29 | area: {}, 30 | elementAttr: Object, 31 | handles: String, 32 | height: [Function, Number, String], 33 | keepAspectRatio: Boolean, 34 | maxHeight: Number, 35 | maxWidth: Number, 36 | minHeight: Number, 37 | minWidth: Number, 38 | onDisposing: Function, 39 | onInitialized: Function, 40 | onOptionChanged: Function, 41 | onResize: Function, 42 | onResizeEnd: Function, 43 | onResizeStart: Function, 44 | rtlEnabled: Boolean, 45 | width: [Function, Number, String] 46 | }, 47 | emits: { 48 | "update:isActive": null, 49 | "update:hoveredElement": null, 50 | "update:area": null, 51 | "update:elementAttr": null, 52 | "update:handles": null, 53 | "update:height": null, 54 | "update:keepAspectRatio": null, 55 | "update:maxHeight": null, 56 | "update:maxWidth": null, 57 | "update:minHeight": null, 58 | "update:minWidth": null, 59 | "update:onDisposing": null, 60 | "update:onInitialized": null, 61 | "update:onOptionChanged": null, 62 | "update:onResize": null, 63 | "update:onResizeEnd": null, 64 | "update:onResizeStart": null, 65 | "update:rtlEnabled": null, 66 | "update:width": null, 67 | }, 68 | computed: { 69 | instance(): Resizable { 70 | return (this as any).$_instance; 71 | } 72 | }, 73 | beforeCreate() { 74 | (this as any).$_WidgetClass = Resizable; 75 | (this as any).$_hasAsyncTemplate = true; 76 | } 77 | }); 78 | 79 | export default DxResizable; 80 | export { 81 | DxResizable 82 | }; 83 | import type * as DxResizableTypes from "devextreme/ui/resizable_types"; 84 | export { DxResizableTypes }; 85 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/scroll-view.ts: -------------------------------------------------------------------------------- 1 | import ScrollView, { Properties } from "devextreme/ui/scroll_view"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 28 | 29 | interface DxScrollView extends AccessibleOptions { 30 | readonly instance?: ScrollView; 31 | } 32 | const DxScrollView = createComponent({ 33 | props: { 34 | bounceEnabled: Boolean, 35 | direction: String, 36 | disabled: Boolean, 37 | elementAttr: Object, 38 | height: [Function, Number, String], 39 | onDisposing: Function, 40 | onInitialized: Function, 41 | onOptionChanged: Function, 42 | onPullDown: Function, 43 | onReachBottom: Function, 44 | onScroll: Function, 45 | onUpdated: Function, 46 | pulledDownText: String, 47 | pullingDownText: String, 48 | reachBottomText: String, 49 | refreshingText: String, 50 | rtlEnabled: Boolean, 51 | scrollByContent: Boolean, 52 | scrollByThumb: Boolean, 53 | showScrollbar: String, 54 | useNative: Boolean, 55 | width: [Function, Number, String] 56 | }, 57 | emits: { 58 | "update:isActive": null, 59 | "update:hoveredElement": null, 60 | "update:bounceEnabled": null, 61 | "update:direction": null, 62 | "update:disabled": null, 63 | "update:elementAttr": null, 64 | "update:height": null, 65 | "update:onDisposing": null, 66 | "update:onInitialized": null, 67 | "update:onOptionChanged": null, 68 | "update:onPullDown": null, 69 | "update:onReachBottom": null, 70 | "update:onScroll": null, 71 | "update:onUpdated": null, 72 | "update:pulledDownText": null, 73 | "update:pullingDownText": null, 74 | "update:reachBottomText": null, 75 | "update:refreshingText": null, 76 | "update:rtlEnabled": null, 77 | "update:scrollByContent": null, 78 | "update:scrollByThumb": null, 79 | "update:showScrollbar": null, 80 | "update:useNative": null, 81 | "update:width": null, 82 | }, 83 | computed: { 84 | instance(): ScrollView { 85 | return (this as any).$_instance; 86 | } 87 | }, 88 | beforeCreate() { 89 | (this as any).$_WidgetClass = ScrollView; 90 | (this as any).$_hasAsyncTemplate = true; 91 | } 92 | }); 93 | 94 | export default DxScrollView; 95 | export { 96 | DxScrollView 97 | }; 98 | import type * as DxScrollViewTypes from "devextreme/ui/scroll_view_types"; 99 | export { DxScrollViewTypes }; 100 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/speed-dial-action.ts: -------------------------------------------------------------------------------- 1 | import SpeedDialAction, { Properties } from "devextreme/ui/speed_dial_action"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 23 | 24 | interface DxSpeedDialAction extends AccessibleOptions { 25 | readonly instance?: SpeedDialAction; 26 | } 27 | const DxSpeedDialAction = createComponent({ 28 | props: { 29 | accessKey: String, 30 | activeStateEnabled: Boolean, 31 | elementAttr: Object, 32 | focusStateEnabled: Boolean, 33 | hint: String, 34 | hoverStateEnabled: Boolean, 35 | icon: String, 36 | index: Number, 37 | label: String, 38 | onClick: Function, 39 | onContentReady: Function, 40 | onDisposing: Function, 41 | onInitialized: Function, 42 | onOptionChanged: Function, 43 | rtlEnabled: Boolean, 44 | tabIndex: Number, 45 | visible: Boolean 46 | }, 47 | emits: { 48 | "update:isActive": null, 49 | "update:hoveredElement": null, 50 | "update:accessKey": null, 51 | "update:activeStateEnabled": null, 52 | "update:elementAttr": null, 53 | "update:focusStateEnabled": null, 54 | "update:hint": null, 55 | "update:hoverStateEnabled": null, 56 | "update:icon": null, 57 | "update:index": null, 58 | "update:label": null, 59 | "update:onClick": null, 60 | "update:onContentReady": null, 61 | "update:onDisposing": null, 62 | "update:onInitialized": null, 63 | "update:onOptionChanged": null, 64 | "update:rtlEnabled": null, 65 | "update:tabIndex": null, 66 | "update:visible": null, 67 | }, 68 | computed: { 69 | instance(): SpeedDialAction { 70 | return (this as any).$_instance; 71 | } 72 | }, 73 | beforeCreate() { 74 | (this as any).$_WidgetClass = SpeedDialAction; 75 | (this as any).$_hasAsyncTemplate = true; 76 | } 77 | }); 78 | 79 | export default DxSpeedDialAction; 80 | export { 81 | DxSpeedDialAction 82 | }; 83 | import type * as DxSpeedDialActionTypes from "devextreme/ui/speed_dial_action_types"; 84 | export { DxSpeedDialActionTypes }; 85 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/validation-group.ts: -------------------------------------------------------------------------------- 1 | import ValidationGroup, { Properties } from "devextreme/ui/validation_group"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 12 | 13 | interface DxValidationGroup extends AccessibleOptions { 14 | readonly instance?: ValidationGroup; 15 | } 16 | const DxValidationGroup = createComponent({ 17 | props: { 18 | elementAttr: Object, 19 | height: [Function, Number, String], 20 | onDisposing: Function, 21 | onInitialized: Function, 22 | onOptionChanged: Function, 23 | width: [Function, Number, String] 24 | }, 25 | emits: { 26 | "update:isActive": null, 27 | "update:hoveredElement": null, 28 | "update:elementAttr": null, 29 | "update:height": null, 30 | "update:onDisposing": null, 31 | "update:onInitialized": null, 32 | "update:onOptionChanged": null, 33 | "update:width": null, 34 | }, 35 | computed: { 36 | instance(): ValidationGroup { 37 | return (this as any).$_instance; 38 | } 39 | }, 40 | beforeCreate() { 41 | (this as any).$_WidgetClass = ValidationGroup; 42 | (this as any).$_hasAsyncTemplate = true; 43 | } 44 | }); 45 | 46 | export default DxValidationGroup; 47 | export { 48 | DxValidationGroup 49 | }; 50 | import type * as DxValidationGroupTypes from "devextreme/ui/validation_group_types"; 51 | export { DxValidationGroupTypes }; 52 | -------------------------------------------------------------------------------- /packages/devextreme-vue/src/validation-summary.ts: -------------------------------------------------------------------------------- 1 | export { ExplicitTypes } from "devextreme/ui/validation_summary"; 2 | import ValidationSummary, { Properties } from "devextreme/ui/validation_summary"; 3 | import { createComponent } from "./core/index"; 4 | import { createConfigurationComponent } from "./core/index"; 5 | 6 | type AccessibleOptions = Pick; 18 | 19 | interface DxValidationSummary extends AccessibleOptions { 20 | readonly instance?: ValidationSummary; 21 | } 22 | const DxValidationSummary = createComponent({ 23 | props: { 24 | elementAttr: Object, 25 | hoverStateEnabled: Boolean, 26 | items: Array, 27 | itemTemplate: {}, 28 | onContentReady: Function, 29 | onDisposing: Function, 30 | onInitialized: Function, 31 | onItemClick: Function, 32 | onOptionChanged: Function, 33 | validationGroup: String 34 | }, 35 | emits: { 36 | "update:isActive": null, 37 | "update:hoveredElement": null, 38 | "update:elementAttr": null, 39 | "update:hoverStateEnabled": null, 40 | "update:items": null, 41 | "update:itemTemplate": null, 42 | "update:onContentReady": null, 43 | "update:onDisposing": null, 44 | "update:onInitialized": null, 45 | "update:onItemClick": null, 46 | "update:onOptionChanged": null, 47 | "update:validationGroup": null, 48 | }, 49 | computed: { 50 | instance(): ValidationSummary { 51 | return (this as any).$_instance; 52 | } 53 | }, 54 | beforeCreate() { 55 | (this as any).$_WidgetClass = ValidationSummary; 56 | (this as any).$_hasAsyncTemplate = true; 57 | (this as any).$_expectedChildren = { 58 | item: { isCollectionItem: true, optionName: "items" } 59 | }; 60 | } 61 | }); 62 | 63 | const DxItem = createConfigurationComponent({ 64 | emits: { 65 | "update:isActive": null, 66 | "update:hoveredElement": null, 67 | "update:disabled": null, 68 | "update:html": null, 69 | "update:template": null, 70 | "update:text": null, 71 | "update:visible": null, 72 | }, 73 | props: { 74 | disabled: Boolean, 75 | html: String, 76 | template: {}, 77 | text: String, 78 | visible: Boolean 79 | } 80 | }); 81 | (DxItem as any).$_optionName = "items"; 82 | (DxItem as any).$_isCollectionItem = true; 83 | 84 | export default DxValidationSummary; 85 | export { 86 | DxValidationSummary, 87 | DxItem 88 | }; 89 | import type * as DxValidationSummaryTypes from "devextreme/ui/validation_summary_types"; 90 | export { DxValidationSummaryTypes }; 91 | -------------------------------------------------------------------------------- /packages/devextreme-vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "skipLibCheck": true, 5 | "lib": [ 6 | "es2015", 7 | "dom" 8 | ] 9 | }, 10 | "include": [ 11 | "src/core/**/*" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/sandbox/app.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 70 | -------------------------------------------------------------------------------- /packages/sandbox/components/accessing-instance-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 42 | -------------------------------------------------------------------------------- /packages/sandbox/components/button-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 10 | 14 | 15 | 16 | 17 | 38 | 39 | 46 | -------------------------------------------------------------------------------- /packages/sandbox/components/chart-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 35 | -------------------------------------------------------------------------------- /packages/sandbox/components/example-block.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ title }} 4 | {{ stateStr }} 5 | 6 | 7 | 8 | 9 | 25 | 26 | 33 | -------------------------------------------------------------------------------- /packages/sandbox/components/form-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 13 | 18 | 19 | 20 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 44 | 45 | 46 | 47 | 48 | 95 | -------------------------------------------------------------------------------- /packages/sandbox/components/list-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | List of simple items 4 | 5 | 6 | 7 | 8 | List with item template 9 | 10 | 11 | {{ index + 1 }} - {{ data.day }} 12 | 13 | 14 | No templates on weekend 15 | 16 | 17 | 18 | List with static items 19 | 20 | 1 21 | 2 22 | 23 | 3 - third 24 | 25 | 26 | List with several templates 27 | 31 | 32 | {{ data.day }} 33 | 34 | 35 | {{ data.day }} 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 78 | -------------------------------------------------------------------------------- /packages/sandbox/components/map-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | Move! 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 62 | -------------------------------------------------------------------------------- /packages/sandbox/components/menu-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | Menu with simple items 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Inline template 13 | 14 | 15 | 16 | 17 | 18 | 19 | Menu with static item templates 20 | 21 | 22 | Item 1 23 | Item 1-1 24 | Item 1-2 25 | 26 | Item 1-3 27 | Item 1-3-1 28 | 29 | 30 | 31 | Item 2 32 | 33 | 34 | Item 3 35 | 36 | 37 | 38 | 39 | 40 | 52 | -------------------------------------------------------------------------------- /packages/sandbox/components/number-box-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | This is {{ value }} 5 | 6 | 7 | 8 | 24 | -------------------------------------------------------------------------------- /packages/sandbox/components/popup-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | This is popup content 12 | 13 | 14 | 15 | 16 | 17 | 18 | 52 | -------------------------------------------------------------------------------- /packages/sandbox/components/scheduler-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 32 | -------------------------------------------------------------------------------- /packages/sandbox/components/tab-panel-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | First Tab Content 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 48 | -------------------------------------------------------------------------------- /packages/sandbox/components/text-box-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | v-bind 4 | 8 | @update:value 9 | 13 | @valueChanged 14 | 18 | @value-changed 19 | 23 | v-model 24 | 28 | @focusIn (clears text) 29 | 33 | Validation (Required) 34 | 35 | 36 | 37 | 38 | 39 | 40 | 64 | 65 | 71 | -------------------------------------------------------------------------------- /packages/sandbox/components/validation-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Custom value validation 22 | 23 | 24 | Validate custom value 25 | 26 | 27 | Not empty: {{ customValueIsNotEmpty }} 28 | Not less than 0, not greater than 9: {{ customValueIsDigit }} 29 | Is valid: {{ customValueIsValid }} 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 99 | 100 | 106 | -------------------------------------------------------------------------------- /packages/sandbox/main.ts: -------------------------------------------------------------------------------- 1 | import "devextreme/dist/css/dx.common.css"; 2 | import "devextreme/dist/css/dx.light.compact.css"; 3 | 4 | import { createApp } from 'vue'; 5 | import App from './app.vue'; 6 | 7 | createApp(App).mount('#app'); 8 | -------------------------------------------------------------------------------- /packages/sandbox/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devextreme-vue-sandbox", 3 | "description": "DevExtreme Vue UI and Visualization Components", 4 | "private": true, 5 | "version": "23.2.0", 6 | "author": "Developer Express Inc.", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/DevExpress/devextreme-vue.git" 11 | }, 12 | "dependencies": { 13 | "devextreme-vue": "~23.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/jest": "^22.2.3", 17 | "@types/node": "^16.18.6", 18 | "@vue/compiler-sfc": "3.0.0", 19 | "@vue/test-utils": "^2.0.0-beta.7", 20 | "css-loader": "^2.1.1", 21 | "devextreme": "23.2-next", 22 | "minimatch": "^3.0.2", 23 | "source-map-loader": "^0.2.4", 24 | "style-loader": "^0.20.3", 25 | "ts-loader": "^8.0.4", 26 | "tsconfig-paths-webpack-plugin": "^3.3.0", 27 | "typescript": "^4.7.4", 28 | "url-loader": "^1.1.2", 29 | "vue": "^3.0.0", 30 | "vue-loader": "^16.0.0-beta.8", 31 | "vue-router": "^4.0.16", 32 | "webpack": "^4.29.2", 33 | "webpack-cli": "^3.2.3", 34 | "webpack-dev-server": "^4.0.0" 35 | }, 36 | "scripts": { 37 | "start": "webpack-dev-server" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/sandbox/public/css/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #222222; 3 | background-color: #ffffff; 4 | } -------------------------------------------------------------------------------- /packages/sandbox/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | DevExtreme Vue Components 7 | 8 | 9 | 10 | 11 | 12 | 13 | DevExtreme Vue Components 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /packages/sandbox/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "baseUrl": ".", 6 | "outDir": "./temp", 7 | "noEmitOnError": true, 8 | "experimentalDecorators": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "noImplicitAny": true, 12 | "noImplicitReturns": true, 13 | "noImplicitThis": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "strictNullChecks": true 17 | }, 18 | "include": [ 19 | "*.ts" 20 | ], 21 | "exclude": [ 22 | "**/*.test.ts" 23 | ] 24 | } -------------------------------------------------------------------------------- /packages/sandbox/vue-shim.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import Vue from "vue"; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /packages/sandbox/webpack.config.js: -------------------------------------------------------------------------------- 1 | const { VueLoaderPlugin } = require('vue-loader'); 2 | const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: "./main.ts", 6 | output: { 7 | filename: "./public/js/app/bundle.js", 8 | }, 9 | devtool: "source-map", 10 | devServer: { 11 | port: 9901, 12 | open: true, 13 | openPage: "public/index.html" 14 | }, 15 | resolve: { 16 | extensions: [".webpack.js", ".web.js", ".ts", ".vue", ".js"], 17 | alias: { 18 | 'vue$': 'vue/dist/vue.cjs.js' 19 | }, 20 | plugins: [new TsconfigPathsPlugin({ 21 | configFile: "../../tsconfig.json" 22 | })] 23 | }, 24 | mode: "development", 25 | module: { 26 | rules: [ 27 | { 28 | test: /\.vue$/, 29 | loader: 'vue-loader', 30 | options: { 31 | esModule: true 32 | } 33 | }, 34 | { 35 | test: /\.js$/, 36 | loader: "source-map-loader", 37 | options: { 38 | enforce: "pre", 39 | filterSourceMappingUrl: (url, resourcePath) => { 40 | if (/vue2-strategy.*/i.test(url)) { 41 | return false; 42 | } 43 | 44 | return true; 45 | }, 46 | } 47 | }, 48 | { 49 | test: /\.tsx?$/, 50 | use: { 51 | loader: "ts-loader", 52 | options: { 53 | appendTsSuffixTo: [/\.vue$/], 54 | compilerOptions: { 55 | "noImplicitAny": false 56 | } 57 | } 58 | } 59 | }, 60 | { 61 | test: /\.css$/, 62 | use: [ 63 | { loader: "style-loader" }, 64 | { loader: "css-loader" } 65 | ] 66 | }, 67 | { 68 | test: /\.(eot|svg|ttf|woff|woff2)$/, 69 | use: "url-loader?name=[name].[ext]" 70 | } 71 | ] 72 | }, 73 | plugins: [ 74 | new VueLoaderPlugin() 75 | ] 76 | }; 77 | -------------------------------------------------------------------------------- /packages/vue2-strategy/.gitignore: -------------------------------------------------------------------------------- 1 | npm/ 2 | -------------------------------------------------------------------------------- /packages/vue2-strategy/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 | -------------------------------------------------------------------------------- /packages/vue2-strategy/build.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | core: './src/core/**/*.ts', 4 | ignoredGlobs: ['!./src/core/**/*.test.ts', '!./src/core/**/__mocks__/*'], 5 | npm: { 6 | dist: './npm/', 7 | license: '../../LICENSE' 8 | }, 9 | metadataPath: '../devextreme-vue/metadata/integration-data.json', 10 | generatedComponentsDir: './src', 11 | coreComponentsDir: './src/core', 12 | indexFileName: './src/index.ts', 13 | baseComponent: './core/index', 14 | configComponent: './core/index', 15 | widgetsPackage: 'devextreme' 16 | }; 17 | -------------------------------------------------------------------------------- /packages/vue2-strategy/gulpfile.js: -------------------------------------------------------------------------------- 1 | const del = require('del'); 2 | const mkdir = require('mkdirp'); 3 | const fs = require('fs'); 4 | const gulp = require('gulp'); 5 | const ts = require('gulp-typescript'); 6 | 7 | const generateSync = require('devextreme-vue-generator').default; 8 | 9 | const config = require('./build.config'); 10 | const VUE_VERSION = 2; 11 | 12 | gulp.task('build.components', gulp.series( 13 | (done) => { 14 | generateSync( 15 | JSON.parse(fs.readFileSync(config.metadataPath).toString()), 16 | config.baseComponent, 17 | config.configComponent, 18 | { 19 | componentsDir: config.generatedComponentsDir, 20 | indexFileName: config.indexFileName 21 | }, 22 | config.widgetsPackage, 23 | VUE_VERSION, 24 | true 25 | ); 26 | 27 | done(); 28 | } 29 | )); 30 | 31 | gulp.task('clean', gulp.parallel( 32 | (c) => del([`${config.generatedComponentsDir}\\*`, `!${config.coreComponentsDir}`], c), 33 | (c) => del(config.npm.dist, c) 34 | )); 35 | 36 | gulp.task('copy-helper', () => { 37 | return gulp.src('../devextreme-vue/src/core/helpers.ts') 38 | .pipe(gulp.dest('./src/core')); 39 | }); 40 | 41 | gulp.task('build.strategy', function() { 42 | return gulp.src([ 43 | config.core, 44 | ...config.ignoredGlobs 45 | ]) 46 | .pipe(ts('tsconfig.json')) 47 | .pipe(gulp.dest(config.npm.dist)) 48 | }); 49 | 50 | gulp.task('build', gulp.series( 51 | 'clean', 52 | 'copy-helper', 53 | 'build.strategy', 54 | 'build.components' 55 | )); 56 | -------------------------------------------------------------------------------- /packages/vue2-strategy/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 | roots: [ 11 | "/src/core" 12 | ], 13 | moduleNameMapper: { 14 | "^vue$": "vue/dist/vue.common.js", 15 | "^@/(.*)$": "/src/core/$1" 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /packages/vue2-strategy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devextreme-vue2-strategy", 3 | "private": true, 4 | "version": "23.2.0", 5 | "main": "npm/index", 6 | "types": "npm/index", 7 | "files": [ 8 | "npm" 9 | ], 10 | "scripts": { 11 | "build": "gulp build", 12 | "pack": "npm run build", 13 | "test": "jest", 14 | "start": "webpack-dev-server" 15 | }, 16 | "peerDependencies": { 17 | "devextreme": "23.2-next" 18 | }, 19 | "devDependencies": { 20 | "@types/jest": "^26.0.0", 21 | "@vue/test-utils": "1.0.0-beta.29", 22 | "css-loader": "^2.1.1", 23 | "del": "^3.0.0", 24 | "devextreme-vue-generator": "^3.0.1", 25 | "gulp": "^4.0.2", 26 | "gulp-typescript": "^5.0.1", 27 | "jest": "^26.0.0", 28 | "source-map-loader": "^0.2.4", 29 | "style-loader": "^0.20.3", 30 | "ts-jest": "^26.0.0", 31 | "ts-loader": "^8.0.4", 32 | "tslint": "^5.11.0", 33 | "typescript": "^4.7.4", 34 | "url-loader": "^1.1.2", 35 | "vue": "^2.6.3", 36 | "vue-loader": "^15.0.0", 37 | "vue-template-compiler": "^2.6.3", 38 | "webpack": "^4.29.2", 39 | "webpack-cli": "^3.2.3", 40 | "webpack-dev-server": "^4.0.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/app.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 74 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/accessing-instance-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 42 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/button-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 12 | 13 | 14 | 15 | 34 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/chart-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 35 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/example-block.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{title}} 4 | {{stateStr}} 5 | 6 | 7 | 8 | 9 | 25 | 26 | 33 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/form-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | 20 | 25 | 26 | 27 | 31 | 36 | 37 | 38 | 42 | 43 | 44 | 45 | 93 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/list-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | List of simple items 4 | 5 | 6 | 7 | 8 | List with item template 9 | 10 | 11 | {{ index + 1 }} - {{ data.day }} 12 | 13 | 14 | No templates on weekend 15 | 16 | 17 | 18 | List with static items 19 | 20 | 1 21 | 2 22 | 23 | 3 - third 24 | 25 | 26 | List with several templates 27 | 31 | 32 | {{ data.day }} 33 | 34 | 35 | {{ data.day }} 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 78 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/map-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | Move! 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 66 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/menu-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | Menu with simple items 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Inline template 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Menu with static item templates 21 | 22 | 23 | Item 1 24 | 25 | Item 1-1 26 | 27 | 28 | Item 1-2 29 | 30 | 31 | Item 1-3 32 | 33 | Item 1-3-1 34 | 35 | 36 | 37 | 38 | Item 2 39 | 40 | 41 | Item 3 42 | 43 | 44 | 45 | 46 | 47 | 59 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/number-box-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | This is {{ value }} 10 | 11 | 12 | 13 | 29 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/popup-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 18 | This is popup content 19 | 23 | 27 | 28 | 29 | 30 | 31 | 66 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/scheduler-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 32 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/tab-panel-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | First Tab Content 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 39 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/text-box-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | v-bind 5 | 9 | 10 | @update:value 11 | 15 | 16 | @valueChanged 17 | 21 | 22 | @value-changed 23 | 27 | 28 | :value.sync 29 | 33 | 34 | v-model 35 | 39 | 40 | @focusIn (clears text) 41 | 46 | 47 | Validation (Required) 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 79 | 80 | 86 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/components/validation-example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Custom value validation 22 | 23 | 24 | Validate custom value 25 | 26 | 27 | Not empty: {{customValueIsNotEmpty}} 28 | Not less than 0, not greater than 9: {{customValueIsDigit}} 29 | Is valid: {{customValueIsValid}} 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 108 | 109 | 115 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/main.ts: -------------------------------------------------------------------------------- 1 | import "devextreme/dist/css/dx.common.css"; 2 | import "devextreme/dist/css/dx.light.compact.css"; 3 | 4 | import Vue from "vue"; 5 | import App from "./app.vue"; 6 | 7 | // tslint:disable-next-line:no-unused-expression 8 | new Vue({ 9 | el: "#app", 10 | template: "", 11 | components: { App } 12 | }); 13 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/public/css/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #222222; 3 | background-color: #ffffff; 4 | } 5 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | DevExtreme Vue Components 7 | 8 | 9 | 10 | 11 | 12 | 13 | DevExtreme Vue Components 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "baseUrl": ".", 6 | "outDir": "./temp", 7 | "noEmitOnError": true, 8 | "experimentalDecorators": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "noImplicitAny": true, 12 | "noImplicitReturns": true, 13 | "noImplicitThis": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "strictNullChecks": true 17 | }, 18 | "include": [ 19 | "*.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/vue2-strategy/sandbox/vue-shim.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import Vue from "vue"; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /packages/vue2-strategy/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 | } 97 | }); 98 | 99 | export default DxButton; 100 | export { 101 | DxButton 102 | }; 103 | import type * as DxButtonTypes from "devextreme/ui/button_types"; 104 | export { DxButtonTypes }; 105 | -------------------------------------------------------------------------------- /packages/vue2-strategy/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/vue2-strategy/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/vue2-strategy/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/vue2-strategy/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/vue2-strategy/src/core/__mocks__/templates-discovering.ts: -------------------------------------------------------------------------------- 1 | export const discover = jest.fn(() => ({})); 2 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/core/__tests__/form.test.ts: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | 3 | import { DxForm, DxItem } from "../../form"; 4 | 5 | jest.setTimeout(1000); 6 | beforeEach(() => { 7 | jest.clearAllMocks(); 8 | }); 9 | 10 | describe("component rendering", () => { 11 | 12 | it("rendering with configuration options", () => { 13 | const vm = { 14 | template: 15 | ` 16 | 17 | `, 18 | data() { 19 | return { 20 | data: { name: "test" } 21 | }; 22 | }, 23 | components: { 24 | DxForm, 25 | DxItem 26 | } 27 | }; 28 | expect(() => mount(vm)).not.toThrow(""); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/core/__tests__/helper.test.ts: -------------------------------------------------------------------------------- 1 | import { getOptionValue } from "../helpers"; 2 | 3 | describe("getOptionValue", () => { 4 | it("returns for simple option", () => { 5 | const optionValue = getOptionValue({ test: "text" }, "test"); 6 | 7 | expect(optionValue).toEqual("text"); 8 | }); 9 | 10 | it("returns for complex option", () => { 11 | const optionValue = getOptionValue({ test: { value: "text" } }, "test"); 12 | const optionValue1 = getOptionValue({ test: { value: "text" } }, "test.value"); 13 | const optionValue2 = getOptionValue({ test: { value: "text" } }, "test1.value"); 14 | 15 | expect(optionValue).toEqual({ value: "text" }); 16 | expect(optionValue1).toEqual("text"); 17 | expect(optionValue2).toEqual(undefined); 18 | }); 19 | 20 | it("returns for collection option", () => { 21 | const value = [ 22 | { text: "value1"}, 23 | { text: "value2"}, 24 | { text: "value3", 25 | test: [{ 26 | option: { 27 | text: "value1" 28 | } 29 | }, { 30 | text: "value2" 31 | }] } 32 | ]; 33 | 34 | const optionValue1 = getOptionValue({ test: value }, "test[1]"); 35 | const optionValue2 = getOptionValue({ test: value }, "test"); 36 | const optionValue3 = getOptionValue({ test: value }, "test[2].test[1]"); 37 | const optionValue4 = getOptionValue({ test: value }, "test[2].test"); 38 | const optionValue5 = getOptionValue({ test: value }, "test[2].test[0].option"); 39 | const optionValue6 = getOptionValue({ test: value }, "test[2].test[0].option.text"); 40 | 41 | expect(optionValue1).toEqual({ text: "value2" }); 42 | expect(optionValue2).toEqual(value); 43 | expect(optionValue3).toEqual({ text: "value2" }); 44 | expect(optionValue4).toEqual([{ 45 | option: { text: "value1" } 46 | }, { 47 | text: "value2" 48 | }]); 49 | expect(optionValue5).toEqual({ text: "value1" }); 50 | expect(optionValue6).toEqual("value1"); 51 | }); 52 | 53 | it("returns for empty", () => { 54 | const optionValue = getOptionValue({}, "test"); 55 | const optionValue2 = getOptionValue({ test: [{ text: "value1" }] }, "test[1]"); 56 | 57 | expect(optionValue).toEqual(undefined); 58 | expect(optionValue2).toEqual(undefined); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/core/children-processing.ts: -------------------------------------------------------------------------------- 1 | import { VNode } from "vue"; 2 | import Configuration from "./configuration"; 3 | import { IConfigurable, IConfigurationComponent } from "./configuration-component"; 4 | 5 | function pullAllChildren(directChildren: VNode[], allChildren: VNode[], config: Configuration): void { 6 | if (!directChildren || directChildren.length === 0) { return; } 7 | 8 | pullConfigComponents(directChildren, allChildren, config); 9 | } 10 | 11 | function pullConfigComponents(children: VNode[], nodes: VNode[], ownerConfig: Configuration): void { 12 | 13 | children.forEach((node) => { 14 | nodes.push(node); 15 | if (!node.componentOptions) { return; } 16 | 17 | const configComponent = node.componentOptions.Ctor as any as IConfigurationComponent; 18 | if (!configComponent.$_optionName) { return; } 19 | 20 | const initialValues = { 21 | ...configComponent.$_predefinedProps, 22 | ...node.componentOptions.propsData 23 | }; 24 | 25 | const config = ownerConfig.createNested( 26 | configComponent.$_optionName, 27 | initialValues, 28 | configComponent.$_isCollectionItem, 29 | configComponent.$_expectedChildren 30 | ); 31 | 32 | (node.componentOptions as any as IConfigurable).$_config = config; 33 | (node.componentOptions as any as IConfigurable).$_innerChanges = {}; 34 | 35 | if (node.componentOptions.children) { 36 | pullConfigComponents(node.componentOptions.children as VNode[], nodes, config); 37 | } 38 | }); 39 | } 40 | 41 | export { 42 | pullAllChildren 43 | }; 44 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/core/config.test.ts: -------------------------------------------------------------------------------- 1 | import * as VueType from "vue"; 2 | import { DxComponent, IWidgetComponent } from "./component"; 3 | import config, { getOption } from "./config"; 4 | 5 | const Vue = VueType.default || VueType; 6 | 7 | const Widget = { 8 | option: jest.fn(), 9 | dispose: jest.fn(), 10 | on: jest.fn(), 11 | fire: jest.fn(), 12 | beginUpdate: jest.fn(), 13 | endUpdate: jest.fn(), 14 | }; 15 | 16 | const WidgetClass = jest.fn(() => Widget); 17 | 18 | const TestComponent = Vue.extend({ 19 | extends: DxComponent(), 20 | beforeCreate() { 21 | (this as any as IWidgetComponent).$_WidgetClass = WidgetClass; 22 | } 23 | }); 24 | 25 | describe("useLegacyTemplateEngine", () => { 26 | const originalValue = getOption("useLegacyTemplateEngine"); 27 | 28 | beforeEach(() => { 29 | config({ useLegacyTemplateEngine: true }); 30 | }); 31 | 32 | afterEach(() => { 33 | config({ useLegacyTemplateEngine: originalValue }); 34 | }); 35 | 36 | it("has model as scope", () => { 37 | new Vue({ 38 | template: ` 39 | Template {{data.text}} 40 | `, 41 | components: { 42 | TestComponent 43 | } 44 | }).$mount(); 45 | 46 | // @ts-ignore 47 | const render = WidgetClass.mock.calls[0][1].integrationOptions.templates.item.render; 48 | const renderedTemplate = render({ 49 | container: document.createElement("div"), 50 | model: { text: "with data" } 51 | }); 52 | 53 | expect(renderedTemplate.innerHTML).toBe("Template with data"); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/core/config.ts: -------------------------------------------------------------------------------- 1 | interface IOptions { 2 | useLegacyTemplateEngine: boolean; 3 | } 4 | 5 | let config: IOptions = { 6 | useLegacyTemplateEngine: false 7 | }; 8 | 9 | function setOptions(options: Partial): void { 10 | config = { ...config, ...options }; 11 | } 12 | 13 | function getOption(optionName: TName): IOptions[TName] { 14 | return config[optionName]; 15 | } 16 | 17 | export default setOptions; 18 | export { getOption }; 19 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/core/configuration-component.ts: -------------------------------------------------------------------------------- 1 | import * as VueType from "vue"; 2 | import IVue, { VNode, VueConstructor } from "vue"; 3 | 4 | const Vue = VueType.default || VueType; 5 | 6 | import Configuration, { bindOptionWatchers, ExpectedChild, setEmitOptionChangedFunc } from "./configuration"; 7 | 8 | interface IConfigurationOwner { 9 | $_expectedChildren: Record; 10 | } 11 | 12 | interface IConfigurationComponent extends IConfigurationOwner { 13 | $_optionName: string; 14 | $_isCollectionItem: boolean; 15 | $_predefinedProps: Record; 16 | } 17 | 18 | interface IConfigurable extends IConfigurationOwner { 19 | $_config: Configuration; 20 | $_innerChanges: any; 21 | } 22 | 23 | interface IComponentInfo { 24 | optionPath: string; 25 | isCollection: boolean; 26 | removed?: boolean; 27 | } 28 | 29 | function getConfig(vueInstance: Pick): Configuration | undefined { 30 | if (!vueInstance.$vnode) { 31 | return; 32 | } 33 | 34 | const componentOptions = (vueInstance.$vnode.componentOptions as any as IConfigurable); 35 | 36 | return componentOptions && componentOptions.$_config; 37 | } 38 | 39 | function getInnerChanges(vueInstance: Pick): any { 40 | if (!vueInstance.$vnode) { 41 | return; 42 | } 43 | 44 | const componentOptions = (vueInstance.$vnode.componentOptions as any as IConfigurable); 45 | 46 | return componentOptions && componentOptions.$_innerChanges; 47 | } 48 | 49 | function initOptionChangedFunc(config, vueInstance: Pick, innerChanges: any) { 50 | if (!config) { 51 | return; 52 | } 53 | 54 | config.init(Object.keys(vueInstance.$props)); 55 | setEmitOptionChangedFunc(config, vueInstance, innerChanges); 56 | } 57 | 58 | function getComponentInfo({name, isCollectionItem, ownerConfig }: Configuration, removed?: boolean): IComponentInfo { 59 | const parentPath = ownerConfig && ownerConfig.fullPath; 60 | const optionPath = name && parentPath ? `${parentPath}.${name}` : name || ""; 61 | 62 | return { 63 | optionPath, 64 | isCollection: isCollectionItem, 65 | removed 66 | }; 67 | } 68 | 69 | const DxConfiguration = (): VueConstructor => Vue.extend({ 70 | beforeMount() { 71 | const config = getConfig(this) as Configuration; 72 | const innerChanges = getInnerChanges(this); 73 | initOptionChangedFunc(config, this, innerChanges); 74 | bindOptionWatchers(config, this, innerChanges); 75 | }, 76 | 77 | mounted() { 78 | if ((this.$parent as any).$_instance) { 79 | (this.$parent as any).$_config.componentsCountChanged 80 | .push(getComponentInfo(getConfig(this) as Configuration)); 81 | } 82 | }, 83 | 84 | beforeDestroy() { 85 | (this.$parent as any).$_config.componentsCountChanged 86 | .push(getComponentInfo(getConfig(this) as Configuration, true)); 87 | }, 88 | 89 | render(createElement: (...args) => VNode): VNode { 90 | return createElement(); 91 | } 92 | }); 93 | 94 | export { 95 | DxConfiguration, 96 | IComponentInfo, 97 | IConfigurable, 98 | IConfigurationComponent, 99 | initOptionChangedFunc, 100 | getConfig, 101 | getInnerChanges 102 | }; 103 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/core/constants.ts: -------------------------------------------------------------------------------- 1 | const DX_TEMPLATE_WRAPPER_CLASS = "dx-template-wrapper"; 2 | const DX_REMOVE_EVENT = "dxremove"; 3 | 4 | export { 5 | DX_TEMPLATE_WRAPPER_CLASS, 6 | DX_REMOVE_EVENT 7 | }; 8 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/core/errors.ts: -------------------------------------------------------------------------------- 1 | export const TEMPLATE_MULTIPLE_ROOTS_ERROR = "Template must have a single root node."; 2 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/core/extension-component.ts: -------------------------------------------------------------------------------- 1 | import { VueConstructor } from "vue"; 2 | import { BaseComponent } from "./component"; 3 | 4 | interface IExtension { 5 | $_isExtension: boolean; 6 | attachTo(element: any); 7 | } 8 | 9 | interface IExtensionComponentNode { 10 | $_hasOwner: boolean; 11 | } 12 | 13 | const DxExtensionComponent = (): VueConstructor => BaseComponent().extend({ 14 | created(): void { 15 | this.$_isExtension = true; 16 | }, 17 | 18 | mounted() { 19 | this.$el.setAttribute("isExtension", "true"); 20 | if (this.$vnode && (this.$vnode.componentOptions as any as IExtensionComponentNode).$_hasOwner) { return; } 21 | 22 | this.attachTo(this.$el); 23 | }, 24 | 25 | methods: { 26 | attachTo(element: any) { 27 | this.$_createWidget(element); 28 | } 29 | } 30 | }); 31 | 32 | export { 33 | DxExtensionComponent, 34 | IExtension, 35 | IExtensionComponentNode 36 | }; 37 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/core/helpers.ts: -------------------------------------------------------------------------------- 1 | export function getTemplatePropName(props: Record | null, templateName: string): string { 2 | for (const propName in props) { 3 | if (props[propName] === templateName) { 4 | return propName; 5 | } 6 | } 7 | 8 | return templateName; 9 | } 10 | 11 | export function uppercaseFirst(value: string): string { 12 | return value[0].toUpperCase() + value.substr(1); 13 | } 14 | 15 | export function lowercaseFirst(value: string): string { 16 | return value[0].toLowerCase() + value.substr(1); 17 | } 18 | 19 | export function camelize(value: string): string { 20 | return lowercaseFirst(value.split("-").map((v) => uppercaseFirst(v)).join("")); 21 | } 22 | 23 | export function toComparable(value: any): any { 24 | return value instanceof Date ? value.getTime() : value; 25 | } 26 | 27 | export function isEqual(value1, value2) { 28 | if (toComparable(value1) === toComparable(value2)) { 29 | return true; 30 | } 31 | 32 | if (Array.isArray(value1) && Array.isArray(value2)) { 33 | return value1.length === 0 && value2.length === 0; 34 | } 35 | 36 | return false; 37 | } 38 | 39 | export function forEachChildNode( 40 | el: Node, 41 | callback: (child: ReturnType) => void 42 | ) { 43 | Array.prototype.slice.call(el.childNodes).forEach(callback); 44 | } 45 | 46 | export function allKeysAreEqual(obj1: object, obj2: object) { 47 | const obj1Keys = Object.keys(obj1); 48 | 49 | if (obj1Keys.length !== Object.keys(obj2).length) { 50 | return false; 51 | } 52 | 53 | for (const key of obj1Keys) { 54 | if (!obj2.hasOwnProperty(key)) { 55 | return false; 56 | } 57 | } 58 | 59 | return true; 60 | } 61 | 62 | export function getOptionValue(options, optionPath) { 63 | let value = options; 64 | 65 | optionPath.split(".").forEach((p) => { 66 | const optionInfo = getOptionInfo(p); 67 | if (value) { 68 | value = optionInfo.isCollection ? 69 | value[optionInfo.name] && value[optionInfo.name][optionInfo.index] : 70 | value[optionInfo.name]; 71 | } 72 | }); 73 | 74 | return value; 75 | } 76 | 77 | export function getOptionInfo(name: string): IOptionInfo | ICollectionOptionInfo { 78 | const parts = name.split("["); 79 | 80 | if (parts.length === 1) { 81 | return { 82 | isCollection: false, 83 | name, 84 | fullName: name 85 | }; 86 | } 87 | 88 | return { 89 | isCollection: true, 90 | name: parts[0], 91 | fullName: name, 92 | index: Number(parts[1].slice(0, -1)) 93 | }; 94 | } 95 | 96 | interface IOptionInfo { 97 | isCollection: false; 98 | name: string; 99 | fullName: string; 100 | } 101 | 102 | interface ICollectionOptionInfo { 103 | isCollection: true; 104 | name: string; 105 | fullName: string; 106 | index: number; 107 | } 108 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/core/index.ts: -------------------------------------------------------------------------------- 1 | import * as VueType from "vue"; 2 | const Vue = VueType.default || VueType; 3 | 4 | import { DxComponent } from "./component"; 5 | import { DxConfiguration } from "./configuration-component"; 6 | import { DxExtensionComponent } from "./extension-component"; 7 | 8 | export function createComponent(config: any): any { 9 | config.extends = DxComponent(); 10 | return Vue.extend(config); 11 | } 12 | 13 | export function createConfigurationComponent(config: any): any { 14 | config.extends = DxConfiguration(); 15 | return Vue.extend(config); 16 | } 17 | 18 | export function createExtensionComponent(config: any): any { 19 | config.extends = DxExtensionComponent(); 20 | return Vue.extend(config); 21 | } 22 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/core/templates-discovering.ts: -------------------------------------------------------------------------------- 1 | import * as VueType from "vue"; 2 | import IVue, { CreateElement } from "vue"; 3 | import { ScopedSlot } from "vue/types/vnode"; 4 | 5 | import { IConfigurable } from "./configuration-component"; 6 | import { TEMPLATE_MULTIPLE_ROOTS_ERROR } from "./errors"; 7 | 8 | const TEMPLATE_PROP = "template"; 9 | const Vue = VueType.default || VueType; 10 | 11 | interface IEventBusHolder { 12 | eventBus: IVue; 13 | } 14 | 15 | function asConfigurable(component: IVue): IConfigurable | undefined { 16 | if (!component.$vnode) { 17 | return undefined; 18 | } 19 | 20 | const configurable = component.$vnode.componentOptions as any as IConfigurable; 21 | if (!configurable.$_config || !configurable.$_config.name) { 22 | return undefined; 23 | } 24 | 25 | return configurable; 26 | } 27 | 28 | function hasTemplate(component: IVue) { 29 | return TEMPLATE_PROP in component.$props && (component.$vnode.data && component.$vnode.data.scopedSlots); 30 | } 31 | 32 | function discover(component: IVue): Record { 33 | const templates: Record = {}; 34 | for (const slotName in component.$scopedSlots) { 35 | if (slotName === "default" && component.$slots.default) { 36 | continue; 37 | } 38 | 39 | const slot = component.$scopedSlots[slotName]; 40 | if (!slot) { 41 | continue; 42 | } 43 | 44 | templates[slotName] = slot; 45 | } 46 | 47 | for (const childComponent of component.$children) { 48 | const configurable = asConfigurable(childComponent); 49 | if (!configurable) { 50 | continue; 51 | } 52 | 53 | const defaultSlot = childComponent.$scopedSlots.default; 54 | if (!defaultSlot || !hasTemplate(childComponent)) { 55 | continue; 56 | } 57 | 58 | const templateName = `${configurable.$_config.fullPath}.${TEMPLATE_PROP}`; 59 | templates[templateName] = defaultSlot; 60 | } 61 | 62 | return templates; 63 | } 64 | 65 | function mountTemplate( 66 | getSlot: () => ScopedSlot, 67 | parent: IVue, 68 | data: any, 69 | name: string, 70 | placeholder: Element 71 | ): IVue { 72 | return new Vue({ 73 | el: placeholder, 74 | name, 75 | inject: ["eventBus"], 76 | parent, 77 | created(this: IVue & IEventBusHolder) { 78 | this.eventBus.$on("updated", () => { 79 | this.$forceUpdate(); 80 | }); 81 | }, 82 | render: (createElement: CreateElement) => { 83 | const content = getSlot()(data) as any; 84 | if (!content) { 85 | return createElement("div"); 86 | } 87 | 88 | if (content.length > 1) { 89 | throw new Error(TEMPLATE_MULTIPLE_ROOTS_ERROR); 90 | } 91 | 92 | return content[0]; 93 | }, 94 | destroyed() { 95 | // T857821 96 | (this as unknown as IEventBusHolder).eventBus.$off("updated"); 97 | } 98 | }); 99 | } 100 | 101 | export { 102 | mountTemplate, 103 | discover, 104 | IEventBusHolder 105 | }; 106 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/core/templates-manager.ts: -------------------------------------------------------------------------------- 1 | import IVue from "vue"; 2 | import { ScopedSlot } from "vue/types/vnode"; 3 | import { getOption } from "./config"; 4 | import { 5 | discover as discoverSlots, 6 | mountTemplate 7 | } from "./templates-discovering"; 8 | 9 | import domAdapter from "devextreme/core/dom_adapter"; 10 | import { one } from "devextreme/events"; 11 | import { DX_REMOVE_EVENT, DX_TEMPLATE_WRAPPER_CLASS } from "./constants"; 12 | import { allKeysAreEqual } from "./helpers"; 13 | 14 | class TemplatesManager { 15 | private _component: IVue; 16 | private _slots: Record = {}; 17 | private _templates: Record = {}; 18 | private _isDirty: boolean = false; 19 | 20 | constructor(component: IVue) { 21 | this._component = component; 22 | this.discover(); 23 | } 24 | 25 | public discover() { 26 | const slots = discoverSlots(this._component); 27 | this._slots = { 28 | ...this._slots, 29 | ...slots 30 | }; 31 | 32 | if (!allKeysAreEqual(this._templates, slots)) { 33 | this._prepareTemplates(); 34 | } 35 | } 36 | 37 | public get templates() { 38 | return this._templates; 39 | } 40 | 41 | public get isDirty() { 42 | return this._isDirty; 43 | } 44 | 45 | public resetDirtyFlag() { 46 | this._isDirty = false; 47 | } 48 | 49 | private _prepareTemplates() { 50 | this._templates = {}; 51 | 52 | for (const name of Object.keys(this._slots)) { 53 | this._templates[name] = this.createDxTemplate(name); 54 | } 55 | 56 | this._isDirty = true; 57 | } 58 | 59 | private createDxTemplate(name: string) { 60 | return { 61 | render: (data: any) => { 62 | const scopeData = getOption("useLegacyTemplateEngine") 63 | ? data.model 64 | : { data: data.model, index: data.index }; 65 | 66 | const container = data.container.get ? data.container.get(0) : data.container; 67 | const placeholder = document.createElement("div"); 68 | container.appendChild(placeholder); 69 | const mountedTemplate = mountTemplate( 70 | () => this._slots[name], 71 | this._component, 72 | scopeData, 73 | name, 74 | placeholder 75 | ); 76 | 77 | const element = mountedTemplate.$el as HTMLElement; 78 | 79 | domAdapter.setClass(element, DX_TEMPLATE_WRAPPER_CLASS, true); 80 | 81 | if (element.nodeType === Node.TEXT_NODE) { 82 | const removalListener = document.createElement(container.nodeName === "TABLE" ? "tbody" : "span"); 83 | removalListener.style.display = "none"; 84 | container.appendChild(removalListener); 85 | one(removalListener, DX_REMOVE_EVENT, mountedTemplate.$destroy.bind(mountedTemplate)); 86 | } else { 87 | one(element, DX_REMOVE_EVENT, mountedTemplate.$destroy.bind(mountedTemplate)); 88 | } 89 | 90 | return element; 91 | } 92 | }; 93 | } 94 | } 95 | 96 | export { TemplatesManager }; 97 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/draggable.ts: -------------------------------------------------------------------------------- 1 | import Draggable, { Properties } from "devextreme/ui/draggable"; 2 | import { createComponent } from "./core/index"; 3 | import { createConfigurationComponent } from "./core/index"; 4 | 5 | type AccessibleOptions = Pick; 29 | 30 | interface DxDraggable extends AccessibleOptions { 31 | readonly instance?: Draggable; 32 | } 33 | const DxDraggable = createComponent({ 34 | props: { 35 | autoScroll: Boolean, 36 | boundary: {}, 37 | clone: Boolean, 38 | container: {}, 39 | cursorOffset: [Object, String], 40 | data: {}, 41 | dragDirection: String, 42 | dragTemplate: {}, 43 | elementAttr: Object, 44 | group: String, 45 | handle: String, 46 | height: [Function, Number, String], 47 | onDisposing: Function, 48 | onDragEnd: Function, 49 | onDragMove: Function, 50 | onDragStart: Function, 51 | onInitialized: Function, 52 | onOptionChanged: Function, 53 | rtlEnabled: Boolean, 54 | scrollSensitivity: Number, 55 | scrollSpeed: Number, 56 | width: [Function, Number, String] 57 | }, 58 | emits: { 59 | "update:isActive": null, 60 | "update:hoveredElement": null, 61 | "update:autoScroll": null, 62 | "update:boundary": null, 63 | "update:clone": null, 64 | "update:container": null, 65 | "update:cursorOffset": null, 66 | "update:data": null, 67 | "update:dragDirection": null, 68 | "update:dragTemplate": null, 69 | "update:elementAttr": null, 70 | "update:group": null, 71 | "update:handle": null, 72 | "update:height": null, 73 | "update:onDisposing": null, 74 | "update:onDragEnd": null, 75 | "update:onDragMove": null, 76 | "update:onDragStart": null, 77 | "update:onInitialized": null, 78 | "update:onOptionChanged": null, 79 | "update:rtlEnabled": null, 80 | "update:scrollSensitivity": null, 81 | "update:scrollSpeed": null, 82 | "update:width": null, 83 | }, 84 | computed: { 85 | instance(): Draggable { 86 | return (this as any).$_instance; 87 | } 88 | }, 89 | beforeCreate() { 90 | (this as any).$_WidgetClass = Draggable; 91 | (this as any).$_expectedChildren = { 92 | cursorOffset: { isCollectionItem: false, optionName: "cursorOffset" } 93 | }; 94 | } 95 | }); 96 | 97 | const DxCursorOffset = createConfigurationComponent({ 98 | emits: { 99 | "update:isActive": null, 100 | "update:hoveredElement": null, 101 | "update:x": null, 102 | "update:y": null, 103 | }, 104 | props: { 105 | x: Number, 106 | y: Number 107 | } 108 | }); 109 | (DxCursorOffset as any).$_optionName = "cursorOffset"; 110 | 111 | export default DxDraggable; 112 | export { 113 | DxDraggable, 114 | DxCursorOffset 115 | }; 116 | import type * as DxDraggableTypes from "devextreme/ui/draggable_types"; 117 | export { DxDraggableTypes }; 118 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/drawer.ts: -------------------------------------------------------------------------------- 1 | import Drawer, { Properties } from "devextreme/ui/drawer"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 29 | 30 | interface DxDrawer extends AccessibleOptions { 31 | readonly instance?: Drawer; 32 | } 33 | const DxDrawer = createComponent({ 34 | props: { 35 | activeStateEnabled: Boolean, 36 | animationDuration: Number, 37 | animationEnabled: Boolean, 38 | closeOnOutsideClick: [Boolean, Function], 39 | disabled: Boolean, 40 | elementAttr: Object, 41 | height: [Function, Number, String], 42 | hint: String, 43 | hoverStateEnabled: Boolean, 44 | maxSize: Number, 45 | minSize: Number, 46 | onDisposing: Function, 47 | onInitialized: Function, 48 | onOptionChanged: Function, 49 | opened: Boolean, 50 | openedStateMode: String, 51 | position: String, 52 | revealMode: String, 53 | rtlEnabled: Boolean, 54 | shading: Boolean, 55 | template: {}, 56 | visible: Boolean, 57 | width: [Function, Number, String] 58 | }, 59 | emits: { 60 | "update:isActive": null, 61 | "update:hoveredElement": null, 62 | "update:activeStateEnabled": null, 63 | "update:animationDuration": null, 64 | "update:animationEnabled": null, 65 | "update:closeOnOutsideClick": null, 66 | "update:disabled": null, 67 | "update:elementAttr": null, 68 | "update:height": null, 69 | "update:hint": null, 70 | "update:hoverStateEnabled": null, 71 | "update:maxSize": null, 72 | "update:minSize": null, 73 | "update:onDisposing": null, 74 | "update:onInitialized": null, 75 | "update:onOptionChanged": null, 76 | "update:opened": null, 77 | "update:openedStateMode": null, 78 | "update:position": null, 79 | "update:revealMode": null, 80 | "update:rtlEnabled": null, 81 | "update:shading": null, 82 | "update:template": null, 83 | "update:visible": null, 84 | "update:width": null, 85 | }, 86 | computed: { 87 | instance(): Drawer { 88 | return (this as any).$_instance; 89 | } 90 | }, 91 | beforeCreate() { 92 | (this as any).$_WidgetClass = Drawer; 93 | } 94 | }); 95 | 96 | export default DxDrawer; 97 | export { 98 | DxDrawer 99 | }; 100 | import type * as DxDrawerTypes from "devextreme/ui/drawer_types"; 101 | export { DxDrawerTypes }; 102 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/load-indicator.ts: -------------------------------------------------------------------------------- 1 | import LoadIndicator, { Properties } from "devextreme/ui/load_indicator"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 17 | 18 | interface DxLoadIndicator extends AccessibleOptions { 19 | readonly instance?: LoadIndicator; 20 | } 21 | const DxLoadIndicator = createComponent({ 22 | props: { 23 | elementAttr: Object, 24 | height: [Function, Number, String], 25 | hint: String, 26 | indicatorSrc: String, 27 | onContentReady: Function, 28 | onDisposing: Function, 29 | onInitialized: Function, 30 | onOptionChanged: Function, 31 | rtlEnabled: Boolean, 32 | visible: Boolean, 33 | width: [Function, Number, String] 34 | }, 35 | emits: { 36 | "update:isActive": null, 37 | "update:hoveredElement": null, 38 | "update:elementAttr": null, 39 | "update:height": null, 40 | "update:hint": null, 41 | "update:indicatorSrc": null, 42 | "update:onContentReady": null, 43 | "update:onDisposing": null, 44 | "update:onInitialized": null, 45 | "update:onOptionChanged": null, 46 | "update:rtlEnabled": null, 47 | "update:visible": null, 48 | "update:width": null, 49 | }, 50 | computed: { 51 | instance(): LoadIndicator { 52 | return (this as any).$_instance; 53 | } 54 | }, 55 | beforeCreate() { 56 | (this as any).$_WidgetClass = LoadIndicator; 57 | } 58 | }); 59 | 60 | export default DxLoadIndicator; 61 | export { 62 | DxLoadIndicator 63 | }; 64 | import type * as DxLoadIndicatorTypes from "devextreme/ui/load_indicator_types"; 65 | export { DxLoadIndicatorTypes }; 66 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/progress-bar.ts: -------------------------------------------------------------------------------- 1 | import ProgressBar, { Properties } from "devextreme/ui/progress_bar"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 33 | 34 | interface DxProgressBar extends AccessibleOptions { 35 | readonly instance?: ProgressBar; 36 | } 37 | const DxProgressBar = createComponent({ 38 | props: { 39 | disabled: Boolean, 40 | elementAttr: Object, 41 | height: [Function, Number, String], 42 | hint: String, 43 | hoverStateEnabled: Boolean, 44 | isDirty: Boolean, 45 | isValid: Boolean, 46 | max: Number, 47 | min: Number, 48 | onComplete: Function, 49 | onContentReady: Function, 50 | onDisposing: Function, 51 | onInitialized: Function, 52 | onOptionChanged: Function, 53 | onValueChanged: Function, 54 | readOnly: Boolean, 55 | rtlEnabled: Boolean, 56 | showStatus: Boolean, 57 | statusFormat: [Function, String], 58 | validationError: {}, 59 | validationErrors: Array, 60 | validationMessageMode: String, 61 | validationMessagePosition: String, 62 | validationStatus: String, 63 | value: {}, 64 | visible: Boolean, 65 | width: [Function, Number, String] 66 | }, 67 | emits: { 68 | "update:isActive": null, 69 | "update:hoveredElement": null, 70 | "update:disabled": null, 71 | "update:elementAttr": null, 72 | "update:height": null, 73 | "update:hint": null, 74 | "update:hoverStateEnabled": null, 75 | "update:isDirty": null, 76 | "update:isValid": null, 77 | "update:max": null, 78 | "update:min": null, 79 | "update:onComplete": null, 80 | "update:onContentReady": null, 81 | "update:onDisposing": null, 82 | "update:onInitialized": null, 83 | "update:onOptionChanged": null, 84 | "update:onValueChanged": null, 85 | "update:readOnly": null, 86 | "update:rtlEnabled": null, 87 | "update:showStatus": null, 88 | "update:statusFormat": null, 89 | "update:validationError": null, 90 | "update:validationErrors": null, 91 | "update:validationMessageMode": null, 92 | "update:validationMessagePosition": null, 93 | "update:validationStatus": null, 94 | "update:value": null, 95 | "update:visible": null, 96 | "update:width": null, 97 | }, 98 | computed: { 99 | instance(): ProgressBar { 100 | return (this as any).$_instance; 101 | } 102 | }, 103 | beforeCreate() { 104 | (this as any).$_WidgetClass = ProgressBar; 105 | } 106 | }); 107 | 108 | export default DxProgressBar; 109 | export { 110 | DxProgressBar 111 | }; 112 | import type * as DxProgressBarTypes from "devextreme/ui/progress_bar_types"; 113 | export { DxProgressBarTypes }; 114 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/resizable.ts: -------------------------------------------------------------------------------- 1 | import Resizable, { Properties } from "devextreme/ui/resizable"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 23 | 24 | interface DxResizable extends AccessibleOptions { 25 | readonly instance?: Resizable; 26 | } 27 | const DxResizable = createComponent({ 28 | props: { 29 | area: {}, 30 | elementAttr: Object, 31 | handles: String, 32 | height: [Function, Number, String], 33 | keepAspectRatio: Boolean, 34 | maxHeight: Number, 35 | maxWidth: Number, 36 | minHeight: Number, 37 | minWidth: Number, 38 | onDisposing: Function, 39 | onInitialized: Function, 40 | onOptionChanged: Function, 41 | onResize: Function, 42 | onResizeEnd: Function, 43 | onResizeStart: Function, 44 | rtlEnabled: Boolean, 45 | width: [Function, Number, String] 46 | }, 47 | emits: { 48 | "update:isActive": null, 49 | "update:hoveredElement": null, 50 | "update:area": null, 51 | "update:elementAttr": null, 52 | "update:handles": null, 53 | "update:height": null, 54 | "update:keepAspectRatio": null, 55 | "update:maxHeight": null, 56 | "update:maxWidth": null, 57 | "update:minHeight": null, 58 | "update:minWidth": null, 59 | "update:onDisposing": null, 60 | "update:onInitialized": null, 61 | "update:onOptionChanged": null, 62 | "update:onResize": null, 63 | "update:onResizeEnd": null, 64 | "update:onResizeStart": null, 65 | "update:rtlEnabled": null, 66 | "update:width": null, 67 | }, 68 | computed: { 69 | instance(): Resizable { 70 | return (this as any).$_instance; 71 | } 72 | }, 73 | beforeCreate() { 74 | (this as any).$_WidgetClass = Resizable; 75 | } 76 | }); 77 | 78 | export default DxResizable; 79 | export { 80 | DxResizable 81 | }; 82 | import type * as DxResizableTypes from "devextreme/ui/resizable_types"; 83 | export { DxResizableTypes }; 84 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/scroll-view.ts: -------------------------------------------------------------------------------- 1 | import ScrollView, { Properties } from "devextreme/ui/scroll_view"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 28 | 29 | interface DxScrollView extends AccessibleOptions { 30 | readonly instance?: ScrollView; 31 | } 32 | const DxScrollView = createComponent({ 33 | props: { 34 | bounceEnabled: Boolean, 35 | direction: String, 36 | disabled: Boolean, 37 | elementAttr: Object, 38 | height: [Function, Number, String], 39 | onDisposing: Function, 40 | onInitialized: Function, 41 | onOptionChanged: Function, 42 | onPullDown: Function, 43 | onReachBottom: Function, 44 | onScroll: Function, 45 | onUpdated: Function, 46 | pulledDownText: String, 47 | pullingDownText: String, 48 | reachBottomText: String, 49 | refreshingText: String, 50 | rtlEnabled: Boolean, 51 | scrollByContent: Boolean, 52 | scrollByThumb: Boolean, 53 | showScrollbar: String, 54 | useNative: Boolean, 55 | width: [Function, Number, String] 56 | }, 57 | emits: { 58 | "update:isActive": null, 59 | "update:hoveredElement": null, 60 | "update:bounceEnabled": null, 61 | "update:direction": null, 62 | "update:disabled": null, 63 | "update:elementAttr": null, 64 | "update:height": null, 65 | "update:onDisposing": null, 66 | "update:onInitialized": null, 67 | "update:onOptionChanged": null, 68 | "update:onPullDown": null, 69 | "update:onReachBottom": null, 70 | "update:onScroll": null, 71 | "update:onUpdated": null, 72 | "update:pulledDownText": null, 73 | "update:pullingDownText": null, 74 | "update:reachBottomText": null, 75 | "update:refreshingText": null, 76 | "update:rtlEnabled": null, 77 | "update:scrollByContent": null, 78 | "update:scrollByThumb": null, 79 | "update:showScrollbar": null, 80 | "update:useNative": null, 81 | "update:width": null, 82 | }, 83 | computed: { 84 | instance(): ScrollView { 85 | return (this as any).$_instance; 86 | } 87 | }, 88 | beforeCreate() { 89 | (this as any).$_WidgetClass = ScrollView; 90 | } 91 | }); 92 | 93 | export default DxScrollView; 94 | export { 95 | DxScrollView 96 | }; 97 | import type * as DxScrollViewTypes from "devextreme/ui/scroll_view_types"; 98 | export { DxScrollViewTypes }; 99 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/speed-dial-action.ts: -------------------------------------------------------------------------------- 1 | import SpeedDialAction, { Properties } from "devextreme/ui/speed_dial_action"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 23 | 24 | interface DxSpeedDialAction extends AccessibleOptions { 25 | readonly instance?: SpeedDialAction; 26 | } 27 | const DxSpeedDialAction = createComponent({ 28 | props: { 29 | accessKey: String, 30 | activeStateEnabled: Boolean, 31 | elementAttr: Object, 32 | focusStateEnabled: Boolean, 33 | hint: String, 34 | hoverStateEnabled: Boolean, 35 | icon: String, 36 | index: Number, 37 | label: String, 38 | onClick: Function, 39 | onContentReady: Function, 40 | onDisposing: Function, 41 | onInitialized: Function, 42 | onOptionChanged: Function, 43 | rtlEnabled: Boolean, 44 | tabIndex: Number, 45 | visible: Boolean 46 | }, 47 | emits: { 48 | "update:isActive": null, 49 | "update:hoveredElement": null, 50 | "update:accessKey": null, 51 | "update:activeStateEnabled": null, 52 | "update:elementAttr": null, 53 | "update:focusStateEnabled": null, 54 | "update:hint": null, 55 | "update:hoverStateEnabled": null, 56 | "update:icon": null, 57 | "update:index": null, 58 | "update:label": null, 59 | "update:onClick": null, 60 | "update:onContentReady": null, 61 | "update:onDisposing": null, 62 | "update:onInitialized": null, 63 | "update:onOptionChanged": null, 64 | "update:rtlEnabled": null, 65 | "update:tabIndex": null, 66 | "update:visible": null, 67 | }, 68 | computed: { 69 | instance(): SpeedDialAction { 70 | return (this as any).$_instance; 71 | } 72 | }, 73 | beforeCreate() { 74 | (this as any).$_WidgetClass = SpeedDialAction; 75 | } 76 | }); 77 | 78 | export default DxSpeedDialAction; 79 | export { 80 | DxSpeedDialAction 81 | }; 82 | import type * as DxSpeedDialActionTypes from "devextreme/ui/speed_dial_action_types"; 83 | export { DxSpeedDialActionTypes }; 84 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/validation-group.ts: -------------------------------------------------------------------------------- 1 | import ValidationGroup, { Properties } from "devextreme/ui/validation_group"; 2 | import { createComponent } from "./core/index"; 3 | 4 | type AccessibleOptions = Pick; 12 | 13 | interface DxValidationGroup extends AccessibleOptions { 14 | readonly instance?: ValidationGroup; 15 | } 16 | const DxValidationGroup = createComponent({ 17 | props: { 18 | elementAttr: Object, 19 | height: [Function, Number, String], 20 | onDisposing: Function, 21 | onInitialized: Function, 22 | onOptionChanged: Function, 23 | width: [Function, Number, String] 24 | }, 25 | emits: { 26 | "update:isActive": null, 27 | "update:hoveredElement": null, 28 | "update:elementAttr": null, 29 | "update:height": null, 30 | "update:onDisposing": null, 31 | "update:onInitialized": null, 32 | "update:onOptionChanged": null, 33 | "update:width": null, 34 | }, 35 | computed: { 36 | instance(): ValidationGroup { 37 | return (this as any).$_instance; 38 | } 39 | }, 40 | beforeCreate() { 41 | (this as any).$_WidgetClass = ValidationGroup; 42 | } 43 | }); 44 | 45 | export default DxValidationGroup; 46 | export { 47 | DxValidationGroup 48 | }; 49 | import type * as DxValidationGroupTypes from "devextreme/ui/validation_group_types"; 50 | export { DxValidationGroupTypes }; 51 | -------------------------------------------------------------------------------- /packages/vue2-strategy/src/validation-summary.ts: -------------------------------------------------------------------------------- 1 | export { ExplicitTypes } from "devextreme/ui/validation_summary"; 2 | import ValidationSummary, { Properties } from "devextreme/ui/validation_summary"; 3 | import { createComponent } from "./core/index"; 4 | import { createConfigurationComponent } from "./core/index"; 5 | 6 | type AccessibleOptions = Pick; 18 | 19 | interface DxValidationSummary extends AccessibleOptions { 20 | readonly instance?: ValidationSummary; 21 | } 22 | const DxValidationSummary = createComponent({ 23 | props: { 24 | elementAttr: Object, 25 | hoverStateEnabled: Boolean, 26 | items: Array, 27 | itemTemplate: {}, 28 | onContentReady: Function, 29 | onDisposing: Function, 30 | onInitialized: Function, 31 | onItemClick: Function, 32 | onOptionChanged: Function, 33 | validationGroup: String 34 | }, 35 | emits: { 36 | "update:isActive": null, 37 | "update:hoveredElement": null, 38 | "update:elementAttr": null, 39 | "update:hoverStateEnabled": null, 40 | "update:items": null, 41 | "update:itemTemplate": null, 42 | "update:onContentReady": null, 43 | "update:onDisposing": null, 44 | "update:onInitialized": null, 45 | "update:onItemClick": null, 46 | "update:onOptionChanged": null, 47 | "update:validationGroup": null, 48 | }, 49 | computed: { 50 | instance(): ValidationSummary { 51 | return (this as any).$_instance; 52 | } 53 | }, 54 | beforeCreate() { 55 | (this as any).$_WidgetClass = ValidationSummary; 56 | (this as any).$_expectedChildren = { 57 | item: { isCollectionItem: true, optionName: "items" } 58 | }; 59 | } 60 | }); 61 | 62 | const DxItem = createConfigurationComponent({ 63 | emits: { 64 | "update:isActive": null, 65 | "update:hoveredElement": null, 66 | "update:disabled": null, 67 | "update:html": null, 68 | "update:template": null, 69 | "update:text": null, 70 | "update:visible": null, 71 | }, 72 | props: { 73 | disabled: Boolean, 74 | html: String, 75 | template: {}, 76 | text: String, 77 | visible: Boolean 78 | } 79 | }); 80 | (DxItem as any).$_optionName = "items"; 81 | (DxItem as any).$_isCollectionItem = true; 82 | 83 | export default DxValidationSummary; 84 | export { 85 | DxValidationSummary, 86 | DxItem 87 | }; 88 | import type * as DxValidationSummaryTypes from "devextreme/ui/validation_summary_types"; 89 | export { DxValidationSummaryTypes }; 90 | -------------------------------------------------------------------------------- /packages/vue2-strategy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "skipLibCheck": true, 5 | "lib": [ 6 | "es2015", 7 | "dom" 8 | ] 9 | }, 10 | "include": [ 11 | "src/core/**/*" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/vue2-strategy/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: "./sandbox/main.ts", 3 | output: { 4 | filename: "./sandbox/public/js/app/bundle.js", 5 | }, 6 | devtool: "source-map", 7 | devServer: { 8 | port: 9900, 9 | open: true, 10 | openPage: "sandbox/public/index.html" 11 | }, 12 | resolve: { 13 | extensions: [".webpack.js", ".web.js", ".ts", ".vue", ".js"], 14 | alias: { 15 | 'vue$': 'vue/dist/vue.esm.js' 16 | } 17 | }, 18 | mode: "development", 19 | module: { 20 | rules: [ 21 | { 22 | test: /\.vue$/, 23 | loader: 'vue-loader', 24 | options: { 25 | esModule: true 26 | } 27 | }, 28 | { 29 | test: /\.js$/, 30 | use: "source-map-loader", 31 | enforce: "pre" 32 | }, 33 | { 34 | test: /\.tsx?$/, 35 | use: { 36 | loader: "ts-loader", 37 | options: { 38 | appendTsSuffixTo: [/\.vue$/], 39 | compilerOptions: { 40 | "noImplicitAny": false 41 | } 42 | } 43 | } 44 | }, 45 | { 46 | test: /\.css$/, 47 | use: [ 48 | { loader: "style-loader" }, 49 | { loader: "css-loader" } 50 | ] 51 | }, 52 | { 53 | test: /\.(eot|svg|ttf|woff|woff2)$/, 54 | use: "url-loader?name=[name].[ext]" 55 | } 56 | ] 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./packages", 4 | "paths": { 5 | "devextreme-vue-generator": [ 6 | "devextreme-vue-generator/src" 7 | ], 8 | "devextreme-vue": [ 9 | "devextreme-vue/src" 10 | ], 11 | "devextreme-vue/*": [ 12 | "devextreme-vue/src/*" 13 | ], 14 | "devextreme-vue2-strategy": [ 15 | "vue2-strategy/src/core" 16 | ] 17 | }, 18 | "esModuleInterop": true, 19 | "allowSyntheticDefaultImports": true, 20 | "target": "es5", 21 | "module": "commonjs", 22 | "noEmitOnError": true, 23 | "declaration": true, 24 | "experimentalDecorators": true, 25 | "forceConsistentCasingInFileNames": true, 26 | "noFallthroughCasesInSwitch": true, 27 | "noImplicitAny": false, 28 | "noImplicitReturns": true, 29 | "noImplicitThis": true, 30 | "noUnusedLocals": true, 31 | "noUnusedParameters": true, 32 | "strictNullChecks": true, 33 | "lib": [ 34 | "esnext", 35 | "dom" 36 | ] 37 | }, 38 | "exclude": [ 39 | "node_modules", 40 | "dist" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "object-literal-sort-keys": false, 9 | "trailing-comma": false, 10 | "variable-name": false, 11 | "interface-name": false 12 | }, 13 | "rulesDirectory": [] 14 | } --------------------------------------------------------------------------------
{{ stateStr }}
{{stateStr}}