diff --git a/.config/checkstyle/checkstyle.xml b/.config/checkstyle/checkstyle.xml
index 463a629a..ce1e09d2 100644
--- a/.config/checkstyle/checkstyle.xml
+++ b/.config/checkstyle/checkstyle.xml
@@ -79,7 +79,13 @@
+
+
+
+
+
+
diff --git a/.config/pmd/java/ruleset.xml b/.config/pmd/java/ruleset.xml
index e96576b1..e42e77a4 100644
--- a/.config/pmd/java/ruleset.xml
+++ b/.config/pmd/java/ruleset.xml
@@ -146,7 +146,6 @@
-
@@ -164,6 +163,7 @@
+
@@ -208,6 +208,36 @@
+
+
+`Optional#get` can be interpreted as a getter by developers, however this is not the case as it throws an exception when empty.
+
+It should be replaced by
+* doing a mapping directly using `.map` or `.ifPresent`
+* using the preferred `.orElseThrow`, `.orElse` or `.or` methods
+
+Java Developer Brian Goetz also writes regarding this topic:
+
+> Java 8 was a huge improvement to the platform, but one of the few mistakes we made was the naming of `Optional.get()`, because the name just invites people to call it without calling `isPresent()`, undermining the whole point of using `Optional` in the first place.
+>
+> During the Java 9 time frame, we proposed to deprecate `Optional.get()`, but the public response to that was ... let's say cold. As a smaller step, we introduced `orElseThrow()` in 10 (see [JDK-8140281](https://bugs.openjdk.java.net/browse/JDK-8140281)) as a more transparently named synonym for the current pernicious behavior of `get()`. IDEs warn on unconditional use of `get()`, but not on `orElseThrow()`, which is a step forward in teaching people to code better. The question is, in a sense, a "glass half empty" view of the current situation; `get()` is still problematic.
+
+ 3
+
+
+
+
+
+
+
+
+
diff --git a/.config/topo/upstream.yml b/.config/topo/upstream.yml
new file mode 100644
index 00000000..fced0c57
--- /dev/null
+++ b/.config/topo/upstream.yml
@@ -0,0 +1,2 @@
+- url: https://github.com/xdev-software/vaadin-addon-template.git
+ branch: master
diff --git a/.github/workflows/broken-links.yml b/.github/workflows/broken-links.yml
index 2675c8b6..7b1481c5 100644
--- a/.github/workflows/broken-links.yml
+++ b/.github/workflows/broken-links.yml
@@ -3,7 +3,7 @@ name: Broken links
on:
workflow_dispatch:
schedule:
- - cron: "23 23 * * 0"
+ - cron: "23 5 * * 0"
permissions:
issues: write
@@ -19,8 +19,9 @@ jobs:
- name: Link Checker
id: lychee
- uses: lycheeverse/lychee-action@a8c4c7cb88f0c7386610c35eb25108e448569cb0 # v2
+ uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2
with:
+ args: "--verbose --no-progress './**/*.md'"
fail: false # Don't fail on broken links, create an issue instead
- name: Find already existing issue
diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml
index 5acbddfd..0a788ee4 100644
--- a/.github/workflows/check-build.yml
+++ b/.github/workflows/check-build.yml
@@ -78,7 +78,7 @@ jobs:
fi
- name: Upload demo files
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: demo-files-java-${{ matrix.java }}
path: ${{ env.DEMO_MAVEN_MODULE }}/target/${{ env.DEMO_MAVEN_MODULE }}.jar
@@ -160,8 +160,8 @@ jobs:
run: ./mvnw -B pmd:aggregate-cpd pmd:cpd-check -P pmd -DskipTests -T2C
- name: Upload report
- if: always()
- uses: actions/upload-artifact@v6
+ if: ${{ !cancelled() }}
+ uses: actions/upload-artifact@v7
with:
name: pmd-report
if-no-files-found: ignore
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6a53a5bf..4aebeeb5 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -36,7 +36,7 @@ jobs:
${{ runner.os }}-mvn-build-
- name: Build with Maven
- run: ./mvnw -B clean package -Pproduction -T2C
+ run: ./mvnw -B clean package -T2C
- name: Check for uncommited changes
run: |
@@ -91,7 +91,7 @@ jobs:
- name: Create Release
id: create-release
- uses: shogo82148/actions-create-release@559c27ce7eb834825e2b55927c64f6d1bd1db716 # v1
+ uses: shogo82148/actions-create-release@6a396031bc74c57403da1018fec74d24c6aa03cd # v1
with:
tag_name: v${{ steps.version.outputs.release }}
release_name: v${{ steps.version.outputs.release }}
diff --git a/.github/workflows/report-gha-workflow-security-problems.yml b/.github/workflows/report-gha-workflow-security-problems.yml
new file mode 100644
index 00000000..b17aa539
--- /dev/null
+++ b/.github/workflows/report-gha-workflow-security-problems.yml
@@ -0,0 +1,61 @@
+name: Report workflow security problems
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [ develop ]
+ paths:
+ - '.github/workflows/**'
+
+permissions:
+ issues: write
+
+jobs:
+ prt:
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+ # Only run this in our repos (Prevent notification spam by forks)
+ if: ${{ github.repository_owner == 'xdev-software' }}
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Check
+ id: check
+ run: |
+ grep -l 'pull_request_target:' --exclude report-gha-workflow-security-problems.yml *.yml > reported.txt && exit 1 || exit 0
+ working-directory: .github/workflows
+
+ - name: Find already existing issue
+ id: find-issue
+ if: ${{ !cancelled() }}
+ run: |
+ echo "number=$(gh issue list -l 'bug' -l 'automated' -L 1 -S 'in:title "Incorrectly configure GHA workflow (prt)"' -s 'open' --json 'number' --jq '.[].number')" >> $GITHUB_OUTPUT
+ env:
+ GH_TOKEN: ${{ github.token }}
+
+ - name: Close issue if everything is fine
+ if: ${{ success() && steps.find-issue.outputs.number != '' }}
+ run: gh issue close -r 'not planned' ${{ steps.find-issue.outputs.number }}
+ env:
+ GH_TOKEN: ${{ github.token }}
+
+ - name: Create report
+ if: ${{ failure() && steps.check.conclusion == 'failure' }}
+ run: |
+ echo 'Detected usage of `pull_request_target`. This event is dangerous and MUST NOT BE USED AT ALL COST!' > reported.md
+ echo '' >> reported.md
+ echo '/cc @xdev-software/gha-workflow-security' >> reported.md
+ echo '' >> reported.md
+ echo '```' >> reported.md
+ cat .github/workflows/reported.txt >> reported.md
+ echo '```' >> reported.md
+ cat reported.md
+
+ - name: Create Issue From File
+ if: ${{ failure() && steps.check.conclusion == 'failure' }}
+ uses: peter-evans/create-issue-from-file@fca9117c27cdc29c6c4db3b86c48e4115a786710 # v6
+ with:
+ issue-number: ${{ steps.find-issue.outputs.number }}
+ title: 'Incorrectly configure GHA workflow (prt)'
+ content-filepath: ./reported.md
+ labels: bug, automated
diff --git a/.github/workflows/update-from-template.yml b/.github/workflows/update-from-template.yml
deleted file mode 100644
index a5e3b956..00000000
--- a/.github/workflows/update-from-template.yml
+++ /dev/null
@@ -1,320 +0,0 @@
-name: Update from Template
-
-# This workflow keeps the repo up to date with changes from the template repo (REMOTE_URL)
-# It duplicates the REMOTE_BRANCH (into UPDATE_BRANCH) and tries to merge it into
-# this repos default branch (which is checked out here)
-# Note that this requires a PAT (Personal Access Token) - at best from a servicing account
-# PAT permissions: read:discussion, read:org, repo, workflow
-# Also note that you should have at least once merged the template repo into the current repo manually
-# otherwise a "refusing to merge unrelated histories" error might occur.
-
-on:
- schedule:
- - cron: '55 2 * * 1'
- workflow_dispatch:
- inputs:
- no_automatic_merge:
- type: boolean
- description: 'No automatic merge'
- default: false
-
-env:
- UPDATE_BRANCH: update-from-template
- UPDATE_BRANCH_MERGED: update-from-template-merged
- REMOTE_URL: https://github.com/xdev-software/vaadin-addon-template.git
- REMOTE_BRANCH: master
-
-permissions:
- contents: write
- pull-requests: write
-
-jobs:
- update:
- runs-on: ubuntu-latest
- timeout-minutes: 60
- outputs:
- update_branch_merged_commit: ${{ steps.manage-branches.outputs.update_branch_merged_commit }}
- create_update_branch_merged_pr: ${{ steps.manage-branches.outputs.create_update_branch_merged_pr }}
- steps:
- - uses: actions/checkout@v6
- with:
- # Required because otherwise there are always changes detected when executing diff/rev-list
- fetch-depth: 0
- # If no PAT is used the following error occurs on a push:
- # refusing to allow a GitHub App to create or update workflow `.github/workflows/xxx.yml` without `workflows` permission
- token: ${{ secrets.UPDATE_FROM_TEMPLATE_PAT }}
-
- - name: Init Git
- run: |
- git config --global user.email "111048771+xdev-gh-bot@users.noreply.github.com"
- git config --global user.name "XDEV Bot"
-
- - name: Manage branches
- id: manage-branches
- run: |
- echo "Adding remote template-repo"
- git remote add template ${{ env.REMOTE_URL }}
-
- echo "Fetching remote template repo"
- git fetch template
-
- echo "Deleting local branches that will contain the updates - if present"
- git branch -D ${{ env.UPDATE_BRANCH }} || true
- git branch -D ${{ env.UPDATE_BRANCH_MERGED }} || true
-
- echo "Checking if the remote template repo has new commits"
- git rev-list ..template/${{ env.REMOTE_BRANCH }}
-
- if [ $(git rev-list --count ..template/${{ env.REMOTE_BRANCH }}) -eq 0 ]; then
- echo "There are no commits new commits on the template repo"
-
- echo "Deleting origin branch(es) that contain the updates - if present"
- git push -f origin --delete ${{ env.UPDATE_BRANCH }} || true
- git push -f origin --delete ${{ env.UPDATE_BRANCH_MERGED }} || true
-
- echo "create_update_branch_pr=0" >> $GITHUB_OUTPUT
- echo "create_update_branch_merged_pr=0" >> $GITHUB_OUTPUT
- exit 0
- fi
-
- echo "Found new commits on the template repo"
-
- echo "Creating update branch"
- git branch ${{ env.UPDATE_BRANCH }} template/${{ env.REMOTE_BRANCH }}
- git branch --unset-upstream ${{ env.UPDATE_BRANCH }}
-
- echo "Pushing update branch"
- git push -f -u origin ${{ env.UPDATE_BRANCH }}
-
- echo "Getting base branch"
- base_branch=$(git branch --show-current)
- echo "Base branch is $base_branch"
- echo "base_branch=$base_branch" >> $GITHUB_OUTPUT
-
- echo "Trying to create auto-merged branch ${{ env.UPDATE_BRANCH_MERGED }}"
- git branch ${{ env.UPDATE_BRANCH_MERGED }} ${{ env.UPDATE_BRANCH }}
- git checkout ${{ env.UPDATE_BRANCH_MERGED }}
-
- echo "Merging branch $base_branch into ${{ env.UPDATE_BRANCH_MERGED }}"
- git merge $base_branch && merge_exit_code=$? || merge_exit_code=$?
- if [ $merge_exit_code -ne 0 ]; then
- echo "Auto merge failed! Manual merge required"
- echo "::notice ::Auto merge failed - Manual merge required"
-
- echo "Cleaning up failed merge"
- git merge --abort
- git checkout $base_branch
- git branch -D ${{ env.UPDATE_BRANCH_MERGED }} || true
-
- echo "Deleting auto-merge branch - if present"
- git push -f origin --delete ${{ env.UPDATE_BRANCH_MERGED }} || true
-
- echo "create_update_branch_pr=1" >> $GITHUB_OUTPUT
- echo "create_update_branch_merged_pr=0" >> $GITHUB_OUTPUT
- exit 0
- fi
-
- echo "Post processing: Trying to automatically fill in template variables"
- find . -type f \
- -not -path "./.git/**" \
- -not -path "./.github/workflows/update-from-template.yml" -print0 \
- | xargs -0 sed -i "s/template-placeholder/${GITHUB_REPOSITORY#*/}/g"
-
- git status
- git add --all
-
- if [[ "$(git status --porcelain)" != "" ]]; then
- echo "Filled in template; Committing"
-
- git commit -m "Fill in template"
- fi
-
- echo "Pushing auto-merged branch"
- git push -f -u origin ${{ env.UPDATE_BRANCH_MERGED }}
-
- echo "update_branch_merged_commit=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
-
- echo "Restoring base branch $base_branch"
- git checkout $base_branch
-
- echo "create_update_branch_pr=0" >> $GITHUB_OUTPUT
- echo "create_update_branch_merged_pr=1" >> $GITHUB_OUTPUT
- echo "try_close_update_branch_pr=1" >> $GITHUB_OUTPUT
-
- - name: Create/Update PR update_branch
- if: steps.manage-branches.outputs.create_update_branch_pr == 1
- env:
- GH_TOKEN: ${{ secrets.UPDATE_FROM_TEMPLATE_PAT }}
- run: |
- gh_pr_up() {
- gh pr create -H "${{ env.UPDATE_BRANCH }}" "$@" || (git checkout "${{ env.UPDATE_BRANCH }}" && gh pr edit "$@")
- }
- gh_pr_up -B "${{ steps.manage-branches.outputs.base_branch }}" \
- --title "Update from template" \
- --body "An automated PR to sync changes from the template into this repo"
-
- # Ensure that only a single PR is open (otherwise confusion and spam)
- - name: Close PR update_branch
- if: steps.manage-branches.outputs.try_close_update_branch_pr == 1
- env:
- GH_TOKEN: ${{ secrets.UPDATE_FROM_TEMPLATE_PAT }}
- run: |
- gh pr close "${{ env.UPDATE_BRANCH }}" || true
-
- - name: Create/Update PR update_branch_merged
- if: steps.manage-branches.outputs.create_update_branch_merged_pr == 1
- env:
- GH_TOKEN: ${{ secrets.UPDATE_FROM_TEMPLATE_PAT }}
- run: |
- gh_pr_up() {
- gh pr create -H "${{ env.UPDATE_BRANCH_MERGED }}" "$@" || (git checkout "${{ env.UPDATE_BRANCH_MERGED }}" && gh pr edit "$@")
- }
- gh_pr_up -B "${{ steps.manage-branches.outputs.base_branch }}" \
- --title "Update from template (auto-merged)" \
- --body "An automated PR to sync changes from the template into this repo"
-
- # Wait a moment so that checks of PR have higher prio than following job
- sleep 3
-
- # Split into two jobs to help with executor starvation
- auto-merge:
- needs: [update]
- if: needs.update.outputs.create_update_branch_merged_pr == 1
- runs-on: ubuntu-latest
- timeout-minutes: 60
- steps:
- - uses: actions/checkout@v6
- with:
- # Required because otherwise there are always changes detected when executing diff/rev-list
- fetch-depth: 0
- # If no PAT is used the following error occurs on a push:
- # refusing to allow a GitHub App to create or update workflow `.github/workflows/xxx.yml` without `workflows` permission
- token: ${{ secrets.UPDATE_FROM_TEMPLATE_PAT }}
-
- - name: Init Git
- run: |
- git config --global user.email "111048771+xdev-gh-bot@users.noreply.github.com"
- git config --global user.name "XDEV Bot"
-
- - name: Checking if auto-merge for PR update_branch_merged can be done
- id: auto-merge-check
- env:
- GH_TOKEN: ${{ secrets.UPDATE_FROM_TEMPLATE_PAT }}
- run: |
- not_failed_conclusion="skipped|neutral|success"
- not_relevant_app_slug="dependabot|github-pages|sonarqubecloud"
-
- echo "Waiting for checks to start..."
- sleep 40s
-
- for i in {1..20}; do
- echo "Checking if PR can be auto-merged. Try: $i"
-
- echo "Checking if update-branch-merged exists"
- git fetch
- if [[ $(git ls-remote --heads origin refs/heads/${{ env.UPDATE_BRANCH_MERGED }}) ]]; then
- echo "Branch still exists; Continuing..."
- else
- echo "Branch origin/${{ env.UPDATE_BRANCH_MERGED }} is missing"
- exit 0
- fi
-
- echo "Fetching checks"
- cs_response=$(curl -sL \
- --fail-with-body \
- --connect-timeout 60 \
- --max-time 120 \
- -H "Accept: application/vnd.github+json" \
- -H "Authorization: Bearer $GH_TOKEN" \
- -H "X-GitHub-Api-Version: 2022-11-28" \
- https://api.github.com/repos/${{ github.repository }}/commits/${{ needs.update.outputs.update_branch_merged_commit }}/check-suites)
-
- cs_data=$(echo $cs_response | jq '.check_suites[] | { conclusion: .conclusion, slug: .app.slug, check_runs_url: .check_runs_url }')
- echo $cs_data
-
- if [[ -z "$cs_data" ]]; then
- echo "No check suite data - Assuming that there are no checks to run"
-
- echo "perform=1" >> $GITHUB_OUTPUT
- exit 0
- fi
-
- cs_failed=$(echo $cs_data | jq --arg x "$not_failed_conclusion" 'select ((.conclusion == null or (.conclusion | test($x))) | not)')
- if [[ -z "$cs_failed" ]]; then
- echo "No check failed so far; Checking if relevant checks are still running"
-
- cs_relevant_still_running=$(echo $cs_data | jq --arg x "$not_relevant_app_slug" 'select (.conclusion == null and (.slug | test($x) | not))')
- if [[ -z $cs_relevant_still_running ]]; then
- echo "All relevant checks finished - PR can be merged"
-
- echo "perform=1" >> $GITHUB_OUTPUT
- exit 0
- else
- echo "Relevant checks are still running"
- echo $cs_relevant_still_running
- fi
- else
- echo "Detected failed check"
- echo $cs_failed
-
- echo "perform=0" >> $GITHUB_OUTPUT
- exit 0
- fi
-
- echo "Waiting before next run..."
- sleep 30s
- done
-
- echo "Timed out - Assuming executor starvation - Forcing merge"
- echo "perform=1" >> $GITHUB_OUTPUT
-
- - name: Auto-merge update_branch_merged
- if: steps.auto-merge-check.outputs.perform == 1
- run: |
- echo "Getting base branch"
- base_branch=$(git branch --show-current)
- echo "Base branch is $base_branch"
-
- echo "Fetching..."
- git fetch
- if [[ $(git rev-parse origin/${{ env.UPDATE_BRANCH_MERGED }}) ]]; then
- echo "Branch still exists; Continuing..."
- else
- echo "Branch origin/${{ env.UPDATE_BRANCH_MERGED }} is missing"
- exit 0
- fi
-
- expected_commit="${{ needs.update.outputs.update_branch_merged_commit }}"
- actual_commit=$(git rev-parse origin/${{ env.UPDATE_BRANCH_MERGED }})
- if [[ "$expected_commit" != "$actual_commit" ]]; then
- echo "Branch ${{ env.UPDATE_BRANCH_MERGED }} contains unexpected commit $actual_commit"
- echo "Expected: $expected_commit"
-
- exit 0
- fi
-
- echo "Ensuring that current branch $base_branch is up-to-date"
- git pull
-
- echo "Merging origin/${{ env.UPDATE_BRANCH_MERGED }} into $base_branch"
- git merge origin/${{ env.UPDATE_BRANCH_MERGED }} && merge_exit_code=$? || merge_exit_code=$?
- if [ $merge_exit_code -ne 0 ]; then
- echo "Unexpected merge failure $merge_exit_code - Requires manual resolution"
-
- exit 0
- fi
-
- if [[ "${{ inputs.no_automatic_merge }}" == "true" ]]; then
- echo "Exiting due no_automatic_merge"
-
- exit 0
- fi
-
- echo "Pushing"
- git push
-
- echo "Cleaning up"
- git branch -D ${{ env.UPDATE_BRANCH }} || true
- git branch -D ${{ env.UPDATE_BRANCH_MERGED }} || true
- git push -f origin --delete ${{ env.UPDATE_BRANCH }} || true
- git push -f origin --delete ${{ env.UPDATE_BRANCH_MERGED }} || true
diff --git a/.gitignore b/.gitignore
index 53935857..85928e5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,6 +43,8 @@ node_modules/
# Vaadin
package.json
package-lock.json
+.npmrc
+pnpm-lock.yaml
webpack.generated.js
webpack.config.js
tsconfig.json
diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml
index a751c417..27f72cee 100644
--- a/.idea/checkstyle-idea.xml
+++ b/.idea/checkstyle-idea.xml
@@ -1,7 +1,7 @@
- 13.0.0
+ 13.4.0
JavaOnlyWithTests
true
true
diff --git a/.idea/externalDependencies.xml b/.idea/externalDependencies.xml
index 78be5b8e..0b477b88 100644
--- a/.idea/externalDependencies.xml
+++ b/.idea/externalDependencies.xml
@@ -3,5 +3,6 @@
+
\ No newline at end of file
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index 8dea6c22..216df058 100644
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -1,3 +1,3 @@
wrapperVersion=3.3.4
distributionType=only-script
-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e1408d2e..64190053 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 3.1.0
+* Added `is not empty`
+* Added a demo to show how custom type handling can be done
+
# 3.0.0
* Updated to Vaadin 25
diff --git a/pom.xml b/pom.xml
index 1e31d181..163c8eb5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
software.xdev
vaadin-grid-filter-root
- 3.0.1-SNAPSHOT
+ 3.1.0-SNAPSHOT
pom
@@ -45,7 +45,7 @@
com.puppycrawl.tools
checkstyle
- 13.0.0
+ 13.4.2
@@ -83,12 +83,12 @@
net.sourceforge.pmd
pmd-core
- 7.20.0
+ 7.24.0
net.sourceforge.pmd
pmd-java
- 7.20.0
+ 7.24.0
diff --git a/vaadin-grid-filter-demo/pnpm-workspace.yaml b/vaadin-grid-filter-demo/pnpm-workspace.yaml
index 9ac0828b..3ac055f8 100644
--- a/vaadin-grid-filter-demo/pnpm-workspace.yaml
+++ b/vaadin-grid-filter-demo/pnpm-workspace.yaml
@@ -1,25 +1,13 @@
# Delay install of newly released packages to prevent supply chain attacks
minimumReleaseAge: 180 # 3h
+minimumReleaseAgeExclude:
+ - "@xdevsoftware/*"
overrides:
# Remove unused packages
- # glob CLI unused
- "jackspeak": "npm:empty-npm-package@1.0.0"
- "foreground-child": "npm:empty-npm-package@1.0.0"
- "package-json-from-dist": "npm:empty-npm-package@1.0.0"
# rollup-plugin-visualizer CLI unused
"yargs": "npm:empty-npm-package@1.0.0"
"open": "npm:empty-npm-package@1.0.0"
# transform-ast test only
"nanobench": "npm:empty-npm-package@1.0.0"
- # Workbox unused
- "workbox-google-analytics": "npm:empty-npm-package@1.0.0"
- "@surma/rollup-plugin-off-main-thread": "npm:empty-npm-package@1.0.0"
- "@babel/preset-env": "npm:empty-npm-package@1.0.0"
- "@babel/runtime": "npm:empty-npm-package@1.0.0"
- "@rollup/plugin-replace@2.4.2": "npm:empty-npm-package@1.0.0"
- "@rollup/plugin-babel": "npm:empty-npm-package@1.0.0"
- "@rollup/plugin-node-resolve": "npm:empty-npm-package@1.0.0"
- "@rollup/plugin-terser": "npm:empty-npm-package@1.0.0"
- "tempy": "npm:empty-npm-package@1.0.0"
- # Disable telemetry
- "@vaadin/vaadin-usage-statistics": "npm:empty-npm-package@1.0.0"
+ # Usage statistics
+ "@vaadin/vaadin-usage-statistics": "npm:@xdevsoftware/vaadin-usage-statistics-opt-out@1.0.2"
diff --git a/vaadin-grid-filter-demo/pom.xml b/vaadin-grid-filter-demo/pom.xml
index 57e8ba3e..eb9993fa 100644
--- a/vaadin-grid-filter-demo/pom.xml
+++ b/vaadin-grid-filter-demo/pom.xml
@@ -7,11 +7,11 @@
software.xdev
vaadin-grid-filter-root
- 3.0.1-SNAPSHOT
+ 3.1.0-SNAPSHOT
vaadin-grid-filter-demo
- 3.0.1-SNAPSHOT
+ 3.1.0-SNAPSHOT
jar
@@ -29,9 +29,9 @@
software.xdev.vaadin.Application
- 25.0.3
+ 25.1.5
- 4.0.1
+ 4.0.6
@@ -62,11 +62,15 @@
com.vaadin
vaadin-core
-
+
com.vaadin
collaboration-engine
+
+ com.vaadin
+ vaadin-ai-components-flow
+
@@ -152,7 +156,7 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.14.1
+ 3.15.0
${maven.compiler.release}
diff --git a/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/DemoView.java b/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/DemoView.java
index 705792d2..41cea3d1 100644
--- a/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/DemoView.java
+++ b/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/DemoView.java
@@ -13,6 +13,7 @@
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
+import software.xdev.vaadin.gridfilter.demo.CustomTypeDemo;
import software.xdev.vaadin.gridfilter.demo.LocalizationDemo;
import software.xdev.vaadin.gridfilter.demo.MaxNestedDepthDemo;
import software.xdev.vaadin.gridfilter.demo.MinimalisticDemo;
@@ -71,6 +72,11 @@ protected void onAttach(final AttachEvent attachEvent)
LocalizationDemo.NAV,
"Localization",
"Showcases how localization can be done (UI in German)"
+ ),
+ new Example(
+ CustomTypeDemo.NAV,
+ "Custom type",
+ "Shows how a custom type can be handled"
)
));
}
diff --git a/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/demo/CustomTypeDemo.java b/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/demo/CustomTypeDemo.java
new file mode 100644
index 00000000..dfedbef0
--- /dev/null
+++ b/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/demo/CustomTypeDemo.java
@@ -0,0 +1,42 @@
+package software.xdev.vaadin.gridfilter.demo;
+
+import com.vaadin.flow.component.combobox.ComboBox;
+import com.vaadin.flow.component.details.Details;
+import com.vaadin.flow.component.details.DetailsVariant;
+import com.vaadin.flow.router.Route;
+
+import software.xdev.vaadin.gridfilter.GridFilter;
+import software.xdev.vaadin.gridfilter.business.typevaluecomp.single.SingleValueComponentProvider;
+import software.xdev.vaadin.gridfilter.model.City;
+import software.xdev.vaadin.gridfilter.model.Person;
+
+
+@Route(CustomTypeDemo.NAV)
+public class CustomTypeDemo extends AbstractDemo
+{
+ public static final String NAV = "/customtype";
+
+ public CustomTypeDemo()
+ {
+ final GridFilter filter = this.createDefaultFilter()
+ .addTypeValueComponent(new SingleValueComponentProvider<>(
+ City.class,
+ () -> {
+ final ComboBox comboBox = new ComboBox<>();
+ comboBox.setItemLabelGenerator(City::name);
+ comboBox.setItems(City.allAvailable().values());
+ return comboBox;
+ },
+ City::name,
+ name -> City.allAvailable().get(name)
+ ))
+ .withFilterableField("City", Person::city, City.class);
+
+ // Add filter inside details block for better looking UI
+ final Details details = new Details("Filter data");
+ details.addThemeVariants(DetailsVariant.LUMO_FILLED);
+ details.add(filter);
+ details.setOpened(true);
+ this.add(details, this.grid);
+ }
+}
diff --git a/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/model/City.java b/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/model/City.java
new file mode 100644
index 00000000..848b6b60
--- /dev/null
+++ b/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/model/City.java
@@ -0,0 +1,27 @@
+package software.xdev.vaadin.gridfilter.model;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+
+public record City(
+ String name
+)
+{
+ public static final City AMSTERDAM = new City("Amsterdam");
+ public static final City BERLIN = new City("Berlin");
+ public static final City NEW_YORK = new City("New York");
+
+ public static Map allAvailable()
+ {
+ return Stream.of(AMSTERDAM, BERLIN, NEW_YORK)
+ .collect(Collectors.toMap(
+ City::name,
+ Function.identity(),
+ (l, r) -> l,
+ LinkedHashMap::new));
+ }
+}
diff --git a/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/model/Person.java b/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/model/Person.java
index f2a30edd..447ae5b5 100644
--- a/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/model/Person.java
+++ b/vaadin-grid-filter-demo/src/main/java/software/xdev/vaadin/gridfilter/model/Person.java
@@ -3,6 +3,8 @@
import java.time.LocalDate;
import java.util.List;
+import org.jspecify.annotations.Nullable;
+
public record Person(
Integer id,
@@ -11,22 +13,32 @@ public record Person(
LocalDate birthday,
boolean married,
double salary,
- Department department
+ Department department,
+ @Nullable
+ City city
)
{
@SuppressWarnings("checkstyle:MagicNumber")
public static List list()
{
return List.of(
- new Person(0, "Siegbert", "Schmidt", LocalDate.of(1990, 12, 17), false, 1000, Department.HR),
- new Person(1, "Herbert", "Maier", LocalDate.of(1967, 10, 13), false, 1000, Department.HR),
- new Person(2, "Hans", "Lang", LocalDate.of(2002, 5, 9), true, 9050.60, Department.HR),
- new Person(3, "Anton", "Meier", LocalDate.of(1985, 1, 24), true, 8000.75, Department.HR),
- new Person(4, "Sarah", "Smith", LocalDate.of(1999, 6, 1), false, 5000, Department.IT),
- new Person(5, "Niklas", "Sommer", LocalDate.of(1994, 11, 8), true, 4000.33, Department.HR),
- new Person(6, "Hanna", "Neubaum", LocalDate.of(1986, 8, 15), true, 3000, Department.HR),
- new Person(8, "Laura", "Fels", LocalDate.of(1996, 3, 20), true, 1000.50, Department.HR),
- new Person(7, "Sofia", "Sommer", LocalDate.of(1972, 4, 14), false, 2000, Department.ACCOUNTING)
+ new Person(
+ 1,
+ "Siegbert",
+ "Schmidt",
+ LocalDate.of(1990, 12, 17),
+ false,
+ 1000,
+ Department.HR,
+ City.AMSTERDAM),
+ new Person(2, "Herbert", "Maier", LocalDate.of(1967, 10, 13), false, 1000, Department.HR, City.AMSTERDAM),
+ new Person(3, "Hans", "Lang", LocalDate.of(2002, 5, 9), true, 9050.60, Department.HR, City.AMSTERDAM),
+ new Person(4, "Anton", "Meier", LocalDate.of(1985, 1, 24), true, 8000.75, Department.HR, City.NEW_YORK),
+ new Person(5, "Sarah", "Smith", LocalDate.of(1999, 6, 1), false, 5000, Department.IT, City.BERLIN),
+ new Person(6, "Niklas", "Sommer", LocalDate.of(1994, 11, 8), true, 4000.33, Department.HR, City.BERLIN),
+ new Person(7, "Hanna", "Neubaum", LocalDate.of(1986, 8, 15), true, 3000, Department.HR, null),
+ new Person(8, "Laura", "Fels", LocalDate.of(1996, 3, 20), true, 1000.50, Department.HR, null),
+ new Person(9, "Sofia", "Sommer", LocalDate.of(1972, 4, 14), false, 2000, Department.ACCOUNTING, null)
);
}
}
diff --git a/vaadin-grid-filter/pom.xml b/vaadin-grid-filter/pom.xml
index 275341fc..24f806f7 100644
--- a/vaadin-grid-filter/pom.xml
+++ b/vaadin-grid-filter/pom.xml
@@ -6,7 +6,7 @@
software.xdev
vaadin-grid-filter
- 3.0.1-SNAPSHOT
+ 3.1.0-SNAPSHOT
jar
Grid Filter for Vaadin
@@ -49,7 +49,7 @@
UTF-8
- 25.0.3
+ 25.1.5
@@ -119,7 +119,7 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.14.1
+ 3.15.0
${maven.compiler.release}
@@ -261,7 +261,7 @@
com.puppycrawl.tools
checkstyle
- 13.0.0
+ 13.4.2
@@ -299,12 +299,12 @@
net.sourceforge.pmd
pmd-core
- 7.20.0
+ 7.24.0
net.sourceforge.pmd
pmd-java
- 7.20.0
+ 7.24.0
diff --git a/vaadin-grid-filter/src/main/java/software/xdev/vaadin/gridfilter/GridFilter.java b/vaadin-grid-filter/src/main/java/software/xdev/vaadin/gridfilter/GridFilter.java
index 4f87f3fc..813da000 100644
--- a/vaadin-grid-filter/src/main/java/software/xdev/vaadin/gridfilter/GridFilter.java
+++ b/vaadin-grid-filter/src/main/java/software/xdev/vaadin/gridfilter/GridFilter.java
@@ -54,6 +54,7 @@
import software.xdev.vaadin.gridfilter.business.operation.EqualsOp;
import software.xdev.vaadin.gridfilter.business.operation.GreaterThanOp;
import software.xdev.vaadin.gridfilter.business.operation.IsEmptyOp;
+import software.xdev.vaadin.gridfilter.business.operation.IsNotEmptyOp;
import software.xdev.vaadin.gridfilter.business.operation.LessThanOp;
import software.xdev.vaadin.gridfilter.business.operation.Operation;
import software.xdev.vaadin.gridfilter.business.typevaluecomp.TypeValueComponentProvider;
@@ -406,7 +407,8 @@ public static GridFilter createDefault(final Grid grid)
new GreaterThanOp(),
new LessThanOp(),
new ContainsOp(),
- new IsEmptyOp()
+ new IsEmptyOp(),
+ new IsNotEmptyOp()
))
.addTypeValueComponents(List.of(
new NoValueComponentProvider(),
diff --git a/vaadin-grid-filter/src/main/java/software/xdev/vaadin/gridfilter/GridFilterLocalizationConfig.java b/vaadin-grid-filter/src/main/java/software/xdev/vaadin/gridfilter/GridFilterLocalizationConfig.java
index 5ae55f23..819bac49 100644
--- a/vaadin-grid-filter/src/main/java/software/xdev/vaadin/gridfilter/GridFilterLocalizationConfig.java
+++ b/vaadin-grid-filter/src/main/java/software/xdev/vaadin/gridfilter/GridFilterLocalizationConfig.java
@@ -34,6 +34,7 @@ public class GridFilterLocalizationConfig
public static final String OP_GREATER_THAN = PREFIX_OP + "gt";
public static final String OP_LESS_THAN = PREFIX_OP + "lt";
public static final String OP_IS_EMPTY = PREFIX_OP + "empty";
+ public static final String OP_IS_NOT_EMPTY = PREFIX_OP + "not_empty";
public static final String PREFIX_BLOCK = PREFIX + "block.";
public static final String BLOCK_OR = PREFIX_BLOCK + "or";
@@ -49,6 +50,7 @@ public class GridFilterLocalizationConfig
entry(OP_GREATER_THAN, ">"),
entry(OP_LESS_THAN, "<"),
entry(OP_IS_EMPTY, "is empty"),
+ entry(OP_IS_NOT_EMPTY, "is not empty"),
// Block
entry(BLOCK_OR, "OR"),
entry(BLOCK_AND, "AND"),
diff --git a/vaadin-grid-filter/src/main/java/software/xdev/vaadin/gridfilter/business/operation/IsNotEmptyOp.java b/vaadin-grid-filter/src/main/java/software/xdev/vaadin/gridfilter/business/operation/IsNotEmptyOp.java
new file mode 100644
index 00000000..004fd462
--- /dev/null
+++ b/vaadin-grid-filter/src/main/java/software/xdev/vaadin/gridfilter/business/operation/IsNotEmptyOp.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2024 XDEV Software (https://xdev.software)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package software.xdev.vaadin.gridfilter.business.operation;
+
+import software.xdev.vaadin.gridfilter.GridFilterLocalizationConfig;
+import software.xdev.vaadin.gridfilter.business.value.NoValue;
+
+
+public class IsNotEmptyOp implements Operation
+{
+ @Override
+ public Class> valueContainerClass()
+ {
+ return NoValue.class;
+ }
+
+ @Override
+ public boolean canHandle(final Class> clazz)
+ {
+ return true;
+ }
+
+ @Override
+ public String identifier()
+ {
+ return "is not empty";
+ }
+
+ @Override
+ public String displayKey()
+ {
+ return GridFilterLocalizationConfig.OP_IS_NOT_EMPTY;
+ }
+
+ @Override
+ public boolean test(final Object input, final NoValue filterValue)
+ {
+ if(input instanceof final String s)
+ {
+ return !s.isEmpty();
+ }
+
+ return input != null;
+ }
+}