├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ └── feature-request.yml └── workflows │ ├── commits.yml │ └── maven.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── assembly ├── MANIFEST.MF └── assembly.xml ├── drivers.xml ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── flowingcode │ │ └── addons │ │ └── applayout │ │ ├── AppLayout.java │ │ ├── HasMenuItemCommands.java │ │ ├── HasMenuItemIcon.java │ │ ├── MenuItem.java │ │ ├── MenuSeparator.java │ │ ├── MouseClickEvent.java │ │ ├── MouseClickListener.java │ │ ├── PaperListbox.java │ │ ├── SlottedMenuItem.java │ │ ├── ToolbarIconButton.java │ │ └── endpoint │ │ ├── MenuEndpoint.java │ │ ├── MenuItemDto.java │ │ └── MenuItemsProvider.java └── resources │ └── META-INF │ ├── VAADIN │ └── package.properties │ ├── resources │ └── frontend │ │ ├── fc-applayout │ │ ├── fc-fusion-layout.ts │ │ └── fc-separator.js │ │ └── styles │ │ └── applayout-styles.css │ └── spring.factories └── test ├── java └── com │ └── flowingcode │ ├── addons │ └── applayout │ │ ├── ApplayoutDemoView.java │ │ ├── CustomAppLayout.java │ │ ├── DemoSettings.java │ │ ├── SampleInternalView.java │ │ ├── SampleView.java │ │ ├── integration │ │ ├── AbstractViewTest.java │ │ └── ViewIT.java │ │ ├── menu │ │ ├── PaperButton.java │ │ ├── PaperCard.java │ │ ├── PaperIconItem.java │ │ └── PaperToggle.java │ │ └── test │ │ └── SerializationTest.java │ └── vaadin │ └── addons │ ├── DemoLayout.java │ └── applayout │ ├── AppLayoutDemo.java │ ├── AppLayoutDemoView.java │ ├── DemoView.java │ └── ExtendingAppLayoutDemo.java ├── resources ├── META-INF │ ├── native-image │ │ └── com.flowingcode.addons │ │ │ └── app-layout │ │ │ ├── reflect-config.json │ │ │ └── resource-config.json │ └── resources │ │ └── frontend │ │ ├── images │ │ ├── applogo.png │ │ └── avatar.png │ │ └── styles │ │ ├── app-layout │ │ └── demo-styles.css │ │ └── shared-styles.html ├── hamlet └── vaadin-featureflags.properties └── webjar-debug ├── META-INF └── resources │ └── README.txt └── README.md /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Please report issues related to AppLayout add-on here. 3 | body: 4 | - type: textarea 5 | id: problem-description 6 | attributes: 7 | label: Describe the bug 8 | description: A clear description of the issue you're experiencing. 9 | validations: 10 | required: true 11 | - type: textarea 12 | id: expected-behavior 13 | attributes: 14 | label: Expected behavior 15 | description: A clear and concise description of the expected behavior. 16 | validations: 17 | required: false 18 | - type: textarea 19 | id: minimal-reproduction 20 | attributes: 21 | label: Minimal reproducible example 22 | description: If possible, add a concise code snippet that reproduces the issue and describe the steps needed to follow to reproduce it. 23 | validations: 24 | required: false 25 | - type: input 26 | id: addon-version 27 | attributes: 28 | label: Add-on Version 29 | description: The version of the add-on on which you're experiencing the issue. 30 | validations: 31 | required: true 32 | - type: input 33 | id: vaadin-version 34 | attributes: 35 | label: Vaadin Version 36 | description: The complete Vaadin version (X.Y.Z) on which the issue is reproducible. 37 | validations: 38 | required: true 39 | - type: textarea 40 | id: additional-information 41 | attributes: 42 | label: Additional information 43 | description: "Any other context/information about the issue can be added here (browser, OS, etc.)." 44 | validations: 45 | required: false 46 | 47 | 48 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Please add feature suggestions related to AppLayout add-on here. 3 | body: 4 | - type: textarea 5 | id: feature-proposal 6 | attributes: 7 | label: Feature proposal 8 | description: A concise but detailed description of the feature that you would like to see in the add-on. 9 | validations: 10 | required: true 11 | - type: textarea 12 | id: feature-implementation 13 | attributes: 14 | label: Describe solution expectations 15 | description: Do you have an idea/expectations of how it could be implemented? Did you try a possible solution that you want to share? 16 | validations: 17 | required: false 18 | - type: textarea 19 | id: additional-information 20 | attributes: 21 | label: Additional information 22 | description: Add any extra information you think it might be relevant to the request. 23 | validations: 24 | required: false 25 | -------------------------------------------------------------------------------- /.github/workflows/commits.yml: -------------------------------------------------------------------------------- 1 | name: Check Commits 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | check-commits: 8 | uses: FlowingCode/GithubActions/.github/workflows/check-commits.yml@main 9 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Java CI with Maven 10 | 11 | on: 12 | push: 13 | branches: [ "master" ] 14 | pull_request: 15 | branches: [ "master" ] 16 | 17 | jobs: 18 | 19 | build-vaadin24: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Set up JDK 24 | uses: actions/setup-java@v3 25 | with: 26 | java-version: '17' 27 | distribution: 'temurin' 28 | cache: maven 29 | - name: Build (Vaadin 24) 30 | run: mvn -B package --file pom.xml 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | /target 3 | /.classpath 4 | /.project 5 | /node_modules 6 | /webpack.generated.js 7 | /node 8 | /drivers 9 | /package-lock.json 10 | /webpack.config.js 11 | /package.json 12 | /error-screenshots 13 | /pnpm-lock.yaml 14 | /pnpmfile.js 15 | /tsconfig.json 16 | /types.d.ts 17 | /.vscode 18 | .npmrc 19 | /frontend/generated 20 | frontend/index.html 21 | vite.config.ts 22 | vite.generated.ts 23 | src/main/bundles/ -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Published on Vaadin Directory](https://img.shields.io/badge/Vaadin%20Directory-published-00b4f0.svg)](https://vaadin.com/directory/component/app-layout-addon) 2 | [![Stars on vaadin.com/directory](https://img.shields.io/vaadin-directory/star/app-layout-addon.svg)](https://vaadin.com/directory/component/app-layout-addon) 3 | [![Build Status](https://jenkins.flowingcode.com/job/AppLayout-5.x-addon/badge/icon)](https://jenkins.flowingcode.com/job/AppLayout-5.x-addon) 4 | [![Javadoc](https://img.shields.io/badge/javadoc-00b4f0)](https://javadoc.flowingcode.com/artifact/com.flowingcode.addons.applayout/app-layout-addon) 5 | 6 | # App Layout Add-on 7 | 8 | Vaadin Flow Java integration of https://github.com/PolymerElements/app-layout 9 | This addon is particularly useful if you want to create a new application with some initial support for responsiveness. 10 | 11 | ## Features 12 | 13 | * Left side menu with hamburguer button and avatar image. 14 | * Header with toolbar icons. 15 | * Hierarchical menus with icons (infinite level of submenus). 16 | * Application header with logo, title and toolbar icons. 17 | * Menu separator with optional label. 18 | * Support for left, middle and right click listeners. 19 | * Menu items accept arbitrary content (such as checkboxes, or buttons) 20 | 21 | ## Versions 22 | 23 | * 4.x Compatible with Vaadin 14-22 24 | * 5.x Compatible with Vaadin 23 25 | 26 | ## Online demo 27 | 28 | [Online demo here](http://addonsv14.flowingcode.com/applayout) 29 | 30 | ## Download release 31 | 32 | [Available in Vaadin Directory](https://vaadin.com/directory/component/app-layout-addon) 33 | 34 | ## Building and running demo 35 | 36 | - git clone repository 37 | - mvn clean install jetty:run 38 | 39 | To see the demo, navigate to http://localhost:8080/ 40 | 41 | ## Release notes 42 | 43 | See [here](https://github.com/FlowingCode/AppLayoutAddon/releases) 44 | 45 | ## Issue tracking 46 | 47 | The issues for this add-on are tracked on its github.com page. All bug reports and feature requests are appreciated. 48 | 49 | ## Contributions 50 | 51 | Contributions are welcome, but there are no guarantees that they are accepted as such. Process for contributing is the following: 52 | 53 | - Fork this project 54 | - Create an issue to this project about the contribution (bug or feature) if there is no such issue about it already. Try to keep the scope minimal. 55 | - Develop and test the fix or functionality carefully. Only include minimum amount of code needed to fix the issue. 56 | - Refer to the fixed issue in commit 57 | - Send a pull request for the original project 58 | - Comment on the original issue that you have implemented a fix for it 59 | 60 | ## License & Author 61 | 62 | This add-on is distributed under Apache License 2.0. For license terms, see LICENSE.txt. 63 | 64 | AppLayout is written by Flowing Code S.A. 65 | 66 | # Developer Guide 67 | 68 | ## Getting started 69 | 70 | Creating the AppLayout: 71 | ``` 72 | AppLayout app =new AppLayout("AppLayout Addon for Vaadin 10 Demo"); 73 | ``` 74 | Adding menu items with sub-menus & icons: 75 | ``` 76 | app.setMenuItems(new MenuItem("Say hello", "star", () -> showContent("Hello!")), 77 | new MenuItem("About", "cloud", () -> showContent("About")), 78 | new MenuItem("SubMenu").setIcon("build").add( 79 | new MenuItem("Hello Again", "inbox",()->showContent("Hello Again!")), 80 | new MenuItem("And Again",()->showContent("And Again!")), 81 | new MenuItem("SubMenu").add( 82 | new MenuItem("Hello Again",()->showContent("Hello Again!")), 83 | new MenuItem("And Again",()->showContent("And Again!")) 84 | ) 85 | ) 86 | ); 87 | ``` 88 | Toolbar icons: 89 | ``` 90 | app.setToolbarIconButtons( 91 | new ToolbarIconButton("Delete", "delete", ()->Notification.show("Delete action")), 92 | new ToolbarIconButton("Search", "search", ()->Notification.show("Search action")), 93 | new ToolbarIconButton("Close", "close", ()->Notification.show("Close action")) 94 | ); 95 | ``` 96 | 97 | ## Special configuration when using Spring 98 | 99 | By default, Vaadin Flow only includes ```com/vaadin/flow/component``` to be always scanned for UI components and views. For this reason, the add-on might need to be allowed in order to display correctly. 100 | 101 | To do so, just add ```com.flowingcode``` to the ```vaadin.allowed-packages``` property in ```src/main/resources/application.properties```, like: 102 | 103 | ```vaadin.allowed-packages = com.vaadin,org.vaadin,dev.hilla,com.flowingcode``` 104 | 105 | More information on Spring scanning configuration [here](https://vaadin.com/docs/latest/integrations/spring/configuration/#configure-the-scanning-of-packages). 106 | -------------------------------------------------------------------------------- /assembly/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Vaadin-Package-Version: 1 3 | Vaadin-Addon: ${project.build.finalName}.${project.packaging} 4 | Implementation-Vendor: ${organization.name} 5 | Implementation-Title: ${project.name} 6 | Implementation-Version: ${project.version} 7 | -------------------------------------------------------------------------------- /assembly/assembly.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | app-layout 7 | 8 | 9 | zip 10 | 11 | 12 | 13 | false 14 | 15 | 16 | 17 | . 18 | 19 | LICENSE 20 | README.md 21 | 22 | 23 | 24 | target 25 | 26 | 27 | *.jar 28 | *.pdf 29 | 30 | 31 | *-demo.jar 32 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | assembly/MANIFEST.MF 41 | META-INF 42 | true 43 | 44 | 45 | -------------------------------------------------------------------------------- /drivers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | https://chromedriver.storage.googleapis.com/78.0.3904.70/chromedriver_win32.zip 8 | 26705e683632c6e1f9c62179b6143b409f4d7681 9 | sha1 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | https://chromedriver.storage.googleapis.com/78.0.3904.70/chromedriver_linux64.zip 19 | 63e13e5f0df96af1cc3a2006c00b2f1871b62caf 20 | sha1 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | https://chromedriver.storage.googleapis.com/78.0.3904.70/chromedriver_mac64.zip 30 | ce06a7d3a9d18b3d054d3b4c3c32d2cd74e81a91 31 | sha1 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.flowingcode.addons.applayout 6 | app-layout-addon 7 | 6.0.1-SNAPSHOT 8 | App Layout Addon 9 | Integration of app-layout for Vaadin Flow 10 | 11 | 12 | 24.4.4 13 | 17 14 | 17 15 | UTF-8 16 | UTF-8 17 | ${project.basedir}/drivers 18 | 4.0.0 19 | 11.0.14 20 | 21 | 22 | Flowing Code 23 | https://www.flowingcode.com 24 | 25 | 2018 26 | 27 | 28 | Apache 2 29 | http://www.apache.org/licenses/LICENSE-2.0.txt 30 | repo 31 | 32 | 33 | 34 | 35 | https://github.com/FlowingCode/AppLayoutAddon 36 | scm:git:git://github.com/FlowingCode/AppLayoutAddon.git 37 | scm:git:ssh://git@github.com:/FlowingCode/AppLayoutAddon.git 38 | app-layout-addon-2.0.0 39 | 40 | 41 | 42 | 43 | flowingcode 44 | Flowing Code 45 | https://www.flowingcode.com 46 | 47 | 48 | 49 | 50 | 51 | 52 | com.vaadin 53 | vaadin-bom 54 | pom 55 | import 56 | ${vaadin.version} 57 | 58 | 59 | com.flowingcode.vaadin.addons.demo 60 | commons-demo 61 | ${flowingcode.commons.demo.version} 62 | 63 | 64 | 65 | 66 | 67 | 68 | central 69 | https://repo.maven.apache.org/maven2 70 | 71 | false 72 | 73 | 74 | 75 | Vaadin Directory 76 | https://maven.vaadin.com/vaadin-addons 77 | 78 | 79 | FlowingCode Releases 80 | https://maven.flowingcode.com/releases 81 | 82 | 83 | FlowingCode Snapshots 84 | https://maven.flowingcode.com/snapshots 85 | 86 | true 87 | 88 | 89 | false 90 | 91 | 92 | 93 | 94 | 95 | 96 | com.vaadin 97 | vaadin-core 98 | true 99 | 100 | 101 | com.vaadin 102 | hilla 103 | 104 | 105 | org.springframework.boot 106 | spring-boot-autoconfigure 107 | 3.2.4 108 | true 109 | 110 | 111 | org.vaadin.artur 112 | a-vaadin-helper 113 | 1.7.1 114 | 115 | 116 | org.springframework 117 | spring-context 118 | 6.1.14 119 | true 120 | 121 | 122 | com.flowingcode.vaadin.addons.demo 123 | commons-demo 124 | test 125 | 126 | 127 | org.slf4j 128 | slf4j-simple 129 | test 130 | 131 | 132 | com.vaadin 133 | vaadin-testbench 134 | test 135 | 136 | 137 | org.hamcrest 138 | hamcrest-library 139 | 1.3 140 | test 141 | 142 | 143 | javax.servlet 144 | javax.servlet-api 145 | 3.1.0 146 | provided 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | org.apache.maven.plugins 155 | maven-release-plugin 156 | 2.5.3 157 | 158 | 159 | org.apache.maven.plugins 160 | maven-deploy-plugin 161 | 2.8.2 162 | 163 | 164 | org.apache.maven.plugins 165 | maven-surefire-plugin 166 | 2.22.1 167 | 168 | false 169 | 170 | 171 | 172 | org.apache.maven.plugins 173 | maven-site-plugin 174 | 3.12.1 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | org.codehaus.mojo 183 | license-maven-plugin 184 | 1.14 185 | 186 | apache_v2 187 | false 188 | 189 | 190 | 191 | 192 | org.apache.maven.plugins 193 | maven-jar-plugin 194 | 3.1.2 195 | 196 | 197 | true 198 | 199 | false 200 | true 201 | 202 | 203 | 1 204 | 205 | 206 | 207 | 208 | 209 | 210 | com.vaadin 211 | vaadin-maven-plugin 212 | ${vaadin.version} 213 | 214 | true 215 | 216 | 217 | 218 | 219 | prepare-frontend 220 | 221 | 222 | 223 | 224 | 225 | maven-resources-plugin 226 | 3.2.0 227 | 228 | 229 | org.eclipse.jetty 230 | jetty-maven-plugin 231 | ${jetty.version} 232 | 233 | 3 234 | 236 | true 237 | 238 | 239 | src/test/resources/META-INF/resources 240 | 241 | 242 | 243 | jar 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | directory 253 | 254 | 255 | 256 | org.apache.maven.plugins 257 | maven-assembly-plugin 258 | 3.3.0 259 | 260 | false 261 | 262 | assembly/assembly.xml 263 | 264 | 265 | 266 | 267 | 268 | single 269 | 270 | install 271 | 272 | 273 | 274 | 275 | org.apache.maven.plugins 276 | maven-source-plugin 277 | 3.0.1 278 | 279 | 280 | attach-sources 281 | verify 282 | 283 | jar-no-fork 284 | 285 | 286 | 287 | 288 | 289 | org.apache.maven.plugins 290 | maven-javadoc-plugin 291 | 3.11.1 292 | 293 | 294 | attach-javadocs 295 | package 296 | 297 | jar 298 | 299 | 300 | 301 | 302 | true 303 | none 304 | true 305 | 306 | https://javadoc.io/doc/com.vaadin/vaadin-platform-javadoc/${vaadin.version} 307 | 308 | 309 | 310 | 311 | com.vaadin 312 | vaadin-maven-plugin 313 | 314 | 315 | 316 | prepare-frontend 317 | build-frontend 318 | 319 | 320 | 321 | 322 | 323 | maven-resources-plugin 324 | 3.2.0 325 | 326 | 327 | copy-resources 328 | 329 | prepare-package 330 | 331 | copy-resources 332 | 333 | 334 | ${basedir}/target/classes/META-INF/frontend 335 | 336 | 337 | frontend 338 | true 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | org.apache.maven.plugins 347 | maven-jar-plugin 348 | 3.1.2 349 | 350 | 351 | 352 | META-INF/VAADIN/**/*.* 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | production 363 | 364 | true 365 | 366 | 367 | 368 | com.vaadin 369 | flow-server-production-mode 370 | 371 | 372 | 373 | 374 | 375 | 376 | com.vaadin 377 | vaadin-maven-plugin 378 | 379 | 380 | 381 | build-frontend 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | integration-tests 392 | 393 | 394 | 395 | org.eclipse.jetty 396 | jetty-maven-plugin 397 | ${jetty.version} 398 | 399 | 0 400 | 401 | jar 402 | 403 | ${project.artifactId} 404 | 8081 405 | 406 | 407 | 408 | start-jetty 409 | pre-integration-test 410 | 411 | start 412 | 413 | 414 | 415 | stop-jetty 416 | post-integration-test 417 | 418 | stop 419 | 420 | 421 | 422 | 423 | 424 | 425 | com.lazerycode.selenium 426 | driver-binary-downloader-maven-plugin 427 | 428 | 1.0.17 429 | 430 | 431 | true 432 | 433 | 434 | ${drivers.dir}/driver 435 | 436 | 437 | ${drivers.dir}/driver_zips 438 | 439 | 440 | ${project.basedir}/drivers.xml 441 | 442 | 443 | 444 | 445 | 446 | selenium 447 | 448 | 449 | 450 | 451 | 452 | 453 | org.apache.maven.plugins 454 | maven-failsafe-plugin 455 | 2.22.2 456 | 457 | 458 | 459 | integration-test 460 | verify 461 | 462 | 463 | 464 | 465 | false 466 | true 467 | 468 | 469 | 470 | ${webdriver.chrome.driver} 471 | 472 | 473 | 474 | 475 | 476 | maven-resources-plugin 477 | 3.1.0 478 | 479 | 480 | 483 | copy-test-to-classes 484 | process-test-classes 485 | 486 | copy-resources 487 | 488 | 489 | ${project.build.outputDirectory} 490 | 491 | 492 | ${project.build.testOutputDirectory} 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | demo-jar 505 | 506 | 507 | 508 | org.apache.maven.plugins 509 | maven-jar-plugin 510 | 511 | 512 | 513 | test-jar 514 | 515 | 516 | demo 517 | 518 | META-INF/resources/frontend/styles/shared-styles.css 519 | **/test/* 520 | **/integration/* 521 | **/DemoView.class 522 | **/DemoLayout.class 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | -------------------------------------------------------------------------------- /src/main/java/com/flowingcode/addons/applayout/AppLayout.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.vaadin.flow.component.Component; 23 | import com.vaadin.flow.component.Tag; 24 | import com.vaadin.flow.component.dependency.CssImport; 25 | import com.vaadin.flow.component.dependency.JsModule; 26 | import com.vaadin.flow.component.dependency.NpmPackage; 27 | import com.vaadin.flow.component.html.Div; 28 | import com.vaadin.flow.component.html.Image; 29 | import com.vaadin.flow.component.html.Span; 30 | import com.vaadin.flow.dom.Element; 31 | import com.vaadin.flow.router.RouterLayout; 32 | import java.util.ArrayList; 33 | import java.util.Arrays; 34 | import java.util.List; 35 | import java.util.stream.Collectors; 36 | import org.apache.commons.lang3.StringUtils; 37 | 38 | /** 39 | * Component that renders the div that contains the entire layout. 40 | * 41 | * @author mlopez 42 | */ 43 | @SuppressWarnings("serial") 44 | @Tag("fc-applayout") 45 | @JsModule("@flowingcode/fc-applayout/fc-applayout.js") 46 | @NpmPackage(value = "@flowingcode/fc-applayout", version = "1.3.0") 47 | @CssImport(value = "./styles/applayout-styles.css", themeFor = "fc-applayout") 48 | public class AppLayout extends Div implements RouterLayout { 49 | 50 | private static final String PROFILE_SLOT_NAME = "profile"; 51 | private static final String APP_LAYOUT_TITLE_SLOT_NAME = "title"; 52 | private final List menuItems = new ArrayList<>(); 53 | private final List toolbarComponents = new ArrayList<>(); 54 | 55 | public AppLayout() { 56 | this(null,"",null); 57 | } 58 | 59 | public AppLayout(String title) { 60 | this(null, title, null); 61 | } 62 | 63 | public AppLayout(Component menuHeader, String title) { 64 | this(menuHeader, title, null); 65 | } 66 | 67 | public AppLayout(Image logo, Component menuHeader, String title) { 68 | this(menuHeader, title, logo); 69 | } 70 | 71 | private AppLayout(Component menuHeader, String aTitle, Image aLogo) { 72 | if (aLogo != null) { 73 | addToTitleSection(aLogo); 74 | } 75 | if (menuHeader != null) { 76 | setMenuHeader(menuHeader); 77 | } 78 | Div title = new Div(); 79 | title.setText(aTitle); 80 | addToTitleSection(title); 81 | setDrawerRightAlignment(false); 82 | } 83 | 84 | public void addToTitleSection(Component component) { 85 | component.getElement().setAttribute("slot", APP_LAYOUT_TITLE_SLOT_NAME); 86 | add(component); 87 | } 88 | 89 | /** 90 | * Sets the component to be shown before the menu in the drawer. 91 | * @param menuHeader 92 | */ 93 | public void setMenuHeader(Component menuHeader) { 94 | getChildren().filter(item->PROFILE_SLOT_NAME.equals(item.getElement().getAttribute("slot"))).forEach(this::remove); 95 | menuHeader.getElement().setAttribute("slot", PROFILE_SLOT_NAME); 96 | add(menuHeader); 97 | } 98 | 99 | public void setMenuItems(Component... someMenuitems) { 100 | menuItems.addAll(Arrays.asList(someMenuitems)); 101 | menuItems.forEach(item -> item.getElement().setAttribute("slot", "menu")); 102 | this.add(someMenuitems); 103 | } 104 | 105 | public void clearMenuItems() { 106 | getChildren() 107 | .forEach( 108 | item -> { 109 | if (menuItems.contains(item)) { 110 | remove(item); 111 | } 112 | }); 113 | menuItems.clear(); 114 | } 115 | 116 | public void setToolbarIconButtons(Component... components) { 117 | toolbarComponents.forEach(this::remove); 118 | addToolbarIconButtons(components); 119 | } 120 | 121 | public void addToolbarIconButtons(Component... components) { 122 | List componentsToAdd = Arrays.asList(components); 123 | componentsToAdd.forEach(comp -> comp.getElement().setAttribute("slot", "toolbar")); 124 | toolbarComponents.addAll(componentsToAdd); 125 | this.add(components); 126 | } 127 | 128 | public void addToolbarIconButtonAsFirst(Component component) { 129 | toolbarComponents.add(0, component); 130 | toolbarComponents.forEach(this::remove); 131 | addToolbarIconButtons(toolbarComponents.toArray(new Component[toolbarComponents.size()])); 132 | } 133 | 134 | public void clearToolbarIconButtons() { 135 | toolbarComponents.forEach(this::remove); 136 | toolbarComponents.clear(); 137 | } 138 | 139 | public void setMenuVisible(boolean visible) { 140 | getElement().setProperty("drawerVisible", visible); 141 | } 142 | 143 | public boolean isMenuVisible() { 144 | return getElement().getProperty("drawerVisible", true); 145 | } 146 | 147 | /** 148 | * Sets the toolbar title 149 | * @param caption 150 | */ 151 | public void setCaption(String caption) { 152 | getChildren().map(Component::getElement) 153 | .filter(child -> APP_LAYOUT_TITLE_SLOT_NAME.equals(child.getAttribute("slot"))) 154 | .collect(Collectors.toList()).forEach(Element::removeFromParent); 155 | if (!StringUtils.isEmpty(caption)) { 156 | addToTitleSection(new Span(caption)); 157 | } 158 | } 159 | 160 | /** 161 | * Sets the fixed attribute so it mantains the header fixed at the top 162 | * so it never moves away. 163 | * @param fixed 164 | */ 165 | public void setFixed(boolean fixed) { 166 | getElement().setAttribute("fixed", fixed); 167 | } 168 | 169 | /** 170 | * Sets the reveals attribute so it slides back the header when scrolling 171 | * back up. 172 | * @param reveals 173 | */ 174 | public void setReveals(boolean reveals) { 175 | getElement().setAttribute("reveals", reveals); 176 | } 177 | 178 | /** 179 | * Sets the swipeOpen attribute so it creates an area at the edge of the 180 | * screen to swipe open the app-drawer 181 | * @param swipeOpen 182 | */ 183 | public void setSwipeOpen(boolean swipeOpen) { 184 | getElement().setAttribute("swipeOpen", swipeOpen); 185 | } 186 | 187 | /** 188 | * Sets the persistent attribute so it will make the drawer to be always 189 | * opened in a non-modal way 190 | * @param drawerPersistent 191 | */ 192 | public void setDrawerPersistent(boolean drawerPersistent) { 193 | getElement().setAttribute("drawerPersistent", drawerPersistent); 194 | } 195 | 196 | /** 197 | * Sets the drawerBelowHeader attribute so the drawer will be show below 198 | * the header of the applayout 199 | * @param drawerBelowHeader 200 | */ 201 | public void setDrawerBelowHeader(boolean drawerBelowHeader) { 202 | getElement().setAttribute("drawerBelowHeader", drawerBelowHeader); 203 | } 204 | 205 | /** 206 | * Sets the alignment of the drawer to the right side. 207 | * @param drawerRightAlignment 208 | */ 209 | public void setDrawerRightAlignment(boolean drawerRightAlignment) { 210 | getElement().setAttribute("drawerAlign", drawerRightAlignment?"right":"left"); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/com/flowingcode/addons/applayout/HasMenuItemCommands.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.flowingcode.addons.applayout.MouseClickEvent.MouseButton; 23 | import com.vaadin.flow.component.Component; 24 | import com.vaadin.flow.component.ComponentUtil; 25 | import com.vaadin.flow.component.HasElement; 26 | import com.vaadin.flow.server.Command; 27 | import com.vaadin.flow.shared.Registration; 28 | import java.io.Serializable; 29 | import java.util.EnumMap; 30 | import org.apache.commons.lang3.tuple.Pair; 31 | 32 | interface HasMenuItemCommands> extends HasElement { 33 | 34 | class Data implements Serializable { 35 | private static final long serialVersionUID = 1L; 36 | private final EnumMap> commands = 37 | new EnumMap<>(MouseButton.class); 38 | } 39 | 40 | public default T setCommand(Command command) { 41 | return setCommand(MouseButton.LEFT, command); 42 | } 43 | 44 | public default T setCommand(MouseButton button, Command command) { 45 | Data data = ComponentUtil.getData((Component) this, HasMenuItemCommands.Data.class); 46 | if (data == null) { 47 | data = new Data(); 48 | } 49 | 50 | data.commands.remove(button); 51 | if (command != null) { 52 | Registration registration = 53 | getElement() 54 | .addEventListener( 55 | "mouseup", 56 | ev -> { 57 | command.execute(); 58 | ((Component) this) 59 | .getElement() 60 | .executeJs( 61 | "this.dispatchEvent(new CustomEvent('item-clicked', {bubbles: true}))"); 62 | }) 63 | .setFilter("event.button==" + button.ordinal()); 64 | data.commands.put(button, Pair.of(command, registration)); 65 | } 66 | 67 | if (data.commands.isEmpty()) { 68 | data = null; 69 | } 70 | 71 | ComponentUtil.setData((Component) this, HasMenuItemCommands.Data.class, data); 72 | 73 | @SuppressWarnings("unchecked") 74 | T self = (T) this; 75 | return self; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/flowingcode/addons/applayout/HasMenuItemIcon.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.vaadin.flow.component.HasElement; 23 | 24 | interface HasMenuItemIcon> extends HasElement { 25 | 26 | public default String getIcon() { 27 | return getElement().getAttribute("icon"); 28 | } 29 | 30 | public default T setIcon(String icon) { 31 | if (icon != null) { 32 | getElement().setAttribute("icon", icon); 33 | getElement().removeAttribute("src"); 34 | } else { 35 | getElement().removeAttribute("icon"); 36 | } 37 | 38 | @SuppressWarnings("unchecked") 39 | T self = (T) this; 40 | return self; 41 | } 42 | 43 | public default String getImage() { 44 | return getElement().getAttribute("src"); 45 | } 46 | 47 | public default T setImage(String imageUrl) { 48 | if (imageUrl != null) { 49 | getElement().setAttribute("src", imageUrl); 50 | getElement().removeAttribute("icon"); 51 | } else { 52 | getElement().removeAttribute("src"); 53 | } 54 | 55 | @SuppressWarnings("unchecked") 56 | T self = (T) this; 57 | return self; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/flowingcode/addons/applayout/MenuItem.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.vaadin.flow.component.Component; 23 | import com.vaadin.flow.component.HasOrderedComponents; 24 | import com.vaadin.flow.component.Tag; 25 | import com.vaadin.flow.component.dependency.JsModule; 26 | import com.vaadin.flow.component.dependency.NpmPackage; 27 | import com.vaadin.flow.component.icon.IconFactory; 28 | import com.vaadin.flow.router.RouterLink; 29 | import com.vaadin.flow.server.Command; 30 | import java.util.function.Consumer; 31 | 32 | /** 33 | * Menu item component. 34 | * 35 | * @author mlopez 36 | */ 37 | @SuppressWarnings("serial") 38 | @NpmPackage(value = "@polymer/iron-collapse", version = "3.0.1") 39 | @NpmPackage(value = "@polymer/paper-item", version = "3.0.1") 40 | @Tag("fc-menuitem") 41 | @JsModule("@flowingcode/fc-menuitem/fc-menuitem.js") 42 | @NpmPackage(value = "@flowingcode/fc-menuitem", version = "1.0.0") 43 | public class MenuItem extends SlottedMenuItem 44 | implements HasOrderedComponents, HasMenuItemCommands, HasMenuItemIcon { 45 | 46 | static final String BLANK = "fc-menuitem-icons:empty"; 47 | 48 | /** No argument constructor */ 49 | public MenuItem() {} 50 | 51 | /** Create a new instance of {@code MenuItem} with a label. */ 52 | public MenuItem(String label) { 53 | this.setLabel(label); 54 | } 55 | 56 | /** Create a new instance of {@code MenuItem} with a label and left-button command. */ 57 | public MenuItem(String label, Command command) { 58 | this.setLabel(label); 59 | this.setCommand(command); 60 | } 61 | 62 | /** Create a new instance of {@code MenuItem} with a label for the given navigation target. */ 63 | public MenuItem(String label, Class navigationTarget) { 64 | setLink(new RouterLink(label, navigationTarget)); 65 | } 66 | 67 | /** Create a new instance of {@code MenuItem} with a label and an icon. */ 68 | public MenuItem(String label, String icon) { 69 | this(label); 70 | setIcon(icon); 71 | } 72 | 73 | /** Create a new instance of {@code MenuItem} with a label and an {@code IconFactory}. */ 74 | public MenuItem(String label, IconFactory icon) { 75 | this(label); 76 | setIcon(icon.create().getElement().getAttribute("icon")); 77 | } 78 | 79 | /** 80 | * Create a new instance of {@code MenuItem} with a label, an {@code IconFactory}, and left-button 81 | * command. 82 | */ 83 | public MenuItem(String label, IconFactory icon, Command command) { 84 | this(label, icon); 85 | this.setCommand(command); 86 | } 87 | 88 | /** Create a new instance of {@code MenuItem} with a label, an icon, and left-button command. */ 89 | public MenuItem(String label, String icon, Command command) { 90 | this(label, icon); 91 | this.setCommand(command); 92 | } 93 | 94 | /** Adds the given menu items as children of this component. */ 95 | public final MenuItem add(MenuItem... items) { 96 | this.add((Component[]) items); 97 | return this; 98 | } 99 | 100 | @Override 101 | public void add(Component... components) { 102 | HasOrderedComponents.super.add(components); 103 | } 104 | 105 | public String getTitle() { 106 | return getElement().getAttribute("title"); 107 | } 108 | 109 | public MenuItem setTitle(String title) { 110 | getElement().setAttribute("title", title); 111 | return this; 112 | } 113 | 114 | public String getLabel() { 115 | return getElement().getAttribute("label"); 116 | } 117 | 118 | public String getHref() { 119 | return getElement().getProperty("href"); 120 | } 121 | 122 | public MenuItem setLabel(String label) { 123 | getElement().setAttribute("label", label); 124 | return this; 125 | } 126 | 127 | public MenuItem setLink(RouterLink link) { 128 | setLabel(link.getText()); 129 | setHref(link.getHref()); 130 | return this; 131 | } 132 | 133 | public MenuItem setHref(String href) { 134 | getElement().setProperty("href", href); 135 | return this; 136 | } 137 | 138 | public MenuItem setOpened(boolean opened) { 139 | if (opened) { 140 | getElement().setAttribute("opened", opened); 141 | } else { 142 | getElement().removeAttribute("opened"); 143 | } 144 | return this; 145 | } 146 | 147 | public MenuItem configure(Consumer consumer) { 148 | consumer.accept(this); 149 | return this; 150 | } 151 | 152 | /** @deprecated This method ignores the parameter. Use {@link #setIconBlank()}. */ 153 | @Deprecated 154 | public void setIconSpacing(boolean value) { 155 | setIcon(MenuItem.BLANK); 156 | } 157 | 158 | public void setIconBlank() { 159 | setIcon(MenuItem.BLANK); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/com/flowingcode/addons/applayout/MenuSeparator.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.vaadin.flow.component.Component; 23 | import com.vaadin.flow.component.Tag; 24 | import com.vaadin.flow.component.dependency.JsModule; 25 | import com.vaadin.flow.dom.Element; 26 | 27 | @JsModule("./fc-applayout/fc-separator.js") 28 | @Tag("fc-separator") 29 | public class MenuSeparator extends Component { 30 | 31 | private static final long serialVersionUID = 1L; 32 | 33 | public MenuSeparator() {} 34 | 35 | public MenuSeparator(String label) { 36 | Element div = new Element("div"); 37 | div.setText(label); 38 | div.setAttribute("slot", "label"); 39 | getElement().removeAllChildren(); 40 | getElement().appendChild(div); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/flowingcode/addons/applayout/MouseClickEvent.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.vaadin.flow.component.Component; 23 | import com.vaadin.flow.component.ComponentEvent; 24 | 25 | public class MouseClickEvent extends ComponentEvent { 26 | 27 | private static final long serialVersionUID = 1L; 28 | 29 | public enum MouseButton { 30 | LEFT, 31 | MIDDLE, 32 | RIGHT; 33 | } 34 | 35 | private MouseButton button; 36 | 37 | public MouseClickEvent(T source, MouseButton button, boolean fromClient) { 38 | super(source, fromClient); 39 | this.button = button; 40 | } 41 | 42 | public MouseButton getButton() { 43 | return button; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/flowingcode/addons/applayout/MouseClickListener.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.vaadin.flow.component.Component; 23 | import com.vaadin.flow.component.ComponentEventListener; 24 | 25 | @FunctionalInterface 26 | public interface MouseClickListener 27 | extends ComponentEventListener> {} 28 | -------------------------------------------------------------------------------- /src/main/java/com/flowingcode/addons/applayout/PaperListbox.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.vaadin.flow.component.Component; 23 | import com.vaadin.flow.component.HasOrderedComponents; 24 | import com.vaadin.flow.component.Tag; 25 | import com.vaadin.flow.component.dependency.JsModule; 26 | import com.vaadin.flow.component.dependency.NpmPackage; 27 | import java.util.List; 28 | 29 | /** 30 | * Component that renders a paper-listbox 31 | * 32 | * @author mlopez 33 | */ 34 | @SuppressWarnings("serial") 35 | @NpmPackage(value = "@polymer/paper-listbox", version = "3.0.1") 36 | @JsModule("@polymer/paper-listbox/paper-listbox.js") 37 | @Tag("paper-listbox") 38 | class PaperListbox extends Component implements HasOrderedComponents { 39 | 40 | public PaperListbox(List components) { 41 | components.stream().forEach(this::add); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/flowingcode/addons/applayout/SlottedMenuItem.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.vaadin.flow.component.AttachEvent; 23 | import com.vaadin.flow.component.Component; 24 | 25 | /** Menu item component. */ 26 | @SuppressWarnings("serial") 27 | public abstract class SlottedMenuItem extends Component { 28 | 29 | @Override 30 | protected void onAttach(AttachEvent attachEvent) { 31 | if (getParent().orElse(null) instanceof SlottedMenuItem) { 32 | getElement().setAttribute("slot", "menu-item"); 33 | } else { 34 | if ("menu-item".equals(getElement().getAttribute("slot"))) { 35 | getElement().removeAttribute("slot"); 36 | } 37 | } 38 | super.onAttach(attachEvent); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/flowingcode/addons/applayout/ToolbarIconButton.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.vaadin.flow.component.Component; 23 | import com.vaadin.flow.component.HasEnabled; 24 | import com.vaadin.flow.component.Tag; 25 | import com.vaadin.flow.component.dependency.JsModule; 26 | import com.vaadin.flow.component.dependency.NpmPackage; 27 | import com.vaadin.flow.component.icon.IconFactory; 28 | import com.vaadin.flow.server.Command; 29 | 30 | /** Toolbar menu item component. */ 31 | @SuppressWarnings("serial") 32 | @NpmPackage(value = "@polymer/paper-icon-button", version = "3.0.2") 33 | @JsModule("@polymer/paper-icon-button/paper-icon-button.js") 34 | @Tag("paper-icon-button") 35 | public class ToolbarIconButton extends Component 36 | implements HasEnabled, 37 | HasMenuItemCommands, 38 | HasMenuItemIcon { 39 | 40 | /** No argument constructor */ 41 | public ToolbarIconButton() {} 42 | 43 | /** Create a new instance of {@code ToolbarIconButton} with a title */ 44 | public ToolbarIconButton(String title) { 45 | this.setTitle(title); 46 | } 47 | 48 | /** Create a new instance of {@code ToolbarIconButton} with a left-button command. */ 49 | public ToolbarIconButton(Command command) { 50 | this.setCommand(command); 51 | } 52 | 53 | /** Create a new instance of {@code MenuItem} with a title and a left-button command. */ 54 | public ToolbarIconButton(String title, Command command) { 55 | this.setTitle(title); 56 | this.setCommand(command); 57 | } 58 | 59 | /** 60 | * Create a new instance of {@code ToolbarIconButton} with a title, an {@code IconFactory}, and 61 | * left-button command. 62 | */ 63 | public ToolbarIconButton(String title, IconFactory icon, Command command) { 64 | this(command); 65 | setTitle(title); 66 | setIcon(icon.create().getElement().getAttribute("icon")); 67 | } 68 | 69 | /** Create a new instance of {@code MenuItem} with a title, an icon, and left-button command. */ 70 | public ToolbarIconButton(String title, String icon, Command command) { 71 | this(command); 72 | setTitle(title); 73 | setIcon(icon); 74 | } 75 | 76 | public String getTitle() { 77 | return getElement().getAttribute("title"); 78 | } 79 | 80 | public ToolbarIconButton setTitle(String title) { 81 | getElement().setAttribute("title", title); 82 | return this; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/flowingcode/addons/applayout/endpoint/MenuEndpoint.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout.endpoint; 21 | 22 | import com.flowingcode.addons.applayout.MenuItem; 23 | import com.vaadin.flow.server.auth.AnonymousAllowed; 24 | import com.vaadin.hilla.Endpoint; 25 | import com.vaadin.hilla.Nonnull; 26 | import java.util.List; 27 | import java.util.stream.Collectors; 28 | 29 | @Endpoint 30 | @AnonymousAllowed 31 | public class MenuEndpoint { 32 | 33 | List menuItemsProviders; 34 | 35 | public MenuEndpoint(List menuItemsProviders) { 36 | this.menuItemsProviders = menuItemsProviders; 37 | } 38 | 39 | public @Nonnull List<@Nonnull MenuItemDto> getMenuItems() { 40 | List menuItems = menuItemsProviders.stream().map(MenuItemsProvider::getMenuItems).flatMap(List::stream).collect(Collectors.toList()); 41 | return convertMenuItems(menuItems); 42 | } 43 | 44 | private List convertMenuItems(List menuItems) { 45 | return menuItems.stream().map(mi->new MenuItemDto(mi.getLabel(),mi.getHref(),extractMenuItems(mi))).collect(Collectors.toList()); 46 | } 47 | 48 | private List extractMenuItems(MenuItem mi) { 49 | return convertMenuItems(mi.getChildren().filter(MenuItem.class::isInstance).map(MenuItem.class::cast).collect(Collectors.toList())); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/flowingcode/addons/applayout/endpoint/MenuItemDto.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout.endpoint; 21 | 22 | import com.vaadin.hilla.Nonnull; 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | import javax.annotation.Nullable; 26 | 27 | public class MenuItemDto { 28 | 29 | private String label; 30 | @Nullable 31 | private String href; 32 | private @Nonnull List<@Nonnull MenuItemDto> children = new ArrayList<>(); 33 | 34 | public MenuItemDto(String label, List children) { 35 | this(label,null,children); 36 | } 37 | 38 | public MenuItemDto(String label, String href) { 39 | this(label,href,new ArrayList<>()); 40 | } 41 | 42 | public MenuItemDto(String label, String href, List children) { 43 | this.label = label; 44 | this.href = href; 45 | this.children = children; 46 | } 47 | 48 | public List getChildren() { 49 | return children; 50 | } 51 | 52 | public void setChildren(List children) { 53 | this.children = children; 54 | } 55 | 56 | public String getLabel() { 57 | return label; 58 | } 59 | public String getHref() { 60 | return href; 61 | } 62 | public void setHref(@Nonnull String href) { 63 | this.href = href; 64 | } 65 | public void setLabel(@Nonnull String label) { 66 | this.label = label; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/flowingcode/addons/applayout/endpoint/MenuItemsProvider.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout.endpoint; 21 | 22 | import java.util.List; 23 | 24 | import com.flowingcode.addons.applayout.MenuItem; 25 | 26 | @FunctionalInterface 27 | public interface MenuItemsProvider { 28 | 29 | List getMenuItems(); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/VAADIN/package.properties: -------------------------------------------------------------------------------- 1 | vaadin.allowed-packages=com.flowingcode 2 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/resources/frontend/fc-applayout/fc-fusion-layout.ts: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | import { CSSModule } from '@vaadin/flow-frontend/css-utils'; 21 | import { css, customElement, html, LitElement, state, property, query } from 'lit-element'; 22 | import "@flowingcode/fc-applayout"; 23 | import {FcAppLayoutElement} from "@flowingcode/fc-applayout/src/fc-applayout"; 24 | import "@flowingcode/fc-menuitem"; 25 | import * as menuItemEndpoint from 'Frontend/generated/MenuEndpoint'; 26 | import MenuItemDto from 'Frontend/generated/com/flowingcode/addons/applayout/endpoint/MenuItemDto'; 27 | import { EndpointError } from '@hilla/frontend'; 28 | import { Router } from '@vaadin/router'; 29 | 30 | @customElement('fc-fusion-layout') 31 | export class FusionLayout extends LitElement { 32 | static get is() { return 'fc-fusion-layout'; } 33 | 34 | @query("#fusionLayout") 35 | fcAppLayout!: FcAppLayoutElement; 36 | 37 | @state() 38 | private menuItems: MenuItemDto[] = []; 39 | 40 | @property({type: String}) 41 | title = "My Application"; 42 | 43 | @property({type: String}) 44 | profilePicture = "./images/user.svg"; 45 | 46 | @property({type: String}) 47 | profilePictureAlt = "Profile picture"; 48 | 49 | @property({type: String}) 50 | appLogo = "./icons/icon.png"; 51 | 52 | @property({type: String}) 53 | appLogoAlt = "Logo"; 54 | 55 | @property({type: String}) 56 | welcomeRoute = "welcome"; 57 | 58 | @property({type: String}) 59 | userName = "User"; 60 | 61 | @property({type: Boolean}) 62 | reveals = true; 63 | 64 | @property({type: Boolean}) 65 | swipeOpen = true; 66 | 67 | @property({type: Boolean}) 68 | fixed = false; 69 | 70 | @property({type: Router, attribute: false}) 71 | router: Router | null = null; 72 | 73 | constructor() { 74 | super(); 75 | this.addEventListener("menuitem-clicked-event", (e:Event) => { 76 | let fcmi: any = e.composedPath()[0]; 77 | if (!fcmi.isSubmenu) { 78 | this.fcAppLayout.drawer.close(); 79 | } 80 | }); 81 | } 82 | 83 | static get styles() { 84 | return [ 85 | CSSModule('lumo-typography'), 86 | CSSModule('lumo-color'), 87 | css` 88 | :host { 89 | display: block; 90 | height: 100%; 91 | } 92 | 93 | header { 94 | align-items: center; 95 | box-shadow: var(--lumo-box-shadow-s); 96 | display: flex; 97 | height: var(--lumo-size-xl); 98 | width: 100%; 99 | } 100 | 101 | header h1 { 102 | font-size: var(--lumo-font-size-l); 103 | margin: 0; 104 | } 105 | 106 | header img { 107 | border-radius: 50%; 108 | height: var(--lumo-size-s); 109 | margin-left: auto; 110 | margin-right: var(--lumo-space-m); 111 | overflow: hidden; 112 | background-color: var(--lumo-contrast); 113 | } 114 | 115 | #logo { 116 | align-items: center; 117 | box-sizing: border-box; 118 | display: flex; 119 | padding: var(--lumo-space-s) var(--lumo-space-m); 120 | } 121 | 122 | #logo img { 123 | height: calc(var(--lumo-size-l) * 1.5); 124 | } 125 | 126 | #logo span { 127 | font-size: var(--lumo-font-size-xl); 128 | font-weight: 600; 129 | margin: 0 var(--lumo-space-s); 130 | } 131 | 132 | .other { 133 | background-color: white; 134 | color: var(--lumo-contrast); 135 | } 136 | .current { 137 | background-color: var(--lumo-contrast); 138 | color: white; 139 | } 140 | .fusion-content-container { 141 | width: 100%; 142 | height: 100%; 143 | } 144 | .profile-title { 145 | width: 100%; 146 | display: block; 147 | font-size: large; 148 | font-weight: bold; 149 | } 150 | 151 | `, 152 | ]; 153 | } 154 | 155 | render() { 156 | return html` 157 | 158 |
159 | ${this.profilePictureAlt} 160 | ${this.userName} 161 |
162 | 163 |
${this.title}
164 | 165 |
166 | ${this.menuItems.map(item => this.generateFcMenuItem(item))} 167 |
168 |
169 | 170 |
171 |
172 | `; 173 | } 174 | 175 | connectedCallback() { 176 | super.connectedCallback(); 177 | this.buildMenu(); 178 | window.addEventListener('requestRefreshMenu', 179 | this.buildMenuEventListener, 180 | false 181 | ); 182 | let welcomeRoute = this.welcomeRoute; 183 | window.addEventListener("message", function(event) { 184 | if (event.data == 'redirect-welcome') { 185 | Router.go(welcomeRoute); 186 | } 187 | }); 188 | } 189 | 190 | private buildMenuEventListener = () => { 191 | this.buildMenu(); 192 | } 193 | 194 | disconnectedCallback() { 195 | super.disconnectedCallback(); 196 | window.removeEventListener('requestRefreshMenu', 197 | this.buildMenuEventListener, 198 | false 199 | ); 200 | } 201 | 202 | private currentLocationClass(route: string): string { 203 | return !route.startsWith("http://") && !route.startsWith("https://") && this.router!=null && this.router!.urlForPath(route) === this.router!.location.pathname?"current":"other"; 204 | } 205 | 206 | private buildMenu(){ 207 | window.requestAnimationFrame(() => this.updateMenuItems()); 208 | } 209 | 210 | private async updateMenuItems(){ 211 | await menuItemEndpoint.getMenuItems().then((mi: any[]) =>{ 212 | this.menuItems = mi! 213 | } ).catch((error: { detail: any; message: any; })=>{ 214 | if (error instanceof EndpointError) { 215 | console.error("EndpointError"); 216 | console.error(error.detail); 217 | console.error(error.message); 218 | } else { 219 | console.error("Not EndpointError"); 220 | console.error(error); 221 | } 222 | }) 223 | } 224 | 225 | private generateFcMenuItem(item: MenuItemDto): any { 226 | let routerLink = item.href; 227 | let ret; 228 | // TODO: Add attrs: key, src, icon(if hasIcon), disabled, (isSubmenu?), onMenuItemClicked 229 | if(item.children.length>0) { 230 | ret = html` 231 | 236 | ${item.children.length>0 ? item.children.map((i: any) => this.generateFcMenuItem(i)) : ""} 237 | 238 | `; 239 | } else { 240 | ret = html` 241 | 248 | 249 | `; 250 | } 251 | return ret; 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/resources/frontend/fc-applayout/fc-separator.js: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | import {html, PolymerElement} from '@polymer/polymer/polymer-element.js'; 21 | 22 | import {ThemableMixin} from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; 23 | 24 | class MenuSeparator extends ThemableMixin(PolymerElement) { 25 | static get is() { return 'fc-separator'; } 26 | 27 | static get template() { 28 | 29 | return html` 30 | 44 |
45 |
46 | `;} 47 | 48 | } 49 | 50 | customElements.define(MenuSeparator.is, MenuSeparator); 51 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/resources/frontend/styles/applayout-styles.css: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | :host(.compact) [part~="toolbar"] { 21 | height:32px; 22 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.flowingcode.addons.applayout.listener.HillaAutoConfiguration -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/addons/applayout/ApplayoutDemoView.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.flowingcode.addons.applayout.MouseClickEvent.MouseButton; 23 | import com.flowingcode.addons.applayout.menu.PaperCard; 24 | import com.flowingcode.addons.applayout.menu.PaperToggle; 25 | import com.flowingcode.vaadin.addons.DemoLayout; 26 | import com.vaadin.flow.component.Component; 27 | import com.vaadin.flow.component.button.Button; 28 | import com.vaadin.flow.component.checkbox.Checkbox; 29 | import com.vaadin.flow.component.dependency.StyleSheet; 30 | import com.vaadin.flow.component.dialog.Dialog; 31 | import com.vaadin.flow.component.html.Div; 32 | import com.vaadin.flow.component.html.H3; 33 | import com.vaadin.flow.component.html.H5; 34 | import com.vaadin.flow.component.html.Image; 35 | import com.vaadin.flow.component.html.Span; 36 | import com.vaadin.flow.component.icon.VaadinIcon; 37 | import com.vaadin.flow.component.notification.Notification; 38 | import com.vaadin.flow.component.orderedlayout.HorizontalLayout; 39 | import com.vaadin.flow.component.orderedlayout.VerticalLayout; 40 | import com.vaadin.flow.data.binder.Binder; 41 | import com.vaadin.flow.router.Route; 42 | import java.io.BufferedReader; 43 | import java.io.InputStream; 44 | import java.io.InputStreamReader; 45 | import java.util.stream.Collectors; 46 | import org.junit.Ignore; 47 | 48 | @SuppressWarnings("serial") 49 | @Route(value = "applayout-full", layout = DemoLayout.class) 50 | @StyleSheet("context://frontend/styles/app-layout/demo-styles.css") 51 | @Ignore 52 | public class ApplayoutDemoView extends VerticalLayout { 53 | 54 | private VerticalLayout container = new VerticalLayout(); 55 | private final AppLayout app = 56 | new AppLayout(createLogoImage(), createAvatarComponent(), "AppLayout Vaadin Demo"); 57 | private final ToolbarIconButton miSettings = 58 | new ToolbarIconButton("Settings", "settings", this::openSettings); 59 | 60 | private final DemoSettings settings = new DemoSettings(); 61 | 62 | public ApplayoutDemoView() { 63 | container.setDefaultHorizontalComponentAlignment(Alignment.CENTER); 64 | container.setSizeFull(); 65 | 66 | this.setPadding(false); 67 | this.setSpacing(false); 68 | this.setMargin(false); 69 | 70 | app.setMenuItems(createMenuItems()); 71 | 72 | app.setToolbarIconButtons(miSettings); 73 | app.add(container); 74 | this.add(app); 75 | 76 | settings.setSwipeOpen(true); 77 | settings.setMenuVisible(true); 78 | settings.setReveals(true); 79 | applySettings(); 80 | 81 | showHamletContent(); 82 | } 83 | 84 | // apply the current settings 85 | private void applySettings() { 86 | app.setMenuVisible(settings.isMenuVisible()); 87 | app.setSwipeOpen(settings.isSwipeOpen()); 88 | app.setFixed(settings.isFixed()); 89 | app.setReveals(settings.isReveals()); 90 | app.setDrawerPersistent(settings.isDrawerPersistent()); 91 | app.setDrawerBelowHeader(settings.isDrawerBelowHeader()); 92 | app.setDrawerRightAlignment(settings.isDrawerRightAlignment()); 93 | 94 | if (settings.isCompact()) { 95 | app.addClassName("compact"); 96 | } else { 97 | app.removeClassName("compact"); 98 | } 99 | } 100 | 101 | private void openSettings() { 102 | Dialog dialog = new Dialog(); 103 | H3 title = new H3("Demo settings"); 104 | title.getStyle().set("margin-top", "0"); 105 | dialog.add(title); 106 | 107 | Checkbox cbMenuVisible = new Checkbox("Menu visible"); 108 | Checkbox cbSwipeOpen = new Checkbox("Swipe Open"); 109 | Checkbox cbFixed = new Checkbox("Fixed"); 110 | Checkbox cbReveals = new Checkbox("Reveals"); 111 | Checkbox cbCompact = new Checkbox("Compact"); 112 | Checkbox cbPersistent = new Checkbox("Drawer Persistent"); 113 | Checkbox cbBelowHeader = new Checkbox("Drawer Below Header"); 114 | Checkbox cbRightAlignment = new Checkbox("Drawer Aligned to Right"); 115 | 116 | cbMenuVisible.getElement().setAttribute("title", "Toggle visibility of the hamburguer icon."); 117 | cbSwipeOpen 118 | .getElement() 119 | .setAttribute( 120 | "title", 121 | "When enabled, you can open the menu by swiping the left border of the screen."); 122 | cbFixed 123 | .getElement() 124 | .setAttribute( 125 | "title", "When enabled, the header is fixed at the top so it never moves away."); 126 | cbReveals 127 | .getElement() 128 | .setAttribute("title", "When enabled, the header slides back when scrolling back up."); 129 | cbCompact 130 | .getElement() 131 | .setAttribute("title", "When enabled, the height of the header is set to 32px."); 132 | cbPersistent 133 | .getElement() 134 | .setAttribute("title", "When enabled, the drawer will be opened in a non-modal way"); 135 | cbBelowHeader 136 | .getElement() 137 | .setAttribute("title", "When enabled, the drawer will be placed below the header"); 138 | cbRightAlignment 139 | .getElement() 140 | .setAttribute("title", "When enabled, the drawer will be right aligned"); 141 | 142 | Binder binder = new Binder<>(); 143 | binder.forField(cbMenuVisible).bind(DemoSettings::isMenuVisible, DemoSettings::setMenuVisible); 144 | binder.forField(cbSwipeOpen).bind(DemoSettings::isSwipeOpen, DemoSettings::setSwipeOpen); 145 | binder.forField(cbFixed).bind(DemoSettings::isFixed, DemoSettings::setFixed); 146 | binder.forField(cbReveals).bind(DemoSettings::isReveals, DemoSettings::setReveals); 147 | binder.forField(cbCompact).bind(DemoSettings::isCompact, DemoSettings::setCompact); 148 | binder.forField(cbPersistent).bind(DemoSettings::isDrawerPersistent, DemoSettings::setDrawerPersistent); 149 | binder.forField(cbBelowHeader).bind(DemoSettings::isDrawerBelowHeader, DemoSettings::setDrawerBelowHeader); 150 | binder.forField(cbRightAlignment).bind(DemoSettings::isDrawerRightAlignment, DemoSettings::setDrawerRightAlignment); 151 | binder.setBean(this.settings); 152 | 153 | VerticalLayout content = 154 | new VerticalLayout(cbMenuVisible, cbSwipeOpen, cbFixed, cbReveals, cbCompact, cbPersistent, cbBelowHeader, cbRightAlignment); 155 | content.setSpacing(false); 156 | 157 | HorizontalLayout buttons = new HorizontalLayout(); 158 | Button btnOk = 159 | new Button( 160 | "OK", 161 | ev -> { 162 | applySettings(); 163 | dialog.close(); 164 | }); 165 | 166 | Button btnCancel = new Button("Cancel", ev -> dialog.close()); 167 | btnOk.getElement().setAttribute("theme", "primary"); 168 | buttons.setSpacing(true); 169 | buttons.add(btnOk, btnCancel); 170 | buttons.setSpacing(true); 171 | 172 | dialog.add(content, buttons); 173 | dialog.setSizeUndefined(); 174 | dialog.open(); 175 | } 176 | 177 | private Image createLogoImage() { 178 | Image img = new Image("/frontend/images/applogo.png", "applogo"); 179 | img.addClassName("applogo"); 180 | return img; 181 | } 182 | 183 | private Component createAvatarComponent() { 184 | Div container = new Div(); 185 | container.getElement().setAttribute("style", "text-align: center;"); 186 | Image img = new Image("/frontend/images/avatar.png", "avatar"); 187 | img.getStyle().set("width", "80px"); 188 | img.getStyle().set("margin-top", "20px"); 189 | Span userTitle = new Span("User"); 190 | userTitle.setWidthFull(); 191 | userTitle.getStyle().set("display", "block"); 192 | userTitle.getStyle().set("font-size", "large"); 193 | userTitle.getStyle().set("font-weight", "bold"); 194 | container.add(img, userTitle); 195 | return container; 196 | } 197 | 198 | private void toggleSettings(MenuItem toggleSettings) { 199 | settings.setEnabled(!settings.isEnabled()); 200 | miSettings.setEnabled(settings.isEnabled()); 201 | app.setToolbarIconButtons(miSettings); 202 | if (settings.isEnabled()) { 203 | toggleSettings.setLabel("Disable settings"); 204 | } else { 205 | toggleSettings.setLabel("Enable settings"); 206 | } 207 | } 208 | 209 | private Component[] createMenuItems() { 210 | 211 | MenuItem miHello = 212 | new MenuItem("More content", () -> showContent("Hello!")).setIcon("settings"); 213 | 214 | MenuItem miToggleSettings = new MenuItem().setIcon("settings"); 215 | miToggleSettings.setCommand(() -> toggleSettings(miToggleSettings)); 216 | toggleSettings(miToggleSettings); 217 | 218 | this.getElement().getStyle().set("--icon-spacing", "normal"); 219 | 220 | return new Component[] { 221 | 222 | // left, middle and right commands 223 | new MenuItem("Click", VaadinIcon.POINTER) 224 | .setCommand(MouseButton.LEFT, () -> Notification.show("LEFT click")) 225 | .setCommand(MouseButton.MIDDLE, () -> Notification.show("MIDDLE click")) 226 | .setCommand(MouseButton.RIGHT, () -> Notification.show("RIGHT click")), 227 | new MenuItem("No icon"), 228 | new MenuItem("No icon, spaced").configure(mi -> mi.setIconBlank()), 229 | 230 | // menu item with custom content 231 | new MenuItem("Toggle").configure(mi -> mi.add(new PaperToggle())), 232 | new MenuItem("Toggle", VaadinIcon.BACKSPACE).configure(mi -> mi.add(new PaperToggle())), 233 | new MenuItem("Toggle", MenuItem.BLANK).configure(mi -> mi.add(new PaperToggle())), 234 | new MenuItem("External link").setHref("http://www.google.com"), 235 | new MenuItem("Internal Link", SampleInternalView.class), 236 | 237 | // icon as VaadinIcon enum 238 | new MenuItem("Content", VaadinIcon.BOOK, () -> showHamletContent()) 239 | .setCommand( 240 | MouseButton.MIDDLE, 241 | () -> { 242 | getUI() 243 | .ifPresent( 244 | ui -> 245 | ui.getPage().executeJs("window.open(window.location.href, '_blank')")); 246 | }), 247 | miToggleSettings, 248 | miHello, 249 | new MenuItem("About", "cloud", () -> showContent("About")), // icon as string 250 | new MenuItem("Clear Items", "clear", () -> app.clearMenuItems()), 251 | new MenuItem( 252 | "Change Text & Icon", 253 | "cloud", 254 | () -> { 255 | if (miHello.getIcon().equals("star")) { 256 | miHello.setIcon("cloud"); 257 | miHello.setLabel("Say hello modified"); 258 | } else { 259 | miHello.setIcon("star"); 260 | miHello.setLabel("Say hello"); 261 | } 262 | }), 263 | new MenuItem("SubMenu") 264 | .setIcon("build") 265 | .setOpened(true) 266 | .add( 267 | new MenuItem("Hello Again", "inbox", () -> showContent("Hello Again!")), 268 | new MenuItem("And Again", () -> showContent("And Again!")), 269 | new MenuItem("SubMenu") 270 | .setOpened(true) 271 | .add(new MenuItem("Hello Again", () -> showContent("Hello Again!"))) 272 | .add(new MenuItem("And Again", () -> showContent("And Again!")))), 273 | new MenuSeparator("Separator"), 274 | new MenuItem("Item 1"), 275 | new MenuItem("Item 2"), 276 | new MenuItem("Item 3"), 277 | new MenuItem("Item 4"), 278 | new MenuItem("Item 5"), 279 | new MenuItem("Item 6"), 280 | new MenuSeparator(), 281 | new MenuItem("Item 7"), 282 | new MenuItem("Item 8"), 283 | new MenuItem("Item 9"), 284 | new MenuItem("Item 10"), 285 | new MenuItem("Item 11"), 286 | new MenuItem("Item 12") 287 | }; 288 | } 289 | 290 | private void showContent(String content) { 291 | container.setClassName(""); 292 | container.removeAll(); 293 | H3 label = new H3(); 294 | label.setSizeFull(); 295 | label.setText(content); 296 | PaperCard pc = 297 | new PaperCard( 298 | label, 299 | new MenuItem("Delete", () -> Notification.show("Delete action from card")), 300 | new MenuItem("Delete", () -> Notification.show("Delete action from card")) 301 | .setIcon("delete")); 302 | pc.setWidth("100%"); 303 | container.add(pc); 304 | } 305 | 306 | private void showHamletContent() { 307 | InputStream in = this.getClass().getClassLoader().getResourceAsStream("hamlet"); 308 | String text = 309 | new BufferedReader(new InputStreamReader(in)).lines().collect(Collectors.joining("\n")); 310 | 311 | container.removeAll(); 312 | container.setClassName("hamlet"); 313 | for (String block : text.split("\n\n")) { 314 | if (block.startsWith("$")) { 315 | 316 | } else if (block.startsWith("[")) { 317 | PaperCard card = new PaperCard(); 318 | card.setCardContent(new Span(block.substring(1, block.indexOf("]")))); 319 | card.getElement().setAttribute("elevation", "0"); 320 | container.add(card); 321 | } else { 322 | PaperCard card = new PaperCard(); 323 | String ss[] = block.split("\\.", 2); 324 | card.setCardContent(new Div(new H5(ss[0]), new Span(ss[1]))); 325 | if (ss[0].equals("Claudius")) { 326 | container.setHorizontalComponentAlignment(Alignment.END, card); 327 | card.addClassName("claudius"); 328 | } else { 329 | container.setHorizontalComponentAlignment(Alignment.START, card); 330 | } 331 | container.add(card); 332 | } 333 | } 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/addons/applayout/CustomAppLayout.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.vaadin.flow.component.html.Div; 23 | import com.vaadin.flow.component.html.Image; 24 | import com.vaadin.flow.component.html.Span; 25 | 26 | @SuppressWarnings("serial") 27 | public class CustomAppLayout extends AppLayout { 28 | 29 | public CustomAppLayout() { 30 | setMenuItems(new MenuItem("Item 1"), new MenuItem("Item 2")); 31 | 32 | // menu header 33 | Div container = new Div(); 34 | container.getElement().setAttribute("style", "text-align: center;"); 35 | Image img = new Image("frontend/images/avatar.png", "avatar"); 36 | img.getStyle().set("width", "80px"); 37 | img.getStyle().set("margin-top", "20px"); 38 | Span userTitle = new Span("User"); 39 | userTitle.setWidthFull(); 40 | userTitle.getStyle().set("display", "block"); 41 | userTitle.getStyle().set("font-size", "large"); 42 | userTitle.getStyle().set("font-weight", "bold"); 43 | container.add(img, userTitle); 44 | setMenuHeader(container); 45 | 46 | // logo 47 | Image imglogo = new Image("frontend/images/applogo.png", "applogo"); 48 | imglogo.setWidth("25px"); 49 | addToTitleSection(imglogo); 50 | 51 | // title 52 | addToTitleSection(new Div(new Span("Test Application"))); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/addons/applayout/DemoSettings.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | public class DemoSettings { 23 | 24 | private boolean enabled; 25 | 26 | private boolean menuVisible; 27 | 28 | private boolean swipeOpen; 29 | 30 | private boolean fixed; 31 | 32 | private boolean reveals; 33 | 34 | private boolean compact; 35 | 36 | private boolean drawerPersistent; 37 | 38 | private boolean drawerBelowHeader; 39 | 40 | private boolean drawerRightAlignment; 41 | 42 | public boolean isEnabled() { 43 | return enabled; 44 | } 45 | 46 | public void setEnabled(boolean enabled) { 47 | this.enabled = enabled; 48 | } 49 | 50 | public boolean isMenuVisible() { 51 | return menuVisible; 52 | } 53 | 54 | public void setMenuVisible(boolean menuVisible) { 55 | this.menuVisible = menuVisible; 56 | } 57 | 58 | public boolean isSwipeOpen() { 59 | return swipeOpen; 60 | } 61 | 62 | public void setSwipeOpen(boolean swipeOpen) { 63 | this.swipeOpen = swipeOpen; 64 | } 65 | 66 | public boolean isFixed() { 67 | return fixed; 68 | } 69 | 70 | public void setFixed(boolean fixed) { 71 | this.fixed = fixed; 72 | } 73 | 74 | public boolean isReveals() { 75 | return reveals; 76 | } 77 | 78 | public void setReveals(boolean reveals) { 79 | this.reveals = reveals; 80 | } 81 | 82 | public boolean isCompact() { 83 | return compact; 84 | } 85 | 86 | public void setCompact(boolean compact) { 87 | this.compact = compact; 88 | } 89 | 90 | public boolean isDrawerPersistent() { 91 | return drawerPersistent; 92 | } 93 | 94 | public void setDrawerPersistent(boolean drawerPersistent) { 95 | this.drawerPersistent = drawerPersistent; 96 | } 97 | 98 | public boolean isDrawerBelowHeader() { 99 | return drawerBelowHeader; 100 | } 101 | 102 | public void setDrawerBelowHeader(boolean drawerBelowHeader) { 103 | this.drawerBelowHeader = drawerBelowHeader; 104 | } 105 | 106 | public boolean isDrawerRightAlignment() { 107 | return drawerRightAlignment; 108 | } 109 | 110 | public void setDrawerRightAlignment(boolean drawerRightAlignment) { 111 | this.drawerRightAlignment = drawerRightAlignment; 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/addons/applayout/SampleInternalView.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.vaadin.flow.component.html.Div; 23 | import com.vaadin.flow.component.html.Span; 24 | import com.vaadin.flow.router.Route; 25 | import org.junit.Ignore; 26 | 27 | @SuppressWarnings("serial") 28 | @Route("internal-view") 29 | @Ignore 30 | public class SampleInternalView extends Div { 31 | 32 | public SampleInternalView() { 33 | add(new Span("Internal view")); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/addons/applayout/SampleView.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout; 21 | 22 | import com.vaadin.flow.component.dependency.Uses; 23 | import com.vaadin.flow.component.html.Div; 24 | import com.vaadin.flow.component.html.Span; 25 | import com.vaadin.flow.router.Route; 26 | import org.junit.Ignore; 27 | 28 | @SuppressWarnings("serial") 29 | @Route(value = "view", layout = CustomAppLayout.class) 30 | @Uses(AppLayout.class) 31 | @Ignore 32 | public class SampleView extends Div { 33 | 34 | { 35 | add(new Span("Hello world")); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/addons/applayout/integration/AbstractViewTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout.integration; 21 | 22 | import com.vaadin.testbench.ScreenshotOnFailureRule; 23 | import com.vaadin.testbench.TestBench; 24 | import com.vaadin.testbench.parallel.ParallelTest; 25 | import org.junit.Before; 26 | import org.junit.Rule; 27 | import org.openqa.selenium.chrome.ChromeDriver; 28 | 29 | /** 30 | * Base class for ITs 31 | * 32 | *

The tests use Chrome driver (see pom.xml for integration-tests profile) to run integration 33 | * tests on a headless Chrome. If a property {@code test.use .hub} is set to true, {@code 34 | * AbstractViewTest} will assume that the TestBench test is running in a CI environment. In order to 35 | * keep the this class light, it makes certain assumptions about the CI environment (such as 36 | * available environment variables). It is not advisable to use this class as a base class for you 37 | * own TestBench tests. 38 | * 39 | *

To learn more about TestBench, visit Vaadin TestBench. 41 | */ 42 | public abstract class AbstractViewTest extends ParallelTest { 43 | private static final int SERVER_PORT = 8080; 44 | 45 | private final String route; 46 | 47 | @Rule public ScreenshotOnFailureRule rule = new ScreenshotOnFailureRule(this, true); 48 | 49 | public AbstractViewTest() { 50 | this(""); 51 | } 52 | 53 | protected AbstractViewTest(String route) { 54 | this.route = route; 55 | } 56 | 57 | @Before 58 | public void setup() throws Exception { 59 | if (isUsingHub()) { 60 | super.setup(); 61 | } else { 62 | setDriver(TestBench.createDriver(new ChromeDriver())); 63 | } 64 | getDriver().get(getURL(route)); 65 | } 66 | 67 | /** 68 | * Returns deployment host name concatenated with route. 69 | * 70 | * @return URL to route 71 | */ 72 | private static String getURL(String route) { 73 | return String.format("http://%s:%d/%s", getDeploymentHostname(), SERVER_PORT, route); 74 | } 75 | 76 | /** Property set to true when running on a test hub. */ 77 | private static final String USE_HUB_PROPERTY = "test.use.hub"; 78 | 79 | /** 80 | * Returns whether we are using a test hub. This means that the starter is running tests in 81 | * Vaadin's CI environment, and uses TestBench to connect to the testing hub. 82 | * 83 | * @return whether we are using a test hub 84 | */ 85 | private static boolean isUsingHub() { 86 | return Boolean.TRUE.toString().equals(System.getProperty(USE_HUB_PROPERTY)); 87 | } 88 | 89 | /** 90 | * If running on CI, get the host name from environment variable HOSTNAME 91 | * 92 | * @return the host name 93 | */ 94 | private static String getDeploymentHostname() { 95 | return isUsingHub() ? System.getenv("HOSTNAME") : "localhost"; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/addons/applayout/integration/ViewIT.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout.integration; 21 | 22 | import static org.hamcrest.Matchers.empty; 23 | import static org.hamcrest.Matchers.is; 24 | import static org.hamcrest.Matchers.not; 25 | import static org.junit.Assert.assertThat; 26 | 27 | import com.vaadin.testbench.TestBenchElement; 28 | import java.util.List; 29 | import org.hamcrest.Description; 30 | import org.hamcrest.Matcher; 31 | import org.hamcrest.TypeSafeDiagnosingMatcher; 32 | import org.junit.Test; 33 | import org.openqa.selenium.By; 34 | import org.openqa.selenium.WebElement; 35 | 36 | public class ViewIT extends AbstractViewTest { 37 | 38 | private Matcher hasBeenUpgradedToCustomElement = 39 | new TypeSafeDiagnosingMatcher() { 40 | 41 | @Override 42 | public void describeTo(Description description) { 43 | description.appendText("a custom element"); 44 | } 45 | 46 | @Override 47 | protected boolean matchesSafely(TestBenchElement item, Description mismatchDescription) { 48 | String script = "let s=arguments[0].shadowRoot; return !!(s&&s.childElementCount)"; 49 | if (!item.getTagName().contains("-")) return true; 50 | if ((Boolean) item.getCommandExecutor().executeScript(script, item)) return true; 51 | else { 52 | mismatchDescription.appendText(item.getTagName() + " "); 53 | mismatchDescription.appendDescriptionOf(is(not(this))); 54 | return false; 55 | } 56 | } 57 | }; 58 | 59 | @Test 60 | public void componentWorks() { 61 | TestBenchElement header = $("app-header").first(); 62 | TestBenchElement drawer = $("app-drawer").first(); 63 | 64 | assertThat(header, hasBeenUpgradedToCustomElement); 65 | assertThat(drawer, hasBeenUpgradedToCustomElement); 66 | 67 | TestBenchElement toolbar = header.findElement(By.tagName("app-toolbar")); 68 | assertThat(toolbar, hasBeenUpgradedToCustomElement); 69 | 70 | TestBenchElement menu = toolbar.findElement(By.cssSelector("paper-icon-button[icon='menu']")); 71 | toolbar.findElement(By.cssSelector("img.applogo")); 72 | toolbar.findElement(By.cssSelector("div[main-title]")); 73 | toolbar.findElement(By.cssSelector("paper-icon-button[role='button'][icon='settings']")); 74 | 75 | assertThat(menu, hasBeenUpgradedToCustomElement); 76 | 77 | TestBenchElement listbox = drawer.findElement(By.cssSelector("paper-listbox")); 78 | assertThat(listbox, hasBeenUpgradedToCustomElement); 79 | 80 | List items = listbox.findElements(By.cssSelector("*")); 81 | assertThat(items, is(not(empty()))); 82 | 83 | for (WebElement item : items) { 84 | assertThat((TestBenchElement) item, hasBeenUpgradedToCustomElement); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/addons/applayout/menu/PaperButton.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout.menu; 21 | 22 | import com.vaadin.flow.component.Component; 23 | import com.vaadin.flow.component.Tag; 24 | import com.vaadin.flow.component.dependency.JsModule; 25 | import com.vaadin.flow.component.dependency.NpmPackage; 26 | import com.vaadin.flow.server.Command; 27 | 28 | /** 29 | * Component that renders a paper-button 30 | * 31 | * @author mlopez 32 | */ 33 | @SuppressWarnings("serial") 34 | @NpmPackage(value = "@polymer/paper-button", version = "3.0.1") 35 | @JsModule("@polymer/paper-button/paper-button.js") 36 | @Tag("paper-button") 37 | class PaperButton extends Component { 38 | 39 | public PaperButton(String label) { 40 | setLabel(label); 41 | } 42 | 43 | public PaperButton(String label, Command command) { 44 | this.setLabel(label); 45 | this.getElement().addEventListener("click", e -> command.execute()); 46 | } 47 | 48 | public void setLabel(String label) { 49 | this.getElement().setText(label); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/addons/applayout/menu/PaperCard.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout.menu; 21 | 22 | import com.flowingcode.addons.applayout.MenuItem; 23 | import com.vaadin.flow.component.Component; 24 | import com.vaadin.flow.component.HasComponents; 25 | import com.vaadin.flow.component.HasSize; 26 | import com.vaadin.flow.component.HasStyle; 27 | import com.vaadin.flow.component.Tag; 28 | import com.vaadin.flow.component.dependency.JsModule; 29 | import com.vaadin.flow.component.dependency.NpmPackage; 30 | import com.vaadin.flow.component.html.Div; 31 | import com.vaadin.flow.component.orderedlayout.ThemableLayout; 32 | import com.vaadin.flow.dom.Element; 33 | import java.util.ArrayList; 34 | import java.util.List; 35 | 36 | /** 37 | * Component that renders a paper-card 38 | * 39 | * @author mlopez 40 | */ 41 | @SuppressWarnings("serial") 42 | @NpmPackage(value = "@polymer/paper-card", version = "3.0.1") 43 | @JsModule("@polymer/paper-card/paper-card.js") 44 | @Tag("paper-card") 45 | public class PaperCard extends Component implements HasSize, HasStyle, ThemableLayout { 46 | 47 | private final Div cardContentDiv = new Div(); 48 | 49 | private final Div cardActionsDiv = new Div(); 50 | 51 | @SuppressWarnings("squid:S1604") 52 | private final HasComponents hasComponentsVersion = 53 | new HasComponents() { 54 | @Override 55 | public Element getElement() { 56 | return PaperCard.this.getElement(); 57 | } 58 | }; 59 | 60 | public PaperCard() { 61 | this(null); 62 | } 63 | 64 | public PaperCard(final Component cardContent, final MenuItem... cardActions) { 65 | cardContentDiv.setClassName("card-content"); 66 | cardActionsDiv.setClassName("card-actions"); 67 | 68 | hasComponentsVersion.add(cardContentDiv); 69 | 70 | if (cardContent != null) { 71 | setCardContent(cardContent); 72 | } 73 | 74 | setCardActions(cardActions); 75 | } 76 | 77 | public void setCardActions(final MenuItem... cardActions) { 78 | if (cardActions.length > 0) { 79 | final List buttons = new ArrayList<>(); 80 | for (final MenuItem menuItem : cardActions) { 81 | if (menuItem.getIcon() != null) { 82 | buttons.add(menuItem); 83 | } else { 84 | buttons.add(menuItem); 85 | } 86 | } 87 | final Div inner = new Div(); 88 | cardActionsDiv.add(inner); 89 | inner.addClassNames("horizontal", "justified"); 90 | buttons.forEach(inner::add); 91 | hasComponentsVersion.add(cardActionsDiv); 92 | } 93 | } 94 | 95 | public void setCardContent(final Component content) { 96 | cardContentDiv.removeAll(); 97 | cardContentDiv.add(content); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/addons/applayout/menu/PaperIconItem.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout.menu; 21 | 22 | import java.util.Optional; 23 | 24 | import com.vaadin.flow.component.Component; 25 | import com.vaadin.flow.component.HasComponents; 26 | import com.vaadin.flow.component.HasSize; 27 | import com.vaadin.flow.component.HasText; 28 | import com.vaadin.flow.component.Tag; 29 | import com.vaadin.flow.component.Text; 30 | import com.vaadin.flow.component.dependency.JsModule; 31 | import com.vaadin.flow.component.dependency.NpmPackage; 32 | import com.vaadin.flow.component.icon.Icon; 33 | import com.vaadin.flow.dom.Element; 34 | import com.vaadin.flow.server.Command; 35 | 36 | /** 37 | * Component that renders a paper-item 38 | * 39 | * @author mlopez 40 | */ 41 | @SuppressWarnings("serial") 42 | @NpmPackage(value = "@polymer/paper-item", version = "3.0.1") 43 | @JsModule("@polymer/paper-item/paper-icon-item.js") 44 | @Tag("paper-icon-item") 45 | class PaperIconItem extends Component implements HasComponents, HasText, HasSize { 46 | 47 | private com.vaadin.flow.component.icon.Icon icon; 48 | private Text text; 49 | 50 | public PaperIconItem(String title) { 51 | this.setText(""); 52 | this.text = new Text(title); 53 | add(text); 54 | } 55 | 56 | public void addCommand(Command command) { 57 | if (command != null) { 58 | this.getElement() 59 | .addEventListener( 60 | "click", 61 | e -> { 62 | command.execute(); 63 | ((Component) this) 64 | .getElement() 65 | .executeJs( 66 | "this.dispatchEvent(new CustomEvent('item-clicked', {bubbles: true}))"); 67 | }); 68 | } 69 | } 70 | 71 | public void setTitle(String title) { 72 | this.text.setText(title); 73 | } 74 | 75 | public void setIcon(String icon) { 76 | withIronIcon(icon != null) 77 | .ifPresent( 78 | e -> { 79 | e.removeAttribute("src"); 80 | e.setAttribute("icon", icon); 81 | }); 82 | } 83 | 84 | public void setImage(String image) { 85 | withIronIcon(image != null) 86 | .ifPresent( 87 | e -> { 88 | ; 89 | e.removeAttribute("icon"); 90 | e.setAttribute("src", image); 91 | }); 92 | } 93 | 94 | private Optional withIronIcon(boolean create) { 95 | if (create) { 96 | if (this.icon == null) { 97 | this.icon = new Icon("", ""); 98 | icon.getElement().setAttribute("slot", "item-icon"); 99 | add(icon); 100 | } 101 | return Optional.of(icon.getElement()); 102 | } else { 103 | if (this.icon != null) { 104 | remove(icon); 105 | this.icon = null; 106 | } 107 | return Optional.empty(); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/addons/applayout/menu/PaperToggle.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout.menu; 21 | 22 | import com.vaadin.flow.component.Component; 23 | import com.vaadin.flow.component.Tag; 24 | import com.vaadin.flow.component.dependency.JsModule; 25 | import com.vaadin.flow.component.dependency.NpmPackage; 26 | 27 | @NpmPackage(value = "@polymer/paper-toggle-button", version = "3.0.1") 28 | @JsModule("@polymer/paper-toggle-button/paper-toggle-button.js") 29 | @Tag("paper-toggle-button") 30 | public class PaperToggle extends Component {} 31 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/addons/applayout/test/SerializationTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.addons.applayout.test; 21 | 22 | import com.flowingcode.addons.applayout.AppLayout; 23 | import com.flowingcode.addons.applayout.MenuItem; 24 | import java.io.ByteArrayInputStream; 25 | import java.io.ByteArrayOutputStream; 26 | import java.io.IOException; 27 | import java.io.ObjectInputStream; 28 | import java.io.ObjectOutputStream; 29 | import org.junit.Test; 30 | 31 | public class SerializationTest { 32 | 33 | private void testSerializationOf(Object obj) throws IOException, ClassNotFoundException { 34 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 35 | try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { 36 | oos.writeObject(obj); 37 | } 38 | try (ObjectInputStream in = 39 | new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { 40 | obj.getClass().cast(in.readObject()); 41 | } 42 | } 43 | 44 | @Test 45 | public void testSerialization() throws ClassNotFoundException, IOException { 46 | AppLayout appLayout = new AppLayout(""); 47 | appLayout.setMenuItems(new MenuItem("Item", () -> {})); 48 | testSerializationOf(appLayout); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/vaadin/addons/DemoLayout.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.vaadin.addons; 21 | 22 | import com.vaadin.flow.component.html.Div; 23 | import com.vaadin.flow.router.RouterLayout; 24 | 25 | @SuppressWarnings("serial") 26 | public class DemoLayout extends Div implements RouterLayout { 27 | 28 | public DemoLayout() { 29 | setSizeFull(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/vaadin/addons/applayout/AppLayoutDemo.java: -------------------------------------------------------------------------------- 1 | package com.flowingcode.vaadin.addons.applayout; 2 | 3 | import com.flowingcode.vaadin.addons.demo.DemoSource; 4 | import com.vaadin.flow.component.html.Div; 5 | import com.vaadin.flow.component.html.IFrame; 6 | import com.vaadin.flow.router.PageTitle; 7 | import com.vaadin.flow.router.Route; 8 | 9 | @DemoSource("/src/test/java/com/flowingcode/addons/applayout/ApplayoutDemoView.java") 10 | @DemoSource("/frontend/styles/app-layout/demo-styles.css") 11 | @PageTitle("AppLayout Full Demo") 12 | @SuppressWarnings("serial") 13 | @Route(value = "applayout/applayout-demo", layout = AppLayoutDemoView.class) 14 | public class AppLayoutDemo extends Div { 15 | 16 | public AppLayoutDemo() { 17 | setClassName("wrap-iframe"); 18 | IFrame iframe = new IFrame("/applayout-full"); 19 | iframe.setClassName("frame"); 20 | iframe.setSizeFull(); 21 | iframe.getElement().setAttribute("frameBorder", "0"); 22 | add(iframe); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/vaadin/addons/applayout/AppLayoutDemoView.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Template Add-on 4 | * %% 5 | * Copyright (C) 2023 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.vaadin.addons.applayout; 21 | 22 | import com.flowingcode.vaadin.addons.DemoLayout; 23 | import com.flowingcode.vaadin.addons.GithubLink; 24 | import com.flowingcode.vaadin.addons.demo.TabbedDemo; 25 | import com.vaadin.flow.router.ParentLayout; 26 | import com.vaadin.flow.router.Route; 27 | 28 | @SuppressWarnings("serial") 29 | @ParentLayout(DemoLayout.class) 30 | @Route("applayout") 31 | @GithubLink("https://github.com/FlowingCode/AppLayoutAddon") 32 | public class AppLayoutDemoView extends TabbedDemo { 33 | 34 | public AppLayoutDemoView() { 35 | addDemo(AppLayoutDemo.class); 36 | addDemo(ExtendingAppLayoutDemo.class); 37 | 38 | setSizeFull(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/vaadin/addons/applayout/DemoView.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | package com.flowingcode.vaadin.addons.applayout; 21 | 22 | import com.vaadin.flow.component.orderedlayout.VerticalLayout; 23 | import com.vaadin.flow.router.BeforeEnterEvent; 24 | import com.vaadin.flow.router.BeforeEnterObserver; 25 | import com.vaadin.flow.router.Route; 26 | 27 | @SuppressWarnings("serial") 28 | @Route("") 29 | public class DemoView extends VerticalLayout implements BeforeEnterObserver { 30 | 31 | @Override 32 | public void beforeEnter(BeforeEnterEvent event) { 33 | event.forwardTo(AppLayoutDemoView.class); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/com/flowingcode/vaadin/addons/applayout/ExtendingAppLayoutDemo.java: -------------------------------------------------------------------------------- 1 | package com.flowingcode.vaadin.addons.applayout; 2 | 3 | import com.flowingcode.vaadin.addons.demo.DemoSource; 4 | import com.vaadin.flow.component.html.Div; 5 | import com.vaadin.flow.component.html.IFrame; 6 | import com.vaadin.flow.router.PageTitle; 7 | import com.vaadin.flow.router.Route; 8 | 9 | @DemoSource("/src/test/java/com/flowingcode/addons/applayout/CustomAppLayout.java") 10 | @DemoSource("/src/test/java/com/flowingcode/addons/applayout/SampleView.java") 11 | @PageTitle("Extending AppLayout Demo") 12 | @SuppressWarnings("serial") 13 | @Route(value = "applayout/extending-applayout-demo", layout = AppLayoutDemoView.class) 14 | public class ExtendingAppLayoutDemo extends Div { 15 | 16 | public ExtendingAppLayoutDemo() { 17 | setClassName("wrap-iframe"); 18 | IFrame iframe = new IFrame("/view"); 19 | iframe.setClassName("frame"); 20 | iframe.setSizeFull(); 21 | iframe.getElement().setAttribute("frameBorder", "0"); 22 | add(iframe); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/native-image/com.flowingcode.addons/app-layout/reflect-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name":"com.flowingcode.addons.applayout.ApplayoutDemoView" 4 | } 5 | ] -------------------------------------------------------------------------------- /src/test/resources/META-INF/native-image/com.flowingcode.addons/app-layout/resource-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources":{ 3 | "includes":[{ 4 | "pattern":"\\Qhamlet\\E" 5 | }], 6 | "bundles":[] 7 | }} -------------------------------------------------------------------------------- /src/test/resources/META-INF/resources/frontend/images/applogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlowingCode/AppLayoutAddon/ea9f4b2b704346bcd3468e9882c120314e265b25/src/test/resources/META-INF/resources/frontend/images/applogo.png -------------------------------------------------------------------------------- /src/test/resources/META-INF/resources/frontend/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlowingCode/AppLayoutAddon/ea9f4b2b704346bcd3468e9882c120314e265b25/src/test/resources/META-INF/resources/frontend/images/avatar.png -------------------------------------------------------------------------------- /src/test/resources/META-INF/resources/frontend/styles/app-layout/demo-styles.css: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * App Layout Addon 4 | * %% 5 | * Copyright (C) 2018 - 2022 Flowing Code 6 | * %% 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * #L% 19 | */ 20 | html, body { 21 | margin: 0; 22 | font-family: 'Roboto', 'Noto', sans-serif; 23 | -webkit-font-smoothing: antialiased; 24 | background: #f1f1f1; 25 | max-height: 368px; 26 | max-width: 100%; 27 | } 28 | 29 | sample-content { 30 | padding-top: 64px; 31 | } 32 | 33 | app-drawer { 34 | --app-drawer-scrim-background: rgba(0, 0, 100, 0.8); 35 | --app-drawer-content-container: { 36 | background-color: #F1F1F1; 37 | } 38 | z-index: 1001; 39 | } 40 | 41 | app-drawer paper-listbox { 42 | background-color:transparent; 43 | } 44 | 45 | img.applogo { 46 | margin-right: 8px; 47 | width: 48px; 48 | height: 48px; 49 | } 50 | 51 | .compact img.applogo { 52 | width: 22px; 53 | height: 22px; 54 | } 55 | 56 | .hamlet { 57 | user-select: none; 58 | } 59 | 60 | .hamlet paper-card { 61 | max-width: 600px; 62 | min-width: 200px; 63 | } 64 | 65 | .hamlet paper-card.claudius { 66 | background: rgb(220,248,198); 67 | } 68 | 69 | .hamlet paper-card h5 {margin-top: 0.25em} 70 | 71 | .hamlet paper-card[elevation="0"] { 72 | background: rgb(254,244,197); 73 | border-radius: 8px; 74 | min-width: 30%; 75 | height: 42px; 76 | } 77 | 78 | .hamlet paper-card[elevation="0"] .card-content { 79 | padding: 8px; 80 | text-align: center; 81 | } 82 | 83 | menu-item vcf-toggle-button { 84 | padding-bottom: 2px; 85 | } 86 | 87 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/resources/frontend/styles/shared-styles.html: -------------------------------------------------------------------------------- 1 | 20 | 21 | 22 | 23 | 81 | 82 | -------------------------------------------------------------------------------- /src/test/resources/hamlet: -------------------------------------------------------------------------------- 1 | $Act IV, Scene 3 2 | $Elsinore. A room in the Castle. 3 | 4 | [Enter King] 5 | 6 | Claudius. 7 | I have sent to seek him and to find the body. 8 | How dangerous is it that this man goes loose! 9 | Yet must not we put the strong law on him. 10 | He's loved of the distracted multitude, 11 | Who like not in their judgment, but their eyes. 12 | And where 'tis so, th' offender's scourge is weighed, 13 | But never the offense. To bear all smooth and even, 14 | This sudden sending him away must seem 15 | Deliberate pause. Diseases desperate grown 16 | By desperate appliance are relieved, 17 | Or not at all. 18 | 19 | [Enter Rosencrantz] 20 | 21 | Claudius. 22 | How now, what hath befall'n? 23 | 24 | Rosencrantz. 25 | Where the dead body is bestowed, my lord, 26 | We cannot get from him. 27 | 28 | Claudius. But where is he? 29 | 30 | Rosencrantz. Without, my lord; guarded, to know your pleasure. 31 | 32 | Claudius. Bring him before us. 33 | 34 | Rosencrantz. Ho, Guildenstern! Bring in my lord. 35 | 36 | [Enter Hamlet and Guildenstern, with Attendants] 37 | 38 | Claudius. Now, Hamlet, where's Polonius? 39 | 40 | Hamlet. At supper. 41 | 42 | Claudius. At supper? Where? 43 | 44 | Hamlet. Not where he eats, but where he is eaten. A certain 45 | convocation of politic worms are e'en at him. Your worm is your 46 | only emperor for diet. We fat all creatures else to fat us, and 47 | we fat ourselves for maggots. Your fat king and your lean beggar 48 | is but variable service- two dishes, but to one table. That's the 49 | end. 50 | 51 | Claudius. Alas, alas! 52 | 53 | Hamlet. A man may fish with the worm that hath eat of a king, and eat 54 | of the fish that hath fed of that worm. 55 | 56 | Claudius. What dost thou mean by this? 57 | 58 | Hamlet. Nothing but to show you how a king may go a progress through 59 | the guts of a beggar. 60 | 61 | Claudius. Where is Polonius? 62 | 63 | Hamlet. In heaven. Send thither to see. If your messenger find him not 64 | there, seek him i' th' other place yourself. But indeed, if you 65 | find him not within this month, you shall nose him as you go up 66 | the stair, into the lobby. 67 | 68 | Claudius. Go seek him there. 69 | 70 | Hamlet. He will stay till you come. 71 | 72 | [Exeunt Attendants] 73 | 74 | Claudius. Hamlet, this deed, for thine especial safety,- 75 | Which we do tender as we dearly grieve 76 | For that which thou hast done,- must send thee hence 77 | With fiery quickness. Therefore prepare thyself. 78 | The bark is ready and the wind at help, 79 | Th' associates tend, and everything is bent 80 | For England. 81 | 82 | Hamlet. For England? 83 | 84 | Claudius. Ay, Hamlet. 85 | 86 | Hamlet. Good. 87 | 88 | Claudius. So is it, if thou knew'st our purposes. 89 | 90 | Hamlet. I see a cherub that sees them. But come, for England! 91 | Farewell, dear mother. 92 | 93 | Claudius. Thy loving father, Hamlet. 94 | 95 | Hamlet. My mother! Father and mother is man and wife; man and wife is 96 | one flesh; and so, my mother. Come, for England! 97 | 98 | [Hamlet exits] 99 | 100 | Claudius. Follow him at foot; tempt him with speed aboard. 101 | Delay it not; I'll have him hence to-night. 102 | Away! for everything is seal'd and done 103 | That else leans on th' affair. Pray you make haste. 104 | 105 | [Exeunt Rosencrantz and Guildenstern] 106 | 107 | Claudius. And, England, if my love thou hold'st at aught,- 108 | As my great power thereof may give thee sense, 109 | Since yet thy cicatrice looks raw and red 110 | After the Danish sword, and thy free awe 111 | Pays homage to us,- thou mayst not coldly set 112 | Our sovereign process, which imports at full, 113 | By letters congruing to that effect, 114 | The present death of Hamlet. Do it, England; 115 | For like the hectic in my blood he rages, 116 | And thou must cure me. Till I know 'tis done, 117 | Howe'er my haps, my joys were ne'er begun. 118 | 119 | [Claudius exits] -------------------------------------------------------------------------------- /src/test/resources/vaadin-featureflags.properties: -------------------------------------------------------------------------------- 1 | # Use Webpack for front-end builds (Deprecated) 2 | com.vaadin.experimental.webpackForFrontendBuild=true 3 | -------------------------------------------------------------------------------- /src/test/webjar-debug/META-INF/resources/README.txt: -------------------------------------------------------------------------------- 1 | This folder needs to exist for Jetty to start 2 | -------------------------------------------------------------------------------- /src/test/webjar-debug/README.md: -------------------------------------------------------------------------------- 1 | This folder exists so you can unzip webjars here for debugging purposes. 2 | The webjar files should end up inside META-INF/resources/webjar/... to be loaded instead of the files inside the actual webjars. 3 | For instance `paper-slider.html` should be in `/src/test/webjar-debug/META-INNF/resources/webjars/paper-slider/paper-slider.html` 4 | --------------------------------------------------------------------------------