diff --git a/.eslintignore b/.eslintignore index a448724fb20..22f48fb5f6d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,7 +2,9 @@ node_modules/* MVCDemos/* bundles/* NetCoreDemos/* +publish-demos/* JSDemos/Demos/**/testcafe-test-code.js /**/azure.file.system.js JSDemos/Demos/**/config.js +JSDemos/Demos/**/Vue/**/*.html diff --git a/.github/workflows/build-aspnet.yml b/.github/workflows/build-aspnet.yml new file mode 100644 index 00000000000..7649fdc37b0 --- /dev/null +++ b/.github/workflows/build-aspnet.yml @@ -0,0 +1,67 @@ +name: Build aspnet demos + +concurrency: + group: wf-${{ github.event.pull_request.number || github.sha }}-${{ github.workflow }} + cancel-in-progress: true + +on: + pull_request: + push: + branches: + - "[0-9][0-9]_[0-9]" + +jobs: + build: + name: Build aspnet demos + runs-on: windows-2019 + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork == false + + steps: + - name: Get sources + uses: actions/checkout@v4 + + - name: Get version + id: get_version + run: | + $env:version=$(node -p -e "require('./package.json').version.slice(0, 4).replace('.', '_')") + echo "version=$env:version" >> $GITHUB_OUTPUT + + - name: Clone devextreme-aspnet repo + uses: actions/checkout@v4 + with: + repository: DevExpress/devextreme-aspnet + ref: ${{ steps.get_version.outputs.version }} + path: devextreme-aspnet + token: ${{ secrets.ASPNET_PAT }} + + - name: Restore npm cache + uses: actions/cache@v3 + with: + path: ./node_modules + key: ${{ runner.os }}-node-modules-${{ hashFiles('**/package-lock.json') }} + + - name: Run npm install + run: npm install --no-audit --no-fund + + - name: Update repository.config.json + run: | + $jsonData = Get-Content -Raw -Path "repository.config.json" | ConvertFrom-Json + $jsonData.DevExtreme = "${{ github.workspace }}\DevExtreme" + $jsonData."devextreme-aspnet" = "${{ github.workspace }}\devextreme-aspnet" + $jsonData | ConvertTo-Json | Set-Content -Path "repository.config.json" + + - name: Run prepare-aspnet + run: npm run prepare-aspnet + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1 + + - name: Build MVC demos + working-directory: MVCDemos + run: | + nuget restore DevExtreme.MVC.Demos.sln + msbuild DevExtreme.MVC.Demos.sln + + - name: Build Core demos + working-directory: NetCoreDemos + run: dotnet build DevExtreme.NETCore.Demos.sln diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 36b593261c8..fd7cf7cde88 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -30,41 +30,4 @@ jobs: npm install --no-audit --no-fund --ignore-scripts git diff --exit-code package-lock.json - check_generated_demos: - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - name: Get sources - uses: actions/checkout@v3 - - - name: Use Node.js - uses: actions/setup-node@v3 - with: - node-version: '16' - - - name: Restore npm cache - uses: actions/cache@v3 - with: - path: ./node_modules - key: ${{ runner.os }}-node-modules-${{ hashFiles('**/package-lock.json') }} - restore-keys: ${{ runner.os }}-node-modules - - - name: Check generated JS demos - run: | - npm install --no-audit --no-fund --ignore-scripts - - git reset --hard - - npm run convert-to-js ./JSDemos/Demos/**/React - - git add ./JSDemos/Demos -N - - if git diff --exit-code ; then - echo "Generated JS demos are up-to-date" - else - echo "Generated JS demos are outdated. Execute 'npm run convert-to-js ./JSDemos/Demos/**/React' and commit changes." - echo "If you see another diff, ensure that extra listed files have LF endings." - exit 1 - fi - diff --git a/.github/workflows/publish-demos.yml b/.github/workflows/publish-demos.yml new file mode 100644 index 00000000000..7bb301bf2dd --- /dev/null +++ b/.github/workflows/publish-demos.yml @@ -0,0 +1,46 @@ +name: Publish Demos to GH Pages + +on: + workflow_dispatch: + push: + tags: + - 'release-*' + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Install jq + run: sudo apt-get install -y jq + + - name: Read version from package.json and append "-stable" + run: echo echo "STABLE_VERSION=$(jq -r '.version' package.json | awk -F. '{print $1 "." $2}')-stable" >> $GITHUB_ENV + + - name: Install Dependencies + run: npm install + + - name: Install stable devextreme pacakges + run: npm install devextreme@${{ env.STABLE_VERSION }} devextreme-angular@${{ env.STABLE_VERSION }} devextreme-react@${{ env.STABLE_VERSION }} devextreme-vue@${{ env.STABLE_VERSION }} + + - name: Copy metadata + run: npm run make-demos-bundle -- --copy-metadata + + - name: Prepare React demo bundles + run: npm run make-demos-bundle -- --framework React + + - name: Prepare Vue demo bundles + run: npm run make-demos-bundle -- --framework Vue + + - name: Deploy to gh-pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./publish-demos diff --git a/.github/workflows/visual_tests.yml b/.github/workflows/visual_tests.yml index 0301bd2cfaf..b4ba7e43b0d 100644 --- a/.github/workflows/visual_tests.yml +++ b/.github/workflows/visual_tests.yml @@ -40,7 +40,7 @@ jobs: uses: actions/cache@v3 with: path: 'devextreme-repo/**/node_modules' - key: ${{ runner.os }}-node-modules-${{ hashFiles('devextreme-repo/**/package-lock.json') }} + key: ${{ runner.os }}-devextreme-node-modules-${{ hashFiles('devextreme-repo/**/package-lock.json') }} - name: Use Node.js 18 uses: actions/setup-node@v3 @@ -112,7 +112,7 @@ jobs: steps: - name: Pin Chrome version run: | - CHROME_VERSION=116.0.5845.110 + CHROME_VERSION=117.0.5938.92 wget "https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROME_VERSION}-1_amd64.deb" sudo dpkg -i "google-chrome-stable_${CHROME_VERSION}-1_amd64.deb" diff --git a/.github/workflows/visual_tests_frameworks.yml b/.github/workflows/visual_tests_frameworks.yml index 43644407e79..77d9bc387e3 100644 --- a/.github/workflows/visual_tests_frameworks.yml +++ b/.github/workflows/visual_tests_frameworks.yml @@ -43,7 +43,7 @@ jobs: uses: actions/cache@v3 with: path: 'devextreme/**/node_modules' - key: ${{ runner.os }}-node-modules-${{ hashFiles('devextreme/**/package-lock.json') }} + key: ${{ runner.os }}-devextreme-node-modules-${{ hashFiles('devextreme/**/package-lock.json') }} - name: Use Node.js 18 uses: actions/setup-node@v3 @@ -150,8 +150,6 @@ jobs: with: path: ./node_modules key: ${{ runner.os }}-node-modules-${{ hashFiles('package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node-modules - name: Delete DX packages run: sed -i '/23.2-next/d' ./package.json @@ -187,17 +185,39 @@ jobs: retention-days: 1 lint: - name: Lint code base - needs: - - build-demos + name: ${{ matrix.name }} + needs: build-devextreme runs-on: ubuntu-latest timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + - name: Lint code base (excluding demos) + command: npm run lint-non-demos + - name: Lint demos (1/4) + command: CONSTEL=1/4 npm run lint-demos + - name: Lint demos (2/4) + command: CONSTEL=2/4 npm run lint-demos + - name: Lint demos (3/4) + command: CONSTEL=3/4 npm run lint-demos + - name: Lint demos (4/4) + command: CONSTEL=4/4 npm run lint-demos + steps: - name: Get sources uses: actions/checkout@v3 + - name: Get changed files + uses: ./.github/actions/get-changed-files + if: github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'force all tests') + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + path: changed-files.json + - name: Delete DX packages run: sed -i '/23.2-next/d' ./package.json @@ -206,9 +226,6 @@ jobs: with: name: devextreme-sources - - name: Unpack artifacts - run: 7z x node_modules.7z - - name: Install packages run: | npm install devextreme-installer.tgz @@ -230,7 +247,73 @@ jobs: dotnet tool install -g dotnet-format --version 5.1.225507 - name: Run lint - run: npm run lint + env: + CHANGEDFILEINFOSPATH: ${{ github.workspace }}/changed-files.json + DEBUG: 'eslint:cli-engine,stylelint:standalone' + run: ${{ matrix.command }} + + check_generated_demos: + name: ${{ matrix.name }} + runs-on: ubuntu-latest + timeout-minutes: 10 + needs: build-demos + strategy: + fail-fast: false + matrix: + include: + - name: Check generated demos (1/5) + command: CONSTEL=1/5 npm run convert-to-js + - name: Check generated demos (2/5) + command: CONSTEL=2/5 npm run convert-to-js + - name: Check generated demos (3/5) + command: CONSTEL=3/5 npm run convert-to-js + - name: Check generated demos (4/5) + command: CONSTEL=4/5 npm run convert-to-js + - name: Check generated demos (5/5) + command: CONSTEL=5/5 npm run convert-to-js + steps: + - name: Get sources + uses: actions/checkout@v3 + + - name: Delete DX packages + run: sed -i '/23.2-next/d' ./package.json + + - name: Download devextreme sources + uses: actions/download-artifact@v3 + with: + name: devextreme-sources + + - name: Unpack artifacts + run: 7z x node_modules.7z + + - name: Install packages + run: | + npm install devextreme-installer.tgz + npm install devextreme-dist-installer.tgz + npm install devextreme-react-installer.tgz + npm install devextreme-vue-installer.tgz + npm install devextreme-angular-installer.tgz + + - name: Run npm install + run: npm install --no-audit --no-fund + + - name: Prepare JS + run: npm run prepare-js + + - name: Check generated JS demos + run: | + ${{ matrix.command }} + + git add ./JSDemos/Demos -N + + if git diff --exit-code . ':!package-lock.json' ':!package.json' ; then + echo "Generated JS demos are up-to-date" + else + echo "Generated JS demos are outdated. Execute 'npm run convert-to-js ./JSDemos/Demos/**/React' and commit changes." + echo "If you see another diff, ensure that extra listed files have LF endings." + exit 1 + fi + testcafe: needs: build-demos @@ -246,7 +329,7 @@ jobs: steps: - name: Pin Chrome version run: | - CHROME_VERSION=116.0.5845.110 + CHROME_VERSION=117.0.5938.92 wget "https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROME_VERSION}-1_amd64.deb" sudo dpkg -i "google-chrome-stable_${CHROME_VERSION}-1_amd64.deb" @@ -267,8 +350,8 @@ jobs: - name: Prepare JS run: npm run prepare-js - - name: Prepare Bundles - run: npx gulp bundles + - name: Update bundles config + run: npx gulp update-config - name: Run Web Server run: | diff --git a/.gitignore b/.gitignore index dc4fd98119c..4dba461b6ab 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ bin obj lib NuGet -.vscode +.vscode/* .idea .vs .idea @@ -21,6 +21,7 @@ screenshots bundles changed-files.json +!.vscode/settings.json gulpfile.js/.eslintrc.js **/azure.file.system.js @@ -28,6 +29,7 @@ JSDemos/Demos/FileManager/AzureClientBinding/Angular/app/app.service.ts JSDemos/Demos/FileUploader/AzureDirectUploading/Angular/app/app.service.ts JSDemos/Demos/**/config.js +JSDemos/Demos/**/tsconfig.json MVCDemos/App_Data/Northwind.mdf MVCDemos/Content/icons @@ -56,3 +58,4 @@ NetCoreDemos/menuMeta.json NetCoreDemos/wwwroot/bundle.min.css NetCoreDemos/wwwroot/bundle.min.js .DS_Store +publish-demos diff --git a/.prettierignore b/.prettierignore index 84651dba2d0..836f34e9ff3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,4 +8,5 @@ *.json MVCDemos NetCoreDemos -.github \ No newline at end of file +.github +publish-demos \ No newline at end of file diff --git a/.stylelintignore b/.stylelintignore index c4ae7064380..216990a3962 100644 --- a/.stylelintignore +++ b/.stylelintignore @@ -1,2 +1,3 @@ MVCDemos NetCoreDemos +publish-demos diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..4e38f6cfcf9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.tabSize": 2, +} \ No newline at end of file diff --git a/JSDemos/Demos/Accordion/Overview/Angular/app/app.component.css b/JSDemos/Demos/Accordion/Overview/Angular/app/app.component.css index 7cb869d9c8a..24b96ccbbf1 100644 --- a/JSDemos/Demos/Accordion/Overview/Angular/app/app.component.css +++ b/JSDemos/Demos/Accordion/Overview/Angular/app/app.component.css @@ -2,11 +2,11 @@ height: 700px; } -::ng-deep #accordion h1 { +::ng-deep #accordion .header { font-size: 20px; } -::ng-deep #accordion h1, +::ng-deep #accordion .header, ::ng-deep #accordion p { margin: 0; } @@ -19,7 +19,7 @@ display: flex; } -::ng-deep .dx-theme-material #accordion h1 { +::ng-deep .dx-theme-material #accordion .header { align-self: center; } diff --git a/JSDemos/Demos/Accordion/Overview/Angular/app/app.component.html b/JSDemos/Demos/Accordion/Overview/Angular/app/app.component.html index 9a2347c9b77..753a795a3ee 100644 --- a/JSDemos/Demos/Accordion/Overview/Angular/app/app.component.html +++ b/JSDemos/Demos/Accordion/Overview/Angular/app/app.component.html @@ -9,7 +9,7 @@ id="accordion-container" >
-

{{ company.CompanyName }}

+
{{ company.CompanyName }}
diff --git a/JSDemos/Demos/Accordion/Overview/React/CustomTitle.tsx b/JSDemos/Demos/Accordion/Overview/React/CustomTitle.tsx index 403596babda..a5ec8700ba1 100644 --- a/JSDemos/Demos/Accordion/Overview/React/CustomTitle.tsx +++ b/JSDemos/Demos/Accordion/Overview/React/CustomTitle.tsx @@ -2,6 +2,6 @@ import React from 'react'; export default function CustomTitle(data) { return ( -

{data.CompanyName}

+
{data.CompanyName}
); } diff --git a/JSDemos/Demos/Accordion/Overview/React/styles.css b/JSDemos/Demos/Accordion/Overview/React/styles.css index 63a29aa3130..aa555ce5344 100644 --- a/JSDemos/Demos/Accordion/Overview/React/styles.css +++ b/JSDemos/Demos/Accordion/Overview/React/styles.css @@ -2,11 +2,11 @@ height: 700px; } -#accordion h1 { +#accordion .header { font-size: 20px; } -#accordion h1, +#accordion .header, #accordion p { margin: 0; } @@ -19,7 +19,7 @@ display: flex; } -.dx-theme-material #accordion h1 { +.dx-theme-material #accordion .header { align-self: center; } diff --git a/JSDemos/Demos/Accordion/Overview/Vue/App.vue b/JSDemos/Demos/Accordion/Overview/Vue/App.vue index 30861cdce70..db48bbbc318 100644 --- a/JSDemos/Demos/Accordion/Overview/Vue/App.vue +++ b/JSDemos/Demos/Accordion/Overview/Vue/App.vue @@ -88,11 +88,11 @@ export default { height: 700px; } -#accordion h1 { +#accordion .header { font-size: 20px; } -#accordion h1, +#accordion .header, #accordion p { margin: 0; } @@ -105,7 +105,7 @@ export default { display: flex; } -.dx-theme-material #accordion h1 { +.dx-theme-material #accordion .header { align-self: center; } diff --git a/JSDemos/Demos/Accordion/Overview/Vue/CustomTitle.vue b/JSDemos/Demos/Accordion/Overview/Vue/CustomTitle.vue index 78d99eacb63..ea090aeec71 100644 --- a/JSDemos/Demos/Accordion/Overview/Vue/CustomTitle.vue +++ b/JSDemos/Demos/Accordion/Overview/Vue/CustomTitle.vue @@ -1,5 +1,5 @@ + + + + + + + + + +
+ Loading... +
+ + diff --git a/JSDemos/Demos/Calendar/MultipleSelection/React/App.tsx b/JSDemos/Demos/Calendar/MultipleSelection/React/App.tsx new file mode 100644 index 00000000000..59a9e18b8ab --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/React/App.tsx @@ -0,0 +1,120 @@ +import React from 'react'; +import CheckBox from 'devextreme-react/check-box'; +import SelectBox from 'devextreme-react/select-box'; +import Button from 'devextreme-react/button'; +import Calendar, { CalendarTypes } from 'devextreme-react/calendar'; + +const selectionModes = ['single', 'multiple', 'range']; +const selectionModeLabel = { 'aria-label': 'Selection Mode' }; + +const isWeekend = (date) => { + const day = date.getDay(); + return day === 0 || day === 6; +}; +const isDateDisabled = ({ view, date }) => view === 'month' && isWeekend(date); +const now = new Date().getTime(); +const msInDay = 1000 * 60 * 60 * 24; +const initialValue = [now, now + msInDay]; + +export default function App() { + const calendar = React.useRef(null); + const [selectWeekOnClick, setSelectWeekOnClick] = React.useState(true); + const [selectionMode, setSelectionMode] = React.useState('multiple'); + const [minDateValue, setMinDateValue] = React.useState(null); + const [maxDateValue, setMaxDateValue] = React.useState(null); + const [weekendDisabled, setWeekendDisabled] = React.useState(null); + + const onSelectWeekOnClickChange = React.useCallback(({ value }) => { + setSelectWeekOnClick(value); + }, [setSelectWeekOnClick]); + + const onSelectionModeChange = React.useCallback(({ value }) => { + setSelectionMode(value); + }, [setSelectionMode]); + + const onMinDateChange = React.useCallback(({ value }) => { + setMinDateValue( + value ? new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 3) : null, + ); + }, [setMinDateValue]); + + const onMaxDateChange = React.useCallback(({ value }) => { + setMaxDateValue( + value ? new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 3) : null, + ); + }, [setMaxDateValue]); + + const onDisableWeekendChange = React.useCallback(({ value }) => { + setWeekendDisabled(value); + }, [setWeekendDisabled]); + + const onClearButtonClick = React.useCallback(() => { + calendar.current.instance.clear(); + }, []); + + return ( +
+
+ +
+
+
Options
+
+ +
+
+ Selection mode + +
+
+ Date availability +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ ); +} diff --git a/JSDemos/Demos/Calendar/MultipleSelection/React/index.html b/JSDemos/Demos/Calendar/MultipleSelection/React/index.html new file mode 100644 index 00000000000..b3417c2c091 --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/React/index.html @@ -0,0 +1,25 @@ + + + + DevExtreme Demo + + + + + + + + + + + + + + +
+
+
+ + diff --git a/JSDemos/Demos/Calendar/MultipleSelection/React/index.tsx b/JSDemos/Demos/Calendar/MultipleSelection/React/index.tsx new file mode 100644 index 00000000000..8acbec4b617 --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/React/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import App from './App.tsx'; + +ReactDOM.render( + , + document.getElementById('app'), +); diff --git a/JSDemos/Demos/Calendar/MultipleSelection/React/styles.css b/JSDemos/Demos/Calendar/MultipleSelection/React/styles.css new file mode 100644 index 00000000000..0ea76f53fe4 --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/React/styles.css @@ -0,0 +1,25 @@ +#container { + display: flex; +} + +.calendar-container { + display: flex; + flex-direction: column; + flex-grow: 1; + align-items: center; + justify-content: center; +} + +.caption { + font-weight: 500; + font-size: 18px; +} + +.options { + padding: 20px; + background-color: rgba(191, 191, 191, 0.15); +} + +.option { + margin-top: 10px; +} diff --git a/JSDemos/Demos/Calendar/MultipleSelection/ReactJs/App.js b/JSDemos/Demos/Calendar/MultipleSelection/ReactJs/App.js new file mode 100644 index 00000000000..0411f47653d --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/ReactJs/App.js @@ -0,0 +1,122 @@ +import React from 'react'; +import CheckBox from 'devextreme-react/check-box'; +import SelectBox from 'devextreme-react/select-box'; +import Button from 'devextreme-react/button'; +import Calendar from 'devextreme-react/calendar'; + +const selectionModes = ['single', 'multiple', 'range']; +const selectionModeLabel = { 'aria-label': 'Selection Mode' }; +const isWeekend = (date) => { + const day = date.getDay(); + return day === 0 || day === 6; +}; +const isDateDisabled = ({ view, date }) => view === 'month' && isWeekend(date); +const now = new Date().getTime(); +const msInDay = 1000 * 60 * 60 * 24; +const initialValue = [now, now + msInDay]; +export default function App() { + const calendar = React.useRef(null); + const [selectWeekOnClick, setSelectWeekOnClick] = React.useState(true); + const [selectionMode, setSelectionMode] = React.useState('multiple'); + const [minDateValue, setMinDateValue] = React.useState(null); + const [maxDateValue, setMaxDateValue] = React.useState(null); + const [weekendDisabled, setWeekendDisabled] = React.useState(null); + const onSelectWeekOnClickChange = React.useCallback( + ({ value }) => { + setSelectWeekOnClick(value); + }, + [setSelectWeekOnClick], + ); + const onSelectionModeChange = React.useCallback( + ({ value }) => { + setSelectionMode(value); + }, + [setSelectionMode], + ); + const onMinDateChange = React.useCallback( + ({ value }) => { + setMinDateValue(value ? new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 3) : null); + }, + [setMinDateValue], + ); + const onMaxDateChange = React.useCallback( + ({ value }) => { + setMaxDateValue(value ? new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 3) : null); + }, + [setMaxDateValue], + ); + const onDisableWeekendChange = React.useCallback( + ({ value }) => { + setWeekendDisabled(value); + }, + [setWeekendDisabled], + ); + const onClearButtonClick = React.useCallback(() => { + calendar.current.instance.clear(); + }, []); + return ( +
+
+ +
+
+
Options
+
+ +
+
+ Selection mode + +
+
+ Date availability +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ ); +} diff --git a/JSDemos/Demos/Calendar/MultipleSelection/ReactJs/index.html b/JSDemos/Demos/Calendar/MultipleSelection/ReactJs/index.html new file mode 100644 index 00000000000..3251cec07a2 --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/ReactJs/index.html @@ -0,0 +1,49 @@ + + + + DevExtreme Demo + + + + + + + + + + + + + + +
+
+
+ + diff --git a/JSDemos/Demos/Calendar/MultipleSelection/ReactJs/index.js b/JSDemos/Demos/Calendar/MultipleSelection/ReactJs/index.js new file mode 100644 index 00000000000..b853e0be824 --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/ReactJs/index.js @@ -0,0 +1,5 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App.js'; + +ReactDOM.render(, document.getElementById('app')); diff --git a/JSDemos/Demos/Calendar/MultipleSelection/ReactJs/styles.css b/JSDemos/Demos/Calendar/MultipleSelection/ReactJs/styles.css new file mode 100644 index 00000000000..0ea76f53fe4 --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/ReactJs/styles.css @@ -0,0 +1,25 @@ +#container { + display: flex; +} + +.calendar-container { + display: flex; + flex-direction: column; + flex-grow: 1; + align-items: center; + justify-content: center; +} + +.caption { + font-weight: 500; + font-size: 18px; +} + +.options { + padding: 20px; + background-color: rgba(191, 191, 191, 0.15); +} + +.option { + margin-top: 10px; +} diff --git a/JSDemos/Demos/Calendar/MultipleSelection/Vue/App.vue b/JSDemos/Demos/Calendar/MultipleSelection/Vue/App.vue new file mode 100644 index 00000000000..b7a8c068fc1 --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/Vue/App.vue @@ -0,0 +1,134 @@ + + + diff --git a/JSDemos/Demos/Calendar/MultipleSelection/Vue/index.html b/JSDemos/Demos/Calendar/MultipleSelection/Vue/index.html new file mode 100644 index 00000000000..84f23c93428 --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/Vue/index.html @@ -0,0 +1,30 @@ + + + + DevExtreme Demo + + + + + + + + + + + + + + + +
+
+
+ + diff --git a/JSDemos/Demos/Calendar/MultipleSelection/Vue/index.js b/JSDemos/Demos/Calendar/MultipleSelection/Vue/index.js new file mode 100644 index 00000000000..684d04215d7 --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/Vue/index.js @@ -0,0 +1,4 @@ +import { createApp } from 'vue'; +import App from './App.vue'; + +createApp(App).mount('#app'); diff --git a/JSDemos/Demos/Calendar/MultipleSelection/description.md b/JSDemos/Demos/Calendar/MultipleSelection/description.md new file mode 100644 index 00000000000..c85f1b952eb --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/description.md @@ -0,0 +1,30 @@ +This demo shows different selection modes and date availability options in Calendar. + +## Selection Modes + +The selected value or values are stored in the [value](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#value) property. The following [selection modes](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#selectionMode) are available: + +- *'single'* +A user can select only one date at a time. + +- *'multiple'* +A user can select multiple dates at a time. + +- *'range'* +A user can select a range of dates. The first and the last date in the range are stored in the [value](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#value) property. + +If you enable [selectWeekOnClick](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#selectWeekOnClick) in *'multiple'* or *'range'* modes, users can select a week by clicking on the week number. + +## Disable and Clear Dates + +Use the [min](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#min) and [max](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#max) properties to specify the range of available dates. In this demo, these properties limit the range to three days before and after the current date. Enable the "Set minimum date" and "Set maximum date" checkboxes to apply the properties. + +If you need to disable specific dates, use the [disabledDates](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#disabledDates) property. Toggle the "Disable weekends" checkbox to see how this setting affects the component's behavior. You can specify either an array of predefined dates or a function that determines whether a date is available. + +In cases of *'multiple'* and *'range'* [selection modes](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#selectionMode), the behavior of disabled dates in Calendar is the following: + +- If you specify the [value](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#value) property programmatically, disabled dates are selected in the values array. + +- If you use UI to change selection (clicks on dates or weeks, the Enter key), you cannot select disabled dates in *'multiple'* mode. In *'range'* mode, disabled dates cannot start or end a range, but can be included in the middle. + +To clear selected values, call the Calendar [clear()](/Documentation/ApiReference/UI_Components/dxCalendar/Methods/#clear) method. \ No newline at end of file diff --git a/JSDemos/Demos/Calendar/MultipleSelection/jQuery/index.html b/JSDemos/Demos/Calendar/MultipleSelection/jQuery/index.html new file mode 100644 index 00000000000..104bbbb7c6f --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/jQuery/index.html @@ -0,0 +1,46 @@ + + + + DevExtreme Demo + + + + + + + + + + +
+
+
+
+
+
Options
+
+
+
+
+ Selection mode +
+
+
+ Date availability +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/JSDemos/Demos/Calendar/MultipleSelection/jQuery/index.js b/JSDemos/Demos/Calendar/MultipleSelection/jQuery/index.js new file mode 100644 index 00000000000..7a7f4d6afca --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/jQuery/index.js @@ -0,0 +1,69 @@ +$(() => { + const msInDay = 1000 * 60 * 60 * 24; + const selectionModes = ['single', 'multiple', 'range']; + const date = new Date().getTime(); + + const calendar = $('#calendar').dxCalendar({ + value: [date, date + msInDay], + showWeekNumbers: true, + selectWeekOnClick: true, + selectionMode: 'multiple', + }).dxCalendar('instance'); + + $('#select-week').dxCheckBox({ + text: 'Select week on click', + value: true, + onValueChanged(data) { + calendar.option('selectWeekOnClick', data.value); + }, + }); + + $('#selection-mode').dxSelectBox({ + dataSource: selectionModes, + value: selectionModes[1], + inputAttr: { 'aria-label': 'Selection Mode' }, + onValueChanged(data) { + calendar.option('selectionMode', data.value); + }, + }).dxSelectBox('instance'); + + $('#min-date').dxCheckBox({ + text: 'Set minimum date', + onValueChanged(data) { + const minDate = new Date(date - msInDay * 3); + + calendar.option('min', data.value ? minDate : null); + }, + }); + + $('#max-date').dxCheckBox({ + text: 'Set maximum date', + onValueChanged(data) { + const maxDate = new Date(date + msInDay * 3); + + calendar.option('max', data.value ? maxDate : null); + }, + }); + + $('#disable-dates').dxCheckBox({ + text: 'Disable weekends', + onValueChanged(data) { + if (data.value) { + calendar.option('disabledDates', (d) => d.view === 'month' && isWeekend(d.date)); + } else { + calendar.option('disabledDates', null); + } + }, + }); + + $('#clear-button').dxButton({ + text: 'Clear value', + onClick: () => { calendar.clear(); }, + }); + + function isWeekend(d) { + const day = d.getDay(); + + return day === 0 || day === 6; + } +}); diff --git a/JSDemos/Demos/Calendar/MultipleSelection/jQuery/styles.css b/JSDemos/Demos/Calendar/MultipleSelection/jQuery/styles.css new file mode 100644 index 00000000000..25a6584acf0 --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/jQuery/styles.css @@ -0,0 +1,25 @@ +.demo-container { + display: flex; +} + +.calendar-container { + display: flex; + flex-direction: column; + flex-grow: 1; + align-items: center; + justify-content: center; +} + +.caption { + font-weight: 500; + font-size: 18px; +} + +.options { + padding: 20px; + background-color: rgba(191, 191, 191, 0.15); +} + +.option { + margin-top: 10px; +} diff --git a/JSDemos/Demos/Calendar/MultipleSelection/test-code.js b/JSDemos/Demos/Calendar/MultipleSelection/test-code.js new file mode 100644 index 00000000000..05872e04707 --- /dev/null +++ b/JSDemos/Demos/Calendar/MultipleSelection/test-code.js @@ -0,0 +1,7 @@ +testUtils.importAnd(() => 'devextreme/ui/calendar', () => DevExpress.ui.dxCalendar, (dxCalendar) => testUtils + .postponeUntilFound('.dx-calendar', 100, 10000) + .then(() => { + const instance = dxCalendar.getInstance(document.querySelector('.dx-calendar')); + instance.option('value', [new Date('2023/08/19'), new Date('2023/08/20')]); + }) + .then(() => testUtils.postpone(2000))); diff --git a/JSDemos/Demos/Calendar/Overview/Angular/app/app.component.html b/JSDemos/Demos/Calendar/Overview/Angular/app/app.component.html index a8fdf81a86b..a388c9d26f2 100644 --- a/JSDemos/Demos/Calendar/Overview/Angular/app/app.component.html +++ b/JSDemos/Demos/Calendar/Overview/Angular/app/app.component.html @@ -3,9 +3,6 @@
Options
- Zoom level + - +
- Selected date + - +
- +
+
+ Week numeration +
-
- - -
First day of week
-
- Zoom level - - -
-
- Selected date - - -
diff --git a/JSDemos/Demos/Calendar/Overview/Angular/app/app.component.ts b/JSDemos/Demos/Calendar/Overview/Angular/app/app.component.ts index b2ee61690d3..e2a1fa979ec 100644 --- a/JSDemos/Demos/Calendar/Overview/Angular/app/app.component.ts +++ b/JSDemos/Demos/Calendar/Overview/Angular/app/app.component.ts @@ -24,12 +24,6 @@ export class AppComponent { currentValue: Date = new Date(); - minDateValue: Date | null = null; - - maxDateValue: Date | null = null; - - disabledDates: Function | null = null; - zoomLevels: string[] = [ 'month', 'year', 'decade', 'century', ]; @@ -58,24 +52,6 @@ export class AppComponent { return day === 0 || day === 6; } - setMinDate({ value }) { - this.minDateValue = value - ? new Date(this.now.getTime() - 1000 * 60 * 60 * 24 * 3) - : null; - } - - setMaxDate({ value }) { - this.maxDateValue = value - ? new Date(this.now.getTime() + 1000 * 60 * 60 * 24 * 3) - : null; - } - - disableWeekend({ value }) { - this.disabledDates = value - ? (data) => data.view === 'month' && this.isWeekend(data.date) - : null; - } - useCellTemplate({ value }) { this.cellTemplate = value ? 'custom' : 'cell'; } diff --git a/JSDemos/Demos/Calendar/Overview/React/App.tsx b/JSDemos/Demos/Calendar/Overview/React/App.tsx index 38469975fbe..ffc7d61558b 100644 --- a/JSDemos/Demos/Calendar/Overview/React/App.tsx +++ b/JSDemos/Demos/Calendar/Overview/React/App.tsx @@ -3,7 +3,7 @@ import CheckBox from 'devextreme-react/check-box'; import SelectBox from 'devextreme-react/select-box'; import DateBox from 'devextreme-react/date-box'; import Calendar, { CalendarTypes } from 'devextreme-react/calendar'; -import CustomCell, { isWeekend } from './CustomCell.tsx'; +import CustomCell from './CustomCell.tsx'; const zoomLevels = ['month', 'year', 'decade', 'century']; const weekNumberRules = ['auto', 'firstDay', 'firstFourDays', 'fullWeek']; @@ -22,19 +22,14 @@ const zoomLevelLabel = { 'aria-label': 'Zoom Level' }; const dayLabel = { 'aria-label': 'First Day of Week' }; const ruleLabel = { 'aria-label': 'Week Number Rule' }; -const isDateDisabled = ({ view, date }) => view === 'month' && isWeekend(date); - export default function App() { - const [minDateValue, setMinDateValue] = React.useState(null); - const [maxDateValue, setMaxDateValue] = React.useState(null); - const [weekendDisabled, setWeekendDisabled] = React.useState(null); - const [firstDay, setFirstDay] = React.useState(0); - const [weekNumberRule, setWeekNumberRule] = React.useState('auto'); - const [showWeekNumbers, setShowWeekNumbers] = React.useState(false); + const [zoomLevel, setZoomLevel] = React.useState('month'); const [currentValue, setCurrentValue] = React.useState(new Date()); const [useCellTemplate, setUseCellTemplate] = React.useState(null); const [disabled, setDisabled] = React.useState(false); - const [zoomLevel, setZoomLevel] = React.useState('month'); + const [showWeekNumbers, setShowWeekNumbers] = React.useState(false); + const [firstDay, setFirstDay] = React.useState(0); + const [weekNumberRule, setWeekNumberRule] = React.useState('auto'); const onCurrentValueChange = React.useCallback( ({ value }) => { @@ -57,27 +52,6 @@ export default function App() { [setZoomLevel], ); - const onMinDateChange = React.useCallback( - ({ value }) => { - setMinDateValue(value ? new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 3) : null); - }, - [setMinDateValue], - ); - - const onMaxDateChange = React.useCallback( - ({ value }) => { - setMaxDateValue(value ? new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 3) : null); - }, - [setMaxDateValue], - ); - - const onDisableWeekendChange = React.useCallback( - ({ value }) => { - setWeekendDisabled(value); - }, - [setWeekendDisabled], - ); - const onFirstDayChange = React.useCallback( ({ value }) => { setFirstDay(value); @@ -122,45 +96,32 @@ export default function App() { value={currentValue} onValueChanged={onCurrentValueChange} onOptionChanged={onOptionChange} - min={minDateValue} - max={maxDateValue} - disabledDates={weekendDisabled ? isDateDisabled : null} firstDayOfWeek={firstDay} weekNumberRule={weekNumberRule} showWeekNumbers={showWeekNumbers} disabled={disabled} zoomLevel={zoomLevel} - cellRender={useCellTemplate ? CustomCell : null} + cellComponent={useCellTemplate ? CustomCell : null} />
Options
- -
-
- -
-
- Zoom level +
- Selected date +
@@ -177,6 +138,16 @@ export default function App() { onValueChanged={onDisabledChange} />
+
+ Week numeration +
+
+ +
First day of week
-
- Zoom level - -
-
- Selected date - -
); diff --git a/JSDemos/Demos/Calendar/Overview/ReactJs/App.js b/JSDemos/Demos/Calendar/Overview/ReactJs/App.js index 8702a0e94ba..b597aae794b 100644 --- a/JSDemos/Demos/Calendar/Overview/ReactJs/App.js +++ b/JSDemos/Demos/Calendar/Overview/ReactJs/App.js @@ -3,7 +3,7 @@ import CheckBox from 'devextreme-react/check-box'; import SelectBox from 'devextreme-react/select-box'; import DateBox from 'devextreme-react/date-box'; import Calendar from 'devextreme-react/calendar'; -import CustomCell, { isWeekend } from './CustomCell.js'; +import CustomCell from './CustomCell.js'; const zoomLevels = ['month', 'year', 'decade', 'century']; const weekNumberRules = ['auto', 'firstDay', 'firstFourDays', 'fullWeek']; @@ -20,18 +20,14 @@ const dateBoxLabel = { 'aria-label': 'Date' }; const zoomLevelLabel = { 'aria-label': 'Zoom Level' }; const dayLabel = { 'aria-label': 'First Day of Week' }; const ruleLabel = { 'aria-label': 'Week Number Rule' }; -const isDateDisabled = ({ view, date }) => view === 'month' && isWeekend(date); export default function App() { - const [minDateValue, setMinDateValue] = React.useState(null); - const [maxDateValue, setMaxDateValue] = React.useState(null); - const [weekendDisabled, setWeekendDisabled] = React.useState(null); - const [firstDay, setFirstDay] = React.useState(0); - const [weekNumberRule, setWeekNumberRule] = React.useState('auto'); - const [showWeekNumbers, setShowWeekNumbers] = React.useState(false); + const [zoomLevel, setZoomLevel] = React.useState('month'); const [currentValue, setCurrentValue] = React.useState(new Date()); const [useCellTemplate, setUseCellTemplate] = React.useState(null); const [disabled, setDisabled] = React.useState(false); - const [zoomLevel, setZoomLevel] = React.useState('month'); + const [showWeekNumbers, setShowWeekNumbers] = React.useState(false); + const [firstDay, setFirstDay] = React.useState(0); + const [weekNumberRule, setWeekNumberRule] = React.useState('auto'); const onCurrentValueChange = React.useCallback( ({ value }) => { setCurrentValue(value); @@ -50,24 +46,6 @@ export default function App() { }, [setZoomLevel], ); - const onMinDateChange = React.useCallback( - ({ value }) => { - setMinDateValue(value ? new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 3) : null); - }, - [setMinDateValue], - ); - const onMaxDateChange = React.useCallback( - ({ value }) => { - setMaxDateValue(value ? new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 3) : null); - }, - [setMaxDateValue], - ); - const onDisableWeekendChange = React.useCallback( - ({ value }) => { - setWeekendDisabled(value); - }, - [setWeekendDisabled], - ); const onFirstDayChange = React.useCallback( ({ value }) => { setFirstDay(value); @@ -107,45 +85,32 @@ export default function App() { value={currentValue} onValueChanged={onCurrentValueChange} onOptionChanged={onOptionChange} - min={minDateValue} - max={maxDateValue} - disabledDates={weekendDisabled ? isDateDisabled : null} firstDayOfWeek={firstDay} weekNumberRule={weekNumberRule} showWeekNumbers={showWeekNumbers} disabled={disabled} zoomLevel={zoomLevel} - cellRender={useCellTemplate ? CustomCell : null} + cellComponent={useCellTemplate ? CustomCell : null} />
Options
- -
-
- -
-
- Zoom level +
- Selected date +
@@ -162,6 +127,16 @@ export default function App() { onValueChanged={onDisabledChange} />
+
+ Week numeration +
+
+ +
First day of week
-
- Zoom level - -
-
- Selected date - -
); diff --git a/JSDemos/Demos/Calendar/Overview/Vue/App.vue b/JSDemos/Demos/Calendar/Overview/Vue/App.vue index 245d0919af5..631cc7398f2 100644 --- a/JSDemos/Demos/Calendar/Overview/Vue/App.vue +++ b/JSDemos/Demos/Calendar/Overview/Vue/App.vue @@ -4,9 +4,6 @@
Options
- Zoom level +
- Selected date +
-
- +
+ Week numeration
@@ -80,23 +74,6 @@ :data-source="weekNumberRules" />
-
- Zoom level - -
-
- Selected date - -
@@ -107,17 +84,14 @@ import DxSelectBox from 'devextreme-vue/select-box'; import DxDateBox from 'devextreme-vue/date-box'; import DxCalendar from 'devextreme-vue/calendar'; -const minDateValue = ref(null); -const maxDateValue = ref(null); -const disabledDates = ref(null); -const firstDay = ref(0); -const showWeekNumbers = ref(false); -const weekNumberRule = ref('auto'); -const currentValue = ref(new Date()); const zoomLevels = ['month', 'year', 'decade', 'century']; +const zoomLevel = ref('month'); +const currentValue = ref(new Date()); const cellTemplate = ref('cell'); const disabled = ref(false); -const zoomLevel = ref('month'); +const showWeekNumbers = ref(false); +const firstDay = ref(0); +const weekNumberRule = ref('auto'); const weekDays = [ { id: 0, text: 'Sunday' }, { id: 1, text: 'Monday' }, @@ -134,24 +108,6 @@ function isWeekend(date) { return day === 0 || day === 6; } -function setMinDate({ value }) { - minDateValue.value = value - ? new Date((new Date()).getTime() - 1000 * 60 * 60 * 24 * 3) - : null; -} - -function setMaxDate({ value }) { - maxDateValue.value = value - ? new Date((new Date()).getTime() + 1000 * 60 * 60 * 24 * 3) - : null; -} - -function disableWeekend({ value }) { - disabledDates.value = value - ? (data) => data.view === 'month' && isWeekend(data.date) - : null; -} - function useCellTemplate({ value }) { cellTemplate.value = value ? 'custom' : 'cell'; } diff --git a/JSDemos/Demos/Calendar/Overview/description.md b/JSDemos/Demos/Calendar/Overview/description.md index 80eea0af03e..2928d8f6ad4 100644 --- a/JSDemos/Demos/Calendar/Overview/description.md +++ b/JSDemos/Demos/Calendar/Overview/description.md @@ -1,22 +1,23 @@ When you add a Calendar to an application, you need to specify its [value](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#value) in one of the following formats: -- Date objects +- Date +Specifies the date directly. -- The number of milliseconds since 00:00:00 on January 1, 1970 +- Number +Specifies the date with a timestamp (total milliseconds since 1970/01/01). -- Strings that match the following patterns: - - 'yyyy-MM-dd' - - 'yyyy-MM-ddTHH:mm:ss' - - 'yyyy-MM-ddTHH:mm:ssZ' - - 'yyyy-MM-ddTHH:mm:ssx' +- String +Specifies the date with a string value. The UI component supports the following formats of a date string: -This demo shows how to use additional properties to customize your Calendar. You can toggle the checkboxes on the right to change the Calendar in real time. - -## Disable Dates + - "yyyy-MM-dd" (for example, "2017-03-06") + - "yyyy-MM-ddTHH:mm:ss" (for example, "2017-03-27T16:54:48") + - "yyyy-MM-ddTHH:mm:ssZ" (for example, "2017-03-27T13:55:41Z") + - "yyyy-MM-ddTHH:mm:ssx" (for example, "2017-03-27T16:54:10+03") -Use the [min](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#min) and [max](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#max) properties to specify the range of available dates. In this demo, these properties limit the range to three days before and after the current date. Enable the "Set minimum date" and "Set maximum date" checkboxes to apply the properties. +- Array of the formats mentioned before +Available only for *'multiple'* and *'range'* [selection modes](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#selectionMode). The array includes all selected dates. -If you need to disable specific dates, use the [disabledDates](/Documentation/ApiReference/UI_Components/dxCalendar/Configuration/#disabledDates) property. Toggle the "Disable weekends" checkbox to see how this setting affects the component's behavior. You can specify either an array of predefined dates or a function that determines whether a date is available. +This demo shows how to use additional properties to customize your Calendar. You can toggle the checkboxes on the right to change the Calendar in real time. ## Specify First Day of Week and Display Week Numbers diff --git a/JSDemos/Demos/Calendar/Overview/jQuery/index.html b/JSDemos/Demos/Calendar/Overview/jQuery/index.html index aaad8bd0194..bdc3fcd863d 100644 --- a/JSDemos/Demos/Calendar/Overview/jQuery/index.html +++ b/JSDemos/Demos/Calendar/Overview/jQuery/index.html @@ -19,22 +19,24 @@
Options
-
+ Zoom level +
-
+ Selected date +
-
+
-
+
-
-
+
+ Week numeration
-
+
First day of week @@ -44,14 +46,6 @@ Week number rule
-
- Zoom level -
-
-
- Selected date -
-
diff --git a/JSDemos/Demos/Calendar/Overview/jQuery/index.js b/JSDemos/Demos/Calendar/Overview/jQuery/index.js index d9a0953ddc5..abc0705240e 100644 --- a/JSDemos/Demos/Calendar/Overview/jQuery/index.js +++ b/JSDemos/Demos/Calendar/Overview/jQuery/index.js @@ -1,5 +1,4 @@ $(() => { - const msInDay = 1000 * 60 * 60 * 24; const zoomLevels = ['month', 'year', 'decade', 'century']; const weekDays = [ { id: 0, text: 'Sunday' }, @@ -11,7 +10,6 @@ $(() => { { id: 6, text: 'Saturday' }, ]; const weekNumberRules = ['auto', 'firstDay', 'firstFourDays', 'fullWeek']; - const date = new Date().getTime(); const calendar = $('#calendar').dxCalendar({ value: new Date(), @@ -30,41 +28,28 @@ $(() => { }, }).dxCalendar('instance'); - $('#min-date').dxCheckBox({ - text: 'Set minimum date', - onValueChanged(data) { - const minDate = new Date(date - msInDay * 3); - - calendar.option('min', data.value ? minDate : null); - selectedDate.option('min', data.value ? minDate : null); - }, - }); - - $('#max-date').dxCheckBox({ - text: 'Set maximum date', + const zoomLevel = $('#zoom-level').dxSelectBox({ + dataSource: zoomLevels, + value: zoomLevels[0], + inputAttr: { 'aria-label': 'Zoom Level' }, onValueChanged(data) { - const maxDate = new Date(date + msInDay * 3); - - calendar.option('max', data.value ? maxDate : null); - selectedDate.option('max', data.value ? maxDate : null); + calendar.option('zoomLevel', data.value); }, - }); + }).dxSelectBox('instance'); - $('#disable-dates').dxCheckBox({ - text: 'Disable weekends', + const selectedDate = $('#selected-date').dxDateBox({ + value: new Date(), + inputAttr: { 'aria-label': 'Date' }, onValueChanged(data) { - if (data.value) { - calendar.option('disabledDates', (d) => d.view === 'month' && isWeekend(d.date)); - } else { - calendar.option('disabledDates', null); - } + calendar.option('value', data.value); }, - }); + }).dxDateBox('instance'); - $('#week-numbers').dxCheckBox({ - text: 'Show week numbers', + $('#custom-cell').dxCheckBox({ + text: 'Use custom cell template', + value: false, onValueChanged(data) { - calendar.option('showWeekNumbers', data.value); + calendar.option('cellTemplate', data.value ? getCellTemplate : 'cell'); }, }); @@ -75,31 +60,13 @@ $(() => { }, }); - $('#custom-cell').dxCheckBox({ - text: 'Use custom cell template', - value: false, + $('#week-numbers').dxCheckBox({ + text: 'Show week numbers', onValueChanged(data) { - calendar.option('cellTemplate', data.value ? getCellTemplate : 'cell'); + calendar.option('showWeekNumbers', data.value); }, }); - const zoomLevel = $('#zoom-level').dxSelectBox({ - dataSource: zoomLevels, - value: zoomLevels[0], - inputAttr: { 'aria-label': 'Zoom Level' }, - onValueChanged(data) { - calendar.option('zoomLevel', data.value); - }, - }).dxSelectBox('instance'); - - const selectedDate = $('#selected-date').dxDateBox({ - value: new Date(), - inputAttr: { 'aria-label': 'Date' }, - onValueChanged(data) { - calendar.option('value', data.value); - }, - }).dxDateBox('instance'); - $('#first-day-of-week').dxSelectBox({ dataSource: weekDays, value: 0, diff --git a/JSDemos/Demos/Charts/ExportCustomMarkup/React/Form.js b/JSDemos/Demos/Charts/ExportCustomMarkup/React/Form.js index 8bd41d653cf..56adf43df9b 100644 --- a/JSDemos/Demos/Charts/ExportCustomMarkup/React/Form.js +++ b/JSDemos/Demos/Charts/ExportCustomMarkup/React/Form.js @@ -2,7 +2,7 @@ import React from 'react'; const fontFamily = "'Segoe UI', 'Helvetica Neue', 'Trebuchet MS', Verdana, sans-serif"; -const Form = React.forwardRef(({ ref }) => ( +const Form = React.forwardRef((_, ref) => (
diff --git a/JSDemos/Demos/Charts/PieWithCustomStyles/visualtestrc.json b/JSDemos/Demos/Charts/PieWithCustomStyles/visualtestrc.json deleted file mode 100644 index b64da046b1b..00000000000 --- a/JSDemos/Demos/Charts/PieWithCustomStyles/visualtestrc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "vue": { - "ignore": true - }, - "react": { - "ignore": true - } -} diff --git a/JSDemos/Demos/Charts/ServerSideDataProcessing/Angular/app/app.component.ts b/JSDemos/Demos/Charts/ServerSideDataProcessing/Angular/app/app.component.ts index ac0a4080768..e7cf9ac3c2d 100644 --- a/JSDemos/Demos/Charts/ServerSideDataProcessing/Angular/app/app.component.ts +++ b/JSDemos/Demos/Charts/ServerSideDataProcessing/Angular/app/app.component.ts @@ -28,6 +28,7 @@ export class AppComponent { this.chartDataSource = new DataSource({ store: { type: 'odata', + version: 2, url: 'https://js.devexpress.com/Demos/WidgetsGallery/odata/WeatherItems', }, postProcess: (results) => results[0].DayItems, diff --git a/JSDemos/Demos/Charts/ServerSideDataProcessing/AngularJS/index.js b/JSDemos/Demos/Charts/ServerSideDataProcessing/AngularJS/index.js index 0159dec2a3a..7b5a8217ea9 100644 --- a/JSDemos/Demos/Charts/ServerSideDataProcessing/AngularJS/index.js +++ b/JSDemos/Demos/Charts/ServerSideDataProcessing/AngularJS/index.js @@ -4,6 +4,7 @@ DemoApp.controller('DemoController', ($scope) => { const chartDataSource = new DevExpress.data.DataSource({ store: { type: 'odata', + version: 2, url: 'https://js.devexpress.com/Demos/WidgetsGallery/odata/WeatherItems', }, postProcess(results) { diff --git a/JSDemos/Demos/Charts/ServerSideDataProcessing/React/App.js b/JSDemos/Demos/Charts/ServerSideDataProcessing/React/App.js index 45c4942ef44..5741465ecfc 100644 --- a/JSDemos/Demos/Charts/ServerSideDataProcessing/React/App.js +++ b/JSDemos/Demos/Charts/ServerSideDataProcessing/React/App.js @@ -21,6 +21,7 @@ import { months, monthLabel } from './data.js'; const chartDataSource = new DataSource({ store: { type: 'odata', + version: 2, url: 'https://js.devexpress.com/Demos/WidgetsGallery/odata/WeatherItems', }, postProcess(results) { diff --git a/JSDemos/Demos/Charts/ServerSideDataProcessing/Vue/App.vue b/JSDemos/Demos/Charts/ServerSideDataProcessing/Vue/App.vue index 33210ed8132..47dfc8eb0b1 100644 --- a/JSDemos/Demos/Charts/ServerSideDataProcessing/Vue/App.vue +++ b/JSDemos/Demos/Charts/ServerSideDataProcessing/Vue/App.vue @@ -100,6 +100,7 @@ export default { const chartDataSource = new DataSource({ store: { type: 'odata', + version: 2, url: 'https://js.devexpress.com/Demos/WidgetsGallery/odata/WeatherItems', }, postProcess(results) { diff --git a/JSDemos/Demos/Charts/ServerSideDataProcessing/jQuery/index.js b/JSDemos/Demos/Charts/ServerSideDataProcessing/jQuery/index.js index 2ebd02a98b6..316601f99e0 100644 --- a/JSDemos/Demos/Charts/ServerSideDataProcessing/jQuery/index.js +++ b/JSDemos/Demos/Charts/ServerSideDataProcessing/jQuery/index.js @@ -2,6 +2,7 @@ $(() => { const chartDataSource = new DevExpress.data.DataSource({ store: { type: 'odata', + version: 2, url: 'https://js.devexpress.com/Demos/WidgetsGallery/odata/WeatherItems', }, postProcess(results) { diff --git a/JSDemos/Demos/Common/EditorAppearanceVariants/Angular/app/app.component.html b/JSDemos/Demos/Common/EditorAppearanceVariants/Angular/app/app.component.html index 85ffc386e8d..44454ddf4db 100644 --- a/JSDemos/Demos/Common/EditorAppearanceVariants/Angular/app/app.component.html +++ b/JSDemos/Demos/Common/EditorAppearanceVariants/Angular/app/app.component.html @@ -3,20 +3,22 @@
Options
-
-
diff --git a/JSDemos/Demos/Common/EditorAppearanceVariants/AngularJS/index.html b/JSDemos/Demos/Common/EditorAppearanceVariants/AngularJS/index.html index f1b7dc3e01a..674ed1097f9 100644 --- a/JSDemos/Demos/Common/EditorAppearanceVariants/AngularJS/index.html +++ b/JSDemos/Demos/Common/EditorAppearanceVariants/AngularJS/index.html @@ -193,7 +193,7 @@
Options
-
- Options
-
- Options
-
-
diff --git a/JSDemos/Demos/Common/EditorAppearanceVariants/description.md b/JSDemos/Demos/Common/EditorAppearanceVariants/description.md index 8717176fbb9..543bc9ec3ce 100644 --- a/JSDemos/Demos/Common/EditorAppearanceVariants/description.md +++ b/JSDemos/Demos/Common/EditorAppearanceVariants/description.md @@ -3,7 +3,7 @@ - [stylingMode](/Documentation/ApiReference/UI_Components/dxTextBox/Configuration/#stylingMode): *"filled"* | *"outlined"* | *"underlined"* Specifies the style used for text fields. -- [labelMode](/Documentation/ApiReference/UI_Components/dxTextBox/Configuration/#labelMode): *"static"* | *"floating"* | *"hidden"* +- [labelMode](/Documentation/ApiReference/UI_Components/dxTextBox/Configuration/#labelMode): *"static"* | *"floating"* | *"hidden"* | *"outside"* Specifies label display mode. To specify styles globally (as an alternative to **stylingMode**), set the [editorStylingMode](/Documentation/ApiReference/Common/Object_Structures/globalConfig/#editorStylingMode) property instead (**editorStylingMode** applies the same style to all editors on the page). diff --git a/JSDemos/Demos/Common/EditorAppearanceVariants/jQuery/index.html b/JSDemos/Demos/Common/EditorAppearanceVariants/jQuery/index.html index 338be36611b..bbf016c69c6 100644 --- a/JSDemos/Demos/Common/EditorAppearanceVariants/jQuery/index.html +++ b/JSDemos/Demos/Common/EditorAppearanceVariants/jQuery/index.html @@ -18,11 +18,9 @@
Options
-
Styling Mode
-
Label Mode
diff --git a/JSDemos/Demos/Common/EditorAppearanceVariants/jQuery/index.js b/JSDemos/Demos/Common/EditorAppearanceVariants/jQuery/index.js index 23a41e0bfbd..5a375bc2086 100644 --- a/JSDemos/Demos/Common/EditorAppearanceVariants/jQuery/index.js +++ b/JSDemos/Demos/Common/EditorAppearanceVariants/jQuery/index.js @@ -10,6 +10,7 @@ $(() => { placeholder: 'Type...', inputAttr: { 'aria-label': 'Name' }, label: 'Name', + labelMode, }).dxValidator({ validationRules: [{ type: 'required', @@ -32,6 +33,7 @@ $(() => { placeholder: 'Select...', value: '6/3/1981', inputAttr: { 'aria-label': 'Birth Date' }, + labelMode, }).dxValidator({ validationRules: [{ type: 'required', @@ -54,6 +56,7 @@ $(() => { startDateLabel: 'Start Vacation Date', endDate: '12/3/2023', endDateLabel: 'End Vacation Date', + labelMode, }).dxDateRangeBox('instance'); const state = $('#state').dxSelectBox({ @@ -75,6 +78,7 @@ $(() => { maskRules: { X: /[02-9]/, }, + labelMode, }).dxValidator({ validationRules: [{ type: 'required', @@ -85,6 +89,7 @@ $(() => { value: 'Olivia loves to sell. She has been selling DevAV products since 2012.', placeholder: 'Type...', label: 'Notes', + labelMode, inputAttr: { 'aria-label': 'Notes' }, }).dxTextArea('instance'); @@ -111,13 +116,17 @@ $(() => { $('#styling-mode-selector').dxSelectBox({ items: ['outlined', 'filled', 'underlined'], value: 'outlined', - inputAttr: { 'aria-label': 'Mode' }, + label: 'Styling Mode', + labelMode: 'outside', + inputAttr: { 'aria-label': 'Styling Mode' }, onValueChanged: getValueChangedHandler('stylingMode'), }).dxSelectBox('instance'); $('#label-mode-selector').dxSelectBox({ - items: ['static', 'floating', 'hidden'], + items: ['static', 'floating', 'hidden', 'outside'], value: 'static', + label: 'Label Mode', + labelMode: 'outside', inputAttr: { 'aria-label': 'Label Mode' }, onValueChanged: getValueChangedHandler('labelMode'), }).dxSelectBox('instance'); diff --git a/JSDemos/Demos/Common/EditorsRightToLeftSupport/React/styles.css b/JSDemos/Demos/Common/EditorsRightToLeftSupport/React/styles.css index d6562fb23a8..2261e5b698d 100644 --- a/JSDemos/Demos/Common/EditorsRightToLeftSupport/React/styles.css +++ b/JSDemos/Demos/Common/EditorsRightToLeftSupport/React/styles.css @@ -1,4 +1,3 @@ - .options { padding: 20px; background-color: rgba(191, 191, 191, 0.15); diff --git a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/Angular/app/app.component.css b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/Angular/app/app.component.css index 1608b43cdc9..432998a9c7d 100644 --- a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/Angular/app/app.component.css +++ b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/Angular/app/app.component.css @@ -1,12 +1,7 @@ -::ng-deep .first-group, -::ng-deep .second-group { +::ng-deep .form-group { padding: 20px; } -::ng-deep .second-group { - background-color: rgba(191, 191, 191, 0.15); -} - ::ng-deep .form-avatar { height: 128px; width: 128px; diff --git a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/Angular/app/app.component.html b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/Angular/app/app.component.html index cd3ad407ac4..9f65e998dbf 100644 --- a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/Angular/app/app.component.html +++ b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/Angular/app/app.component.html @@ -1,5 +1,5 @@ - +
@@ -13,7 +13,7 @@ >
- + diff --git a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/AngularJS/index.js b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/AngularJS/index.js index 29418b3a60f..e185b02b082 100644 --- a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/AngularJS/index.js +++ b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/AngularJS/index.js @@ -5,7 +5,7 @@ DemoApp.controller('DemoController', ($scope) => { formData: employee, items: [{ itemType: 'group', - cssClass: 'first-group', + cssClass: 'form-group', colCount: 4, items: [{ template: "
", @@ -26,7 +26,7 @@ DemoApp.controller('DemoController', ($scope) => { }], }, { itemType: 'group', - cssClass: 'second-group', + cssClass: 'form-group', colCount: 2, items: [{ itemType: 'group', diff --git a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/AngularJS/styles.css b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/AngularJS/styles.css index 9371601a05f..a8a271680b6 100644 --- a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/AngularJS/styles.css +++ b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/AngularJS/styles.css @@ -1,12 +1,7 @@ -.first-group, -.second-group { +.form-group { padding: 20px; } -.second-group { - background-color: rgba(191, 191, 191, 0.15); -} - .form-avatar { height: 128px; width: 128px; diff --git a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/React/App.js b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/React/App.js index 11a485dfbcd..6cf0ae1dadc 100644 --- a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/React/App.js +++ b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/React/App.js @@ -23,7 +23,7 @@ const avatarRender = () => ( export default function App() { return (
- + @@ -36,7 +36,7 @@ export default function App() { /> - + diff --git a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/React/styles.css b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/React/styles.css index 9371601a05f..a8a271680b6 100644 --- a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/React/styles.css +++ b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/React/styles.css @@ -1,12 +1,7 @@ -.first-group, -.second-group { +.form-group { padding: 20px; } -.second-group { - background-color: rgba(191, 191, 191, 0.15); -} - .form-avatar { height: 128px; width: 128px; diff --git a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/Vue/App.vue b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/Vue/App.vue index cace32cfa73..b39536277db 100644 --- a/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/Vue/App.vue +++ b/JSDemos/Demos/Common/FormsAndMultiPurposeOverview/Vue/App.vue @@ -5,7 +5,7 @@ @@ -20,7 +20,7 @@ @@ -90,15 +90,10 @@ export default { }; diff --git a/JSDemos/Demos/DataGrid/ToolbarCustomization/jQuery/index.js b/JSDemos/Demos/DataGrid/ToolbarCustomization/jQuery/index.js index 4d77a24a755..14acdcbc700 100644 --- a/JSDemos/Demos/DataGrid/ToolbarCustomization/jQuery/index.js +++ b/JSDemos/Demos/DataGrid/ToolbarCustomization/jQuery/index.js @@ -32,11 +32,10 @@ $(() => { return $('
') .addClass('informer') .append( - $('

') + $('
') .addClass('count') .text(getGroupCount('CustomerStoreState')), $('') - .addClass('name') .text('Total Count'), ); }, diff --git a/JSDemos/Demos/DataGrid/ToolbarCustomization/jQuery/styles.css b/JSDemos/Demos/DataGrid/ToolbarCustomization/jQuery/styles.css index ffe6785d278..aeead376316 100644 --- a/JSDemos/Demos/DataGrid/ToolbarCustomization/jQuery/styles.css +++ b/JSDemos/Demos/DataGrid/ToolbarCustomization/jQuery/styles.css @@ -1,44 +1,12 @@ -#gridContainer .dx-datagrid-header-panel { - padding: 0; - background-color: rgba(85, 149, 222, 0.6); -} - -#gridContainer .dx-datagrid-header-panel .dx-toolbar { - margin: 0; - padding-right: 20px; - background-color: transparent; -} - -#gridContainer .dx-datagrid-header-panel .dx-toolbar-items-container { - height: 70px; -} - -#gridContainer .dx-datagrid-header-panel .dx-toolbar-before .dx-toolbar-item:not(:first-child) { - background-color: rgba(103, 171, 255, 0.6); -} - -#gridContainer .dx-datagrid-header-panel .dx-toolbar-before .dx-toolbar-item:last-child { - padding-right: 10px; -} - -#gridContainer .dx-datagrid-header-panel .dx-selectbox { - margin: auto 10px; -} - -#gridContainer .dx-datagrid-header-panel .dx-button { - margin: auto 0; -} - #gridContainer .informer { - height: 70px; - width: 130px; + display: grid; + width: 120px; + grid-template-columns: 100%; + padding-right: 20px; text-align: center; - color: #fff; } #gridContainer .count { - padding-top: 15px; - line-height: 27px; - font-size: 28px; - margin: 0; + font-size: 18px; + font-weight: 500; } diff --git a/JSDemos/Demos/DataGrid/VirtualScrolling/React/App.js b/JSDemos/Demos/DataGrid/VirtualScrolling/React/App.js deleted file mode 100644 index 5c927ad578d..00000000000 --- a/JSDemos/Demos/DataGrid/VirtualScrolling/React/App.js +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import DataGrid, { Scrolling, Sorting, LoadPanel } from 'devextreme-react/data-grid'; -import { generateData } from './data.js'; - -const dataSource = generateData(100000); - -const customizeColumns = (columns) => { - columns[0].width = 70; -}; -const App = () => { - const [loadPanelEnabled, setLoadPanelEnabled] = React.useState(true); - - const onContentReady = React.useCallback(() => { - setLoadPanelEnabled(false); - }, []); - - return ( - - - - - - ); -}; - -export default App; diff --git a/JSDemos/Demos/DataGrid/VirtualScrolling/React/App.tsx b/JSDemos/Demos/DataGrid/VirtualScrolling/React/App.tsx new file mode 100644 index 00000000000..94d8f755d23 --- /dev/null +++ b/JSDemos/Demos/DataGrid/VirtualScrolling/React/App.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import DataGrid, { + Scrolling, Sorting, LoadPanel, DataGridTypes, +} from 'devextreme-react/data-grid'; +import { generateData } from './data.ts'; + +const dataSource = generateData(100000); + +const customizeColumns = (columns: DataGridTypes.Column[]) => { + columns[0].width = 70; +}; +const App = () => { + const [loadPanelEnabled, setLoadPanelEnabled] = React.useState(true); + + const onContentReady = React.useCallback(() => { + setLoadPanelEnabled(false); + }, []); + + return ( + + + + + + ); +}; + +export default App; diff --git a/JSDemos/Demos/DataGrid/VirtualScrolling/React/data.js b/JSDemos/Demos/DataGrid/VirtualScrolling/React/data.js deleted file mode 100644 index dcccaf1642e..00000000000 --- a/JSDemos/Demos/DataGrid/VirtualScrolling/React/data.js +++ /dev/null @@ -1,33 +0,0 @@ -let s = 123456789; -function random() { - s = (1103515245 * s + 12345) % 2147483647; - return s % (10 - 1); -} - -export function generateData(count) { - let i; - const surnames = ['Smith', 'Johnson', 'Brown', 'Taylor', 'Anderson', 'Harris', 'Clark', 'Allen', 'Scott', 'Carter']; - const names = ['James', 'John', 'Robert', 'Christopher', 'George', 'Mary', 'Nancy', 'Sandra', 'Michelle', 'Betty']; - const gender = ['Male', 'Female']; - const items = []; - const startBirthDate = Date.parse('1/1/1975'); - const endBirthDate = Date.parse('1/1/1992'); - - for (i = 0; i < count; i += 1) { - const birthDate = new Date(startBirthDate + Math.floor( - (random() * (endBirthDate - startBirthDate)) / 10, - )); - birthDate.setHours(12); - - const nameIndex = random(); - const item = { - id: i + 1, - firstName: names[nameIndex], - lastName: surnames[random()], - gender: gender[Math.floor(nameIndex / 5)], - birthDate, - }; - items.push(item); - } - return items; -} diff --git a/JSDemos/Demos/DataGrid/VirtualScrolling/React/data.ts b/JSDemos/Demos/DataGrid/VirtualScrolling/React/data.ts new file mode 100644 index 00000000000..ca0338ff959 --- /dev/null +++ b/JSDemos/Demos/DataGrid/VirtualScrolling/React/data.ts @@ -0,0 +1,33 @@ +let s = 123456789; +function random() { + s = (1103515245 * s + 12345) % 2147483647; + return s % (10 - 1); +} + +export function generateData(count: number) { + let i; + const surnames = ['Smith', 'Johnson', 'Brown', 'Taylor', 'Anderson', 'Harris', 'Clark', 'Allen', 'Scott', 'Carter']; + const names = ['James', 'John', 'Robert', 'Christopher', 'George', 'Mary', 'Nancy', 'Sandra', 'Michelle', 'Betty']; + const gender = ['Male', 'Female']; + const items: ({ id: number; firstName: string; lastName: string; gender: string; birthDate: Date; })[] = []; + const startBirthDate = Date.parse('1/1/1975'); + const endBirthDate = Date.parse('1/1/1992'); + + for (i = 0; i < count; i += 1) { + const birthDate = new Date(startBirthDate + Math.floor( + (random() * (endBirthDate - startBirthDate)) / 10, + )); + birthDate.setHours(12); + + const nameIndex = random(); + const item = { + id: i + 1, + firstName: names[nameIndex], + lastName: surnames[random()], + gender: gender[Math.floor(nameIndex / 5)], + birthDate, + }; + items.push(item); + } + return items; +} diff --git a/JSDemos/Demos/DataGrid/VirtualScrolling/React/index.html b/JSDemos/Demos/DataGrid/VirtualScrolling/React/index.html index abaf3c473df..0fc1cca0d07 100644 --- a/JSDemos/Demos/DataGrid/VirtualScrolling/React/index.html +++ b/JSDemos/Demos/DataGrid/VirtualScrolling/React/index.html @@ -12,7 +12,7 @@ diff --git a/JSDemos/Demos/DataGrid/VirtualScrolling/React/index.js b/JSDemos/Demos/DataGrid/VirtualScrolling/React/index.js deleted file mode 100644 index d9d7442ce76..00000000000 --- a/JSDemos/Demos/DataGrid/VirtualScrolling/React/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import App from './App.js'; - -ReactDOM.render( - , - document.getElementById('app'), -); diff --git a/JSDemos/Demos/DataGrid/VirtualScrolling/React/index.tsx b/JSDemos/Demos/DataGrid/VirtualScrolling/React/index.tsx new file mode 100644 index 00000000000..8acbec4b617 --- /dev/null +++ b/JSDemos/Demos/DataGrid/VirtualScrolling/React/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import App from './App.tsx'; + +ReactDOM.render( + , + document.getElementById('app'), +); diff --git a/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/App.js b/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/App.js new file mode 100644 index 00000000000..b24f5458bb0 --- /dev/null +++ b/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/App.js @@ -0,0 +1,29 @@ +import React from 'react'; +import DataGrid, { Scrolling, Sorting, LoadPanel } from 'devextreme-react/data-grid'; +import { generateData } from './data.js'; + +const dataSource = generateData(100000); +const customizeColumns = (columns) => { + columns[0].width = 70; +}; +const App = () => { + const [loadPanelEnabled, setLoadPanelEnabled] = React.useState(true); + const onContentReady = React.useCallback(() => { + setLoadPanelEnabled(false); + }, []); + return ( + + + + + + ); +}; +export default App; diff --git a/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/data.js b/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/data.js new file mode 100644 index 00000000000..9e5bbcb8d34 --- /dev/null +++ b/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/data.js @@ -0,0 +1,52 @@ +let s = 123456789; +function random() { + s = (1103515245 * s + 12345) % 2147483647; + return s % (10 - 1); +} +export function generateData(count) { + let i; + const surnames = [ + 'Smith', + 'Johnson', + 'Brown', + 'Taylor', + 'Anderson', + 'Harris', + 'Clark', + 'Allen', + 'Scott', + 'Carter', + ]; + const names = [ + 'James', + 'John', + 'Robert', + 'Christopher', + 'George', + 'Mary', + 'Nancy', + 'Sandra', + 'Michelle', + 'Betty', + ]; + const gender = ['Male', 'Female']; + const items = []; + const startBirthDate = Date.parse('1/1/1975'); + const endBirthDate = Date.parse('1/1/1992'); + for (i = 0; i < count; i += 1) { + const birthDate = new Date( + startBirthDate + Math.floor((random() * (endBirthDate - startBirthDate)) / 10), + ); + birthDate.setHours(12); + const nameIndex = random(); + const item = { + id: i + 1, + firstName: names[nameIndex], + lastName: surnames[random()], + gender: gender[Math.floor(nameIndex / 5)], + birthDate, + }; + items.push(item); + } + return items; +} diff --git a/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/index.html b/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/index.html new file mode 100644 index 00000000000..22df8d3607c --- /dev/null +++ b/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/index.html @@ -0,0 +1,44 @@ + + + + DevExtreme Demo + + + + + + + + + + + + + +
+
+
+ + diff --git a/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/index.js b/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/index.js new file mode 100644 index 00000000000..b853e0be824 --- /dev/null +++ b/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/index.js @@ -0,0 +1,5 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App.js'; + +ReactDOM.render(, document.getElementById('app')); diff --git a/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/styles.css b/JSDemos/Demos/DataGrid/VirtualScrolling/ReactJs/styles.css new file mode 100644 index 00000000000..e69de29bb2d diff --git a/JSDemos/Demos/DataGrid/WebAPIService/React/App.js b/JSDemos/Demos/DataGrid/WebAPIService/React/App.js deleted file mode 100644 index 01cb4c87991..00000000000 --- a/JSDemos/Demos/DataGrid/WebAPIService/React/App.js +++ /dev/null @@ -1,118 +0,0 @@ -import React from 'react'; -import 'devextreme/data/odata/store'; -import { - Column, - DataGrid, - FilterRow, - HeaderFilter, - GroupPanel, - Scrolling, - Editing, - Grouping, - Lookup, - MasterDetail, - Summary, - RangeRule, - RequiredRule, - StringLengthRule, - GroupItem, - TotalItem, - ValueFormat, -} from 'devextreme-react/data-grid'; -import { createStore } from 'devextreme-aspnet-data-nojquery'; -import MasterDetailGrid from './MasterDetailGrid.js'; - -const url = 'https://js.devexpress.com/Demos/Mvc/api/DataGridWebApi'; - -const dataSource = createStore({ - key: 'OrderID', - loadUrl: `${url}/Orders`, - insertUrl: `${url}/InsertOrder`, - updateUrl: `${url}/UpdateOrder`, - deleteUrl: `${url}/DeleteOrder`, - onBeforeSend: (method, ajaxOptions) => { - ajaxOptions.xhrFields = { withCredentials: true }; - }, -}); - -const customersData = createStore({ - key: 'Value', - loadUrl: `${url}/CustomersLookup`, - onBeforeSend: (method, ajaxOptions) => { - ajaxOptions.xhrFields = { withCredentials: true }; - }, -}); - -const shippersData = createStore({ - key: 'Value', - loadUrl: `${url}/ShippersLookup`, - onBeforeSend: (method, ajaxOptions) => { - ajaxOptions.xhrFields = { withCredentials: true }; - }, -}); - -const App = () => ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -); - -export default App; diff --git a/JSDemos/Demos/DataGrid/WebAPIService/React/App.tsx b/JSDemos/Demos/DataGrid/WebAPIService/React/App.tsx new file mode 100644 index 00000000000..a4ad5d10340 --- /dev/null +++ b/JSDemos/Demos/DataGrid/WebAPIService/React/App.tsx @@ -0,0 +1,118 @@ +import React from 'react'; +import 'devextreme/data/odata/store'; +import { + Column, + DataGrid, + FilterRow, + HeaderFilter, + GroupPanel, + Scrolling, + Editing, + Grouping, + Lookup, + MasterDetail, + Summary, + RangeRule, + RequiredRule, + StringLengthRule, + GroupItem, + TotalItem, + ValueFormat, +} from 'devextreme-react/data-grid'; +import { createStore } from 'devextreme-aspnet-data-nojquery'; +import MasterDetailGrid from './MasterDetailGrid.tsx'; + +const url = 'https://js.devexpress.com/Demos/Mvc/api/DataGridWebApi'; + +const dataSource = createStore({ + key: 'OrderID', + loadUrl: `${url}/Orders`, + insertUrl: `${url}/InsertOrder`, + updateUrl: `${url}/UpdateOrder`, + deleteUrl: `${url}/DeleteOrder`, + onBeforeSend: (method, ajaxOptions) => { + ajaxOptions.xhrFields = { withCredentials: true }; + }, +}); + +const customersData = createStore({ + key: 'Value', + loadUrl: `${url}/CustomersLookup`, + onBeforeSend: (method, ajaxOptions) => { + ajaxOptions.xhrFields = { withCredentials: true }; + }, +}); + +const shippersData = createStore({ + key: 'Value', + loadUrl: `${url}/ShippersLookup`, + onBeforeSend: (method, ajaxOptions) => { + ajaxOptions.xhrFields = { withCredentials: true }; + }, +}); + +const App = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +export default App; diff --git a/JSDemos/Demos/DataGrid/WebAPIService/React/MasterDetailGrid.tsx b/JSDemos/Demos/DataGrid/WebAPIService/React/MasterDetailGrid.tsx new file mode 100644 index 00000000000..320259ad8ea --- /dev/null +++ b/JSDemos/Demos/DataGrid/WebAPIService/React/MasterDetailGrid.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import DataGrid, { DataGridTypes } from 'devextreme-react/data-grid'; + +import { createStore } from 'devextreme-aspnet-data-nojquery'; + +const url = 'https://js.devexpress.com/Demos/Mvc/api/DataGridWebApi'; + +const getMasterDetailGridDataSource = (id) => ({ + store: createStore({ + loadUrl: `${url}/OrderDetails`, + loadParams: { orderID: id }, + onBeforeSend: (method, ajaxOptions) => { + ajaxOptions.xhrFields = { withCredentials: true }; + }, + }), +}); + +const MasterDetailGrid = (props: DataGridTypes.MasterDetailTemplateData) => { + const [dataSource, setDataSource] = React.useState(null); + + React.useEffect(() => { + const masterDetailDataSource = getMasterDetailGridDataSource(props.data.key); + setDataSource(masterDetailDataSource); + }, [props.data.key]); + + return ( + + ); +}; + +export default MasterDetailGrid; diff --git a/JSDemos/Demos/DataGrid/WebAPIService/React/index.html b/JSDemos/Demos/DataGrid/WebAPIService/React/index.html index 537a755f3bb..fc2ebd0a819 100644 --- a/JSDemos/Demos/DataGrid/WebAPIService/React/index.html +++ b/JSDemos/Demos/DataGrid/WebAPIService/React/index.html @@ -11,7 +11,7 @@ diff --git a/JSDemos/Demos/DataGrid/WebAPIService/React/index.js b/JSDemos/Demos/DataGrid/WebAPIService/React/index.js deleted file mode 100644 index d9d7442ce76..00000000000 --- a/JSDemos/Demos/DataGrid/WebAPIService/React/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import App from './App.js'; - -ReactDOM.render( - , - document.getElementById('app'), -); diff --git a/JSDemos/Demos/DataGrid/WebAPIService/React/index.tsx b/JSDemos/Demos/DataGrid/WebAPIService/React/index.tsx new file mode 100644 index 00000000000..8acbec4b617 --- /dev/null +++ b/JSDemos/Demos/DataGrid/WebAPIService/React/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import App from './App.tsx'; + +ReactDOM.render( + , + document.getElementById('app'), +); diff --git a/JSDemos/Demos/DataGrid/WebAPIService/ReactJs/App.js b/JSDemos/Demos/DataGrid/WebAPIService/ReactJs/App.js new file mode 100644 index 00000000000..90042b18fbe --- /dev/null +++ b/JSDemos/Demos/DataGrid/WebAPIService/ReactJs/App.js @@ -0,0 +1,148 @@ +import React from 'react'; +import 'devextreme/data/odata/store'; +import { + Column, + DataGrid, + FilterRow, + HeaderFilter, + GroupPanel, + Scrolling, + Editing, + Grouping, + Lookup, + MasterDetail, + Summary, + RangeRule, + RequiredRule, + StringLengthRule, + GroupItem, + TotalItem, + ValueFormat, +} from 'devextreme-react/data-grid'; +import { createStore } from 'devextreme-aspnet-data-nojquery'; +import MasterDetailGrid from './MasterDetailGrid.js'; + +const url = 'https://js.devexpress.com/Demos/Mvc/api/DataGridWebApi'; +const dataSource = createStore({ + key: 'OrderID', + loadUrl: `${url}/Orders`, + insertUrl: `${url}/InsertOrder`, + updateUrl: `${url}/UpdateOrder`, + deleteUrl: `${url}/DeleteOrder`, + onBeforeSend: (method, ajaxOptions) => { + ajaxOptions.xhrFields = { withCredentials: true }; + }, +}); +const customersData = createStore({ + key: 'Value', + loadUrl: `${url}/CustomersLookup`, + onBeforeSend: (method, ajaxOptions) => { + ajaxOptions.xhrFields = { withCredentials: true }; + }, +}); +const shippersData = createStore({ + key: 'Value', + loadUrl: `${url}/ShippersLookup`, + onBeforeSend: (method, ajaxOptions) => { + ajaxOptions.xhrFields = { withCredentials: true }; + }, +}); +const App = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); +export default App; diff --git a/JSDemos/Demos/DataGrid/WebAPIService/React/MasterDetailGrid.js b/JSDemos/Demos/DataGrid/WebAPIService/ReactJs/MasterDetailGrid.js similarity index 99% rename from JSDemos/Demos/DataGrid/WebAPIService/React/MasterDetailGrid.js rename to JSDemos/Demos/DataGrid/WebAPIService/ReactJs/MasterDetailGrid.js index dce32733b6c..351b5e9aff9 100644 --- a/JSDemos/Demos/DataGrid/WebAPIService/React/MasterDetailGrid.js +++ b/JSDemos/Demos/DataGrid/WebAPIService/ReactJs/MasterDetailGrid.js @@ -1,10 +1,8 @@ import React from 'react'; import DataGrid from 'devextreme-react/data-grid'; - import { createStore } from 'devextreme-aspnet-data-nojquery'; const url = 'https://js.devexpress.com/Demos/Mvc/api/DataGridWebApi'; - const getMasterDetailGridDataSource = (id) => ({ store: createStore({ loadUrl: `${url}/OrderDetails`, @@ -14,15 +12,12 @@ const getMasterDetailGridDataSource = (id) => ({ }, }), }); - const MasterDetailGrid = (props) => { const [dataSource, setDataSource] = React.useState(null); - React.useEffect(() => { const masterDetailDataSource = getMasterDetailGridDataSource(props.data.key); setDataSource(masterDetailDataSource); }, [props.data.key]); - return ( { /> ); }; - export default MasterDetailGrid; diff --git a/JSDemos/Demos/DataGrid/WebAPIService/ReactJs/index.html b/JSDemos/Demos/DataGrid/WebAPIService/ReactJs/index.html new file mode 100644 index 00000000000..54983a89e52 --- /dev/null +++ b/JSDemos/Demos/DataGrid/WebAPIService/ReactJs/index.html @@ -0,0 +1,39 @@ + + + + DevExtreme Demo + + + + + + + + + + + + +
+
+
+ + diff --git a/JSDemos/Demos/DataGrid/WebAPIService/ReactJs/index.js b/JSDemos/Demos/DataGrid/WebAPIService/ReactJs/index.js new file mode 100644 index 00000000000..b853e0be824 --- /dev/null +++ b/JSDemos/Demos/DataGrid/WebAPIService/ReactJs/index.js @@ -0,0 +1,5 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App.js'; + +ReactDOM.render(, document.getElementById('app')); diff --git a/JSDemos/Demos/DateRangeBox/Overview/Angular/app/app.component.html b/JSDemos/Demos/DateRangeBox/Overview/Angular/app/app.component.html index 0978dda2ce9..38362f6d0bb 100644 --- a/JSDemos/Demos/DateRangeBox/Overview/Angular/app/app.component.html +++ b/JSDemos/Demos/DateRangeBox/Overview/Angular/app/app.component.html @@ -29,7 +29,7 @@

-
Single-calendar View
+
Single-calendar view
@@ -46,6 +46,13 @@
+
+
Disable out of range selection
+
+ + +
+
Clear button
diff --git a/JSDemos/Demos/DateRangeBox/Overview/React/App.tsx b/JSDemos/Demos/DateRangeBox/Overview/React/App.tsx index b10ea6094a3..1301392ea5f 100644 --- a/JSDemos/Demos/DateRangeBox/Overview/React/App.tsx +++ b/JSDemos/Demos/DateRangeBox/Overview/React/App.tsx @@ -66,7 +66,7 @@ export default function App() {
-
Single-calendar View
+
Single-calendar view
@@ -86,6 +86,12 @@ export default function App() { />
+
+
Disable out of range selection
+
+ +
+
Clear button
diff --git a/JSDemos/Demos/DateRangeBox/Overview/ReactJs/App.js b/JSDemos/Demos/DateRangeBox/Overview/ReactJs/App.js index aae34bfb325..80de138768d 100644 --- a/JSDemos/Demos/DateRangeBox/Overview/ReactJs/App.js +++ b/JSDemos/Demos/DateRangeBox/Overview/ReactJs/App.js @@ -56,7 +56,7 @@ export default function App() {
-
Single-calendar View
+
Single-calendar view
@@ -76,6 +76,12 @@ export default function App() { />
+
+
Disable out of range selection
+
+ +
+
Clear button
diff --git a/JSDemos/Demos/DateRangeBox/Overview/Vue/App.vue b/JSDemos/Demos/DateRangeBox/Overview/Vue/App.vue index 5dd659e6e1e..b3300039f20 100644 --- a/JSDemos/Demos/DateRangeBox/Overview/Vue/App.vue +++ b/JSDemos/Demos/DateRangeBox/Overview/Vue/App.vue @@ -33,7 +33,7 @@
-
Single-calendar View
+
Single-calendar view
+
+
Disable out of range selection
+
+ +
+
Clear button
diff --git a/JSDemos/Demos/DateRangeBox/Overview/jQuery/index.html b/JSDemos/Demos/DateRangeBox/Overview/jQuery/index.html index 65e7073c606..2f346edd877 100644 --- a/JSDemos/Demos/DateRangeBox/Overview/jQuery/index.html +++ b/JSDemos/Demos/DateRangeBox/Overview/jQuery/index.html @@ -40,7 +40,7 @@
-
Single-calendar View
+
Single-calendar view
@@ -57,6 +57,12 @@
+
+
Disable out of range selection
+
+
+
+
Clear button
diff --git a/JSDemos/Demos/DateRangeBox/Overview/jQuery/index.js b/JSDemos/Demos/DateRangeBox/Overview/jQuery/index.js index eae0036551e..b3c30462bd7 100644 --- a/JSDemos/Demos/DateRangeBox/Overview/jQuery/index.js +++ b/JSDemos/Demos/DateRangeBox/Overview/jQuery/index.js @@ -37,6 +37,10 @@ $(() => { value: initialValue, }); + $('#disable-out-of-range-selection').dxDateRangeBox({ + disableOutOfRangeSelection: true, + }); + function getCurrentMonthRange() { const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate(); diff --git a/JSDemos/Demos/Diagram/UICustomization/React/App.js b/JSDemos/Demos/Diagram/UICustomization/React/App.js index 276192153ff..bbee8631b7c 100644 --- a/JSDemos/Demos/Diagram/UICustomization/React/App.js +++ b/JSDemos/Demos/Diagram/UICustomization/React/App.js @@ -11,7 +11,7 @@ import Diagram, { Command, Toolbox, } from 'devextreme-react/diagram'; -import dialog from 'devextreme/ui/dialog'; +import { confirm } from 'devextreme/ui/dialog'; import 'whatwg-fetch'; const pageCommands = ['pageSize', 'pageOrientation', 'pageColor']; @@ -19,7 +19,7 @@ const menuCommands = ['bringToFront', 'sendToBack', 'lock', 'unlock']; function onCustomCommand(e) { if (e.name === 'clear') { - const result = dialog.confirm('Are you sure you want to clear the diagram? This action cannot be undone.', 'Warning'); + const result = confirm('Are you sure you want to clear the diagram? This action cannot be undone.', 'Warning'); result.then( (dialogResult) => { if (dialogResult) { diff --git a/JSDemos/Demos/Diagram/UICustomization/Vue/App.vue b/JSDemos/Demos/Diagram/UICustomization/Vue/App.vue index 28d2787bb5e..d5c6c1edb86 100644 --- a/JSDemos/Demos/Diagram/UICustomization/Vue/App.vue +++ b/JSDemos/Demos/Diagram/UICustomization/Vue/App.vue @@ -113,7 +113,7 @@ import { DxCommand, DxToolbox, } from 'devextreme-vue/diagram'; -import dialog from 'devextreme/ui/dialog'; +import { confirm } from 'devextreme/ui/dialog'; import 'whatwg-fetch'; export default { @@ -144,7 +144,7 @@ export default { methods: { onCustomCommand(e) { if (e.name === 'clear') { - const result = dialog.confirm('Are you sure you want to clear the diagram? This action cannot be undone.', 'Warning'); + const result = confirm('Are you sure you want to clear the diagram? This action cannot be undone.', 'Warning'); result.then( (dialogResult) => { if (dialogResult) { diff --git a/JSDemos/Demos/DropDownBox/SingleSelection/Angular/app/app.component.html b/JSDemos/Demos/DropDownBox/SingleSelection/Angular/app/app.component.html index 9394647ba28..edc8762e939 100644 --- a/JSDemos/Demos/DropDownBox/SingleSelection/Angular/app/app.component.html +++ b/JSDemos/Demos/DropDownBox/SingleSelection/Angular/app/app.component.html @@ -51,6 +51,7 @@ [dataSource]="gridDataSource" [columns]="gridColumns" [hoverStateEnabled]="true" + showBorders="true" [(selectedRowKeys)]="gridBoxValue" height="100%" > diff --git a/JSDemos/Demos/DropDownBox/SingleSelection/React/App.tsx b/JSDemos/Demos/DropDownBox/SingleSelection/React/App.tsx index ac656837172..29dfa3b1d24 100644 --- a/JSDemos/Demos/DropDownBox/SingleSelection/React/App.tsx +++ b/JSDemos/Demos/DropDownBox/SingleSelection/React/App.tsx @@ -80,6 +80,7 @@ function App() { dataSource={gridDataSource} columns={gridColumns} hoverStateEnabled={true} + showBorders={true} selectedRowKeys={gridBoxValue} onSelectionChanged={dataGridOnSelectionChanged} height="100%" diff --git a/JSDemos/Demos/DropDownBox/SingleSelection/ReactJs/App.js b/JSDemos/Demos/DropDownBox/SingleSelection/ReactJs/App.js index 6c7b2810b6c..077879e06c1 100644 --- a/JSDemos/Demos/DropDownBox/SingleSelection/ReactJs/App.js +++ b/JSDemos/Demos/DropDownBox/SingleSelection/ReactJs/App.js @@ -66,6 +66,7 @@ function App() { dataSource={gridDataSource} columns={gridColumns} hoverStateEnabled={true} + showBorders={true} selectedRowKeys={gridBoxValue} onSelectionChanged={dataGridOnSelectionChanged} height="100%" diff --git a/JSDemos/Demos/DropDownBox/SingleSelection/Vue/App.vue b/JSDemos/Demos/DropDownBox/SingleSelection/Vue/App.vue index 03059a84169..48bd19e366f 100644 --- a/JSDemos/Demos/DropDownBox/SingleSelection/Vue/App.vue +++ b/JSDemos/Demos/DropDownBox/SingleSelection/Vue/App.vue @@ -50,6 +50,7 @@ :data-source="gridDataSource" :columns="gridColumns" :hover-state-enabled="true" + :show-borders="true" v-model:selected-row-keys="gridBoxValue" @selection-changed="onGridSelectionChanged($event)" height="100%" diff --git a/JSDemos/Demos/DropDownBox/SingleSelection/jQuery/index.js b/JSDemos/Demos/DropDownBox/SingleSelection/jQuery/index.js index 2421ec2f903..963759246f6 100644 --- a/JSDemos/Demos/DropDownBox/SingleSelection/jQuery/index.js +++ b/JSDemos/Demos/DropDownBox/SingleSelection/jQuery/index.js @@ -82,6 +82,7 @@ $(() => { selection: { mode: 'single' }, selectedRowKeys: [value], height: '100%', + showBorders: true, onSelectionChanged(selectedItems) { const keys = selectedItems.selectedRowKeys; const hasSelection = keys.length; diff --git a/JSDemos/Demos/FilterBuilder/Customization/Angular/app/app.component.css b/JSDemos/Demos/FilterBuilder/Customization/Angular/app/app.component.css index 4a6e80abf8c..2492378a54d 100644 --- a/JSDemos/Demos/FilterBuilder/Customization/Angular/app/app.component.css +++ b/JSDemos/Demos/FilterBuilder/Customization/Angular/app/app.component.css @@ -28,6 +28,14 @@ min-width: 150px; } +::ng-deep .dx-filterbuilder { + background-color: transparent; + box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.14), 0 0 2px 0 rgba(0, 0, 0, 0.12); + border-radius: 6px; + padding: 15px; + margin: 24px; +} + ::ng-deep .dx-filterbuilder .dx-numberbox { width: 80px; } diff --git a/JSDemos/Demos/FilterBuilder/Customization/React/styles.css b/JSDemos/Demos/FilterBuilder/Customization/React/styles.css index 399342df3f3..6347cb029d7 100644 --- a/JSDemos/Demos/FilterBuilder/Customization/React/styles.css +++ b/JSDemos/Demos/FilterBuilder/Customization/React/styles.css @@ -28,6 +28,14 @@ min-width: 150px; } +.dx-filterbuilder { + background-color: transparent; + box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.14), 0 0 2px 0 rgba(0, 0, 0, 0.12); + border-radius: 6px; + padding: 15px; + margin: 24px; +} + .dx-filterbuilder .dx-numberbox { width: 80px; } diff --git a/JSDemos/Demos/FilterBuilder/Customization/Vue/App.vue b/JSDemos/Demos/FilterBuilder/Customization/Vue/App.vue index 596b009474c..9245a1fc01b 100644 --- a/JSDemos/Demos/FilterBuilder/Customization/Vue/App.vue +++ b/JSDemos/Demos/FilterBuilder/Customization/Vue/App.vue @@ -94,6 +94,14 @@ export default { min-width: 150px; } +.dx-filterbuilder { + background-color: transparent; + box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.14), 0 0 2px 0 rgba(0, 0, 0, 0.12); + border-radius: 6px; + padding: 15px; + margin: 24px; +} + .dx-filterbuilder .dx-numberbox { width: 80px; } diff --git a/JSDemos/Demos/FilterBuilder/Customization/jQuery/styles.css b/JSDemos/Demos/FilterBuilder/Customization/jQuery/styles.css index 399342df3f3..6347cb029d7 100644 --- a/JSDemos/Demos/FilterBuilder/Customization/jQuery/styles.css +++ b/JSDemos/Demos/FilterBuilder/Customization/jQuery/styles.css @@ -28,6 +28,14 @@ min-width: 150px; } +.dx-filterbuilder { + background-color: transparent; + box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.14), 0 0 2px 0 rgba(0, 0, 0, 0.12); + border-radius: 6px; + padding: 15px; + margin: 24px; +} + .dx-filterbuilder .dx-numberbox { width: 80px; } diff --git a/JSDemos/Demos/FilterBuilder/WithDataGrid/Angular/app/app.component.css b/JSDemos/Demos/FilterBuilder/WithDataGrid/Angular/app/app.component.css index 12bf6e5d72d..30f04a1ad4d 100644 --- a/JSDemos/Demos/FilterBuilder/WithDataGrid/Angular/app/app.component.css +++ b/JSDemos/Demos/FilterBuilder/WithDataGrid/Angular/app/app.component.css @@ -1,11 +1,14 @@ ::ng-deep .filter-container { - background-color: rgba(191, 191, 191, 0.15); + background-color: transparent; + box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.14), 0 0 2px 0 rgba(0, 0, 0, 0.12); + border-radius: 6px; padding: 5px; width: 500px; - margin-bottom: 25px; + margin: 24px; } ::ng-deep .dx-filterbuilder { + background-color: transparent; padding: 10px; } diff --git a/JSDemos/Demos/FilterBuilder/WithDataGrid/Angular/app/app.component.ts b/JSDemos/Demos/FilterBuilder/WithDataGrid/Angular/app/app.component.ts index 437fc32ad10..017e82c4876 100644 --- a/JSDemos/Demos/FilterBuilder/WithDataGrid/Angular/app/app.component.ts +++ b/JSDemos/Demos/FilterBuilder/WithDataGrid/Angular/app/app.component.ts @@ -37,6 +37,7 @@ export class AppComponent { this.gridFilterValue = this.filter; this.dataSource = new DataSource({ store: new ODataStore({ + version: 2, fieldTypes: { Product_Cost: 'Decimal', Product_Sale_Price: 'Decimal', diff --git a/JSDemos/Demos/FilterBuilder/WithDataGrid/AngularJS/index.js b/JSDemos/Demos/FilterBuilder/WithDataGrid/AngularJS/index.js index 1158ad7215c..ecd28db080a 100644 --- a/JSDemos/Demos/FilterBuilder/WithDataGrid/AngularJS/index.js +++ b/JSDemos/Demos/FilterBuilder/WithDataGrid/AngularJS/index.js @@ -25,6 +25,7 @@ DemoApp.controller('DemoController', ($scope) => { dataSource: { store: { type: 'odata', + version: 2, fieldTypes: { Product_Cost: 'Decimal', Product_Sale_Price: 'Decimal', diff --git a/JSDemos/Demos/FilterBuilder/WithDataGrid/React/App.js b/JSDemos/Demos/FilterBuilder/WithDataGrid/React/App.js index acc8d07dcd3..5ebcd2a7e56 100644 --- a/JSDemos/Demos/FilterBuilder/WithDataGrid/React/App.js +++ b/JSDemos/Demos/FilterBuilder/WithDataGrid/React/App.js @@ -8,6 +8,7 @@ import { filter, fields } from './data.js'; const dataSource = new DataSource({ store: new ODataStore({ + version: 2, fieldTypes: { Product_Cost: 'Decimal', Product_Sale_Price: 'Decimal', diff --git a/JSDemos/Demos/FilterBuilder/WithDataGrid/React/styles.css b/JSDemos/Demos/FilterBuilder/WithDataGrid/React/styles.css index 63fb40d024f..df0174f4d4a 100644 --- a/JSDemos/Demos/FilterBuilder/WithDataGrid/React/styles.css +++ b/JSDemos/Demos/FilterBuilder/WithDataGrid/React/styles.css @@ -1,11 +1,14 @@ .filter-container { - background-color: rgba(191, 191, 191, 0.15); + background-color: transparent; + box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.14), 0 0 2px 0 rgba(0, 0, 0, 0.12); + border-radius: 6px; padding: 5px; width: 500px; - margin-bottom: 25px; + margin: 24px; } .dx-filterbuilder { + background-color: transparent; padding: 10px; } diff --git a/JSDemos/Demos/FilterBuilder/WithDataGrid/Vue/App.vue b/JSDemos/Demos/FilterBuilder/WithDataGrid/Vue/App.vue index 756c98118ff..e6aec523dfa 100644 --- a/JSDemos/Demos/FilterBuilder/WithDataGrid/Vue/App.vue +++ b/JSDemos/Demos/FilterBuilder/WithDataGrid/Vue/App.vue @@ -42,6 +42,7 @@ export default { gridFilterValue: filter, dataSource: new DataSource({ store: new ODataStore({ + version: 2, fieldTypes: { Product_Cost: 'Decimal', Product_Sale_Price: 'Decimal', @@ -69,13 +70,16 @@ export default { diff --git a/JSDemos/Demos/Form/GroupedFields/jQuery/styles.css b/JSDemos/Demos/Form/GroupedFields/jQuery/styles.css index 92df1a1feb8..6a937005905 100644 --- a/JSDemos/Demos/Form/GroupedFields/jQuery/styles.css +++ b/JSDemos/Demos/Form/GroupedFields/jQuery/styles.css @@ -3,9 +3,7 @@ } .long-title h3 { - font-family: 'Segoe UI Light', 'Helvetica Neue Light', 'Segoe UI', 'Helvetica Neue', 'Trebuchet MS', Verdana; - font-weight: 200; - font-size: 28px; + font-size: 24px; text-align: center; - margin-bottom: 20px; + line-height: 2em; } diff --git a/JSDemos/Demos/Form/Validation/Angular/app/app.component.css b/JSDemos/Demos/Form/Validation/Angular/app/app.component.css index 0c91ed02142..9c8ad8b00b6 100644 --- a/JSDemos/Demos/Form/Validation/Angular/app/app.component.css +++ b/JSDemos/Demos/Form/Validation/Angular/app/app.component.css @@ -1,5 +1,5 @@ ::ng-deep form { - margin: 10px; + margin: 10px 10px 15px; } ::ng-deep .last-group { diff --git a/JSDemos/Demos/Form/Validation/Angular/app/app.component.html b/JSDemos/Demos/Form/Validation/Angular/app/app.component.html index f89a877cba0..a7e8400d34c 100644 --- a/JSDemos/Demos/Form/Validation/Angular/app/app.component.html +++ b/JSDemos/Demos/Form/Validation/Angular/app/app.component.html @@ -80,6 +80,18 @@ [editorOptions]="dateRangeBoxOptions" > + + + + diff --git a/JSDemos/Demos/Form/Validation/Angular/app/app.component.ts b/JSDemos/Demos/Form/Validation/Angular/app/app.component.ts index 08b6cb4d0b1..da912f60714 100644 --- a/JSDemos/Demos/Form/Validation/Angular/app/app.component.ts +++ b/JSDemos/Demos/Form/Validation/Angular/app/app.component.ts @@ -125,16 +125,13 @@ export class AppComponent { dateBoxOptions = { placeholder: 'Birth Date', acceptCustomValue: false, - invalidDateMessage: - 'The date must have the following format: MM/dd/yyyy', + openOnFieldClick: true, }; dateRangeBoxOptions = { startDatePlaceholder: 'Start Date', endDatePlaceholder: 'End Date', acceptCustomValue: false, - invalidDateMessage: - 'The date must have the following format: MM/dd/yyyy', }; registerButtonOptions = { @@ -175,6 +172,29 @@ export class AppComponent { this.customer = service.getCustomer(); } + validateVacationDatesRange({ value }) { + const [startDate, endDate] = value; + + if (startDate === null || endDate === null) { + return true; + } + + const millisecondsPerDay = 24 * 60 * 60 * 1000; + const daysDifference = Math.abs((endDate - startDate) / millisecondsPerDay); + + return daysDifference < 25; + } + + validateVacationDatesPresence({ value }) { + const [startDate, endDate] = value; + + if (startDate === null && endDate === null) { + return true; + } + + return startDate !== null && endDate !== null; + } + asyncValidation(params) { return sendRequest(params.value); } diff --git a/JSDemos/Demos/Form/Validation/React/App.js b/JSDemos/Demos/Form/Validation/React/App.js index d233c15d152..de1b72042fd 100644 --- a/JSDemos/Demos/Form/Validation/React/App.js +++ b/JSDemos/Demos/Form/Validation/React/App.js @@ -11,6 +11,7 @@ import Form, { RequiredRule, StringLengthRule, AsyncRule, + CustomRule, } from 'devextreme-react/form'; import notify from 'devextreme/ui/notify'; import Validator from 'devextreme/ui/validator'; @@ -69,15 +70,13 @@ const maxDate = new Date().setFullYear(new Date().getFullYear() - 21); const dateBoxOptions = { placeholder: 'Birth Date', acceptCustomValue: false, - invalidDateMessage: - 'The date must have the following format: MM/dd/yyyy', + openOnFieldClick: true, }; const dateRangeBoxOptions = { startDatePlaceholder: 'Start Date', endDatePlaceholder: 'End Date', acceptCustomValue: false, - invalidDateMessage: 'The date must have the following format: MM/dd/yyyy', }; function sendRequest(value) { @@ -95,6 +94,29 @@ const checkComparison = () => true; const asyncValidation = (params) => sendRequest(params.value); +const validateVacationDatesRange = ({ value }) => { + const [startDate, endDate] = value; + + if (startDate === null || endDate === null) { + return true; + } + + const millisecondsPerDay = 24 * 60 * 60 * 1000; + const daysDifference = Math.abs((endDate - startDate) / millisecondsPerDay); + + return daysDifference < 25; +}; + +const validateVacationDatesPresence = ({ value }) => { + const [startDate, endDate] = value; + + if (startDate === null && endDate === null) { + return true; + } + + return startDate !== null && endDate !== null; +}; + const registerButtonOptions = { text: 'Register', type: 'default', @@ -231,6 +253,8 @@ function App() { editorOptions={dateRangeBoxOptions} >
- diff --git a/JSDemos/Demos/TabPanel/Overview/Vue/data.js b/JSDemos/Demos/TabPanel/Overview/Vue/data.js index 6401deaca8f..40814f746ce 100644 --- a/JSDemos/Demos/TabPanel/Overview/Vue/data.js +++ b/JSDemos/Demos/TabPanel/Overview/Vue/data.js @@ -1,4 +1,4 @@ -export const tabsPositionsSelectBoxLabel = { 'aria-label': 'Tabs positions' }; +export const tabsPositionsSelectBoxLabel = { 'aria-label': 'Tab position' }; export const tabsPositions = [ 'left', diff --git a/JSDemos/Demos/TabPanel/Overview/description.md b/JSDemos/Demos/TabPanel/Overview/description.md index 37a8b507add..bb1097d53cc 100644 --- a/JSDemos/Demos/TabPanel/Overview/description.md +++ b/JSDemos/Demos/TabPanel/Overview/description.md @@ -1,30 +1,15 @@ The [TabPanel](/Documentation/ApiReference/UI_Components/dxTabPanel/) UI component consists of [Tabs](/Documentation/ApiReference/UI_Components/dxTabs/) and [MultiView](/Documentation/ApiReference/UI_Components/dxMultiView/) components. The TabPanel automatically synchronizes the selected tab with the currently displayed view and vice versa. -### Generate Similar Tabs Based on a Data Source +To get started with the DevExtreme TabPanel component, refer to the following tutorial for step-by-step instructions: [Getting Started with TabPanel](/Documentation/Guide/UI_Components/TabPanel/Getting_Started_with_TabPanel/). -To generate similar tabs and views, bind the TabPanel to data. Use the [items[]](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/items/) or [dataSource](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#dataSource) property to do this. Both these properties can work with local arrays, but **dataSource** also accepts a [DataSource](/Documentation/ApiReference/Data_Layer/DataSource/) object. You can use this object if you need to process the local array or fetch the array from a remote data source. In this demo, the **dataSource** property is set to a local array. +## Generate Similar Tabs Based on a Data Source -Each object in the **items[]** or **dataSource** array can contain predefined fields, such as **title** or **icon** (see the [items[]](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/items/) section for the full list). The TabPanel automatically recognizes these fields and creates the default tab and view appearance based on them. However, if your data objects contain custom fields, you need to specify the [itemTitleTemplate](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#itemTitleTemplate) and [itemTemplate](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#itemTemplate) that define how to display the fields in tabs and views. This demo illustrates the latter use case. +To generate similar tabs and views, bind the TabPanel to data. Use the [items](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/items/) or [dataSource](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#dataSource) property to do this. Both these properties can work with local arrays, but **dataSource** also accepts a [DataSource](/Documentation/ApiReference/Data_Layer/DataSource/) object. You can use this object if you need to process the local array or fetch the array from a remote data source. In this demo, the **dataSource** property is set to a local array. -### Create Individual Tabs +Each object in the **items[]** or **dataSource** array can contain predefined fields, such as **title** or **icon** (see the [items[]](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/items/) section for the full list). The TabPanel automatically recognizes these fields and creates the default tab and view appearance based on them. -If you want each tab and view to have differently structured content, define individual templates. To do this, assign an array of objects to the **items[]** or **dataSource** property and specify the [tabTemplate](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/items/#tabTemplate) and [template](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/items/#template) properties in each object. This use case is illustrated in the following tutorial: [Getting Started with TabPanel](/Documentation/Guide/UI_Components/TabPanel/Getting_Started_with_TabPanel/). +## Customize Tab Contents and Appearance -### Switch Between Tabs +You can initialize a tab’s contents (text, icons and badges) with values from underlying data objects. -To switch between tabs programmatically, use the [selectedIndex](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#selectedIndex) property. It accepts the index of a tab in the **dataSource** or **items[]** array. The [onSelectionChanged](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#onSelectionChanged) function allows you to perform the desired actions when the selected tab changes. - -Use the following properties to configure user navigation between tabs: - -- [swipeEnabled](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#swipeEnabled) -Defines whether to switch between views with a swipe gesture. - -- [loop](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#loop) -Specifies whether to loop views. - -- [animationEnabled](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#animationEnabled) -Specifies whether to animate transition between views. - -You can switch the checkboxes below the TabPanel to change the **loop**, **animationEnabled**, and **swipeEnabled** property values. - -To get started with the DevExtreme TabPanel component, refer to the following tutorial for step-by-step instructions: [Getting Started with TabPanel](/Documentation/Guide/UI_Components/TabPanel/Getting_Started_with_TabPanel/). \ No newline at end of file +Use the drop-down editors on the right to change the tab [position](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#tabsPosition), [styling mode](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#stylingMode), and [icon position](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#iconPosition). \ No newline at end of file diff --git a/JSDemos/Demos/TabPanel/Overview/jQuery/data.js b/JSDemos/Demos/TabPanel/Overview/jQuery/data.js index fbca1714e7e..a68736894aa 100644 --- a/JSDemos/Demos/TabPanel/Overview/jQuery/data.js +++ b/JSDemos/Demos/TabPanel/Overview/jQuery/data.js @@ -1,4 +1,4 @@ -const tabsPositionsSelectBoxLabel = { 'aria-label': 'Tabs positions' }; +const tabsPositionsSelectBoxLabel = { 'aria-label': 'Tab position' }; const tabsPositions = [ 'left', diff --git a/JSDemos/Demos/TabPanel/Overview/jQuery/index.html b/JSDemos/Demos/TabPanel/Overview/jQuery/index.html index d7165e24f19..42cf17bb343 100644 --- a/JSDemos/Demos/TabPanel/Overview/jQuery/index.html +++ b/JSDemos/Demos/TabPanel/Overview/jQuery/index.html @@ -22,7 +22,7 @@
Options
-
Tabs position
+
Tab position
diff --git a/JSDemos/Demos/TabPanel/Overview/jQuery/styles.css b/JSDemos/Demos/TabPanel/Overview/jQuery/styles.css index 95667cdf595..ff40f98567f 100644 --- a/JSDemos/Demos/TabPanel/Overview/jQuery/styles.css +++ b/JSDemos/Demos/TabPanel/Overview/jQuery/styles.css @@ -21,7 +21,7 @@ width: 0; } -.dx-theme-material .dx-tabpanel { +.dx-viewport:not(.dx-theme-generic) .dx-tabpanel { border-radius: 8px; overflow: clip; } @@ -116,3 +116,7 @@ .dx-color-scheme-contrast .task-item { border: 1px solid #fff; } + +.dx-theme-fluent.dx-color-scheme-blue-dark .task-item { + background-color: #1f1f1f; +} diff --git a/JSDemos/Demos/TabPanel/Templates/Angular/app/app.component.html b/JSDemos/Demos/TabPanel/Templates/Angular/app/app.component.html index b01e1c24627..204057261b6 100644 --- a/JSDemos/Demos/TabPanel/Templates/Angular/app/app.component.html +++ b/JSDemos/Demos/TabPanel/Templates/Angular/app/app.component.html @@ -14,13 +14,15 @@

- {{ company.City }} - ({{ company.State }}) + {{ company.Address }}

+ {{ company.City }}, + {{ company.State }} {{ company.Zipcode }} - {{ company.Address }}

diff --git a/JSDemos/Demos/TabPanel/Templates/React/CompanyItem.tsx b/JSDemos/Demos/TabPanel/Templates/React/CompanyItem.tsx index 92b64e02f9a..b3eaeb20284 100644 --- a/JSDemos/Demos/TabPanel/Templates/React/CompanyItem.tsx +++ b/JSDemos/Demos/TabPanel/Templates/React/CompanyItem.tsx @@ -7,12 +7,12 @@ function CompanyItem({ data }) {

- { company.City } - ({ company.State }) + { company.Address }

- { company.Zipcode } - { company.Address } + { company.City } + { company.State }  + { company.Zipcode }

diff --git a/JSDemos/Demos/TabPanel/Templates/React/description.md b/JSDemos/Demos/TabPanel/Templates/React/description.md new file mode 100644 index 00000000000..b0bbd5a12d3 --- /dev/null +++ b/JSDemos/Demos/TabPanel/Templates/React/description.md @@ -0,0 +1,16 @@ +This demo illustrates the use of templates in TabPanel. If your data objects contain custom fields, you need to specify the [itemTitleRender](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#itemTitleRender)/[itemTitleComponent](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#itemTitleComponent) and [itemRender](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#itemRender)/[itemComponent](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#itemComponent) that define how to display the fields in tabs and views. + +If you want each tab and view to have differently structured content, define individual renders. To do this, assign an array of objects to the **items[]** or **dataSource** property and specify the [tabRender](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/items/#tabRender)/[tabComponent](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/items/#tabComponent) and [render](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/items/#render)/[component](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/items/#component) properties in each object. This use case is illustrated in the following tutorial: [Getting Started with TabPanel](/Documentation/Guide/UI_Components/TabPanel/Getting_Started_with_TabPanel/). + +Use the following properties to configure user navigation between tabs: + +- [swipeEnabled](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#swipeEnabled) +Defines whether to switch between views with a swipe gesture. + +- [loop](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#loop) +Specifies whether to loop views. + +- [animationEnabled](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#animationEnabled) +Specifies whether to animate transition between views. + +You can switch the checkboxes below the TabPanel to change the **loop**, **animationEnabled**, and **swipeEnabled** property values. \ No newline at end of file diff --git a/JSDemos/Demos/TabPanel/Templates/Vue/App.vue b/JSDemos/Demos/TabPanel/Templates/Vue/App.vue index 68df34431a9..b029f49ac08 100644 --- a/JSDemos/Demos/TabPanel/Templates/Vue/App.vue +++ b/JSDemos/Demos/TabPanel/Templates/Vue/App.vue @@ -15,12 +15,12 @@

- {{ company.City }} - ({{ company.State }}) + {{ company.Address }}

- {{ company.Zipcode }}  - {{ company.Address }} + {{ company.City }} + {{ company.State }}  + {{ company.Zipcode }}

diff --git a/JSDemos/Demos/TabPanel/Templates/description.md b/JSDemos/Demos/TabPanel/Templates/description.md new file mode 100644 index 00000000000..3073a0fdef7 --- /dev/null +++ b/JSDemos/Demos/TabPanel/Templates/description.md @@ -0,0 +1,16 @@ +This demo illustrates the use of templates in TabPanel. If your data objects contain custom fields, you need to specify the [itemTitleTemplate](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#itemTitleTemplate) and [itemTemplate](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#itemTemplate) that define how to display the fields in tabs and views. + +If you want each tab and view to have differently structured content, define individual templates. To do this, assign an array of objects to the **items[]** or **dataSource** property and specify the [tabTemplate](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/items/#tabTemplate) and [template](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/items/#template) properties in each object. This use case is illustrated in the following tutorial: [Getting Started with TabPanel](/Documentation/Guide/UI_Components/TabPanel/Getting_Started_with_TabPanel/). + +Use the following properties to configure user navigation between tabs: + +- [swipeEnabled](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#swipeEnabled) +Defines whether to switch between views with a swipe gesture. + +- [loop](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#loop) +Specifies whether to loop views. + +- [animationEnabled](/Documentation/ApiReference/UI_Components/dxTabPanel/Configuration/#animationEnabled) +Specifies whether to animate transition between views. + +You can switch the checkboxes below the TabPanel to change the **loop**, **animationEnabled**, and **swipeEnabled** property values. \ No newline at end of file diff --git a/JSDemos/Demos/TabPanel/Templates/jQuery/index.html b/JSDemos/Demos/TabPanel/Templates/jQuery/index.html index c34847214fb..7144eb34304 100644 --- a/JSDemos/Demos/TabPanel/Templates/jQuery/index.html +++ b/JSDemos/Demos/TabPanel/Templates/jQuery/index.html @@ -42,12 +42,12 @@

- {{ City }} - ({{ State }}) + {{ Address }}

+ {{ City }}, + {{ State }} {{ Zipcode }} - {{ Address }}

diff --git a/JSDemos/Demos/Tabs/Overview/Angular/app/app.component.css b/JSDemos/Demos/Tabs/Overview/Angular/app/app.component.css index ea3581c3e8e..eb8a8fc47c4 100644 --- a/JSDemos/Demos/Tabs/Overview/Angular/app/app.component.css +++ b/JSDemos/Demos/Tabs/Overview/Angular/app/app.component.css @@ -5,20 +5,28 @@ ::ng-deep .widget-container { display: flex; - flex-direction: column; - justify-content: center; align-items: center; + justify-content: center; flex-grow: 1; - row-gap: 80px; - column-gap: 4px; max-width: calc(100% - 300px); min-width: 200px; padding: 16px 32px; overflow: clip; } -::ng-deep .widget-container-vertical { +::ng-deep .widget-wrapper { + display: inline-flex; + flex-direction: column; + align-items: flex-start; + justify-content: center; + gap: 80px; + max-width: 100%; +} + +::ng-deep .widget-wrapper-vertical { + width: 100%; flex-direction: row; + align-items: center; } ::ng-deep .options { @@ -29,10 +37,6 @@ background-color: rgba(191, 191, 191, 0.15); } -::ng-deep .dx-tab { - width: 135px; -} - ::ng-deep .caption { font-weight: 500; font-size: 18px; @@ -46,11 +50,19 @@ margin-top: 20px; } -::ng-deep .dx-tabs-horizontal { - border-bottom: 1px solid rgb(225, 225, 225, 0.4); +::ng-deep .dx-tabs { + max-width: 100%; } ::ng-deep .dx-tabs-vertical { - border-right: 1px solid rgb(225, 225, 225, 0.4); - height: 250px; + height: 216px; +} + +::ng-deep .dx-viewport:not(.dx-theme-generic) .dx-tabs-horizontal { + border-block-end: 1px solid rgb(225, 225, 225, 0.4); +} + +::ng-deep .dx-viewport:not(.dx-theme-generic) .dx-tabs-vertical { + height: 232px; + border-inline-end: 1px solid rgb(225, 225, 225, 0.4); } diff --git a/JSDemos/Demos/Tabs/Overview/Angular/app/app.component.html b/JSDemos/Demos/Tabs/Overview/Angular/app/app.component.html index 1f3d4f787f2..46511f936bf 100644 --- a/JSDemos/Demos/Tabs/Overview/Angular/app/app.component.html +++ b/JSDemos/Demos/Tabs/Overview/Angular/app/app.component.html @@ -1,26 +1,45 @@
-
- - - +
+
+ + + + + +
@@ -28,19 +47,18 @@
Orientation
Styling mode
@@ -48,28 +66,39 @@
Icon position
+
+ +
+ +
+ +
+
diff --git a/JSDemos/Demos/Tabs/Overview/Angular/app/app.component.ts b/JSDemos/Demos/Tabs/Overview/Angular/app/app.component.ts index 4ff984f362f..5e29e483ee0 100644 --- a/JSDemos/Demos/Tabs/Overview/Angular/app/app.component.ts +++ b/JSDemos/Demos/Tabs/Overview/Angular/app/app.component.ts @@ -37,7 +37,7 @@ export class AppComponent { stylingModes: string[] = ['primary', 'secondary']; - iconPositions: string[] = ['start', 'top', 'end', 'bottom']; + iconPositions: string[] = ['top', 'start', 'end', 'bottom']; orientation: string; @@ -45,36 +45,30 @@ export class AppComponent { iconPosition: string; - widgetContainerClasses = 'widget-container widget-container-horizontal'; + showNavButtons = false; + + scrollByContent = false; + + rtlEnabled = false; + + widgetWrapperClasses = 'widget-wrapper widget-wrapper-horizontal'; constructor(service: Service) { this.tabsWithText = service.getTabsWithText(); this.tabsWithIconAndText = service.getTabsWithIconAndText(); this.tabsWithIcon = service.getTabsWithIcon(); this.orientation = this.orientations[0]; - this.stylingMode = this.stylingModes[0]; + this.stylingMode = this.stylingModes[1]; this.iconPosition = this.iconPositions[0]; } onOrientationChanged(e) { - this.widgetContainerClasses = `widget-container widget-container-${e.value}`; + this.widgetWrapperClasses = `widget-wrapper widget-wrapper-${e.value}`; this.setTabsOption('orientation', e.value); } - onShowNavigationChanged(e) { - this.setTabsOption('showNavButtons', e.value); - } - - onScrollContentChanged(e) { - this.setTabsOption('scrollByContent', e.value); - } - - onIconPositionChanged(e) { - this.setTabsOption('iconPosition', e.value); - } - - onStylingModeChanged(e) { - this.setTabsOption('stylingMode', e.value); + onFullWidthChanged(e) { + this.setTabsOption('width', e.value ? '100%' : 'auto'); } setTabsOption(option, value) { diff --git a/JSDemos/Demos/Tabs/Overview/React/data.ts b/JSDemos/Demos/Tabs/Overview/React/data.ts index 28b8e611ec9..5259a714ab3 100644 --- a/JSDemos/Demos/Tabs/Overview/React/data.ts +++ b/JSDemos/Demos/Tabs/Overview/React/data.ts @@ -95,12 +95,10 @@ export const stylingModes = [ export const orientationLabel = { 'aria-label': 'Orientation' }; export const stylingModeLabel = { 'aria-label': 'Styling Mode' }; export const iconPositionLabel = { 'aria-label': 'Icon Position' }; -export const showNavigationLabel = { 'aria-label': 'Show Navigation' }; -export const scrollContentLabel = { 'aria-label': 'Scroll Content' }; export const iconPositions = [ - 'start', 'top', + 'start', 'end', 'bottom', ]; diff --git a/JSDemos/Demos/Tabs/Overview/React/styles.css b/JSDemos/Demos/Tabs/Overview/React/styles.css index 5a3eecaa7ca..8c1f34ad9cb 100644 --- a/JSDemos/Demos/Tabs/Overview/React/styles.css +++ b/JSDemos/Demos/Tabs/Overview/React/styles.css @@ -5,20 +5,28 @@ .widget-container { display: flex; - flex-direction: column; - justify-content: center; align-items: center; + justify-content: center; flex-grow: 1; - row-gap: 80px; - column-gap: 4px; max-width: calc(100% - 300px); min-width: 200px; padding: 16px 32px; overflow: clip; } -.widget-container-vertical { +.widget-wrapper { + display: inline-flex; + flex-direction: column; + align-items: flex-start; + justify-content: center; + gap: 80px; + max-width: 100%; +} + +.widget-wrapper-vertical { + width: 100%; flex-direction: row; + align-items: center; } .options { @@ -29,10 +37,6 @@ background-color: rgba(191, 191, 191, 0.15); } -.dx-tab { - width: 135px; -} - .caption { font-weight: 500; font-size: 18px; @@ -46,11 +50,19 @@ margin-top: 20px; } -.dx-tabs-horizontal { - border-bottom: 1px solid rgb(225, 225, 225, 0.4); +.dx-tabs { + max-width: 100%; } .dx-tabs-vertical { - border-right: 1px solid rgb(225, 225, 225, 0.4); - height: 250px; + height: 216px; +} + +.dx-viewport:not(.dx-theme-generic) .dx-tabs-horizontal { + border-block-end: 1px solid rgb(225, 225, 225, 0.4); +} + +.dx-viewport:not(.dx-theme-generic) .dx-tabs-vertical { + height: 232px; + border-inline-end: 1px solid rgb(225, 225, 225, 0.4); } diff --git a/JSDemos/Demos/Tabs/Overview/ReactJs/App.js b/JSDemos/Demos/Tabs/Overview/ReactJs/App.js index 23a554676e9..470309fce40 100644 --- a/JSDemos/Demos/Tabs/Overview/ReactJs/App.js +++ b/JSDemos/Demos/Tabs/Overview/ReactJs/App.js @@ -9,8 +9,6 @@ import { tabsText, stylingModeLabel, iconPositionLabel, - scrollContentLabel, - showNavigationLabel, tabsIconAndText, stylingModes, iconPositions, @@ -29,11 +27,13 @@ function OptionWrapper(props) { const App = () => { const [orientation, setOrientation] = React.useState(orientations[0]); - const [stylingMode, setStylingMode] = React.useState(stylingModes[0]); + const [stylingMode, setStylingMode] = React.useState(stylingModes[1]); const [iconPosition, setIconPosition] = React.useState(iconPositions[0]); const [showNavigation, setShowNavigation] = React.useState(false); const [scrollContent, setScrollContent] = React.useState(false); - const [widgetContainerClasses, setWidgetContainerClasses] = React.useState('widget-container widget-container-horizontal'); + const [fullWidth, setFullWidth] = React.useState(false); + const [rtlEnabled, setRtlEnabled] = React.useState(false); + const [widgetWrapperClasses, setWidgetWrapperClasses] = React.useState('widget-wrapper widget-wrapper-horizontal'); const stylingModeChanged = React.useCallback((e) => { setStylingMode(e.value); @@ -45,9 +45,9 @@ const App = () => { const orientationChanged = React.useCallback( (e) => { - setWidgetContainerClasses(`widget-container widget-container-${e.value}`); + setWidgetWrapperClasses(`widget-wrapper widget-wrapper-${e.value}`); setOrientation(e.value); - }, [setOrientation, setWidgetContainerClasses], + }, [setOrientation, setWidgetWrapperClasses], ); const showNavigationChanged = React.useCallback((e) => { @@ -58,38 +58,57 @@ const App = () => { setScrollContent(e.value); }, [setScrollContent]); + const fullWidthChanged = React.useCallback((e) => { + setFullWidth(e.value); + }, [setFullWidth]); + + const rtlEnabledChanged = React.useCallback((e) => { + setRtlEnabled(e.value); + }, [setRtlEnabled]); + return (
-
- - - +
+
+ + + + + +
@@ -125,21 +144,35 @@ const App = () => {
+
+ +
+ +
+ +
+ +
); diff --git a/JSDemos/Demos/Tabs/Overview/Vue/App.vue b/JSDemos/Demos/Tabs/Overview/Vue/App.vue index 9715cbb1f52..137fa24ca00 100644 --- a/JSDemos/Demos/Tabs/Overview/Vue/App.vue +++ b/JSDemos/Demos/Tabs/Overview/Vue/App.vue @@ -1,30 +1,21 @@