Showing preview only (711K chars total). Download the full file or copy to clipboard to get everything.
Repository: vaadin/vaadin-router
Branch: main
Commit: 74684792d0bd
Files: 222
Total size: 655.0 KB
Directory structure:
gitextract_suxs7xzw/
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── docs.yml
│ └── validation.yml
├── .gitignore
├── .prettierrc.json
├── .run/
│ ├── All tests.run.xml
│ └── Template Karma.run.xml
├── .stylelintrc.json
├── LICENSE
├── README.md
├── analysis.json
├── demo/
│ ├── .eslintrc.json
│ ├── @debug/
│ │ ├── index.html
│ │ └── index.ts
│ ├── @helpers/
│ │ ├── common.css
│ │ ├── common.ts
│ │ ├── iframe.script.ts
│ │ ├── nested-styles.css
│ │ ├── page.css
│ │ ├── shared-styles.css
│ │ ├── theme-controller.ts
│ │ ├── vaadin-demo-code-snippet-file.css
│ │ ├── vaadin-demo-code-snippet-file.ts
│ │ ├── vaadin-demo-code-snippet.css
│ │ ├── vaadin-demo-code-snippet.ts
│ │ ├── vaadin-demo-layout.css
│ │ ├── vaadin-demo-layout.ts
│ │ ├── vaadin-presentation-addressbar.css
│ │ ├── vaadin-presentation-addressbar.ts
│ │ ├── vaadin-presentation.css
│ │ ├── vaadin-presentation.ts
│ │ ├── x-breadcrumbs.ts
│ │ ├── x-home-view.ts
│ │ ├── x-image-view.css
│ │ ├── x-image-view.ts
│ │ ├── x-knowledge-base.ts
│ │ ├── x-login-view.ts
│ │ ├── x-not-found-view.ts
│ │ ├── x-profile-view.ts
│ │ ├── x-user-list.ts
│ │ ├── x-user-not-found-view.ts
│ │ ├── x-user-numeric-view.ts
│ │ └── x-user-profile.ts
│ ├── animated-transitions/
│ │ ├── d1/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── styles.css
│ │ ├── d2/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ ├── styles.css
│ │ │ └── x-wrapper.ts
│ │ ├── index.html
│ │ └── index.ts
│ ├── code-splitting/
│ │ ├── d1/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── user.bundle.ts
│ │ ├── d2/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── user-routes.ts
│ │ ├── index.html
│ │ └── index.ts
│ ├── getting-started/
│ │ ├── d1/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── index.html
│ │ ├── index.ts
│ │ └── snippets/
│ │ ├── s1.html
│ │ ├── s2.ts
│ │ └── s4.ts
│ ├── index.html
│ ├── index.ts
│ ├── lifecycle-callback/
│ │ ├── d1/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── x-countdown.ts
│ │ ├── d2/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── x-friend.ts
│ │ ├── d3/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ ├── x-user-deleted.ts
│ │ │ └── x-user-manage.ts
│ │ ├── d4/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ ├── x-autosave-view.ts
│ │ │ └── x-main-page.ts
│ │ ├── d5/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── d6/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── index.html
│ │ ├── index.ts
│ │ └── snippets/
│ │ ├── my-view-with-after-enter.ts
│ │ ├── my-view-with-after-leave.ts
│ │ ├── my-view-with-before-enter.ts
│ │ └── my-view-with-before-leave.ts
│ ├── navigation-trigger/
│ │ ├── d1/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── d2/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── d3/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── d4/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── index.html
│ │ └── index.ts
│ ├── redirect/
│ │ ├── d1/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── d2/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── x-admin-view.ts
│ │ ├── d3/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── index.html
│ │ ├── index.ts
│ │ └── snippets/
│ │ ├── s1.ts
│ │ ├── s2.ts
│ │ └── s3.ts
│ ├── route-actions/
│ │ ├── d1/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── d2/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── d3/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── d4/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── d5/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── index.html
│ │ └── index.ts
│ ├── route-parameters/
│ │ ├── d1/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── d2/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── x-project-view.ts
│ │ ├── d3/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── d4/
│ │ │ ├── iframe.html
│ │ │ └── script.ts
│ │ ├── d5/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── x-page-number-view.ts
│ │ ├── d6/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── x-hash-view.ts
│ │ ├── index.html
│ │ └── index.ts
│ ├── tsconfig.json
│ ├── types.t.ts
│ ├── url-generation/
│ │ ├── d1/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── x-main-layout.ts
│ │ ├── d2/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── x-main-layout.ts
│ │ ├── d3/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── x-user-layout-d3.ts
│ │ ├── d4/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── x-user-layout-d4.ts
│ │ ├── d5/
│ │ │ ├── iframe.html
│ │ │ ├── script.ts
│ │ │ └── x-pages-menu.ts
│ │ ├── index.html
│ │ └── index.ts
│ └── vite.config.ts
├── index.html
├── karma.config.cjs
├── package.json
├── polymer.json
├── scripts/
│ ├── build.ts
│ ├── codeSnippet.ts
│ ├── constructCss.ts
│ ├── copy-dts.ts
│ ├── loadRegisterJs.ts
│ ├── register.js
│ ├── resolveHTMLImports.ts
│ └── types.d.ts
├── src/
│ ├── index.ts
│ ├── mod.t.ts
│ ├── resolver/
│ │ ├── LICENSE.txt
│ │ ├── generateUrls.ts
│ │ ├── matchPath.ts
│ │ ├── matchRoute.ts
│ │ ├── resolveRoute.ts
│ │ ├── resolver.ts
│ │ ├── types.t.ts
│ │ └── utils.ts
│ ├── router-config.ts
│ ├── router-meta.ts
│ ├── router.ts
│ ├── transitions/
│ │ └── animate.ts
│ ├── triggers/
│ │ ├── click.ts
│ │ ├── navigation.ts
│ │ └── popstate.ts
│ ├── types.t.ts
│ ├── utils.ts
│ └── v1-compat.t.ts
├── test/
│ ├── resolver/
│ │ ├── LICENSE.txt
│ │ ├── generateUrls.spec.ts
│ │ ├── matchPath.spec.ts
│ │ ├── matchRoute.spec.ts
│ │ └── resolver.spec.ts
│ ├── router/
│ │ ├── dynamic-redirect.spec.ts
│ │ ├── lifecycle-events.spec.ts
│ │ ├── parent-layout.spec.ts
│ │ ├── router.spec.ts
│ │ ├── test-utils.ts
│ │ └── url-for.spec.ts
│ ├── setup.ts
│ ├── transitions/
│ │ └── animate.spec.ts
│ ├── triggers/
│ │ ├── click.spec.ts
│ │ ├── popstate.spec.ts
│ │ └── setNavigationTriggers.spec.ts
│ └── typescript/
│ └── compile_fixture.ts
├── tsconfig.build.json
├── tsconfig.json
├── tsdoc.json
├── typedoc.json
├── vite.config.ts
└── wct.conf.cjs
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{java,xml}]
indent_size = 4
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
================================================
FILE: .eslintignore
================================================
src/documentation/*.js
================================================
FILE: .eslintrc.json
================================================
{
"extends": [
"vaadin/typescript-requiring-type-checking",
"vaadin/imports-typescript",
"vaadin/lit",
"vaadin/prettier",
"vaadin/testing"
],
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": ["tsdoc"],
"rules": {
"@typescript-eslint/no-restricted-types": "off",
"@typescript-eslint/no-invalid-void-type": "off",
"@typescript-eslint/no-useless-template-literals": "off",
"import/no-unassigned-import": "off",
"max-params": "off",
"sort-keys": "off",
"tsdoc/syntax": "error",
"import/prefer-default-export": "off"
},
"ignorePatterns": ["*.cjs"]
}
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: 'daily'
versioning-strategy: increase
================================================
FILE: .github/workflows/docs.yml
================================================
name: Publish Docs
on:
push:
branches: ['main']
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: 'pages'
cancel-in-progress: false
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Use NodeJS LTS
uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install
run: npm ci
- name: Build Docs
run: npm run docs
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: './.docs'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
================================================
FILE: .github/workflows/validation.yml
================================================
name: Validation
on:
push:
branches:
- 'master'
pull_request:
permissions:
contents: read
jobs:
test:
name: Validation
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout Project Code
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 'lts/*'
- name: Install dependencies and build
run: npm ci
- name: Build TypeScript
run: npm run build
- name: Check TypeScript
run: npm run typecheck
- name: Test
run: npm test
================================================
FILE: .gitignore
================================================
bower_components
node_modules
build
dist
coverage
.idea
.vscode
.docs
================================================
FILE: .prettierrc.json
================================================
{
"bracketSpacing": true,
"printWidth": 120,
"trailingComma": "all",
"tabWidth": 2,
"singleQuote": true
}
================================================
FILE: .run/All tests.run.xml
================================================
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests" type="JavaScriptTestRunnerKarma">
<config-file value="$PROJECT_DIR$/karma.config.cjs" />
<karma-package-dir value="$PROJECT_DIR$/node_modules/karma" />
<working-directory value="$PROJECT_DIR$" />
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>
================================================
FILE: .run/Template Karma.run.xml
================================================
<component name="ProjectRunConfigurationManager">
<configuration default="true" type="JavaScriptTestRunnerKarma">
<config-file value="$PROJECT_DIR$/karma.config.cjs" />
<karma-package-dir value="$PROJECT_DIR$/node_modules/karma" />
<working-directory value="$PROJECT_DIR$" />
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>
================================================
FILE: .stylelintrc.json
================================================
{
"rules": {
"at-rule-name-case": "lower",
"at-rule-name-space-after": "always-single-line",
"at-rule-semicolon-newline-after": "always",
"block-closing-brace-empty-line-before": "never",
"block-closing-brace-newline-after": "always",
"block-closing-brace-newline-before": "always-multi-line",
"block-closing-brace-space-before": "always-single-line",
"block-no-empty": true,
"block-opening-brace-newline-after": "always-multi-line",
"block-opening-brace-space-after": "always-single-line",
"block-opening-brace-space-before": "always",
"color-hex-length": "short",
"color-no-invalid-hex": true,
"comment-no-empty": true,
"comment-whitespace-inside": "always",
"custom-property-empty-line-before": "never",
"declaration-bang-space-after": "never",
"declaration-bang-space-before": "always",
"declaration-block-no-duplicate-properties": [ true, {
"ignore": ["consecutive-duplicates-with-different-values"]
} ],
"declaration-block-no-redundant-longhand-properties": true,
"declaration-block-no-shorthand-property-overrides": true,
"declaration-block-semicolon-newline-after": "always-multi-line",
"declaration-block-semicolon-space-after": "always-single-line",
"declaration-block-semicolon-space-before": "never",
"declaration-block-single-line-max-declarations": 1,
"declaration-block-trailing-semicolon": "always",
"declaration-colon-newline-after": "always-multi-line",
"declaration-colon-space-after": "always-single-line",
"declaration-colon-space-before": "never",
"font-family-no-duplicate-names": true,
"function-calc-no-unspaced-operator": true,
"function-comma-newline-after": "always-multi-line",
"function-comma-space-after": "always-single-line",
"function-comma-space-before": "never",
"function-linear-gradient-no-nonstandard-direction": true,
"function-max-empty-lines": 0,
"function-name-case": "lower",
"function-parentheses-newline-inside": "always-multi-line",
"function-parentheses-space-inside": "never-single-line",
"function-whitespace-after": "always",
"indentation": null,
"keyframe-declaration-no-important": true,
"length-zero-no-unit": true,
"max-empty-lines": 1,
"media-feature-colon-space-after": "always",
"media-feature-colon-space-before": "never",
"media-feature-name-case": "lower",
"media-feature-name-no-unknown": true,
"media-feature-parentheses-space-inside": "never",
"media-feature-range-operator-space-after": "always",
"media-feature-range-operator-space-before": "always",
"media-query-list-comma-newline-after": "always-multi-line",
"media-query-list-comma-space-after": "always-single-line",
"media-query-list-comma-space-before": "never",
"no-eol-whitespace": true,
"no-invalid-double-slash-comments": true,
"number-no-trailing-zeros": true,
"property-case": "lower",
"property-no-unknown": true,
"rule-empty-line-before": [ "always-multi-line", {
"except": ["first-nested"],
"ignore": ["after-comment"]
} ],
"selector-attribute-brackets-space-inside": "never",
"selector-attribute-operator-space-after": "never",
"selector-attribute-operator-space-before": "never",
"selector-combinator-space-after": "always",
"selector-combinator-space-before": "always",
"selector-descendant-combinator-no-non-space": true,
"selector-list-comma-newline-after": "always",
"selector-list-comma-space-before": "never",
"selector-max-empty-lines": 0,
"selector-pseudo-class-case": "lower",
"selector-pseudo-class-no-unknown": true,
"selector-pseudo-class-parentheses-space-inside": "never",
"selector-pseudo-element-case": "lower",
"selector-pseudo-element-colon-notation": "double",
"selector-pseudo-element-no-unknown": true,
"selector-type-case": "lower",
"shorthand-property-no-redundant-values": true,
"string-no-newline": true,
"unit-case": "lower",
"unit-no-unknown": true,
"value-list-comma-newline-after": "always-multi-line",
"value-list-comma-space-after": "always-single-line",
"value-list-comma-space-before": "never",
"value-list-max-empty-lines": 0
}
}
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2017 Vaadin Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
[](https://www.npmjs.com/package/@vaadin/router)
[](https://bundlephobia.com/result?p=@vaadin/router)
# Vaadin Router is deprecated
This library is no longer actively maintained. Vaadin uses React Router as a primary client-side routing tool.
Consider [`@lit-labs/router`](https://www.npmjs.com/package/@lit-labs/router) as a more lightweight and modern alternative. Also, as the [URLPattern](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) API is universally available in modern browsers nowadays, we recommend using that for building customized client-side routing.
---
# Vaadin Router
[Live demo](https://vaadin.github.io/router/index.html)
| [API documentation](https://vaadin.github.io/router/API/index.html)
Vaadin Router is a small and powerful client-side router JS library. It uses the widely adopted express.js syntax for routes (`/users/:id`) to map URLs to Web Component views. All features one might expect from a modern router are supported: async route resolution, animated transitions, navigation guards, redirects, and more. It is framework-agnostic and works equally well with all Web Components regardless of how they are created (Polymer / SkateJS / Stencil / Angular / Vue / etc).
Vaadin Router is a good fit for developers that do not want to go all-in with one framework, and prefer to have freedom in picking the components that work best for their specific needs.
```
npm install --save @vaadin/router
```
```javascript
import {Router} from '@vaadin/router';
const router = new Router(document.getElementById('outlet'));
router.setRoutes([
{path: '/', component: 'x-home-view'},
{path: '/users', component: 'x-user-list'}
]);
```
## Browser support
A specific version of Vaadin Router supports the same browsers as the Vaadin platform major version which includes that version of Vaadin Router.
See [Vaadin platform release notes](https://github.com/vaadin/platform/releases) for details on included Vaadin Router version and supported technologies.
The Supported Technologies section is typically listed in the release notes of the first publicly available release of a Vaadin platform major version
(for example [Vaadin 18.0.1](https://github.com/vaadin/platform/releases/tag/18.0.1) since 18.0.0 was skipped).
### Desktop browsers
Evergreen versions of the following browsers
- Chrome, Firefox, Firefox ESR, Safari and Edge (Chromium)
### Mobile browsers
Built-in browsers in the following mobile operating systems:
- Safari starting from iOS 13 (Safari 13 or newer)
- Google Chrome evergreen on Android (requiring Android 4.4 or newer)
### Sauce Labs test status
[](https://saucelabs.com/u/vaadin-router)
### Big Thanks
Cross-browser Testing Platform and Open Source <3 Provided by [Sauce Labs](https://saucelabs.com).
## Running demos and tests in the browser
1. Fork the `vaadin/router` GitHub repository and clone it locally.
1. Make sure you have [npm](https://www.npmjs.com/) installed.
1. In the router directory, run `npm install` to install dependencies.
1. Run `npm run build` to build the library.
1. Run `npm start`, and open [http://localhost:4173](http://127.0.0.1:8000/components/vaadin-router) in your browser to see the component live demos and API documentation.
## Running tests from the command line
1. In the router directory, run `npm test`
## Following the coding style
We are using [ESLint](http://eslint.org/) for linting JavaScript code. You can check if your code is following our standards by running `npm run lint`, which will automatically lint all `.js` files as well as JavaScript snippets inside `.html` files.
## Contributing
- Make sure your code is compliant with our code linters: `npm run lint`
- Check that tests are passing: `npm test`
- [Submit a pull request](https://www.digitalocean.com/community/tutorials/how-to-create-a-pull-request-on-github) with detailed title and description
- Wait for response from one of Vaadin components team members
## License
Apache License 2.0
Vaadin collects development time usage statistics to improve this product. For details and to opt-out, see https://github.com/vaadin/vaadin-usage-statistics.
================================================
FILE: analysis.json
================================================
{
"schema_version": "1.0.0",
"namespaces": [
{
"name": "Router",
"description": "",
"summary": "",
"sourceRange": {
"file": "src/documentation/namespace.js",
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 18
}
},
"classes": [
{
"description": "This is a type declaration. It exists for build-time type checking and\ndocumentation purposes. It should not be used in any source code, and it\ncertainly does not exist at the run time.\n\n`Location` describes the state of a router at a given point in time. It is\navailable for your application code in several ways:\n - as the `router.location` property\n - as the `location` property set by Vaadin Router on every view Web Component\n - as the `location` argument passed by Vaadin Router into view Web Component\n lifecycle callbacks\n - as the `event.detail.location` of the global Vaadin Router events",
"summary": "Type declaration for the `router.location` property.",
"path": "src/documentation/location.js",
"properties": [
{
"name": "baseUrl",
"type": "string",
"description": "The base URL used in the router. See [the `baseUrl` property\n](#/classes/Router#property-baseUrl) in the Router.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 25,
"column": 4
},
"end": {
"line": 25,
"column": 17
}
},
"metadata": {}
},
{
"name": "pathname",
"type": "!string",
"description": "The pathname as entered in the browser address bar\n(e.g. `/users/42/messages/12/edit`). It always starts with a `/` (slash).",
"privacy": "public",
"sourceRange": {
"start": {
"line": 34,
"column": 4
},
"end": {
"line": 34,
"column": 18
}
},
"metadata": {}
},
{
"name": "search",
"type": "!string",
"description": "The query string portion of the current url.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 42,
"column": 4
},
"end": {
"line": 42,
"column": 16
}
},
"metadata": {}
},
{
"name": "searchParams",
"type": "URLSearchParams",
"description": "The query search parameters of the current url.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 50,
"column": 4
},
"end": {
"line": 50,
"column": 22
}
},
"metadata": {}
},
{
"name": "hash",
"type": "!string",
"description": "The fragment identifier (including hash character) for the current page.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 58,
"column": 4
},
"end": {
"line": 58,
"column": 14
}
},
"metadata": {}
},
{
"name": "redirectFrom",
"type": "?string",
"description": "(optional) The original pathname string in case if this location is a\nresult of a redirect.\n\nE.g. with the routes config as below a navigation to `/u/12` produces a\nlocation with `pathname: '/user/12'` and `redirectFrom: '/u/12'`.\n\n```javascript\nsetRoutes([\n {path: '/u/:id', redirect: '/user/:id'},\n {path: '/user/:id', component: 'x-user-view'},\n]);\n```\n\nSee the **Redirects** section of the\n[live demos](#/classes/Router/demos/demo/index.html) for more\ndetails.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 81,
"column": 4
},
"end": {
"line": 81,
"column": 22
}
},
"metadata": {}
},
{
"name": "route",
"type": "?Route",
"description": "(optional) The route object associated with `this` Web Component instance.\n\nThis property is defined in the `location` objects that are passed as\nparameters into Web Component lifecycle callbacks, and the `location`\nproperty set by Vaadin Router on the Web Components.\n\nThis property is undefined in the `location` objects that are available\nas `router.location`, and in the `location` that is included into the\nglobal router event details.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 97,
"column": 4
},
"end": {
"line": 97,
"column": 15
}
},
"metadata": {}
},
{
"name": "routes",
"type": "!Array.<!Route>",
"description": "A list of route objects that match the current pathname. This list has\none element for each route that defines a parent layout, and then the\nelement for the route that defines the view.\n\nSee the **Getting Started** section of the\n[live demos](#/classes/Router/demos/demo/index.html) for more\ndetails on child routes and nested layouts.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 111,
"column": 4
},
"end": {
"line": 111,
"column": 16
}
},
"metadata": {}
},
{
"name": "params",
"type": "!IndexedParams",
"description": "A bag of key-value pairs with parameters for the current location. Named\nparameters are available by name, unnamed ones - by index (e.g. for the\n`/users/:id` route the `:id` parameter is available as `location.params.id`).\n\nSee the **Route Parameters** section of the\n[live demos](#/classes/Router/demos/demo/index.html) for more\ndetails.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 125,
"column": 4
},
"end": {
"line": 125,
"column": 16
}
},
"metadata": {}
}
],
"methods": [
{
"name": "getUrl",
"description": "Returns a URL corresponding to the route path and the parameters of this\nlocation. When the parameters object is given in the arguments,\nthe argument parameters override the location ones.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 140,
"column": 2
},
"end": {
"line": 140,
"column": 20
}
},
"metadata": {},
"params": [
{
"name": "params",
"type": "Params=",
"description": "optional object with parameters to override.\nNamed parameters are passed by name (`params[name] = value`), unnamed\nparameters are passed by index (`params[index] = value`)."
}
],
"return": {
"type": "string"
}
}
],
"staticMethods": [],
"demos": [],
"metadata": {},
"sourceRange": {
"start": {
"line": 16,
"column": 7
},
"end": {
"line": 141,
"column": 1
}
},
"privacy": "public",
"name": "Router.Location"
}
]
}
],
"classes": [
{
"description": "",
"summary": "",
"path": "dist/vaadin-router.js",
"properties": [],
"methods": [],
"staticMethods": [],
"demos": [],
"metadata": {},
"sourceRange": {
"start": {
"line": 89,
"column": 28
},
"end": {
"line": 89,
"column": 51
}
},
"privacy": "public",
"name": "NotFoundResult"
},
{
"description": "",
"summary": "",
"path": "dist/vaadin-router.js",
"properties": [
{
"name": "__effectiveBaseUrl",
"type": "?",
"description": "If the baseUrl property is set, transforms the baseUrl and returns the full\nactual `base` string for using in the `new URL(path, base);` and for\nprepernding the paths with. The returned base ends with a trailing slash.\n\nOtherwise, returns empty string.\n ",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1038,
"column": 2
},
"end": {
"line": 1045,
"column": 3
}
},
"metadata": {}
}
],
"methods": [
{
"name": "getRoutes",
"description": "Returns the current list of routes (as a shallow copy). Adding / removing\nroutes to / from the returned array does not affect the routing config,\nbut modifying the route objects does.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 895,
"column": 2
},
"end": {
"line": 897,
"column": 3
}
},
"metadata": {},
"params": [],
"return": {
"type": "!Array.<!Router.Route>"
}
},
{
"name": "setRoutes",
"description": "Sets the routing config (replacing the existing one).",
"privacy": "public",
"sourceRange": {
"start": {
"line": 905,
"column": 2
},
"end": {
"line": 909,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "routes",
"type": "(!Array.<!Router.Route> | !Router.Route)",
"description": "a single route or an array of those\n (the array is shallow copied)"
}
],
"return": {
"type": "void"
}
},
{
"name": "addRoutes",
"description": "Appends one or several routes to the routing config and returns the\neffective routing config after the operation.",
"privacy": "protected",
"sourceRange": {
"start": {
"line": 920,
"column": 2
},
"end": {
"line": 924,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "routes",
"type": "(!Array.<!Router.Route> | !Router.Route)",
"description": "a single route or an array of those\n (the array is shallow copied)"
}
],
"return": {
"type": "!Array.<!Router.Route>"
}
},
{
"name": "removeRoutes",
"description": "Removes all existing routes from the routing config.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 929,
"column": 2
},
"end": {
"line": 931,
"column": 3
}
},
"metadata": {},
"params": [],
"return": {
"type": "void"
}
},
{
"name": "resolve",
"description": "Asynchronously resolves the given pathname, i.e. finds all routes matching\nthe pathname and tries resolving them one after another in the order they\nare listed in the routes config until the first non-null result.\n\nReturns a promise that is fulfilled with the return value of an object that consists of the first\nroute handler result that returns something other than `null` or `undefined` and context used to get this result.\n\nIf no route handlers return a non-null result, or if no route matches the\ngiven pathname the returned promise is rejected with a 'page not found'\n`Error`.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 950,
"column": 2
},
"end": {
"line": 1022,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "pathnameOrContext",
"type": "(!string | !{pathname: !string})",
"description": "the pathname to\n resolve or a context object with a `pathname` property and other\n properties to pass to the route resolver functions."
}
],
"return": {
"type": "!Promise.<any>"
}
},
{
"name": "__normalizePathname",
"description": "If the baseUrl is set, matches the pathname with the router’s baseUrl,\nand returns the local pathname with the baseUrl stripped out.\n\nIf the pathname does not match the baseUrl, returns undefined.\n\nIf the `baseUrl` is not set, returns the unmodified pathname argument.",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1055,
"column": 2
},
"end": {
"line": 1070,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "pathname"
}
]
}
],
"staticMethods": [
{
"name": "__createUrl",
"description": "URL constructor polyfill hook. Creates and returns an URL instance.",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1027,
"column": 2
},
"end": {
"line": 1029,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "args",
"rest": true
}
]
}
],
"demos": [],
"metadata": {},
"sourceRange": {
"start": {
"line": 874,
"column": 0
},
"end": {
"line": 1071,
"column": 1
}
},
"privacy": "public",
"name": "Resolver"
},
{
"description": "A simple client-side router for single-page applications. It uses\nexpress-style middleware and has a first-class support for Web Components and\nlazy-loading. Works great in Polymer and non-Polymer apps.\n\nUse `new Router(outlet, options)` to create a new Router instance.\n\n* The `outlet` parameter is a reference to the DOM node to render\n the content into.\n\n* The `options` parameter is an optional object with options. The following\n keys are supported:\n * `baseUrl` — the initial value for [\n the `baseUrl` property\n ](#/classes/Router#property-baseUrl)\n\nThe Router instance is automatically subscribed to navigation events\non `window`.\n\nSee [Live Examples](#/classes/Router/demos/demo/index.html) for the detailed usage demo and code snippets.\n\nSee also detailed API docs for the following methods, for the advanced usage:\n\n* [setOutlet](#/classes/Router#method-setOutlet) – should be used to configure the outlet.\n* [setTriggers](#/classes/Router#method-setTriggers) – should be used to configure the navigation events.\n* [setRoutes](#/classes/Router#method-setRoutes) – should be used to configure the routes.\n\nOnly `setRoutes` has to be called manually, others are automatically invoked when creating a new instance.",
"summary": "JavaScript class that renders different DOM content depending on\n a given path. It can re-render when triggered or automatically on\n 'popstate' and / or 'click' events.",
"path": "dist/vaadin-router.js",
"properties": [
{
"name": "__effectiveBaseUrl",
"type": "?",
"description": "If the baseUrl property is set, transforms the baseUrl and returns the full\nactual `base` string for using in the `new URL(path, base);` and for\nprepernding the paths with. The returned base ends with a trailing slash.\n\nOtherwise, returns empty string.\n ",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1038,
"column": 2
},
"end": {
"line": 1045,
"column": 3
}
},
"metadata": {},
"inheritedFrom": "Resolver"
},
{
"name": "baseUrl",
"type": "string",
"description": "The base URL for all routes in the router instance. By default,\nif the base element exists in the `<head>`, vaadin-router\ntakes the `<base href>` attribute value, resolved against the current\n`document.URL`.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 1410,
"column": 4
},
"end": {
"line": 1410,
"column": 17
}
},
"metadata": {}
},
{
"name": "ready",
"type": "!Promise.<!RouterLocation>",
"description": "A promise that is settled after the current render cycle completes. If\nthere is no render cycle in progress the promise is immediately settled\nwith the last render cycle result.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 1420,
"column": 4
},
"end": {
"line": 1420,
"column": 15
}
},
"metadata": {}
},
{
"name": "location",
"type": "!RouterLocation",
"description": "Contains read-only information about the current router location:\npathname, active routes, parameters. See the\n[Location type declaration](#/classes/RouterLocation)\nfor more details.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 1432,
"column": 4
},
"end": {
"line": 1432,
"column": 18
}
},
"metadata": {}
}
],
"methods": [
{
"name": "getRoutes",
"description": "Returns the current list of routes (as a shallow copy). Adding / removing\nroutes to / from the returned array does not affect the routing config,\nbut modifying the route objects does.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 895,
"column": 2
},
"end": {
"line": 897,
"column": 3
}
},
"metadata": {},
"params": [],
"return": {
"type": "!Array.<!Router.Route>"
},
"inheritedFrom": "Resolver"
},
{
"name": "setRoutes",
"description": "Sets the routing config (replacing the existing one) and triggers a\nnavigation event so that the router outlet is refreshed according to the\ncurrent `window.location` and the new routing config.\n\nEach route object may have the following properties, listed here in the processing order:\n* `path` – the route path (relative to the parent route if any) in the\n[express.js syntax](https://expressjs.com/en/guide/routing.html#route-paths\").\n\n* `children` – an array of nested routes or a function that provides this\narray at the render time. The function can be synchronous or asynchronous:\nin the latter case the render is delayed until the returned promise is\nresolved. The `children` function is executed every time when this route is\nbeing rendered. This allows for dynamic route structures (e.g. backend-defined),\nbut it might have a performance impact as well. In order to avoid calling\nthe function on subsequent renders, you can override the `children` property\nof the route object and save the calculated array there\n(via `context.route.children = [ route1, route2, ...];`).\nParent routes are fully resolved before resolving the children. Children\n'path' values are relative to the parent ones.\n\n* `action` – the action that is executed before the route is resolved.\nThe value for this property should be a function, accepting `context`\nand `commands` parameters described below. If present, this function is\nalways invoked first, disregarding of the other properties' presence.\nThe action can return a result directly or within a `Promise`, which\nresolves to the result. If the action result is an `HTMLElement` instance,\na `commands.component(name)` result, a `commands.redirect(path)` result,\nor a `context.next()` result, the current route resolution is finished,\nand other route config properties are ignored.\nSee also **Route Actions** section in [Live Examples](#/classes/Router/demos/demo/index.html).\n\n* `redirect` – other route's path to redirect to. Passes all route parameters to the redirect target.\nThe target route should also be defined.\nSee also **Redirects** section in [Live Examples](#/classes/Router/demos/demo/index.html).\n\n* `component` – the tag name of the Web Component to resolve the route to.\nThe property is ignored when either an `action` returns the result or `redirect` property is present.\nIf route contains the `component` property (or an action that return a component)\nand its child route also contains the `component` property, child route's component\nwill be rendered as a light dom child of a parent component.\n\n* `name` – the string name of the route to use in the\n[`router.urlForName(name, params)`](#/classes/Router#method-urlForName)\nnavigation helper method.\n\nFor any route function (`action`, `children`) defined, the corresponding `route` object is available inside the callback\nthrough the `this` reference. If you need to access it, make sure you define the callback as a non-arrow function\nbecause arrow functions do not have their own `this` reference.\n\n`context` object that is passed to `action` function holds the following properties:\n* `context.pathname` – string with the pathname being resolved\n\n* `context.search` – search query string\n\n* `context.hash` – hash string\n\n* `context.params` – object with route parameters\n\n* `context.route` – object that holds the route that is currently being rendered.\n\n* `context.next()` – function for asynchronously getting the next route\ncontents from the resolution chain (if any)\n\n`commands` object that is passed to `action` function has\nthe following methods:\n\n* `commands.redirect(path)` – function that creates a redirect data\nfor the path specified.\n\n* `commands.component(component)` – function that creates a new HTMLElement\nwith current context. Note: the component created by this function is reused if visiting the same path twice in row.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 1609,
"column": 2
},
"end": {
"line": 1617,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "routes",
"type": "(!Array.<!Route> | !Route)",
"description": "a single route or an array of those"
},
{
"name": "skipRender",
"type": "?boolean",
"defaultValue": "false",
"description": "configure the router but skip rendering the\n route corresponding to the current `window.location` values"
}
],
"return": {
"type": "!Promise.<!Node>"
}
},
{
"name": "addRoutes",
"description": "Appends one or several routes to the routing config and returns the\neffective routing config after the operation.",
"privacy": "protected",
"sourceRange": {
"start": {
"line": 920,
"column": 2
},
"end": {
"line": 924,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "routes",
"type": "(!Array.<!Router.Route> | !Router.Route)",
"description": "a single route or an array of those\n (the array is shallow copied)"
}
],
"return": {
"type": "!Array.<!Router.Route>"
},
"inheritedFrom": "Resolver"
},
{
"name": "removeRoutes",
"description": "Removes all existing routes from the routing config.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 929,
"column": 2
},
"end": {
"line": 931,
"column": 3
}
},
"metadata": {},
"params": [],
"return": {
"type": "void"
},
"inheritedFrom": "Resolver"
},
{
"name": "resolve",
"description": "Asynchronously resolves the given pathname, i.e. finds all routes matching\nthe pathname and tries resolving them one after another in the order they\nare listed in the routes config until the first non-null result.\n\nReturns a promise that is fulfilled with the return value of an object that consists of the first\nroute handler result that returns something other than `null` or `undefined` and context used to get this result.\n\nIf no route handlers return a non-null result, or if no route matches the\ngiven pathname the returned promise is rejected with a 'page not found'\n`Error`.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 950,
"column": 2
},
"end": {
"line": 1022,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "pathnameOrContext",
"type": "(!string | !{pathname: !string})",
"description": "the pathname to\n resolve or a context object with a `pathname` property and other\n properties to pass to the route resolver functions."
}
],
"return": {
"type": "!Promise.<any>"
},
"inheritedFrom": "Resolver"
},
{
"name": "__normalizePathname",
"description": "If the baseUrl is set, matches the pathname with the router’s baseUrl,\nand returns the local pathname with the baseUrl stripped out.\n\nIf the pathname does not match the baseUrl, returns undefined.\n\nIf the `baseUrl` is not set, returns the unmodified pathname argument.",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1055,
"column": 2
},
"end": {
"line": 1070,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "pathname"
}
],
"inheritedFrom": "Resolver"
},
{
"name": "__resolveRoute",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1444,
"column": 2
},
"end": {
"line": 1501,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "context"
}
]
},
{
"name": "setOutlet",
"description": "Sets the router outlet (the DOM node where the content for the current\nroute is inserted). Any content pre-existing in the router outlet is\nremoved at the end of each render pass.\n\nNOTE: this method is automatically invoked first time when creating a new Router instance.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 1513,
"column": 2
},
"end": {
"line": 1518,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "outlet",
"type": "?Node",
"description": "the DOM node where the content for the current route\n is inserted."
}
],
"return": {
"type": "void"
}
},
{
"name": "getOutlet",
"description": "Returns the current router outlet. The initial value is `undefined`.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 1525,
"column": 2
},
"end": {
"line": 1527,
"column": 3
}
},
"metadata": {},
"params": [],
"return": {
"type": "?Node",
"desc": "the current router outlet (or `undefined`)"
}
},
{
"name": "render",
"description": "Asynchronously resolves the given pathname and renders the resolved route\ncomponent into the router outlet. If no router outlet is set at the time of\ncalling this method, or at the time when the route resolution is completed,\na `TypeError` is thrown.\n\nReturns a promise that is fulfilled with the router outlet DOM Node after\nthe route component is created and inserted into the router outlet, or\nrejected if no route matches the given path.\n\nIf another render pass is started before the previous one is completed, the\nresult of the previous render pass is ignored.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 1640,
"column": 2
},
"end": {
"line": 1724,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "pathnameOrContext",
"type": "(!string | !{pathname: !string, search: ?string, hash: ?string})",
"description": "the pathname to render or a context object with a `pathname` property,\n optional `search` and `hash` properties, and other properties\n to pass to the resolver."
},
{
"name": "shouldUpdateHistory",
"type": "boolean=",
"description": "update browser history with the rendered location"
}
],
"return": {
"type": "!Promise.<!Node>"
}
},
{
"name": "__fullyResolveChain",
"description": "and 'redirect' callback results.",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1737,
"column": 2
},
"end": {
"line": 1785,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "topOfTheChainContextBeforeRedirects"
},
{
"name": "contextBeforeRedirects",
"defaultValue": "topOfTheChainContextBeforeRedirects"
}
]
},
{
"name": "__findComponentContextAfterAllRedirects",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1787,
"column": 2
},
"end": {
"line": 1807,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "context"
}
]
},
{
"name": "__amendWithOnBeforeCallbacks",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1809,
"column": 2
},
"end": {
"line": 1816,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "contextWithFullChain"
}
]
},
{
"name": "__runOnBeforeCallbacks",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1818,
"column": 2
},
"end": {
"line": 1890,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "newContext"
}
]
},
{
"name": "__runOnBeforeLeaveCallbacks",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1892,
"column": 2
},
"end": {
"line": 1904,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "callbacks"
},
{
"name": "newContext"
},
{
"name": "commands"
},
{
"name": "chainElement"
}
]
},
{
"name": "__runOnBeforeEnterCallbacks",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1906,
"column": 2
},
"end": {
"line": 1914,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "callbacks"
},
{
"name": "newContext"
},
{
"name": "commands"
},
{
"name": "chainElement"
}
]
},
{
"name": "__isReusableElement",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1916,
"column": 2
},
"end": {
"line": 1923,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "element"
},
{
"name": "otherElement"
}
]
},
{
"name": "__isLatestRender",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1925,
"column": 2
},
"end": {
"line": 1927,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "context"
}
]
},
{
"name": "__redirect",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1929,
"column": 2
},
"end": {
"line": 1943,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "redirectData"
},
{
"name": "counter"
},
{
"name": "renderId"
}
]
},
{
"name": "__ensureOutlet",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1945,
"column": 2
},
"end": {
"line": 1949,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "outlet",
"defaultValue": "this.__outlet"
}
],
"return": {
"type": "void"
}
},
{
"name": "__updateBrowserHistory",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1951,
"column": 2
},
"end": {
"line": 1960,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "{\n pathname,\n search = '',\n hash = ''\n}"
},
{
"name": "replace"
}
],
"return": {
"type": "void"
}
},
{
"name": "__copyUnchangedElements",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1962,
"column": 2
},
"end": {
"line": 1978,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "context"
},
{
"name": "previousContext"
}
]
},
{
"name": "__addAppearingContent",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1980,
"column": 2
},
"end": {
"line": 2018,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "context"
},
{
"name": "previousContext"
}
],
"return": {
"type": "void"
}
},
{
"name": "__removeDisappearingContent",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 2020,
"column": 2
},
"end": {
"line": 2026,
"column": 3
}
},
"metadata": {},
"params": [],
"return": {
"type": "void"
}
},
{
"name": "__removeAppearingContent",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 2028,
"column": 2
},
"end": {
"line": 2034,
"column": 3
}
},
"metadata": {},
"params": [],
"return": {
"type": "void"
}
},
{
"name": "__runOnAfterLeaveCallbacks",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 2036,
"column": 2
},
"end": {
"line": 2062,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "currentContext"
},
{
"name": "targetContext"
}
],
"return": {
"type": "void"
}
},
{
"name": "__runOnAfterEnterCallbacks",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 2064,
"column": 2
},
"end": {
"line": 2077,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "currentContext"
}
],
"return": {
"type": "void"
}
},
{
"name": "__animateIfNeeded",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 2079,
"column": 2
},
"end": {
"line": 2101,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "context"
}
]
},
{
"name": "subscribe",
"description": "Subscribes this instance to navigation events on the `window`.\n\nNOTE: beware of resource leaks. For as long as a router instance is\nsubscribed to navigation events, it won't be garbage collected.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 2109,
"column": 2
},
"end": {
"line": 2111,
"column": 3
}
},
"metadata": {},
"params": [],
"return": {
"type": "void"
}
},
{
"name": "unsubscribe",
"description": "Removes the subscription to navigation events created in the `subscribe()`\nmethod.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 2117,
"column": 2
},
"end": {
"line": 2119,
"column": 3
}
},
"metadata": {},
"params": [],
"return": {
"type": "void"
}
},
{
"name": "__onNavigationEvent",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 2121,
"column": 2
},
"end": {
"line": 2129,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "event"
}
],
"return": {
"type": "void"
}
},
{
"name": "urlForName",
"description": "Generates a URL for the route with the given name, optionally performing\nsubstitution of parameters.\n\nThe route is searched in all the Router instances subscribed to\nnavigation events.\n\n**Note:** For child route names, only array children are considered.\nIt is not possible to generate URLs using a name for routes set with\na children function.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 2170,
"column": 2
},
"end": {
"line": 2178,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "name",
"type": "!string",
"description": "the route name or the route’s `component` name."
},
{
"name": "params",
"type": "Params=",
"description": "Optional object with route path parameters.\nNamed parameters are passed by name (`params[name] = value`), unnamed\nparameters are passed by index (`params[index] = value`)."
}
],
"return": {
"type": "string"
}
},
{
"name": "urlForPath",
"description": "Generates a URL for the given route path, optionally performing\nsubstitution of parameters.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 2191,
"column": 2
},
"end": {
"line": 2196,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "path",
"type": "!string",
"description": "string route path declared in [express.js syntax](https://expressjs.com/en/guide/routing.html#route-paths\")."
},
{
"name": "params",
"type": "Params=",
"description": "Optional object with route path parameters.\nNamed parameters are passed by name (`params[name] = value`), unnamed\nparameters are passed by index (`params[index] = value`)."
}
],
"return": {
"type": "string"
}
}
],
"staticMethods": [
{
"name": "__createUrl",
"description": "URL constructor polyfill hook. Creates and returns an URL instance.",
"privacy": "private",
"sourceRange": {
"start": {
"line": 1027,
"column": 2
},
"end": {
"line": 1029,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "args",
"rest": true
}
],
"inheritedFrom": "Resolver"
},
{
"name": "setTriggers",
"description": "Configures what triggers Router navigation events:\n - `POPSTATE`: popstate events on the current `window`\n - `CLICK`: click events on `<a>` links leading to the current page\n\nThis method is invoked with the pre-configured values when creating a new Router instance.\nBy default, both `POPSTATE` and `CLICK` are enabled. This setup is expected to cover most of the use cases.\n\nSee the `router-config.js` for the default navigation triggers config. Based on it, you can\ncreate the own one and only import the triggers you need, instead of pulling in all the code,\ne.g. if you want to handle `click` differently.\n\nSee also **Navigation Triggers** section in [Live Examples](#/classes/Router/demos/demo/index.html).",
"privacy": "public",
"sourceRange": {
"start": {
"line": 2147,
"column": 2
},
"end": {
"line": 2149,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "triggers",
"type": "...NavigationTrigger",
"rest": true
}
],
"return": {
"type": "void"
}
},
{
"name": "go",
"description": "Triggers navigation to a new path. Returns a boolean without waiting until\nthe navigation is complete. Returns `true` if at least one `Router`\nhas handled the navigation (was subscribed and had `baseUrl` matching\nthe `path` argument), otherwise returns `false`.",
"privacy": "public",
"sourceRange": {
"start": {
"line": 2209,
"column": 2
},
"end": {
"line": 2214,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "path",
"type": "(!string | !{pathname: !string, search: (string | undefined), hash: (string | undefined)})",
"description": "a new in-app path string, or an URL-like object with `pathname`\n string property, and optional `search` and `hash` string properties."
}
],
"return": {
"type": "boolean"
}
},
{
"name": "__removeDomNodes",
"description": "",
"privacy": "private",
"sourceRange": {
"start": {
"line": 2216,
"column": 2
},
"end": {
"line": 2225,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "nodes"
}
],
"return": {
"type": "void"
}
}
],
"demos": [
{
"url": "demo/index.html",
"description": ""
},
{
"url": "demo/index.html",
"description": ""
}
],
"metadata": {},
"sourceRange": {
"start": {
"line": 1374,
"column": 0
},
"end": {
"line": 2226,
"column": 1
}
},
"privacy": "public",
"superclass": "Resolver",
"name": "Router"
},
{
"description": "This interface describes the lifecycle callbacks supported by Vaadin Router\non view Web Components. It exists only for documentation purposes, i.e.\nyou _do not need_ to extend it in your code—defining a method with a\nmatching name is enough (this class does not exist at the run time).\n\nIf any of the methods described below are defined in a view Web Component,\nVaadin Router calls them at the corresponding points of the view\nlifecycle. Each method can either be synchronous or asynchronous (i.e. return\na Promise). In the latter case Vaadin Router waits until the promise is\nresolved and continues the navigation after that.\n\nCheck the [documentation on the `Router` class](#/classes/Router)\nto learn more.\n\nLifecycle callbacks are executed after the new path is resolved and after all\n`action` callbacks of the routes in the new path are executed.\n\nExample:\n\nFor the following routes definition,\n```\n// router and action declarations are omitted for brevity\nrouter.setRoutes([\n {path: '/a', action: actionA, children: [\n {path: '/b', action: actionB, component: 'component-b'},\n {path: '/c', action: actionC, component: 'component-c'}\n ]}\n]);\n```\nif the router first navigates to `/a/b` path and there was no view rendered\nbefore, the following events happen:\n* actionA\n* actionB\n* onBeforeEnterB (if defined in component-b)\n* outlet contents updated with component-b\n* onAfterEnterB (if defined in component-b)\n\nthen, if the router navigates to `/a/c`, the following events take place:\n* actionA\n* actionC\n* onBeforeLeaveB (if defined in component-b)\n* onBeforeEnterC (if defined in component-c)\n* onAfterLeaveB (if defined in component-b)\n* outlet contents updated with component-c\n* onAfterEnterC (if defined in component-c)\n\nIf a `Promise` is returned by any of the callbacks, it is resolved before\nproceeding further.\n\nAny of the `onBefore...` callbacks have a possibility to prevent\nthe navigation and fall back to the previous navigation result. If there is\nno result and this is the first resolution, an exception is thrown.\n\n`onAfter...` callbacks are considered as non-preventable, and their return\nvalue is ignored.\n\nOther examples can be found in the\n[live demos](#/classes/Router/demos/demo/index.html) and tests.",
"summary": "",
"path": "src/documentation/web-component-interface.js",
"properties": [],
"methods": [
{
"name": "onBeforeLeave",
"description": "Method that gets executed when user navigates away from the component\nthat had defined the method. The user can prevent the navigation\nby returning `commands.prevent()` from the method or same value wrapped\nin `Promise`. This effectively means that the corresponding component\nshould be resolved by the router before the method can be executed.\nIf the router navigates to the same path twice in a row, and this results\nin rendering the same component name (if the component is created\nusing `component` property in the route object) or the same component instance\n(if the component is created and returned inside `action` property of the route object),\nin the second time the method is not called. In case of navigating to a different path\nbut within the same route object, e.g. the path has parameter or wildcard,\nand this results in rendering the same component instance, the method is called if available.\nThe WebComponent instance on which the callback has been invoked is available inside the callback through\nthe `this` reference.\n\nReturn values:\n\n* if the `commands.prevent()` result is returned (immediately or\nas a Promise), the navigation is aborted and the outlet contents\nis not updated.\n* any other return value is ignored and Vaadin Router proceeds with\nthe navigation.\n\nArguments:",
"privacy": "public",
"sourceRange": {
"start": {
"line": 97,
"column": 2
},
"end": {
"line": 102,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "location",
"description": "the `RouterLocation` object"
},
{
"name": "commands",
"description": "the commands object with the following methods:\n\n| Property | Description\n| -------------------|-------------\n| `commands.prevent()` | function that creates a special object that can be returned to abort the current navigation and fall back to the last one. If there is no existing one, an exception is thrown."
},
{
"name": "router",
"description": "the `Router` instance"
}
]
},
{
"name": "onBeforeEnter",
"description": "Method that gets executed before the outlet contents is updated with\nthe new element. The user can prevent the navigation by returning\n`commands.prevent()` from the method or same value wrapped in `Promise`.\nIf the router navigates to the same path twice in a row, and this results\nin rendering the same component name (if the component is created\nusing `component` property in the route object) or the same component instance\n(if the component is created and returned inside `action` property of the route object),\nin the second time the method is not called. In case of navigating to a different path\nbut within the same route object, e.g. the path has parameter or wildcard,\nand this results in rendering the same component instance, the method is called if available.\nThe WebComponent instance on which the callback has been invoked is available inside the callback through\nthe `this` reference.\n\nReturn values:\n\n* if the `commands.prevent()` result is returned (immediately or\nas a Promise), the navigation is aborted and the outlet contents\nis not updated.\n* if the `commands.redirect(path)` result is returned (immediately or\nas a Promise), Vaadin Router ends navigation to the current path, and\nstarts a new navigation cycle to the new path.\n* any other return value is ignored and Vaadin Router proceeds with\nthe navigation.\n\nArguments:",
"privacy": "public",
"sourceRange": {
"start": {
"line": 141,
"column": 2
},
"end": {
"line": 146,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "location",
"description": "the `RouterLocation` object"
},
{
"name": "commands",
"description": "the commands object with the following methods:\n\n| Property | Description\n| -------------------------|-------------\n| `commands.redirect(path)` | function that creates a redirect data for the path specified, to use as a return value from the callback.\n| `commands.prevent()` | function that creates a special object that can be returned to abort the current navigation and fall back to the last one. If there is no existing one, an exception is thrown."
},
{
"name": "router",
"description": "the `Router` instance"
}
]
},
{
"name": "onAfterLeave",
"description": "Method that gets executed when user navigates away from the component that\nhad defined the method, just before the element is to be removed\nfrom the DOM. The difference between this method and `onBeforeLeave`\nis that when this method is executed, there is no way to abort\nthe navigation. This effectively means that the corresponding component\nshould be resolved by the router before the method can be executed.\nIf the router navigates to the same path twice in a row, and this results\nin rendering the same component name (if the component is created\nusing `component` property in the route object) or the same component instance\n(if the component is created and returned inside `action` property of the route object),\nin the second time the method is not called. The WebComponent instance on which the callback\nhas been invoked is available inside the callback through\nthe `this` reference.\n\nReturn values: any return value is ignored and Vaadin Router proceeds with the navigation.\n\nArguments:",
"privacy": "public",
"sourceRange": {
"start": {
"line": 171,
"column": 2
},
"end": {
"line": 174,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "location",
"description": "the `RouterLocation` object"
},
{
"name": "commands",
"description": "empty object"
},
{
"name": "router",
"description": "the `Router` instance"
}
],
"return": {
"type": "void"
}
},
{
"name": "onAfterEnter",
"description": "Method that gets executed after the outlet contents is updated with the new\nelement. If the router navigates to the same path twice in a row, and this results\nin rendering the same component name (if the component is created\nusing `component` property in the route object) or the same component instance\n(if the component is created and returned inside `action` property of the route object),\nin the second time the method is not called. The WebComponent instance on which the callback\nhas been invoked is available inside the callback through\nthe `this` reference.\n\nThis callback is called asynchronously after the native\n[`connectedCallback()`](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-reactions)\ndefined by the Custom Elements spec.\n\nReturn values: any return value is ignored and Vaadin Router proceeds with the navigation.\n\nArguments:",
"privacy": "public",
"sourceRange": {
"start": {
"line": 198,
"column": 2
},
"end": {
"line": 201,
"column": 3
}
},
"metadata": {},
"params": [
{
"name": "location",
"description": "the `RouterLocation` object"
},
{
"name": "commands",
"description": "empty object"
},
{
"name": "router",
"description": "the `Router` instance"
}
],
"return": {
"type": "void"
}
}
],
"staticMethods": [],
"demos": [],
"metadata": {},
"sourceRange": {
"start": {
"line": 61,
"column": 7
},
"end": {
"line": 202,
"column": 1
}
},
"privacy": "public",
"name": "WebComponentInterface"
}
]
}
================================================
FILE: demo/.eslintrc.json
================================================
{
"extends": ["../.eslintrc.json"],
"root": true,
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {
"@typescript-eslint/unbound-method": "off"
}
}
================================================
FILE: demo/@debug/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Debug</title>
<script async type="module" src="./index.ts"></script>
</head>
<body>
<vaadin-demo-layout app-title="Debug">
<vaadin-demo-debug></vaadin-demo-debug>
</vaadin-demo-layout>
</body>
</html>
================================================
FILE: demo/@debug/index.ts
================================================
/* eslint-disable import/no-duplicates, import/default */
import '@helpers/common.js';
import '@helpers/vaadin-presentation.js';
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
// An URL of the iframe to debug
// eslint-disable-next-line import/order
import url1 from '../url-generation/d4/iframe.html?url';
declare global {
interface HTMLElementTagNameMap {
'vaadin-demo-debug': DemoDebug;
}
}
@customElement('vaadin-demo-debug')
export default class DemoDebug extends LitElement {
override render(): TemplateResult {
return html`<vaadin-presentation src=${url1}></vaadin-presentation>`;
}
}
================================================
FILE: demo/@helpers/common.css
================================================
:host {
display: block;
}
================================================
FILE: demo/@helpers/common.ts
================================================
import '@vaadin/vaadin-lumo-styles/badge-global.js';
import '@vaadin/vaadin-lumo-styles/color-global.js';
import '@vaadin/vaadin-lumo-styles/typography-global.js';
================================================
FILE: demo/@helpers/iframe.script.ts
================================================
import './common.css';
import './common.js';
import { Router } from '@vaadin/router';
history.replaceState(null, '', '/');
type MessageData = Readonly<{ url: string }>;
type ParentData = {
source: MessageEventSource | null;
origin: string;
};
let parentData: ParentData | undefined;
function updateParentUrl() {
if (parentData?.source) {
parentData.source.postMessage({ url: location.href }, { targetOrigin: location.origin });
}
}
addEventListener('message', ({ data, origin, source }: MessageEvent<MessageData | null>) => {
if (data != null) {
Router.go(new URL(data.url, location.origin).href);
} else {
parentData = { source, origin };
}
updateParentUrl();
});
addEventListener('vaadin-router-location-changed', updateParentUrl);
================================================
FILE: demo/@helpers/nested-styles.css
================================================
:host {
display: flex;
flex-direction: column;
background: #ddd;
padding: 5px;
min-height: 100vh;
}
main {
margin-top: 10px;
padding: 5px;
background: #fff;
flex: 1 0 auto;
min-height: 80px;
}
================================================
FILE: demo/@helpers/page.css
================================================
code {
background: var(--code-background);
padding: 0.2em 0.4em;
}
================================================
FILE: demo/@helpers/shared-styles.css
================================================
.note {
background-color: #24c0ea;
padding: 1em;
color: white;
border-radius: 4px;
}
================================================
FILE: demo/@helpers/theme-controller.ts
================================================
import type { ReactiveController, ReactiveControllerHost } from 'lit';
export default class ThemeController implements ReactiveController {
readonly #host: ReactiveControllerHost;
#controller?: AbortController;
constructor(host: ReactiveControllerHost) {
(this.#host = host).addController(this);
}
get value(): string {
return document.documentElement.getAttribute('theme') ?? 'light';
}
hostConnected(): void {
this.#controller = new AbortController();
addEventListener('theme-changed', () => this.#host.requestUpdate(), { signal: this.#controller.signal });
}
hostDisconnected(): void {
this.#controller?.abort();
}
}
================================================
FILE: demo/@helpers/vaadin-demo-code-snippet-file.css
================================================
header {
margin: 1.5em 0 0.5em;
font-size: 0.75rem;
display: flex;
justify-content: space-between;
align-items: baseline;
background: var(--code-file-background);
}
section {
font-size: 0.75rem;
background: var(--code-background);
padding: .5em;
}
section > pre {
white-space: pre-wrap;
}
section > pre > code {
line-break: anywhere;
}
.title {
font-family: monospace;
}
.buttons {
clear: right;
float: right;
display: flex;
margin: -0.25em 0;
}
.buttons > vaadin-button {
margin: 0;
padding: 0;
}
/* ======= Highlight.js styles ======= */
:host([theme~='dark']) {
@nested-import 'highlight.js/styles/atom-one-dark.css';
}
:host([theme~='light']) {
@nested-import 'highlight.js/styles/vs.css';
}
pre {
margin: 0;
padding: 0.5em 1em;
}
================================================
FILE: demo/@helpers/vaadin-demo-code-snippet-file.ts
================================================
/* eslint-disable @typescript-eslint/unbound-method */
import '@vaadin/button/src/vaadin-button';
import '@vaadin/icon/src/vaadin-icon';
import { type TemplateResult, LitElement, html, nothing } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { map } from 'lit/directives/map.js';
import ThemeController from './theme-controller.js';
import css from './vaadin-demo-code-snippet-file.css?ctr';
export type CodeSnippet = Readonly<{
id?: string;
code: [original: string, full: TemplateResult, ...rest: TemplateResult[]];
title?: string;
}>;
@customElement('vaadin-demo-code-snippet-file')
export default class DemoCodeSnippetFile extends LitElement {
static override styles = [css];
@property({ attribute: false }) accessor file: CodeSnippet | undefined;
@state() accessor #expanded = false;
readonly #theme = new ThemeController(this);
override updated(): void {
this.setAttribute('theme', this.#theme.value);
}
override render(): TemplateResult | typeof nothing {
if (!this.file) {
return nothing;
}
const [_, full, ...snippets] = this.file.code;
return html`
<header>
<div class="title">${this.file.title}</div>
</header>
<section>
<div class="buttons">
<vaadin-button theme="tertiary icon" title="Toggle full code" @click=${this.#toggleExpanded}>
<vaadin-icon icon="vaadin:expand"></vaadin-icon>
</vaadin-button>
<vaadin-button theme="tertiary icon" title="Copy to clipboard" @click=${this.#copyToClipboard}>
<vaadin-icon icon="vaadin:copy" @click=${this.#copyToClipboard}></vaadin-icon>
</vaadin-button>
</div>
${this.#expanded
? html`<pre><code>${full}</code></pre>`
: map(snippets, (snippet) => html`<pre><code>${snippet}</code></pre>`)}
</section>
`;
}
#toggleExpanded(): void {
this.#expanded = !this.#expanded;
}
async #copyToClipboard(): Promise<void> {
const [original] = this.file?.code ?? [];
if (original) {
await navigator.clipboard.writeText(original);
}
}
}
declare global {
interface HTMLElementTagNameMap {
'vaadin-demo-code-snippet-file': DemoCodeSnippetFile;
}
}
================================================
FILE: demo/@helpers/vaadin-demo-code-snippet.css
================================================
:host {
display: block;
}
================================================
FILE: demo/@helpers/vaadin-demo-code-snippet.ts
================================================
/* eslint-disable import/no-duplicates */
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import type { CodeSnippet } from './vaadin-demo-code-snippet-file.js';
import './vaadin-demo-code-snippet-file.js';
import css from './vaadin-demo-code-snippet.css?ctr';
declare global {
interface HTMLElementTagNameMap {
'vaadin-demo-code-snippet': DemoCodeSnippet;
}
interface WindowEventMap {
'theme-changed': Event;
}
}
export type { CodeSnippet };
function renderFile(file: CodeSnippet): TemplateResult {
return html`<vaadin-demo-code-snippet-file .file=${file}></vaadin-demo-code-snippet-file>`;
}
@customElement('vaadin-demo-code-snippet')
export default class DemoCodeSnippet extends LitElement {
static override styles = [css];
@property({ attribute: false }) accessor files: readonly CodeSnippet[] = [];
override render(): TemplateResult {
switch (this.files.length) {
case 0:
return html``;
case 1:
return renderFile(this.files[0]);
default:
return html` ${repeat(this.files, ({ id }) => id, renderFile)} `;
}
}
}
================================================
FILE: demo/@helpers/vaadin-demo-layout.css
================================================
:host {
--code-file-background: none;
}
:host([theme~='dark']) {
--code-background: var(--lumo-shade);
}
:host([theme~='light']) {
--code-background: var(--lumo-shade-5pct);
}
main {
max-width: 40rem;
margin: 0 auto;
padding: .5rem;
}
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 0 1rem;
}
================================================
FILE: demo/@helpers/vaadin-demo-layout.ts
================================================
/* eslint-disable @typescript-eslint/unbound-method */
import '@vaadin/app-layout';
import '@vaadin/app-layout/vaadin-drawer-toggle.js';
import '@vaadin/icon';
import '@vaadin/icons';
import '@vaadin/scroller';
import '@vaadin/side-nav';
import '@vaadin/side-nav/vaadin-side-nav-item.js';
import { SignalWatcher } from '@lit-labs/preact-signals';
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import './vaadin-presentation.js';
import css from './vaadin-demo-layout.css?ctr';
const colorScheme = window.localStorage.getItem('color-scheme');
if (colorScheme) {
document.documentElement.setAttribute('theme', colorScheme);
}
declare global {
interface HTMLElementTagNameMap {
'vaadin-demo-layout': DemoLayout;
}
}
@customElement('vaadin-demo-layout')
export default class DemoLayout extends SignalWatcher(LitElement) {
static override styles = [css];
@property({ attribute: 'app-title', type: String }) accessor appTitle = '';
@property({ reflect: true, type: String }) accessor theme = document.documentElement.getAttribute('theme') ?? 'light';
override render(): TemplateResult {
return html`<vaadin-app-layout primary-section="drawer">
<vaadin-drawer-toggle slot="navbar"></vaadin-drawer-toggle>
<header class="navbar" slot="navbar">
<h1>${this.appTitle}</h1>
<vaadin-button theme="icon" slot="navbar" aria-label="Toggle dark mode" @click=${this.#onToggleMode}>
<vaadin-icon icon=${this.theme === 'dark' ? 'vaadin:sun-o' : 'vaadin:moon-o'}></vaadin-icon>
</vaadin-button>
</header>
<main>
<slot></slot>
</main>
<vaadin-scroller slot="drawer">
<vaadin-side-nav>
<vaadin-side-nav-item path="../getting-started/index.html">Getting Started</vaadin-side-nav-item>
<vaadin-side-nav-item path="../code-splitting/index.html">Code Splitting</vaadin-side-nav-item>
<vaadin-side-nav-item path="../animated-transitions/index.html">Animated Transitions</vaadin-side-nav-item>
<vaadin-side-nav-item path="../lifecycle-callback/index.html">Lifecycle Callback</vaadin-side-nav-item>
<vaadin-side-nav-item path="../navigation-trigger/index.html">Navigation Trigger</vaadin-side-nav-item>
<vaadin-side-nav-item path="../redirect/index.html">Redirect</vaadin-side-nav-item>
<vaadin-side-nav-item path="../route-actions/index.html">Route Actions</vaadin-side-nav-item>
<vaadin-side-nav-item path="../route-parameters/index.html">Route Parameters</vaadin-side-nav-item>
<vaadin-side-nav-item path="../url-generation/index.html">URL Generations</vaadin-side-nav-item>
<vaadin-side-nav-item path="../API/index.html">API</vaadin-side-nav-item>
</vaadin-side-nav>
</vaadin-scroller>
</vaadin-app-layout>`;
}
#onToggleMode() {
this.theme = this.theme === 'dark' ? 'light' : 'dark';
window.localStorage.setItem('color-scheme', this.theme);
document.documentElement.setAttribute('theme', this.theme);
dispatchEvent(new Event('theme-changed'));
}
}
================================================
FILE: demo/@helpers/vaadin-presentation-addressbar.css
================================================
:host {
display: flex;
gap: 1rem;
flex: 0 1 0;
}
:host > :last-child {
flex: 1 0 auto;
}
================================================
FILE: demo/@helpers/vaadin-presentation-addressbar.ts
================================================
/* eslint-disable @typescript-eslint/unbound-method */
import '@vaadin/button';
import '@vaadin/icon';
import '@vaadin/icons';
import '@vaadin/text-field';
import '@vaadin/tooltip';
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import css from './vaadin-presentation-addressbar.css?ctr';
declare global {
interface HTMLElementTagNameMap {
'vaadin-presentation-addressbar': PresentationAddressbar;
}
}
function onBack() {
history.back();
}
function onForward() {
history.forward();
}
@customElement('vaadin-presentation-addressbar')
export class PresentationAddressbar extends LitElement {
static override styles = css;
@property({ attribute: true, type: String }) accessor url: string | undefined;
override render(): TemplateResult {
return html`<vaadin-button theme="icon" aria-label="Back" @click=${onBack}>
<vaadin-icon icon="vaadin:arrow-left"></vaadin-icon>
<vaadin-tooltip slot="tooltip" text="Back"></vaadin-tooltip>
</vaadin-button>
<vaadin-button theme="icon" aria-label="Forward" @click=${onForward}>
<vaadin-icon icon="vaadin:arrow-right" aria-label="Forward"></vaadin-icon>
<vaadin-tooltip slot="tooltip" text="Forward"></vaadin-tooltip>
</vaadin-button>
<vaadin-text-field value=${ifDefined(this.url)} @change=${this.#onChange}>
<vaadin-icon slot="prefix" icon="vaadin:lock"></vaadin-icon>
</vaadin-text-field>`;
}
#onChange(event: Event) {
this.url = (event.target as HTMLInputElement).value;
this.dispatchEvent(new CustomEvent<string>('url-changed', { detail: this.url }));
}
}
================================================
FILE: demo/@helpers/vaadin-presentation.css
================================================
:host {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
vaadin-presentation-addressbar,
iframe {
width: 100%;
height: 15rem;
}
iframe {
margin: 0;
}
================================================
FILE: demo/@helpers/vaadin-presentation.ts
================================================
/* eslint-disable @typescript-eslint/unbound-method */
import { html, LitElement, type PropertyValues, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import css from './vaadin-presentation.css?ctr';
import './vaadin-presentation-addressbar.js';
import './vaadin-demo-code-snippet.js';
declare global {
interface HTMLElementTagNameMap {
'vaadin-presentation': Presentation;
}
}
type MessageData = Readonly<{
url: string;
}>;
@customElement('vaadin-presentation')
export default class Presentation extends LitElement {
static override styles = css;
@property() accessor src: string | undefined;
@property({ attribute: true, type: String }) accessor url: string | undefined;
#controller?: AbortController;
#window?: Window;
override connectedCallback(): void {
super.connectedCallback();
this.#controller = new AbortController();
}
override disconnectedCallback(): void {
super.disconnectedCallback();
this.#controller?.abort();
}
override firstUpdated(): void {
if (this.#controller) {
addEventListener(
'message',
({ data, origin, source }: MessageEvent<MessageData>) => {
if (origin === location.origin && source === this.#window) {
this.url = new URL(data.url).pathname;
}
},
{ signal: this.#controller.signal },
);
}
}
changedProperties(map: PropertyValues<this>): void {
if (map.has('url')) {
this.#window?.postMessage({ url: this.url }, '*');
}
}
override render(): TemplateResult {
return html`<vaadin-presentation-addressbar
url=${ifDefined(this.url)}
@url-changed=${this.#onUrlChanged}
></vaadin-presentation-addressbar>
<iframe
id="browser"
src=${ifDefined(this.src)}
sandbox="allow-same-origin allow-scripts allow-modals"
@load=${(event: Event) => {
this.#window = (event.target as HTMLIFrameElement).contentWindow!;
this.#window.postMessage(null, '*');
}}
></iframe>
<slot></slot>`;
}
#onUrlChanged(event: CustomEvent<string>) {
this.url = new URL(event.detail, location.origin).pathname;
this.#window?.postMessage({ url: this.url }, '*');
}
}
================================================
FILE: demo/@helpers/x-breadcrumbs.ts
================================================
import { html, LitElement, nothing, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { map } from 'lit/directives/map.js';
import css from './common.css?ctr';
export type Breadcrumb = Readonly<{
title: string;
href: string;
}>;
@customElement('x-breadcrumbs')
export class Breadcrumbs extends LitElement {
static override styles = css;
@property({ type: Array }) accessor items: readonly Breadcrumb[] = [];
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
#isNotLastIndexOf(items: readonly Breadcrumb[], i: number): boolean {
return i < items.length - 1;
}
override render(): TemplateResult {
return html`
<nav>
${map(
this.items,
(item, index) => html`
<a href=${item.href}>${item.title}</a>
${this.#isNotLastIndexOf(this.items, index) ? html`<span class="delimiter"> > </span>` : nothing}
`,
)}
</nav>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'x-breadcrumbs': Breadcrumbs;
}
}
================================================
FILE: demo/@helpers/x-home-view.ts
================================================
import { LitElement, html, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import css from './common.css?ctr';
declare global {
interface HTMLElementTagNameMap {
'x-home-view': HomeView;
}
}
@customElement('x-home-view')
export default class HomeView extends LitElement {
static override styles = css;
override render(): TemplateResult {
return html`<h1>Home</h1>
<slot></slot>`;
}
}
================================================
FILE: demo/@helpers/x-image-view.css
================================================
.img-view {
width: var(--x-image-view-width, 100%);
height: var(--x-image-view-height, 100%);
background-color: var(--x-image-view-background-color, hotpink);
}
================================================
FILE: demo/@helpers/x-image-view.ts
================================================
import { LitElement, html, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
import type { RouterLocation } from '../../src/index.js';
import commonCss from './common.css?ctr';
import css from './x-image-view.css?ctr';
declare global {
interface HTMLElementTagNameMap {
'x-image-view': ImageView;
}
}
@customElement('x-image-view')
export default class ImageView extends LitElement {
static override styles = [commonCss, css];
@property({ attribute: false }) accessor location: RouterLocation | undefined;
override render(): TemplateResult {
const size = this.location?.params.size as number | undefined;
const color = this.location?.params.color as string | undefined;
return html`<div
class="img-view"
style=${styleMap({
'--x-image-view-width': `${size}px`,
'--x-image-view-height': `${size}px`,
'--x-image-view-background-color': color,
})}
></div>`;
}
}
================================================
FILE: demo/@helpers/x-knowledge-base.ts
================================================
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import type { RouterLocation, WebComponentInterface } from '@vaadin/router';
@customElement('x-knowledge-base')
export class KnowledgeBase extends LitElement implements WebComponentInterface {
@property({ type: Object }) accessor location: RouterLocation | undefined;
override render(): TemplateResult {
return html` Knowledge base path: '${this.location?.params.path ?? 'unknown'}' `;
}
}
declare global {
interface HTMLElementTagNameMap {
'x-knowledge-base': KnowledgeBase;
}
}
================================================
FILE: demo/@helpers/x-login-view.ts
================================================
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import css from './common.css?ctr';
import type { RouterLocation, WebComponentInterface } from '@vaadin/router';
@customElement('x-login-view')
export default class LoginView extends LitElement implements WebComponentInterface {
static override styles = [css];
location?: RouterLocation;
override render(): TemplateResult {
return html`
<h1>Login Form</h1>
<button @click="${this.#login}">Login with OAuth</button>
`;
}
#login() {
window.authorized = true;
dispatchEvent(
new CustomEvent('vaadin-router-go', {
detail: { pathname: this.location?.params.to ?? '/' },
}),
);
}
}
declare global {
interface HTMLElementTagNameMap {
'x-login-view': LoginView;
}
}
================================================
FILE: demo/@helpers/x-not-found-view.ts
================================================
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import css from './common.css?ctr';
@customElement('x-not-found-view')
export class NotFoundView extends LitElement {
static override styles = css;
override render(): TemplateResult {
return html`
<h1>404</h1>
<p>View not found</p>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'x-not-found-view': NotFoundView;
}
}
================================================
FILE: demo/@helpers/x-profile-view.ts
================================================
import { LitElement, html, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import css from './common.css?ctr';
import type { RouterLocation } from '@vaadin/router';
declare global {
interface HTMLElementTagNameMap {
'x-profile-view': ProfileView;
}
}
@customElement('x-profile-view')
export default class ProfileView extends LitElement {
static override styles = [css];
@property({ type: Object }) accessor location: RouterLocation | undefined;
override render(): TemplateResult {
return html`User ID: ${this.location?.params.id ?? 'unknown'}<br />
<code>/user</code> or <code>/users</code>: ${this.location?.params[0] ?? 'unknown'}`;
}
}
================================================
FILE: demo/@helpers/x-user-list.ts
================================================
import { LitElement, html, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import css from './common.css?ctr';
declare global {
interface HTMLElementTagNameMap {
'x-user-list': UserList;
}
}
@customElement('x-user-list')
export default class UserList extends LitElement {
static override styles = css;
override render(): TemplateResult {
return html`<h1>Users</h1>
<ul>
<li><a href="/users/kim">Kim</a></li>
<li><a href="/users/jane">Jane</a></li>
<li><a href="/users/sam">Sam</a></li>
</ul>`;
}
}
================================================
FILE: demo/@helpers/x-user-not-found-view.ts
================================================
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import css from './common.css?ctr';
import type { RouterLocation } from '@vaadin/router';
@customElement('x-user-not-found-view')
export class UserNotFoundView extends LitElement {
@property({ type: Object }) accessor location: RouterLocation | undefined;
static override styles = [css];
override render(): TemplateResult {
return html`
<h1>The princess is in another castle</h1>
<p>You've come to <code>${this.location?.pathname}</code>, but alas, there is nothing there.</p>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'x-user-not-found-view': UserNotFoundView;
}
}
================================================
FILE: demo/@helpers/x-user-numeric-view.ts
================================================
import { LitElement, html, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import css from './common.css?ctr';
import type { RouterLocation } from '@vaadin/router';
declare global {
interface HTMLElementTagNameMap {
'x-user-numeric-view': UserNumericView;
}
}
@customElement('x-user-numeric-view')
export default class UserNumericView extends LitElement {
static override styles = [css];
@property({ type: Object }) accessor location: RouterLocation | undefined;
override render(): TemplateResult {
return html`<h1>User Profile</h1>
<p>ID: ${this.location?.params[0] ?? 'unknown'}</p>`;
}
}
================================================
FILE: demo/@helpers/x-user-profile.ts
================================================
import { LitElement, html, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import css from './common.css?ctr';
import type { RouterLocation } from '@vaadin/router';
declare global {
interface HTMLElementTagNameMap {
'x-user-profile': UserProfile;
}
}
@customElement('x-user-profile')
export default class UserProfile extends LitElement {
static override styles = css;
@property({ attribute: false }) accessor location: RouterLocation | undefined;
override render(): TemplateResult {
return html`<h1>User Profile</h1>
<p>Name: ${this.location?.params.user ?? 'unknown'}</p>`;
}
}
================================================
FILE: demo/animated-transitions/d1/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Animated Transitions</title>
<link rel="stylesheet" href="./styles.css" />
<script async type="module" src="./script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<a href="/">Home</a>
<a href="/image-125px">Image</a>
<a href="/users">Users</a>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/animated-transitions/d1/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-image-view.js';
import '@helpers/x-user-list.js';
import '@helpers/x-user-profile.js';
import { Router } from '@vaadin/router';
// tag::snippet[]
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{
path: '/',
animate: true,
children: [
{ path: '', component: 'x-home-view' },
{ path: '/image-:size(\\d+)px', component: 'x-image-view' },
{ path: '/users', component: 'x-user-list' },
{ path: '/users/:user', component: 'x-user-profile' },
],
},
]);
// end::snippet[]
================================================
FILE: demo/animated-transitions/d1/styles.css
================================================
#outlet > .leaving {
animation: 1s fadeOut ease-in-out;
}
#outlet > .entering {
animation: 1s fadeIn linear;
}
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
================================================
FILE: demo/animated-transitions/d2/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Animated Transitions</title>
<link rel="stylesheet" href="styles.css" />
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/animated-transitions/d2/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-user-list.js';
import '@helpers/x-user-profile.js';
import { Router } from '@vaadin/router';
import './x-wrapper.js';
// tag::snippet[]
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{
path: '/',
component: 'x-wrapper',
children: [
{
path: '/users',
animate: {
enter: 'users-entering',
leave: 'users-leaving',
},
children: [
{ path: '', component: 'x-user-list' },
{
path: '/:user',
component: 'x-user-profile',
animate: true,
},
],
},
{ path: '(.*)', redirect: '/users' },
],
},
]);
// end::snippet[]
================================================
FILE: demo/animated-transitions/d2/styles.css
================================================
.leaving {
animation: 1s slideOutDown ease-in-out;
}
.entering {
animation: 1s slideInDown linear;
}
.users-entering {
animation: 0.5s fadeIn ease-in;
}
.users-leaving {
animation: 0.5s fadeOut linear;
}
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideInDown {
from {
transform: translate3d(0, -100%, 0);
visibility: visible;
}
to {
transform: translate3d(0, 0, 0);
}
}
@keyframes slideOutDown {
from {
transform: translate3d(0, 0, 0);
}
to {
visibility: hidden;
transform: translate3d(0, 100%, 0);
}
}
================================================
FILE: demo/animated-transitions/d2/x-wrapper.ts
================================================
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import sharedCss from '@helpers/shared-styles.css?ctr';
declare global {
interface HTMLElementTagNameMap {
'x-wrapper': Wrapper;
}
}
// tag::snippet[]
@customElement('x-wrapper')
export default class Wrapper extends LitElement {
static override styles = sharedCss;
override render(): TemplateResult {
return html`<nav>
<a href="/users">Users</a>
<a href="/users/kim">Kim</a>
<a href="/users/jane">Jane</a>
<a href="/users/sam">Sam</a>
</nav>
<main>
<slot></slot>
</main>`;
}
}
// end::snippet[]
================================================
FILE: demo/animated-transitions/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Animated Transitions</title>
<script async type="module" src="./index.ts"></script>
</head>
<body>
<vaadin-demo-layout app-title="Animated Transitions">
<vaadin-demo-animated-transitions></vaadin-demo-animated-transitions>
</vaadin-demo-layout>
</body>
</html>
================================================
FILE: demo/animated-transitions/index.ts
================================================
/* eslint-disable import/no-duplicates, import/default */
import '@helpers/common.js';
import '@helpers/vaadin-demo-layout.js';
import '@helpers/vaadin-demo-code-snippet.js';
import '@helpers/vaadin-presentation.js';
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import htmlCode1 from './d1/iframe.html?snippet';
import tsCode1 from './d1/script.js?snippet';
import cssCode1 from './d1/styles.css?snippet';
import htmlCode2 from './d2/iframe.html?snippet';
import tsCode2 from './d2/script.js?snippet';
import cssCode2 from './d2/styles.css?snippet';
import wrapperCode from './d2/x-wrapper.ts?snippet';
import css from '@helpers/page.css?ctr';
import type { CodeSnippet } from '@helpers/vaadin-demo-code-snippet.js';
declare global {
interface HTMLElementTagNameMap {
'vaadin-demo-animated-transitions': DemoAnimatedTransitions;
}
}
const files1: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode1,
title: 'index.html',
},
{
id: 'ts',
code: tsCode1,
title: 'script.ts',
},
{
id: 'css',
code: cssCode1,
title: 'styles.css',
},
];
const files2: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode2,
title: 'index.html',
},
{
id: 'ts',
code: tsCode2,
title: 'script.ts',
},
{
id: 'css',
code: cssCode2,
title: 'styles.css',
},
{
id: 'wrapper',
code: wrapperCode,
title: 'x-wrapper.ts',
},
];
@customElement('vaadin-demo-animated-transitions')
export default class DemoAnimatedTransitions extends LitElement {
static override styles = [css];
override render(): TemplateResult {
return html`<p>
Vaadin Router allows you to animate transitions between routes. In order to add an animation, do the next steps:
</p>
<ol>
<li>update the router config: add the <code>animate</code> property set to <code>true</code></li>
<li>add <code>@keyframes</code> animations, either in the view Web Component styles or in outside CSS</li>
<li>apply CSS for <code>.leaving</code> and <code>.entering</code> classes to use the animations</li>
</ol>
<p>
The demo below illustrates how to add the transition between all the routes in the same group. You might also
add the transition for the specific routes only, by setting the <code>animate</code>
property on the corresponding route config objects.
</p>
<vaadin-presentation src="./d1/iframe.html">
<vaadin-demo-code-snippet .files=${files1}></vaadin-demo-code-snippet>
</vaadin-presentation>
<p>To run the animated transition, Vaadin Router performs the actions in the following order:</p>
<ol>
<li>render the new view component to the outlet content</li>
<li>set the <code>entering</code> CSS class on the new view component</li>
<li>set the <code>leaving</code> CSS class on the old view component, if any</li>
<li>check if some <code>@keyframes</code> animation applies, and wait for it to complete</li>
<li>remove the old view component from the outlet content</li>
<li>continue the remaining navigation steps as usual</li>
</ol>
<h3>Customize CSS Classes</h3>
<p>
In the basic use case, using single type of the animated transition could be enough to make the web app looking
great, but often we need to configure it depending on the route. Vaadin Router supports this feature by setting
object value to <code>animate</code> property, with the <code>enter</code> and <code>leave</code> string keys.
Their values are used for setting CSS classes to be set on the views.
</p>
<p>
Note that you can first configure animated transition for the group of routes, and then override it for the
single route. In particular, you can switch back to using default CSS classes, as shown in the demo below.
</p>
<vaadin-presentation src="./d2/iframe.html">
<vaadin-demo-code-snippet .files=${files2}></vaadin-demo-code-snippet>
</vaadin-presentation>`;
}
}
================================================
FILE: demo/code-splitting/d1/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Code Splitting</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<a href="/">Home</a>
<a href="/user/guest">User Profile</a>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/code-splitting/d1/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import { Router } from '@vaadin/router';
// tag::snippet[]
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{
path: '/user/:id',
async action() {
await import(`./user.bundle.js`);
},
component: 'x-user-js-bundle-view', // <-- defined in the bundle
},
]);
// end::snippet[]
================================================
FILE: demo/code-splitting/d1/user.bundle.ts
================================================
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import type { RouterLocation } from '../../../src/index.js';
import css from '@helpers/common.css?ctr';
declare global {
interface HTMLElementTagNameMap {
'x-user-js-bundle-view': UserJsBundleView;
}
}
// tag::snippet[]
@customElement('x-user-js-bundle-view')
export default class UserJsBundleView extends LitElement {
static override styles = css;
@property({ attribute: false }) accessor location: RouterLocation | undefined;
override render(): TemplateResult {
return html`<h1>User JS Bundle</h1>
<p>User id: <b>${this.location?.params.id}</b>. This view was loaded using JS bundle.</p>`;
}
}
// end::snippet[]
================================================
FILE: demo/code-splitting/d2/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Code Splitting</title>
<script async type="module" src="./script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<a href="/">Home</a>
<a href="/users">Users</a>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/code-splitting/d2/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-image-view.js';
import '@helpers/x-user-list.js';
import '@helpers/x-user-profile.js';
import { Router } from '@vaadin/router';
// tag::snippet[]
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{
path: '/users',
async children() {
return await import('./user-routes.js').then((mod) => mod.default);
},
},
]);
// end::snippet[]
================================================
FILE: demo/code-splitting/d2/user-routes.ts
================================================
import '@helpers/x-user-list.js';
import '@helpers/x-user-profile.js';
import type { Route } from '@vaadin/router';
// tag::snippet[]
const usersRoutes: readonly Route[] = [
{ path: '/', component: 'x-user-list' },
{ path: '/:user', component: 'x-user-profile' },
];
// end::snippet[]
export default usersRoutes;
================================================
FILE: demo/code-splitting/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Code Splitting</title>
<script async type="module" src="./index.ts"></script>
</head>
<body>
<vaadin-demo-layout app-title="Code Splitting">
<vaadin-demo-code-splitting></vaadin-demo-code-splitting>
</vaadin-demo-layout>
</body>
</html>
================================================
FILE: demo/code-splitting/index.ts
================================================
/* eslint-disable import/no-duplicates, import/default */
import '@helpers/common.js';
import '@helpers/vaadin-demo-code-snippet.js';
import '@helpers/vaadin-demo-layout.js';
import '@helpers/vaadin-presentation.js';
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import htmlCode1 from './d1/iframe.html?snippet';
import tsCode1 from './d1/script.js?snippet';
import bundleCode from './d1/user.bundle.js?snippet';
import htmlCode2 from './d2/iframe.html?snippet';
import tsCode2 from './d2/script.js?snippet';
import userRoutesCode from './d2/user-routes.js?snippet';
import css from '@helpers/page.css?ctr';
import type { CodeSnippet } from '@helpers/vaadin-demo-code-snippet.js';
declare global {
interface HTMLElementTagNameMap {
'vaadin-demo-code-splitting': DemoCodeSplitting;
}
}
const files1: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode1,
title: 'iframe1.html',
},
{
id: 'ts',
code: tsCode1,
title: 'script1.ts',
},
{
id: 'bundle',
code: bundleCode,
title: 'user-bundle.ts',
},
];
const files2: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode2,
title: 'iframe2.html',
},
{
id: 'ts',
code: tsCode2,
title: 'script2.ts',
},
{
id: 'routes',
code: userRoutesCode,
title: 'user-routes.ts',
},
];
@customElement('vaadin-demo-code-splitting')
export default class DemoCodeSplitting extends LitElement {
static override styles = [css];
override render(): TemplateResult {
return html`<h3>Using Dynamic Imports</h3>
<p>
Vaadin Router allows you to implement your own loading mechanism for bundles using custom
<a href="/route-actions/">Route Actions</a>. In that case, you can use
<a
href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import"
target="_blank"
rel="noopener"
>dynamic imports</a
>.
</p>
<p>
Note: If the dynamically loaded route has lifecycle callbacks, the action should return a promise that resolves
only when the route component is loaded (like in the example below). Otherwise the lifecycle callbacks on the
dynamically loaded route's web component are not called.
</p>
<vaadin-presentation src="./d1/iframe.html">
<vaadin-demo-code-snippet .files=${files1}></vaadin-demo-code-snippet>
</vaadin-presentation>
<p>
If dynamic imports are used both for parent and child routes, then the example above may possibly slow down
rendering because router would not start importing a child component until its parent is imported.
</p>
<h3>Splitting and Lazy-Loading the Routes Configuration</h3>
<p>
Vaadin Router supports splitting the routes configuration object into parts and lazily loading them on-demand,
enabling developers to create non-monolithic app structures. This might be useful for implementing a distributed
sub routes configuration within a big project, so that multiple teams working on different parts of the app no
longer have to merge their changes into the same file.
</p>
<p>
The <code>children</code> property on the route config object can be set to a function, which returns an array
of the route objects. It may return a <code><b>Promise</b></code
>, which allows to dynamically import the configuration file, and return the children array exported from it.
</p>
<p>
See the <a href="/API/classes/Router.html#setRoutes">API documentation</a> for detailed description of the
<code>children</code> callback function.
</p>
<vaadin-presentation src="./d2/iframe.html">
<vaadin-demo-code-snippet .files=${files2}></vaadin-demo-code-snippet>
</vaadin-presentation>`;
}
}
================================================
FILE: demo/getting-started/d1/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Getting Started</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<a href="/">Home</a>
<a href="/users/guest">User Profile</a>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/getting-started/d1/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-user-list.js';
import '@helpers/x-user-not-found-view.js';
import '@helpers/x-not-found-view.js';
import { Router } from '@vaadin/router';
// tag::snippet[]
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{ path: '/users', component: 'x-user-list' },
{ path: '/users/(.*)', component: 'x-user-not-found-view' },
{ path: '(.*)', component: 'x-not-found-view' },
]);
// end::snippet[]
================================================
FILE: demo/getting-started/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Getting Started</title>
<script async type="module" src="./index.ts"></script>
</head>
<body>
<vaadin-demo-layout app-title="Getting Started">
<vaadin-demo-getting-started></vaadin-demo-animated-transitions>
</vaadin-demo-layout>
</body>
</html>
================================================
FILE: demo/getting-started/index.ts
================================================
/* eslint-disable import/no-duplicates, import/default */
import '@helpers/common.js';
import '@helpers/vaadin-demo-layout.js';
import '@helpers/vaadin-demo-code-snippet.js';
import '@helpers/vaadin-presentation.js';
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import htmlCode1 from './d1/iframe.html?snippet';
import tsCode1 from './d1/script.js?snippet';
import htmlSnippet1 from './snippets/s1.html?snippet';
import tsSnippet1 from './snippets/s2.ts?snippet';
import tsSnippet2 from './snippets/s4.ts?snippet';
import css from '@helpers/page.css?ctr';
import type { CodeSnippet } from '@helpers/vaadin-demo-code-snippet.js';
declare global {
interface HTMLElementTagNameMap {
'vaadin-demo-getting-started': DemoGettingStarted;
}
}
const files1: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode1,
title: 'index.html',
},
{
id: 'ts',
code: tsCode1,
title: 'script.ts',
},
];
@customElement('vaadin-demo-getting-started')
export default class DemoGettingStarted extends LitElement {
static override styles = [css];
override render(): TemplateResult {
return html`<h3>The <code>Router</code> class</h3>
<p>
The <code>Router</code> class is the only thing you need to get started with Vaadin Router. Depending on your
project setup, there are several ways to access it.
</p>
<p>
<strong>In modern browsers that support ES modules</strong> the <code> Router</code> class can be imported
directly into a script tag on a page:
</p>
<vaadin-demo-code-snippet .files=${[{ code: htmlSnippet1 }]}></vaadin-demo-code-snippet>
<p>
<strong>In Vite / Webpack / Rollup projects</strong> the <code>Router</code> class can be imported from the
<code>@vaadin/router</code> npm package:
</p>
<vaadin-demo-code-snippet .files=${[{ code: tsSnippet1 }]}></vaadin-demo-code-snippet>
<h3>Getting Started</h3>
<p>
Vaadin Router automatically listens to navigation events and asynchronously renders a matching Web Component
into the given DOM node (a.k.a. the router <em>outlet</em>). By default, navigation events are triggered by
<code>popstate</code> events on the <code>window</code>, and by <code>click</code> events on
<code><a></code> elements on the page.
</p>
<p>
The routes config maps URL paths to Web Components. Vaadin Router goes through the routes until it finds the
first match, creates an instance of the route component, and inserts it into the router outlet (replacing any
pre-existing outlet content). For details on the route path syntax see the
<a href="/route-parameters/">Route Parameters </a> demos.
</p>
<vaadin-presentation src="./d1/iframe.html">
<vaadin-demo-code-snippet .files=${files1}></vaadin-demo-code-snippet>
</vaadin-presentation>
<p>
Route components can be any Web Components regardless of how they are built: vanilla JavaScript, Lit, Stencil,
SkateJS, Angular, Vue, etc.
</p>
<p>
Vaadin Router follows the lifecycle callbacks convention described in
<a href="/API/interfaces/WebComponentInterface.html">WebComponentInterface</a>: if a route component defines any
of these callback methods, Vaadin Router will call them at the appropriate points in the navigation lifecycle.
See the <a href="/lifecycle-callback/">Lifecycle Callbacks</a> section for more details.
</p>
<p>
In addition to that Vaadin Router also sets a
<a href="/API/interfaces/RouterLocation.html"><code>location</code> property</a> on every route Web Component so
that you could access the current location details via an instance property (e.g.
<code>this.location.pathname</code>).
</p>
<h3>Using <code>this.location</code></h3>
<p>
For LitElement and TypeScript a declaration in the component is required. Declare the
<code>location</code> property in the class and initialize it from the <code>router</code> Vaadin Router
instance:
</p>
<vaadin-demo-code-snippet .files=${[{ code: tsSnippet2 }]}></vaadin-demo-code-snippet>
<p>This property is automatically updated on navigation.</p>
<h3>Fallback Routes (404)</h3>
<p>
If Vaadin Router does not find a matching route, the promise returned from the <code>render()</code> method gets
rejected, and any content in the router outlet is removed. In order to show a user-friendly 'page not found'
view, a fallback route with a wildcard <code>'(.*)'</code> path can be added to the <strong>end</strong> of the
routes list.
</p>
<p>
There can be different fallbacks for different route prefixes, but since the route resolution is based on the
first match, the fallback route should always be <strong>after</strong> other alternatives.
</p>
<p>
The path that leads to the fallback route is available to the route component via the
<code>location.pathname</code> property.
</p>`;
}
}
================================================
FILE: demo/getting-started/snippets/s1.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Snippets</title>
</head>
<body>
<!-- tag::snippet[] -->
<script type="module">
import { Router } from 'https://unpkg.com/@vaadin/router';
</script>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/getting-started/snippets/s2.ts
================================================
// tag::snippet[]
import { Router } from '@vaadin/router';
// end::snippet[]
export const router = new Router();
================================================
FILE: demo/getting-started/snippets/s4.ts
================================================
/* eslint-disable import/order, import/no-duplicates */
// tag::snippet[]
import type { RouterLocation } from '@vaadin/router';
import { html, LitElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { router } from './s2.js';
@customElement('my-view')
class MyViewElement extends LitElement {
@property({ type: Object }) accessor location: RouterLocation = router.location;
override render() {
return html`Current location URL: ${this.location.getUrl()}`;
}
}
// end::snippet[]
declare global {
interface HTMLElementTagNameMap {
'my-view': MyViewElement;
}
}
================================================
FILE: demo/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<script>
if (!window.location.pathname.endsWith('/getting-started/index.html')) {
window.location.replace('./getting-started/index.html');
}
</script>
</head>
</html>
================================================
FILE: demo/index.ts
================================================
import '@vaadin/vaadin-lumo-styles/all-imports.js';
import '@helpers/vaadin-demo-layout.js';
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('theme', 'dark');
}
================================================
FILE: demo/lifecycle-callback/d1/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Lifecycle Callback</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<a href="/">Home</a>
<a href="/go">Are you ready?</a>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/lifecycle-callback/d1/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import { Router } from '@vaadin/router';
import './x-countdown.js';
// tag::snippet[]
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{ path: '/go', component: 'x-countdown' },
]);
// end::snippet[]
================================================
FILE: demo/lifecycle-callback/d1/x-countdown.ts
================================================
import { html, LitElement, render, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import type { RouterLocation, WebComponentInterface } from '../../../src/types.t.js';
// tag::snippet[]
@customElement('x-countdown')
export default class Countdown extends LitElement implements WebComponentInterface {
readonly #home = document.body.querySelector('x-home-view');
#count = 0;
#timer?: ReturnType<typeof setInterval>;
override render(): TemplateResult {
return html`<h1>Go-go-go!</h1>`;
}
async onBeforeEnter(_: RouterLocation): Promise<void> {
this.#count = 3;
this.#tick();
return await new Promise<void>((resolve) => {
this.#timer = setInterval(() => {
if (this.#count < 0) {
this.#clear();
resolve();
} else {
this.#tick();
}
}, 500);
});
}
#tick(): void {
if (this.#home) {
render(html`<h2>${this.#count}</h2>`, this.#home);
}
this.#count -= 1;
}
#clear(): void {
if (this.#home) {
render(html``, this.#home);
}
clearInterval(this.#timer);
}
}
// end::snippet[]
declare global {
interface HTMLElementTagNameMap {
'x-countdown': Countdown;
}
}
================================================
FILE: demo/lifecycle-callback/d2/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Lifecycle Callback</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<a href="/">Home</a>
<a href="/friend">Meet a friend</a>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/lifecycle-callback/d2/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import { Router } from '@vaadin/router';
import './x-friend.js';
// tag::snippet[]
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{ path: '/friend', component: 'x-friend' },
]);
// end::snippet[]
================================================
FILE: demo/lifecycle-callback/d2/x-friend.ts
================================================
import { css, html, LitElement, render, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import type { RouterLocation, WebComponentInterface } from '../../../src/types.t.js';
// tag::snippet[]
@customElement('x-friend')
export default class Friend extends LitElement implements WebComponentInterface {
static override styles = css`
::slotted(h2) {
color: red !important;
}
`;
override render(): TemplateResult {
return html`<div><slot></slot></div>`;
}
onAfterEnter(_: RouterLocation): void {
render(html`<h2>I am here!</h2>`, this);
}
}
// end::snippet[]
declare global {
interface HTMLElementTagNameMap {
'x-friend': Friend;
}
}
================================================
FILE: demo/lifecycle-callback/d3/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Lifecycle Callback</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/lifecycle-callback/d3/script.ts
================================================
import '@helpers/iframe.script.js';
import { Router } from '@vaadin/router';
import './x-user-deleted.js';
import './x-user-manage.js';
// tag::snippet[]
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', redirect: '/user/guest/manage' },
{ path: '/user/:user/manage', component: 'x-user-manage' },
{ path: '/user/delete', component: 'x-user-deleted' },
]);
// end::snippet[]
================================================
FILE: demo/lifecycle-callback/d3/x-user-deleted.ts
================================================
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import type { WebComponentInterface } from '../../../src/types.t.js';
// tag::snippet[]
@customElement('x-user-deleted')
export default class UserDeleted extends LitElement implements WebComponentInterface {
override render(): TemplateResult {
return html` <div>User has been deleted.</div> `;
}
}
// end::snippet[]
declare global {
interface HTMLElementTagNameMap {
'x-user-deleted': UserDeleted;
}
}
================================================
FILE: demo/lifecycle-callback/d3/x-user-manage.ts
================================================
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import type { RouterLocation, PreventAndRedirectCommands, WebComponentInterface, PreventResult } from '@vaadin/router';
// tag::snippet[]
@customElement('x-user-manage')
export default class UserManage extends LitElement implements WebComponentInterface {
@property({ type: Object }) accessor location: RouterLocation | undefined;
override render(): TemplateResult {
return html`
<div>
<h1>Manage user</h1>
<p>User name: ${this.location?.params.user}</p>
<a href="/user/delete">Delete user</a>
</div>
`;
}
onBeforeLeave(location: RouterLocation, commands: PreventAndRedirectCommands): PreventResult | undefined {
if (location.pathname.indexOf('user/delete') > 0) {
// eslint-disable-next-line no-alert
if (!window.confirm('Are you sure you want to delete this user?')) {
return commands.prevent();
}
}
return undefined;
}
}
// end::snippet[]
declare global {
interface HTMLElementTagNameMap {
'x-user-manage': UserManage;
}
}
================================================
FILE: demo/lifecycle-callback/d4/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Lifecycle Callback</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/lifecycle-callback/d4/script.ts
================================================
import '@helpers/iframe.script.js';
import { Router } from '@vaadin/router';
import './x-main-page.js';
import './x-autosave-view.js';
// tag::snippet[]
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-main-page' },
{ path: '/edit', component: 'x-autosave-view' },
]);
// end::snippet[]
================================================
FILE: demo/lifecycle-callback/d4/x-autosave-view.ts
================================================
/* eslint-disable @typescript-eslint/unbound-method */
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import type { WebComponentInterface } from '../../../src/types.t.js';
let savedText = 'This text is automatically saved when router navigates away.';
// tag::snippet[]
@customElement('x-autosave-view')
export class AutosaveView extends LitElement implements WebComponentInterface {
@state() accessor #text = savedText;
override render(): TemplateResult {
return html`
<div>
<textarea rows="5" cols="30" .value="${this.#text}" @input="${this.onInput}"></textarea>
</div>
<a href="/">Stop editing</a>
`;
}
onInput(event: Event): void {
const target = event.target as HTMLTextAreaElement;
this.#text = target.value;
}
onAfterEnter(): void {
this.#text = savedText;
}
onAfterLeave(): void {
savedText = this.#text;
}
}
// end::snippet[]
declare global {
interface HTMLElementTagNameMap {
'x-autosave-view': AutosaveView;
}
}
================================================
FILE: demo/lifecycle-callback/d4/x-main-page.ts
================================================
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
// tag::snippet[]
@customElement('x-main-page')
export class MainPage extends LitElement {
override render(): TemplateResult {
return html`<a href="/edit">Edit the text</a>`;
}
}
// end::snippet[]
declare global {
interface HTMLElementTagNameMap {
'x-main-page': MainPage;
}
}
================================================
FILE: demo/lifecycle-callback/d5/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Lifecycle Callback</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<a href="/">Home</a>
<a href="/users">All Users</a>
<a href="/users/kim">Kim</a>
<a href="/about">About</a>
<p id="breadcrumbs"></p>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/lifecycle-callback/d5/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-user-list.js';
import '@helpers/x-user-profile.js';
import '@helpers/x-not-found-view.js';
import { Router } from '@vaadin/router';
// tag::snippet[]
window.addEventListener('vaadin-router-location-changed', (event) => {
const breadcrumbs = document.querySelector('#breadcrumbs')!;
breadcrumbs.textContent = `You are at '${event.detail.location.pathname}'`;
});
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{ path: '/users', component: 'x-user-list' },
{ path: '/users/:user', component: 'x-user-profile' },
{ path: '(.*)', component: 'x-not-found-view' },
]);
// end::snippet[]
================================================
FILE: demo/lifecycle-callback/d6/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Lifecycle Callback</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<a href="/users">All Users</a>
<a href="/users/kim">Kim</a>
<a href="/about">About</a>
<x-breadcrumbs></x-breadcrumbs>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/lifecycle-callback/d6/script.ts
================================================
/* eslint-disable import/no-duplicates */
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-user-list.js';
import '@helpers/x-user-profile.js';
import '@helpers/x-breadcrumbs.js';
import '@helpers/x-not-found-view.js';
import type { Breadcrumb } from '@helpers/x-breadcrumbs.js';
import { Router, type VaadinRouterLocationChangedEvent } from '@vaadin/router';
// tag::snippet[]
type RouteExtension = Readonly<{
xBreadcrumb?: Breadcrumb;
}>;
window.addEventListener('vaadin-router-location-changed', (event: VaadinRouterLocationChangedEvent<RouteExtension>) => {
const {
router,
location: { params },
} = event.detail;
const breadcrumbs = document.querySelector('x-breadcrumbs')!;
breadcrumbs.items = router.location.routes
.map((route) => route.xBreadcrumb)
.filter((xBreadcrumb) => xBreadcrumb != null)
.map(({ href, title }) => ({
title: title.replace(/:user/u, params.user as string),
href: href.replace(/:user/u, params.user as string),
}));
});
const router = new Router<RouteExtension>(document.getElementById('outlet'));
await router.setRoutes([
{
path: '/',
xBreadcrumb: { title: 'home', href: '/' },
children: [
{ path: '/', component: 'x-home-view' },
{
path: '/users',
xBreadcrumb: { title: 'users', href: '/users' },
children: [
{ path: '/', component: 'x-user-list' },
{ path: '/:user', xBreadcrumb: { title: ':user', href: '/users/:user' }, component: 'x-user-profile' },
],
},
],
},
{ path: '(.*)', component: 'x-not-found-view' },
]);
// end::snippet[]
================================================
FILE: demo/lifecycle-callback/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Lifecycle Callback</title>
<script async type="module" src="./index.ts"></script>
</head>
<body>
<vaadin-demo-layout app-title="Lifecycle Callback">
<vaadin-demo-lifecycle-callback></vaadin-demo-animated-transitions>
</vaadin-demo-layout>
</body>
</html>
================================================
FILE: demo/lifecycle-callback/index.ts
================================================
/* eslint-disable import/no-duplicates, import/default */
import '@helpers/common.js';
import '@helpers/vaadin-demo-layout.js';
import '@helpers/vaadin-demo-code-snippet.js';
import '@helpers/vaadin-presentation.js';
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import htmlCode1 from './d1/iframe.html?snippet';
import tsCode1 from './d1/script.js?snippet';
import xCountdownCode from './d1/x-countdown.js?snippet';
import htmlCode2 from './d2/iframe.html?snippet';
import tsCode2 from './d2/script.js?snippet';
import xFriend from './d2/x-friend.js?snippet';
import htmlCode3 from './d3/iframe.html?snippet';
import tsCode3 from './d3/script.js?snippet';
import xUserDeleted from './d3/x-user-deleted.js?snippet';
import xUserManage from './d3/x-user-manage.js?snippet';
import htmlCode4 from './d4/iframe.html?snippet';
import tsCode4 from './d4/script.js?snippet';
import xAutosaveView from './d4/x-autosave-view.js?snippet';
import xMainPage from './d4/x-main-page.js?snippet';
import htmlCode5 from './d5/iframe.html?snippet';
import tsCode5 from './d5/script.js?snippet';
import htmlCode6 from './d6/iframe.html?snippet';
import tsCode6 from './d6/script.js?snippet';
import onAfterEnterCode from './snippets/my-view-with-after-enter.ts?snippet';
import onAfterLeaveCode from './snippets/my-view-with-after-leave.ts?snippet';
import onBeforeEnterCode from './snippets/my-view-with-before-enter.ts?snippet';
import onBeforeLeaveCode from './snippets/my-view-with-before-leave.ts?snippet';
import css from '@helpers/page.css?ctr';
import type { CodeSnippet } from '@helpers/vaadin-demo-code-snippet.js';
declare global {
interface HTMLElementTagNameMap {
'vaadin-demo-lifecycle-callback': DemoLifecycleCallback;
}
}
const files1: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode1,
title: 'iframe1.html',
},
{
id: 'ts',
code: tsCode1,
title: 'script1.ts',
},
{
id: 'x-countdown',
code: xCountdownCode,
title: 'x-countdown.ts',
},
];
const files2: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode2,
title: 'iframe2.html',
},
{
id: 'ts',
code: tsCode2,
title: 'script2.ts',
},
{
id: 'x-friend',
code: xFriend,
title: 'x-friend.ts',
},
];
const files3: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode3,
title: 'iframe3.html',
},
{
id: 'ts',
code: tsCode3,
title: 'script3.ts',
},
{
id: 'x-user-deleted',
code: xUserDeleted,
title: 'x-user-deleted.ts',
},
{
id: 'x-user-manage',
code: xUserManage,
title: 'x-user-manage.ts',
},
];
const files4: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode4,
title: 'iframe4.html',
},
{
id: 'ts',
code: tsCode4,
title: 'script4.ts',
},
{
id: 'x-autosave-view',
code: xAutosaveView,
title: 'x-autosave-view.ts',
},
{
id: 'x-main-page',
code: xMainPage,
title: 'x-main-page.ts',
},
];
const files5: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode5,
title: 'iframe5.html',
},
{
id: 'ts',
code: tsCode5,
title: 'script5.ts',
},
];
const files6: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode6,
title: 'iframe6.html',
},
{
id: 'ts',
code: tsCode6,
title: 'script6.ts',
},
];
@customElement('vaadin-demo-lifecycle-callback')
export default class DemoLifecycleCallback extends LitElement {
static override styles = [css];
override render(): TemplateResult {
return html`<h3>Lifecycle Callbacks</h3>
<p>
Vaadin Router manages the lifecycle of all route Web Components.
<em>Lifecycle callbacks</em> allow you to add custom logic to any point of this lifecycle:
</p>
<ul>
<li>
<b
><code>
<a href="/API/interfaces/WebComponentInterface.html#onBeforeEnter">
onBeforeEnter(location, commands, router)</a
>: Promise | Prevent | Redirect | void</code
></b
>
</li>
<li>
<b
><code>
<a href="/API/interfaces/WebComponentInterface.html#onAfterEnter">
onAfterEnter(location, commands, router)</a
>: void</code
></b
>
</li>
<li>
<b
><code>
<a href="/API/interfaces/WebComponentInterface.html#onBeforeLeave">
onBeforeLeave(location, commands, router)</a
>: Promise | Prevent | void
</code></b
>
</li>
<li>
<b
><code>
<a href="/API/interfaces/WebComponentInterface.html#onAfterLeave">
onAfterLeave(location, commands, router)</a
>: void</code
></b
>
</li>
<hr />
<li>
the <code>'vaadin-router-location-changed'</code> / <code>'vaadin-router-error'</code> events on the
<code>window</code>
</li>
</ul>
<p>
Vaadin Router lifecycle callbacks can be defined as methods on the route Web Component
<a
href="https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-definition"
target="_blank"
rel="noopener"
>class definition</a
>
in a similar way as the native custom element callbacks (like <code>disconnectedCallback()</code>).
</p>
<h3><code>onBeforeEnter(location, commands, router)</code></h3>
<p>
The component's route has matched the current path, an instance of the component has been created and is about
to be inserted into the DOM. Use this callback to create a route guard (e.g. redirect to the login page if the
user is not logged in).
</p>
<p>
At this point there is yet
<strong>no guarantee that the navigation into this view will actually happen</strong> because another route's
callback may interfere.
</p>
<p>
This callback may return a <em>redirect</em> (<code>return commands.redirect('/new/path')</code>) or a
<em>prevent</em> (<code>return commands.prevent()</code>) router command. If it returns a promise, the router
waits until the promise is resolved before proceeding with the navigation.
</p>
<p>
See the
<a href="/API/interfaces/WebComponentInterface.html#onBeforeEnter">API documentation</a> for more details.
</p>
<p>
<strong>Note: </strong>Navigating to the same route also triggers this callback, e.g., click on the same link
multiple times will trigger the <code>onBeforeEnter</code>
callback on each click.
</p>
<vaadin-presentation src="./d1/iframe.html">
<vaadin-demo-code-snippet .files=${files1}></vaadin-demo-code-snippet>
</vaadin-presentation>
<h3><code>onAfterEnter(location, commands, router)</code></h3>
<p>
The component's route has matched the current path and an instance of the component has been rendered into the
DOM. At this point it is certain that navigation won't be prevented or redirected. Use this callback to process
route params and initialize the view so that it's ready for user interactions.
</p>
<p>
NOTE: When navigating between views the <code>onAfterEnter</code> callback on the new view's component is called
<em>before</em> the <code>onAfterLeave</code> callback on the previous view's component (which is being
removed). At some point both the new and the old view components are present in the DOM to allow
<a href="/animated-transitions/">animating</a> the transition (you can listen to the
<code>animationend</code> event to detect when it is over).
</p>
<p>
Any value returned from this callback is ignored. See the
<a href="/API/interfaces/WebComponentInterface.html#onAfterEnter">API documentation</a> for more details.
</p>
<vaadin-presentation src="./d2/iframe.html">
<vaadin-demo-code-snippet .files=${files2}></vaadin-demo-code-snippet>
</vaadin-presentation>
<h3><code>onBeforeLeave(location, commands, router)</code></h3>
<p>
The component's route does not match the current path anymore and the component is about to be removed from the
DOM. Use this callback to prevent the navigation if necessary like in the demo below. Note that the user is
still able to copy and open that URL manually in the browser.
</p>
<p>
Even if this callback does not prevent the navigation, at this point there is yet
<strong>no guarantee that the navigation away from this view will actually happen</strong> because another
route's callback may also interfere.
</p>
<p>
This callback may return a <em>prevent</em> (<code>return commands.prevent()</code>) router command. If it
returns a promise, the router waits until the promise is resolved before proceeding with the navigation.
</p>
<p>
See the
<a href="/API/interfaces/WebComponentInterface.html#onBeforeLeave">API documentation</a> for more details.
</p>
<p>
<strong>Note: </strong>Navigating to the same route also triggers this callback, e.g., click on the same link
multiple times will trigger the <code>onBeforeLeave</code>
callback on each click.
</p>
<vaadin-presentation src="./d3/iframe.html">
<vaadin-demo-code-snippet .files=${files3}></vaadin-demo-code-snippet>
</vaadin-presentation>
<h3><code>onAfterLeave(location, commands, router)</code></h3>
<p>
The component's route does not match the current path anymore and the component's removal from the DOM has been
started (it will be removed after a transition animation, if any). At this point it is certain that navigation
won't be prevented. Use this callback to clean-up and perform any custom actions that leaving a view should
trigger. For example, the demo below auto-saves any unsaved changes when the user navigates away from the view.
</p>
<p>
NOTE: When navigating between views the <code>onAfterEnter</code> callback on the new view's component is called
<em>before</em> the <code>onAfterLeave</code> callback on the previous view's component (which is being
removed). At some point both the new and the old view components are present in the DOM to allow
<a href="/animated-transitions/">animating</a> the transition (you can listen to the
<code>animationend</code> event to detect when it is over).
</p>
<p>
Any value returned from this callback is ignored. See the
<a href="/API/interfaces/WebComponentInterface.html#onAfterLeave">API documentation</a> for more details.
</p>
<vaadin-presentation src="./d4/iframe.html">
<vaadin-demo-code-snippet .files=${files4}></vaadin-demo-code-snippet>
</vaadin-presentation>
<h3>Listen to Global Navigation Events</h3>
<p>
In order to react to route changes in other parts of the app (outside of route components), you can add an event
listener for the <code> vaadin-router-location-changed</code> events on the <code>window</code>. Vaadin Router
dispatches such events every time after navigating to a new path.
</p>
<vaadin-presentation src="./d5/iframe.html">
<vaadin-demo-code-snippet .files=${files5}></vaadin-demo-code-snippet>
</vaadin-presentation>
<p>
In case if navigation fails for any reason (e.g. if no route matched the given pathname), instead of the
<code>vaadin-router-location-changed</code> event Vaadin Router dispatches <code>vaadin-router-error</code> and
attaches the error object to the event as <code>event.detail.error</code>.
</p>
<h3>Getting the Current Location</h3>
<p>
When handling Vaadin Router events, you can access the router instance via
<code>event.detail.router</code>, and the current location via <code> event.detail.location</code> (which is a
shorthand for <code>event.detail.router.location</code>). The
<a href="/API/interfaces/RouterLocation.html"><code>location</code></a>
object has all details about the current router state. For example,
<code>location.routes</code> is a read-only list of routes that correspond to the last completed navigation,
which may be useful for example when creating a breadcrumbs component to visualize the current in-app location.
</p>
<vaadin-presentation src="./d6/iframe.html">
<vaadin-demo-code-snippet .files=${files6}></vaadin-demo-code-snippet>
</vaadin-presentation>
<p>
The router configuration allows you to add any custom properties to route objects. The example above uses that
to set a custom <code>xBreadcrumb</code> property on the routes that we want to show up in breadcrumbs. That
property is used later when processing the <code>vaadin-router-location-changed </code> events.
</p>
<h3>TypeScript Interfaces</h3>
<p>
For using with components defined as TypeScript classes, the following interfaces are available for
implementing:
</p>
<ul>
<li>
<p>
<b><code>BeforeEnterObserver</code></b>
</p>
<vaadin-demo-code-snippet .files=${[{ code: onBeforeEnterCode }]}></vaadin-demo-code-snippet>
</li>
<li>
<p>
<b><code>AfterEnterObserver</code></b>
</p>
<vaadin-demo-code-snippet .files=${[{ code: onAfterEnterCode }]}></vaadin-demo-code-snippet>
</li>
<li>
<p>
<b><code>BeforeLeaveObserver</code></b>
</p>
<vaadin-demo-code-snippet .files=${[{ code: onBeforeLeaveCode }]}></vaadin-demo-code-snippet>
</li>
<li>
<p>
<b><code>AfterLeaveObserver</code></b>
</p>
<vaadin-demo-code-snippet .files=${[{ code: onAfterLeaveCode }]}></vaadin-demo-code-snippet>
</li>
</ul>`;
}
}
================================================
FILE: demo/lifecycle-callback/snippets/my-view-with-after-enter.ts
================================================
/* eslint-disable @typescript-eslint/no-unused-vars */
import { LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
// tag::snippet[]
import type { EmptyCommands, Router, RouterLocation, WebComponentInterface } from '@vaadin/router';
@customElement('my-view-with-after-enter')
class MyViewWithAfterEnter extends LitElement implements WebComponentInterface {
onAfterEnter(location: RouterLocation, commands: EmptyCommands, router: Router) {
// ...
}
}
// end::snippet[]
declare global {
interface HTMLElementTagNameMap {
'my-view-with-after-enter': MyViewWithAfterEnter;
}
}
================================================
FILE: demo/lifecycle-callback/snippets/my-view-with-after-leave.ts
================================================
/* eslint-disable @typescript-eslint/no-unused-vars */
import { LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
// tag::snippet[]
import type { EmptyCommands, Router, RouterLocation, WebComponentInterface } from '@vaadin/router';
@customElement('my-view-with-after-leave')
class MyViewWithAfterLeave extends LitElement implements WebComponentInterface {
onAfterLeave(location: RouterLocation, commands: EmptyCommands, router: Router) {
// ...
}
}
// end::snippet[]
declare global {
interface HTMLElementTagNameMap {
'my-view-with-after-leave': MyViewWithAfterLeave;
}
}
================================================
FILE: demo/lifecycle-callback/snippets/my-view-with-before-enter.ts
================================================
/* eslint-disable @typescript-eslint/no-unused-vars */
import { LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
// tag::snippet[]
import type { PreventAndRedirectCommands, Router, RouterLocation, WebComponentInterface } from '@vaadin/router';
@customElement('my-view-with-before-enter')
export default class MyViewWithBeforeEnter extends LitElement implements WebComponentInterface {
onBeforeEnter(location: RouterLocation, commands: PreventAndRedirectCommands, router: Router): void {
// ...
}
}
// end::snippet[]
declare global {
interface HTMLElementTagNameMap {
'my-view-with-before-enter': MyViewWithBeforeEnter;
}
}
================================================
FILE: demo/lifecycle-callback/snippets/my-view-with-before-leave.ts
================================================
/* eslint-disable @typescript-eslint/no-unused-vars */
import { LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
// tag::snippet[]
import type { PreventCommands, Router, RouterLocation, WebComponentInterface } from '@vaadin/router';
@customElement('my-view-with-before-leave')
class MyViewWithBeforeLeave extends LitElement implements WebComponentInterface {
onBeforeLeave(location: RouterLocation, commands: PreventCommands, router: Router) {
// ...
}
}
// end::snippet[]
declare global {
interface HTMLElementTagNameMap {
'my-view-with-before-leave': MyViewWithBeforeLeave;
}
}
================================================
FILE: demo/navigation-trigger/d1/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Navigation Trigger</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/navigation-trigger/d1/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-user-list.js';
import { Router } from '@vaadin/router';
// tag::snippet[]
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{ path: '/users', component: 'x-user-list' },
]);
setInterval(() => {
window.history.pushState(null, document.title, window.location.pathname === '/' ? '/users' : '/');
window.dispatchEvent(new PopStateEvent('popstate'));
}, 3000);
// end::snippet[]
================================================
FILE: demo/navigation-trigger/d2/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Navigation Trigger</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<a href="/">Home</a>
<a href="/users">Users</a>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/navigation-trigger/d2/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-user-list.js';
import { Router } from '@vaadin/router';
// tag::snippet[]
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{ path: '/users', component: 'x-user-list' },
]);
// end::snippet[]
================================================
FILE: demo/navigation-trigger/d3/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Navigation Trigger</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<ul>
<li data-href="/">Home</li>
<li data-href="/users">Users</li>
</ul>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/navigation-trigger/d3/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-user-list.js';
import { DEFAULT_TRIGGERS, Router } from '@vaadin/router';
// tag::snippet[]
const { POPSTATE } = DEFAULT_TRIGGERS;
Router.setTriggers(POPSTATE);
document.querySelector('ul')?.addEventListener('click', (event) => {
if (event.target instanceof HTMLLIElement && event.target.dataset.href) {
window.history.pushState({}, '', event.target.dataset.href);
window.dispatchEvent(new PopStateEvent('popstate'));
}
});
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{ path: '/users', component: 'x-user-list' },
]);
// end::snippet[]
================================================
FILE: demo/navigation-trigger/d4/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Navigation Trigger</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<a href="/">Home</a>
<a href="/users">Users</a>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/navigation-trigger/d4/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-user-list.js';
import '@helpers/x-user-profile.js';
import { Router } from '@vaadin/router';
// tag::snippet[]
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{
path: '/users',
children: [
{ path: '', component: 'x-user-list' },
{ path: '/:user', component: 'x-user-profile' },
],
},
]);
router.unsubscribe();
// router will re-render only when the `render()` method is called explicitly:
await router.render('/users');
// end::snippet[]
================================================
FILE: demo/navigation-trigger/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Navigation Trigger</title>
<script async type="module" src="./index.ts"></script>
</head>
<body>
<vaadin-demo-layout app-title="Navigation Trigger">
<vaadin-demo-navigation-trigger></vaadin-demo-navigation-trigger>
</vaadin-demo-layout>
</body>
</html>
================================================
FILE: demo/navigation-trigger/index.ts
================================================
/* eslint-disable import/no-duplicates, import/default */
import '@helpers/common.js';
import '@helpers/vaadin-demo-layout.js';
import '@helpers/vaadin-demo-code-snippet.js';
import '@helpers/vaadin-presentation.js';
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import htmlCode1 from './d1/iframe.html?snippet';
import tsCode1 from './d1/script.js?snippet';
import htmlCode2 from './d2/iframe.html?snippet';
import tsCode2 from './d2/script.js?snippet';
import htmlCode3 from './d3/iframe.html?snippet';
import tsCode3 from './d3/script.js?snippet';
import htmlCode4 from './d4/iframe.html?snippet';
import tsCode4 from './d4/script.js?snippet';
import css from '@helpers/page.css?ctr';
import type { CodeSnippet } from '@helpers/vaadin-demo-code-snippet.js';
declare global {
interface HTMLElementTagNameMap {
'vaadin-demo-navigation-trigger': DemoNavigationTrigger;
}
}
const files1: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode1,
title: 'index.html',
},
{
id: 'ts',
code: tsCode1,
title: 'script.js',
},
];
const files2: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode2,
title: 'index.html',
},
{
id: 'ts',
code: tsCode2,
title: 'script.js',
},
];
const files3: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode3,
title: 'index.html',
},
{
id: 'ts',
code: tsCode3,
title: 'script.js',
},
];
const files4: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode4,
title: 'index.html',
},
{
id: 'ts',
code: tsCode4,
title: 'script.js',
},
];
@customElement('vaadin-demo-navigation-trigger')
export default class DemoNavigationTrigger extends LitElement {
static override styles = [css];
override render(): TemplateResult {
return html`<h3>Navigation Triggers</h3>
<p class="note">This feature is for advanced use cases. Please make sure to read the documentation carefully.</p>
<p>
There are several events that can trigger in-app navigation with Vaadin Router: popstate events, clicks on the
<code><a></code> elements, imperative navigation triggered by JavaScript. In order to make a flexible
solution that can be tweaked to particular app needs and remain efficient, Vaadin Router has a concept of
pluggable <em>Navigation Triggers</em> that listen to specific browser events and convert them into the Vaadin
Router navigation events.
</p>
<p>
The <code>@vaadin/router</code> package comes with two Navigation Triggers bundled by default:
<code>POPSTATE</code> and <code>CLICK</code>.
</p>
<p>
Developers can define and use additional Navigation Triggers that are specific to their application. A
Navigation Trigger object must have two methods: <code>activate()</code> to start listening on some UI events,
and <code>inactivate()</code> to stop.
</p>
<h3><code>NavigationTrigger.POPSTATE</code></h3>
<p>
The default <code>POPSTATE</code> navigation trigger for Vaadin Router listens to <code>popstate</code> events
on the current <code>window</code> and for each of them dispatches a navigation event for Vaadin Router using
the current <code>window.location.pathname</code> as the navigation target. This allows using the browser
Forward and Back buttons for in-app navigation.
</p>
<p>
In the demo below the <code>popstate</code> events are dispatched at 3 seconds intervals. Before dispatching an
event the global <code> location.pathname</code> is toggled between '/' and '/users'.
</p>
<p>
Please note that when using the <code>window.history.pushState()</code> or
<code>window.history.replaceState()</code> methods, you need to dispatch the <code>popstate</code> event
manually—these methods do not do that by themselves (see
<a href="https://developer.mozilla.org/en-US/docs/Web/API/History_API" target="_blank" rel="noopener">MDN</a>
for details).
</p>
<vaadin-presentation src="./d1/iframe.html">
<vaadin-demo-code-snippet .files=${files1}></vaadin-demo-code-snippet>
</vaadin-presentation>
<h3><code>NavigationTrigger.CLICK</code></h3>
<p>
The <code>CLICK</code> navigation trigger intercepts clicks on <code><a></code> elements on the the page
and converts them into navigation events for Vaadin Router if they refer to a location within the app. That
allows using regular <code><a></code> link elements for in-app navigation. You can use
<code>router-ignore</code>
attribute to have the router ignore the link.
</p>
<vaadin-presentation src="./d2/iframe.html">
<vaadin-demo-code-snippet .files=${files2}></vaadin-demo-code-snippet>
</vaadin-presentation>
<h3>Custom Navigation Triggers</h3>
<p>
The set of default navigation triggers can be changed using the <code> Router.setTriggers()</code> static
method. It accepts zero, one or more <code>NavigationTrigger</code>s.
</p>
<p>
This demo shows how to disable the <code>CLICK</code> navigation trigger. It may be useful when the app has an
own custom element for in-app links instead of using the regular <code><a></code> elements for that
purpose. The demo also shows a very basic version of a custom in-app link element based on a list element that
fires <code>popstate</code> events when clicked.
</p>
<p>
Note: if the default Navigation Triggers are not used by the app, they can be excluded from the app bundle to
avoid sending unnecessary code to the users. See <code>src/router-config.js</code> for details.
</p>
<vaadin-presentation src="./d3/iframe.html">
<vaadin-demo-code-snippet .files=${files3}></vaadin-demo-code-snippet>
</vaadin-presentation>
<h3>Unsubscribe from Navigation Events</h3>
<p>
Each Vaadin Router instance is automatically subscribed to navigation events upon creation. Sometimes it might
be necessary to cancel this subscription so that the router would re-render only in response to the explicit
<code>render()</code> method calls. Use the <code>unsubscribe() </code> method to cancel this automatic
subscription and the <code> subscribe()</code> method to re-subscribe.
</p>
<vaadin-presentation src="./d4/iframe.html">
<vaadin-demo-code-snippet .files=${files4}></vaadin-demo-code-snippet>
</vaadin-presentation>`;
}
}
================================================
FILE: demo/redirect/d1/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Redirect</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<a href="/">Home</a>
<a href="/u/Kim">User profile</a>
<a href="/data/entity/view/12">Knowledge Base</a>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/redirect/d1/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-user-profile.js';
import '@helpers/x-knowledge-base.js';
import { Router } from '@vaadin/router';
// tag::snippet[]
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{ path: '/u/:user', redirect: '/user/:user' },
{ path: '/user/:user', component: 'x-user-profile' },
{ path: '/data/:segments+/:path+', redirect: '/kb/:path+' },
{ path: '/kb/:path+', component: 'x-knowledge-base' },
]);
// end::snippet[]
================================================
FILE: demo/redirect/d2/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Redirect</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<a href="/">Home</a>
<a href="/admin">Admin</a>
<a href="/login">Login</a>
<a href="/logout">Logout</a>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/redirect/d2/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-login-view.js';
import { Router } from '@vaadin/router';
import './x-admin-view.js';
// tag::snippet[]
window.authorized = false;
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{ path: '/admin', component: 'x-admin-view' },
{ path: '/login/:to?', component: 'x-login-view' },
{
path: '/logout',
action(_, commands) {
window.authorized = false;
return commands.redirect('/');
},
},
]);
// end::snippet[]
================================================
FILE: demo/redirect/d2/x-admin-view.ts
================================================
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import type { Commands, RedirectResult, RouterLocation, WebComponentInterface } from '@vaadin/router';
declare global {
interface HTMLElementTagNameMap {
'x-admin-view': AdminView;
}
interface Window {
authorized: boolean;
}
}
// tag::snippet[]
@customElement('x-admin-view')
export default class AdminView extends LitElement implements WebComponentInterface {
onBeforeEnter(location: RouterLocation, commands: Commands): RedirectResult | undefined {
if (!window.authorized) {
return commands.redirect(`/login/${encodeURIComponent(location.pathname)}`);
}
return undefined;
}
override render(): TemplateResult {
return html`Secret admin stuff`;
}
}
// end::snippet[]
================================================
FILE: demo/redirect/d3/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Redirect</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<button id="trigger">Open <code>/user/you-know-who</code></button>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/redirect/d3/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-user-profile.js';
import { Router } from '@vaadin/router';
// tag::snippet[]
document.querySelector('#trigger')?.addEventListener('click', () => {
Router.go('/user/you-know-who');
});
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{ path: '/user/:user', component: 'x-user-profile' },
]);
// end::snippet[]
================================================
FILE: demo/redirect/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Redirect</title>
<script async type="module" src="./index.ts"></script>
</head>
<body>
<vaadin-demo-layout app-title="Redirect">
<vaadin-demo-redirect></vaadin-demo-animated-transitions>
</vaadin-demo-layout>
</body>
</html>
================================================
FILE: demo/redirect/index.ts
================================================
/* eslint-disable import/no-duplicates, import/default */
import '@helpers/common.js';
import '@helpers/vaadin-demo-layout.js';
import '@helpers/vaadin-demo-code-snippet.js';
import '@helpers/vaadin-presentation.js';
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import htmlCode1 from './d1/iframe.html?snippet';
import tsCode1 from './d1/script.js?snippet';
import htmlCode2 from './d2/iframe.html?snippet';
import tsCode2 from './d2/script.js?snippet';
import htmlCode3 from './d3/iframe.html?snippet';
import tsCode3 from './d3/script.js?snippet';
import tsSnippet1 from './snippets/s1.ts?snippet';
import tsSnippet2 from './snippets/s2.ts?snippet';
import tsSnippet3 from './snippets/s3.ts?snippet';
import css from '@helpers/page.css?ctr';
import type { CodeSnippet } from '@helpers/vaadin-demo-code-snippet.js';
declare global {
interface HTMLElementTagNameMap {
'vaadin-demo-redirect': DemoRedirect;
}
}
const files1: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode1,
title: 'index.html',
},
{
id: 'ts',
code: tsCode1,
title: 'script.js',
},
];
const files2: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode2,
title: 'index.html',
},
{
id: 'ts',
code: tsCode2,
title: 'script.js',
},
];
const files3: readonly CodeSnippet[] = [
{
id: 'html',
code: htmlCode3,
title: 'index.html',
},
{
id: 'ts',
code: tsCode3,
title: 'script.js',
},
];
@customElement('vaadin-demo-redirect')
export default class DemoRedirect extends LitElement {
static override styles = [css];
override render(): TemplateResult {
return html`<h3>Unconditional Redirects</h3>
<p>
Vaadin Router supports the <code>redirect</code> property on the route
objects, allowing to unconditionally redirect users from one path to
another. The valid values are a path string or a pattern in the same
format as used for the <code>path</code> property.
</p>
<p>
The original path is not stored as the <code>window.history</code> entry
and cannot be reached by pressing the "Back" browser button. Unconditional
redirects work for routes both with and without parameters.
</p>
<p>
The original path is available to route Web Components as the
<a href="/API/interfaces/RouterLocation.html#redirectFrom">
<code>location.redirectFrom</code></a> string property, and to custom
<a href="/route-actions/">route actions</a> –
as <code>context.redirectFrom</code>.
</p>
<p>
Note: If a route has both the <code>redirect</code> and <code>action</code>
properties, <code>action</code> is executed first and if it does not
return a result Vaadin Router proceeds to check the <code>redirect</code>
property. Other route properties (if any) would be ignored. In that case
Vaadin Router would also log a warning to the browser console.
</p>
<vaadin-presentation src="./d1/iframe.html">
<vaadin-demo-code-snippet .files=${files1}></vaadin-demo-code-snippet>
</vaadin-presentation>
<h3>Dynamic Redirects</h3>
<p>
Vaadin Router allows redirecting to another path dynamically based on
a condition evaluated at the run time. In order to do that, <code>
return commands.redirect('/new/path')</code> from the
<a href="/lifecycle-callback/"><code>onBeforeEnter()</code></a> lifecycle
callback of the route Web Component.
</p>
<p>
It is also possible to redirect from a custom route action. The demo below
has an example of that in the <code>/logout</code> route action. See the
<a href="/route-actions/">Route Actions</a> section for
more details.
</p>
<vaadin-presentation src="./d2/iframe.html">
<vaadin-demo-code-snippet .files=${files2}></vaadin-demo-code-snippet>
</vaadin-presentation>
<h3>Navigation from JavaScript</h3>
<p>
If you want to send users to another path in response to a user
action (outside of a lifecycle callback), you can do that by using the
static <a href="/API/classes/Router.html#go"><code>
Router.go('/to/path')</code></a> method on the Vaadin.Router class.
</p>
<p>
You can optionally pass search query string and hash to the method, either
as in-app URL string:
</p>
<vaadin-demo-code-snippet .files=${[{ code: tsSnippet1 }]}></vaadin-demo-code-snippet>
... or using an object with named parameters:
</p>
<vaadin-demo-code-snippet .files=${[{ code: tsSnippet2 }]}></vaadin-demo-code-snippet>
<vaadin-presentation src="./d3/iframe.html">
<vaadin-demo-code-snippet .files=${files3}></vaadin-demo-code-snippet>
</vaadin-presentation>
<p>
NOTE: the same effect can be achieved by dispatching a <code>
vaadin-router-go</code> custom event on the <code>window</code>. The
target path should be provided as <code>event.detail.pathname</code>,
the search and hash strings can be optionally provided
with <code>event.detail.search</code> and <code>event.detail.hash</code>
properties respectively.
</p>
<vaadin-demo-code-snippet .files=${[{ code: tsSnippet3 }]}></vaadin-demo-code-snippet>`;
}
}
================================================
FILE: demo/redirect/snippets/s1.ts
================================================
import { Router } from '@vaadin/router';
// tag::snippet[]
Router.go('/to/path?paramName=value#sectionName');
// end::snippet[]
================================================
FILE: demo/redirect/snippets/s2.ts
================================================
import { Router } from '@vaadin/router';
// tag::snippet[]
Router.go({
pathname: '/to/path',
// optional
search: '?paramName=value',
// optional
hash: '#sectionName',
});
// end::snippet[]
================================================
FILE: demo/redirect/snippets/s3.ts
================================================
// tag::snippet[]
window.dispatchEvent(
new CustomEvent('vaadin-router-go', {
detail: {
pathname: '/to/path',
// optional search query string
search: '?paramName=value',
// optional hash string
hash: '#sectionName',
},
}),
);
// end::snippet[]
export {};
================================================
FILE: demo/route-actions/d1/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Route Actions</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<nav>
<a href="/">Home</a>
<a href="/users">All Users</a>
<a href="/users/kim">Kim</a>
</nav>
<div id="stats"></div>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/route-actions/d1/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-user-list.js';
import '@helpers/x-user-profile.js';
import { Router, type RouteContext } from '@vaadin/router';
// tag::snippet[]
const urlToNumberOfVisits: Record<string, number | undefined> = {};
async function recordUrlVisit(context: RouteContext) {
const visitedPath = context.pathname; // get current path
urlToNumberOfVisits[visitedPath] = (urlToNumberOfVisits[visitedPath] ?? 0) + 1;
document.getElementById('stats')!.textContent =
`Statistics on URL visits: ${JSON.stringify(urlToNumberOfVisits, null, 2)}}`;
return await context.next(); // pass to the next route in the list
}
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{
path: '/users',
action: recordUrlVisit, // will be triggered for all children
children: [
{ path: '/', component: 'x-user-list' },
{ path: '/:user', component: 'x-user-profile' },
],
},
]);
// end::snippet[]
================================================
FILE: demo/route-actions/d2/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Route Actions</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<nav>
<a href="/">Home (no delay on open)</a>
<a href="/users">All Users (slight delay on open)</a>
<a href="/users/kim">Kim (slight delay on open)</a>
</nav>
<div id="outlet"></div>
<!-- end::snippet[] -->
</body>
</html>
================================================
FILE: demo/route-actions/d2/script.ts
================================================
import '@helpers/iframe.script.js';
import '@helpers/x-home-view.js';
import '@helpers/x-user-list.js';
import '@helpers/x-user-profile.js';
import { Router } from '@vaadin/router';
// tag::snippet[]
async function pollBackendForChanges() {
return await new Promise<void>((resolve) => {
// this can be an async backend call
setTimeout(() => resolve(), 1000);
});
}
const router = new Router(document.getElementById('outlet'));
await router.setRoutes([
{ path: '/', component: 'x-home-view' },
{
path: '/users',
action: pollBackendForChanges, // will be triggered for all children
children: [
{ path: '/', component: 'x-user-list' },
{ path: '/:user', component: 'x-user-profile' },
],
},
]);
// end::snippet[]
================================================
FILE: demo/route-actions/d3/iframe.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Route Actions</title>
<script async type="module" src="script.ts"></script>
</head>
<body>
<!-- tag::snippet[] -->
<nav>
<a href="/">Home</a>
<a href="/employees/Kim">Route that redirects</a>
<a href="/users/Kim">Redirect target</a>
</nav>
<div id="outlet"></div>
<!-- end::snippet[] -->
gitextract_suxs7xzw/ ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ ├── docs.yml │ └── validation.yml ├── .gitignore ├── .prettierrc.json ├── .run/ │ ├── All tests.run.xml │ └── Template Karma.run.xml ├── .stylelintrc.json ├── LICENSE ├── README.md ├── analysis.json ├── demo/ │ ├── .eslintrc.json │ ├── @debug/ │ │ ├── index.html │ │ └── index.ts │ ├── @helpers/ │ │ ├── common.css │ │ ├── common.ts │ │ ├── iframe.script.ts │ │ ├── nested-styles.css │ │ ├── page.css │ │ ├── shared-styles.css │ │ ├── theme-controller.ts │ │ ├── vaadin-demo-code-snippet-file.css │ │ ├── vaadin-demo-code-snippet-file.ts │ │ ├── vaadin-demo-code-snippet.css │ │ ├── vaadin-demo-code-snippet.ts │ │ ├── vaadin-demo-layout.css │ │ ├── vaadin-demo-layout.ts │ │ ├── vaadin-presentation-addressbar.css │ │ ├── vaadin-presentation-addressbar.ts │ │ ├── vaadin-presentation.css │ │ ├── vaadin-presentation.ts │ │ ├── x-breadcrumbs.ts │ │ ├── x-home-view.ts │ │ ├── x-image-view.css │ │ ├── x-image-view.ts │ │ ├── x-knowledge-base.ts │ │ ├── x-login-view.ts │ │ ├── x-not-found-view.ts │ │ ├── x-profile-view.ts │ │ ├── x-user-list.ts │ │ ├── x-user-not-found-view.ts │ │ ├── x-user-numeric-view.ts │ │ └── x-user-profile.ts │ ├── animated-transitions/ │ │ ├── d1/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── styles.css │ │ ├── d2/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ ├── styles.css │ │ │ └── x-wrapper.ts │ │ ├── index.html │ │ └── index.ts │ ├── code-splitting/ │ │ ├── d1/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── user.bundle.ts │ │ ├── d2/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── user-routes.ts │ │ ├── index.html │ │ └── index.ts │ ├── getting-started/ │ │ ├── d1/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── index.html │ │ ├── index.ts │ │ └── snippets/ │ │ ├── s1.html │ │ ├── s2.ts │ │ └── s4.ts │ ├── index.html │ ├── index.ts │ ├── lifecycle-callback/ │ │ ├── d1/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── x-countdown.ts │ │ ├── d2/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── x-friend.ts │ │ ├── d3/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ ├── x-user-deleted.ts │ │ │ └── x-user-manage.ts │ │ ├── d4/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ ├── x-autosave-view.ts │ │ │ └── x-main-page.ts │ │ ├── d5/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── d6/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── index.html │ │ ├── index.ts │ │ └── snippets/ │ │ ├── my-view-with-after-enter.ts │ │ ├── my-view-with-after-leave.ts │ │ ├── my-view-with-before-enter.ts │ │ └── my-view-with-before-leave.ts │ ├── navigation-trigger/ │ │ ├── d1/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── d2/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── d3/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── d4/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── index.html │ │ └── index.ts │ ├── redirect/ │ │ ├── d1/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── d2/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── x-admin-view.ts │ │ ├── d3/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── index.html │ │ ├── index.ts │ │ └── snippets/ │ │ ├── s1.ts │ │ ├── s2.ts │ │ └── s3.ts │ ├── route-actions/ │ │ ├── d1/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── d2/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── d3/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── d4/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── d5/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── index.html │ │ └── index.ts │ ├── route-parameters/ │ │ ├── d1/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── d2/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── x-project-view.ts │ │ ├── d3/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── d4/ │ │ │ ├── iframe.html │ │ │ └── script.ts │ │ ├── d5/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── x-page-number-view.ts │ │ ├── d6/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── x-hash-view.ts │ │ ├── index.html │ │ └── index.ts │ ├── tsconfig.json │ ├── types.t.ts │ ├── url-generation/ │ │ ├── d1/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── x-main-layout.ts │ │ ├── d2/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── x-main-layout.ts │ │ ├── d3/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── x-user-layout-d3.ts │ │ ├── d4/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── x-user-layout-d4.ts │ │ ├── d5/ │ │ │ ├── iframe.html │ │ │ ├── script.ts │ │ │ └── x-pages-menu.ts │ │ ├── index.html │ │ └── index.ts │ └── vite.config.ts ├── index.html ├── karma.config.cjs ├── package.json ├── polymer.json ├── scripts/ │ ├── build.ts │ ├── codeSnippet.ts │ ├── constructCss.ts │ ├── copy-dts.ts │ ├── loadRegisterJs.ts │ ├── register.js │ ├── resolveHTMLImports.ts │ └── types.d.ts ├── src/ │ ├── index.ts │ ├── mod.t.ts │ ├── resolver/ │ │ ├── LICENSE.txt │ │ ├── generateUrls.ts │ │ ├── matchPath.ts │ │ ├── matchRoute.ts │ │ ├── resolveRoute.ts │ │ ├── resolver.ts │ │ ├── types.t.ts │ │ └── utils.ts │ ├── router-config.ts │ ├── router-meta.ts │ ├── router.ts │ ├── transitions/ │ │ └── animate.ts │ ├── triggers/ │ │ ├── click.ts │ │ ├── navigation.ts │ │ └── popstate.ts │ ├── types.t.ts │ ├── utils.ts │ └── v1-compat.t.ts ├── test/ │ ├── resolver/ │ │ ├── LICENSE.txt │ │ ├── generateUrls.spec.ts │ │ ├── matchPath.spec.ts │ │ ├── matchRoute.spec.ts │ │ └── resolver.spec.ts │ ├── router/ │ │ ├── dynamic-redirect.spec.ts │ │ ├── lifecycle-events.spec.ts │ │ ├── parent-layout.spec.ts │ │ ├── router.spec.ts │ │ ├── test-utils.ts │ │ └── url-for.spec.ts │ ├── setup.ts │ ├── transitions/ │ │ └── animate.spec.ts │ ├── triggers/ │ │ ├── click.spec.ts │ │ ├── popstate.spec.ts │ │ └── setNavigationTriggers.spec.ts │ └── typescript/ │ └── compile_fixture.ts ├── tsconfig.build.json ├── tsconfig.json ├── tsdoc.json ├── typedoc.json ├── vite.config.ts └── wct.conf.cjs
SYMBOL INDEX (459 symbols across 95 files)
FILE: demo/@debug/index.ts
type HTMLElementTagNameMap (line 12) | interface HTMLElementTagNameMap {
class DemoDebug (line 18) | class DemoDebug extends LitElement {
method render (line 19) | override render(): TemplateResult {
FILE: demo/@helpers/iframe.script.ts
type MessageData (line 8) | type MessageData = Readonly<{ url: string }>;
type ParentData (line 10) | type ParentData = {
function updateParentUrl (line 17) | function updateParentUrl() {
FILE: demo/@helpers/theme-controller.ts
class ThemeController (line 3) | class ThemeController implements ReactiveController {
method constructor (line 7) | constructor(host: ReactiveControllerHost) {
method value (line 11) | get value(): string {
method hostConnected (line 15) | hostConnected(): void {
method hostDisconnected (line 20) | hostDisconnected(): void {
FILE: demo/@helpers/vaadin-demo-code-snippet-file.ts
type CodeSnippet (line 10) | type CodeSnippet = Readonly<{
class DemoCodeSnippetFile (line 17) | class DemoCodeSnippetFile extends LitElement {
method updated (line 26) | override updated(): void {
method render (line 30) | override render(): TemplateResult | typeof nothing {
method #toggleExpanded (line 57) | #toggleExpanded(): void {
method #copyToClipboard (line 61) | async #copyToClipboard(): Promise<void> {
type HTMLElementTagNameMap (line 70) | interface HTMLElementTagNameMap {
FILE: demo/@helpers/vaadin-demo-code-snippet.ts
type HTMLElementTagNameMap (line 10) | interface HTMLElementTagNameMap {
type WindowEventMap (line 14) | interface WindowEventMap {
function renderFile (line 21) | function renderFile(file: CodeSnippet): TemplateResult {
class DemoCodeSnippet (line 26) | class DemoCodeSnippet extends LitElement {
method render (line 31) | override render(): TemplateResult {
FILE: demo/@helpers/vaadin-demo-layout.ts
type HTMLElementTagNameMap (line 21) | interface HTMLElementTagNameMap {
class DemoLayout (line 27) | class DemoLayout extends SignalWatcher(LitElement) {
method render (line 33) | override render(): TemplateResult {
method #onToggleMode (line 62) | #onToggleMode() {
FILE: demo/@helpers/vaadin-presentation-addressbar.ts
type HTMLElementTagNameMap (line 13) | interface HTMLElementTagNameMap {
function onBack (line 18) | function onBack() {
function onForward (line 22) | function onForward() {
class PresentationAddressbar (line 27) | class PresentationAddressbar extends LitElement {
method render (line 32) | override render(): TemplateResult {
method #onChange (line 46) | #onChange(event: Event) {
FILE: demo/@helpers/vaadin-presentation.ts
type HTMLElementTagNameMap (line 10) | interface HTMLElementTagNameMap {
type MessageData (line 15) | type MessageData = Readonly<{
class Presentation (line 20) | class Presentation extends LitElement {
method connectedCallback (line 28) | override connectedCallback(): void {
method disconnectedCallback (line 33) | override disconnectedCallback(): void {
method firstUpdated (line 38) | override firstUpdated(): void {
method changedProperties (line 52) | changedProperties(map: PropertyValues<this>): void {
method render (line 58) | override render(): TemplateResult {
method #onUrlChanged (line 75) | #onUrlChanged(event: CustomEvent<string>) {
FILE: demo/@helpers/x-breadcrumbs.ts
type Breadcrumb (line 6) | type Breadcrumb = Readonly<{
class Breadcrumbs (line 12) | class Breadcrumbs extends LitElement {
method #isNotLastIndexOf (line 18) | #isNotLastIndexOf(items: readonly Breadcrumb[], i: number): boolean {
method render (line 22) | override render(): TemplateResult {
type HTMLElementTagNameMap (line 38) | interface HTMLElementTagNameMap {
FILE: demo/@helpers/x-home-view.ts
type HTMLElementTagNameMap (line 6) | interface HTMLElementTagNameMap {
class HomeView (line 12) | class HomeView extends LitElement {
method render (line 15) | override render(): TemplateResult {
FILE: demo/@helpers/x-image-view.ts
type HTMLElementTagNameMap (line 9) | interface HTMLElementTagNameMap {
class ImageView (line 15) | class ImageView extends LitElement {
method render (line 20) | override render(): TemplateResult {
FILE: demo/@helpers/x-knowledge-base.ts
class KnowledgeBase (line 6) | class KnowledgeBase extends LitElement implements WebComponentInterface {
method render (line 9) | override render(): TemplateResult {
type HTMLElementTagNameMap (line 15) | interface HTMLElementTagNameMap {
FILE: demo/@helpers/x-login-view.ts
class LoginView (line 7) | class LoginView extends LitElement implements WebComponentInterface {
method render (line 12) | override render(): TemplateResult {
method #login (line 19) | #login() {
type HTMLElementTagNameMap (line 30) | interface HTMLElementTagNameMap {
FILE: demo/@helpers/x-not-found-view.ts
class NotFoundView (line 6) | class NotFoundView extends LitElement {
method render (line 9) | override render(): TemplateResult {
type HTMLElementTagNameMap (line 18) | interface HTMLElementTagNameMap {
FILE: demo/@helpers/x-profile-view.ts
type HTMLElementTagNameMap (line 7) | interface HTMLElementTagNameMap {
class ProfileView (line 13) | class ProfileView extends LitElement {
method render (line 17) | override render(): TemplateResult {
FILE: demo/@helpers/x-user-list.ts
type HTMLElementTagNameMap (line 6) | interface HTMLElementTagNameMap {
class UserList (line 12) | class UserList extends LitElement {
method render (line 14) | override render(): TemplateResult {
FILE: demo/@helpers/x-user-not-found-view.ts
class UserNotFoundView (line 7) | class UserNotFoundView extends LitElement {
method render (line 12) | override render(): TemplateResult {
type HTMLElementTagNameMap (line 21) | interface HTMLElementTagNameMap {
FILE: demo/@helpers/x-user-numeric-view.ts
type HTMLElementTagNameMap (line 7) | interface HTMLElementTagNameMap {
class UserNumericView (line 13) | class UserNumericView extends LitElement {
method render (line 17) | override render(): TemplateResult {
FILE: demo/@helpers/x-user-profile.ts
type HTMLElementTagNameMap (line 7) | interface HTMLElementTagNameMap {
class UserProfile (line 13) | class UserProfile extends LitElement {
method render (line 18) | override render(): TemplateResult {
FILE: demo/animated-transitions/d2/x-wrapper.ts
type HTMLElementTagNameMap (line 6) | interface HTMLElementTagNameMap {
class Wrapper (line 13) | class Wrapper extends LitElement {
method render (line 16) | override render(): TemplateResult {
FILE: demo/animated-transitions/index.ts
type HTMLElementTagNameMap (line 22) | interface HTMLElementTagNameMap {
class DemoAnimatedTransitions (line 69) | class DemoAnimatedTransitions extends LitElement {
method render (line 72) | override render(): TemplateResult {
FILE: demo/code-splitting/d1/script.ts
method action (line 11) | async action() {
FILE: demo/code-splitting/d1/user.bundle.ts
type HTMLElementTagNameMap (line 7) | interface HTMLElementTagNameMap {
class UserJsBundleView (line 14) | class UserJsBundleView extends LitElement {
method render (line 17) | override render(): TemplateResult {
FILE: demo/code-splitting/d2/script.ts
method children (line 14) | async children() {
FILE: demo/code-splitting/index.ts
type HTMLElementTagNameMap (line 21) | interface HTMLElementTagNameMap {
class DemoCodeSplitting (line 63) | class DemoCodeSplitting extends LitElement {
method render (line 66) | override render(): TemplateResult {
FILE: demo/getting-started/index.ts
type HTMLElementTagNameMap (line 18) | interface HTMLElementTagNameMap {
class DemoGettingStarted (line 37) | class DemoGettingStarted extends LitElement {
method render (line 40) | override render(): TemplateResult {
FILE: demo/getting-started/snippets/s4.ts
class MyViewElement (line 8) | @customElement('my-view')
method render (line 12) | override render() {
type HTMLElementTagNameMap (line 19) | interface HTMLElementTagNameMap {
FILE: demo/lifecycle-callback/d1/x-countdown.ts
class Countdown (line 7) | class Countdown extends LitElement implements WebComponentInterface {
method render (line 12) | override render(): TemplateResult {
method onBeforeEnter (line 16) | async onBeforeEnter(_: RouterLocation): Promise<void> {
method #tick (line 31) | #tick(): void {
method #clear (line 39) | #clear(): void {
type HTMLElementTagNameMap (line 49) | interface HTMLElementTagNameMap {
FILE: demo/lifecycle-callback/d2/x-friend.ts
class Friend (line 7) | class Friend extends LitElement implements WebComponentInterface {
method render (line 14) | override render(): TemplateResult {
method onAfterEnter (line 18) | onAfterEnter(_: RouterLocation): void {
type HTMLElementTagNameMap (line 25) | interface HTMLElementTagNameMap {
FILE: demo/lifecycle-callback/d3/x-user-deleted.ts
class UserDeleted (line 7) | class UserDeleted extends LitElement implements WebComponentInterface {
method render (line 8) | override render(): TemplateResult {
type HTMLElementTagNameMap (line 15) | interface HTMLElementTagNameMap {
FILE: demo/lifecycle-callback/d3/x-user-manage.ts
class UserManage (line 7) | class UserManage extends LitElement implements WebComponentInterface {
method render (line 10) | override render(): TemplateResult {
method onBeforeLeave (line 20) | onBeforeLeave(location: RouterLocation, commands: PreventAndRedirectCo...
type HTMLElementTagNameMap (line 34) | interface HTMLElementTagNameMap {
FILE: demo/lifecycle-callback/d4/x-autosave-view.ts
class AutosaveView (line 10) | class AutosaveView extends LitElement implements WebComponentInterface {
method render (line 13) | override render(): TemplateResult {
method onInput (line 22) | onInput(event: Event): void {
method onAfterEnter (line 27) | onAfterEnter(): void {
method onAfterLeave (line 31) | onAfterLeave(): void {
type HTMLElementTagNameMap (line 38) | interface HTMLElementTagNameMap {
FILE: demo/lifecycle-callback/d4/x-main-page.ts
class MainPage (line 6) | class MainPage extends LitElement {
method render (line 7) | override render(): TemplateResult {
type HTMLElementTagNameMap (line 14) | interface HTMLElementTagNameMap {
FILE: demo/lifecycle-callback/d6/script.ts
type RouteExtension (line 12) | type RouteExtension = Readonly<{
FILE: demo/lifecycle-callback/index.ts
type HTMLElementTagNameMap (line 41) | interface HTMLElementTagNameMap {
class DemoLifecycleCallback (line 155) | class DemoLifecycleCallback extends LitElement {
method render (line 158) | override render(): TemplateResult {
FILE: demo/lifecycle-callback/snippets/my-view-with-after-enter.ts
class MyViewWithAfterEnter (line 7) | @customElement('my-view-with-after-enter')
method onAfterEnter (line 9) | onAfterEnter(location: RouterLocation, commands: EmptyCommands, router...
type HTMLElementTagNameMap (line 16) | interface HTMLElementTagNameMap {
FILE: demo/lifecycle-callback/snippets/my-view-with-after-leave.ts
class MyViewWithAfterLeave (line 7) | @customElement('my-view-with-after-leave')
method onAfterLeave (line 9) | onAfterLeave(location: RouterLocation, commands: EmptyCommands, router...
type HTMLElementTagNameMap (line 16) | interface HTMLElementTagNameMap {
FILE: demo/lifecycle-callback/snippets/my-view-with-before-enter.ts
class MyViewWithBeforeEnter (line 8) | class MyViewWithBeforeEnter extends LitElement implements WebComponentIn...
method onBeforeEnter (line 9) | onBeforeEnter(location: RouterLocation, commands: PreventAndRedirectCo...
type HTMLElementTagNameMap (line 16) | interface HTMLElementTagNameMap {
FILE: demo/lifecycle-callback/snippets/my-view-with-before-leave.ts
class MyViewWithBeforeLeave (line 7) | @customElement('my-view-with-before-leave')
method onBeforeLeave (line 9) | onBeforeLeave(location: RouterLocation, commands: PreventCommands, rou...
type HTMLElementTagNameMap (line 16) | interface HTMLElementTagNameMap {
FILE: demo/navigation-trigger/index.ts
type HTMLElementTagNameMap (line 25) | interface HTMLElementTagNameMap {
class DemoNavigationTrigger (line 83) | class DemoNavigationTrigger extends LitElement {
method render (line 86) | override render(): TemplateResult {
FILE: demo/redirect/d2/script.ts
method action (line 17) | action(_, commands) {
FILE: demo/redirect/d2/x-admin-view.ts
type HTMLElementTagNameMap (line 6) | interface HTMLElementTagNameMap {
type Window (line 10) | interface Window {
class AdminView (line 17) | class AdminView extends LitElement implements WebComponentInterface {
method onBeforeEnter (line 18) | onBeforeEnter(location: RouterLocation, commands: Commands): RedirectR...
method render (line 26) | override render(): TemplateResult {
FILE: demo/redirect/index.ts
type HTMLElementTagNameMap (line 26) | interface HTMLElementTagNameMap {
class DemoRedirect (line 71) | class DemoRedirect extends LitElement {
method render (line 74) | override render(): TemplateResult {
FILE: demo/route-actions/d1/script.ts
function recordUrlVisit (line 10) | async function recordUrlVisit(context: RouteContext) {
FILE: demo/route-actions/d2/script.ts
function pollBackendForChanges (line 8) | async function pollBackendForChanges() {
FILE: demo/route-actions/d3/script.ts
function redirect (line 7) | function redirect(context: RouteContext, commands: Commands) {
FILE: demo/route-actions/d4/script.ts
function render (line 7) | function render(context: RouteContext, commands: Commands) {
FILE: demo/route-actions/d5/script.ts
method action (line 11) | async action(context: RouteContext, commands: Commands) {
FILE: demo/route-actions/index.ts
type HTMLElementTagNameMap (line 28) | interface HTMLElementTagNameMap {
class DemoRouteActions (line 99) | class DemoRouteActions extends LitElement {
method render (line 102) | override render(): TemplateResult {
FILE: demo/route-parameters/d2/x-project-view.ts
class ProjectView (line 7) | class ProjectView extends LitElement implements WebComponentInterface {
method render (line 10) | override render(): TemplateResult {
type HTMLElementTagNameMap (line 19) | interface HTMLElementTagNameMap {
FILE: demo/route-parameters/d5/x-page-number-view.ts
class PageNumberView (line 8) | class PageNumberView extends LitElement implements WebComponentInterface {
method render (line 11) | override render(): TemplateResult {
method #getPageNumber (line 15) | #getPageNumber(): string {
type HTMLElementTagNameMap (line 22) | interface HTMLElementTagNameMap {
FILE: demo/route-parameters/d6/x-hash-view.ts
class HashView (line 7) | class HashView extends LitElement implements WebComponentInterface {
method render (line 10) | override render(): TemplateResult {
type HTMLElementTagNameMap (line 17) | interface HTMLElementTagNameMap {
FILE: demo/route-parameters/index.ts
type HTMLElementTagNameMap (line 34) | interface HTMLElementTagNameMap {
class DemoRouteParameters (line 133) | class DemoRouteParameters extends LitElement {
method render (line 136) | override render(): TemplateResult {
FILE: demo/url-generation/d1/script.ts
type RouteExtension (line 9) | type RouteExtension = Readonly<{
FILE: demo/url-generation/d1/x-main-layout.ts
class MainLayout (line 8) | class MainLayout extends LitElement implements WebComponentInterface<Rou...
method render (line 11) | override render(): TemplateResult | typeof nothing {
type HTMLElementTagNameMap (line 26) | interface HTMLElementTagNameMap {
FILE: demo/url-generation/d2/script.ts
type RouteExtension (line 9) | type RouteExtension = Readonly<{
FILE: demo/url-generation/d2/x-main-layout.ts
class MainLayout (line 8) | class MainLayout extends LitElement implements WebComponentInterface<Rou...
method render (line 11) | override render(): TemplateResult | typeof nothing {
type HTMLElementTagNameMap (line 25) | interface HTMLElementTagNameMap {
FILE: demo/url-generation/d3/script.ts
type RouteExtension (line 6) | type RouteExtension = Readonly<{
FILE: demo/url-generation/d3/x-user-layout-d3.ts
class UserLayoutD3 (line 10) | class UserLayoutD3 extends LitElement implements WebComponentInterface<R...
method render (line 13) | override render(): TemplateResult | typeof nothing {
method #getUrlForUser (line 24) | #getUrlForUser(user: string): string | undefined {
type HTMLElementTagNameMap (line 31) | interface HTMLElementTagNameMap {
FILE: demo/url-generation/d4/script.ts
type RouteExtension (line 8) | type RouteExtension = Readonly<{
FILE: demo/url-generation/d4/x-user-layout-d4.ts
class UserLayoutD4 (line 9) | class UserLayoutD4 extends LitElement implements WebComponentInterface<R...
method render (line 12) | override render(): TemplateResult | typeof nothing {
type HTMLElementTagNameMap (line 26) | interface HTMLElementTagNameMap {
FILE: demo/url-generation/d5/x-pages-menu.ts
function urlForPageNumber (line 9) | function urlForPageNumber(location: RouterLocation, pageNumber: number) {
function urlForSection (line 14) | function urlForSection(location: RouterLocation, section: string) {
class PagesMenu (line 19) | class PagesMenu extends LitElement {
method render (line 29) | override render(): TemplateResult | typeof nothing {
type HTMLElementTagNameMap (line 49) | interface HTMLElementTagNameMap {
FILE: demo/url-generation/index.ts
type HTMLElementTagNameMap (line 33) | interface HTMLElementTagNameMap {
class DemoUrlGeneration (line 129) | class DemoUrlGeneration extends LitElement {
method render (line 132) | override render(): TemplateResult {
FILE: demo/vite.config.ts
function convertToId (line 10) | function convertToId(str: string) {
FILE: scripts/codeSnippet.ts
function langFromExt (line 16) | function langFromExt(ext: string) {
type SnippetPatternKey (line 27) | type SnippetPatternKey = keyof typeof snippetPattern;
function removePatterns (line 36) | function removePatterns(code: string) {
function extractSnippets (line 40) | function extractSnippets(code: string, language: SnippetPatternKey) {
function escapeString (line 56) | function escapeString(str: string) {
function codeSnippetPlugin (line 60) | function codeSnippetPlugin(): Plugin {
FILE: scripts/constructCss.ts
function constructCss (line 14) | function constructCss(): Plugin {
FILE: scripts/loadRegisterJs.ts
function loadRegisterJs (line 9) | function loadRegisterJs(): Plugin {
FILE: scripts/register.js
function __REGISTER__ (line 1) | function __REGISTER__(feature, vaadinObj = (window.Vaadin ??= {})) {
FILE: scripts/resolveHTMLImports.ts
function resolveHTMLImports (line 6) | function resolveHTMLImports(): Plugin {
FILE: scripts/types.d.ts
type RegExpConstructor (line 5) | interface RegExpConstructor {
FILE: src/resolver/generateUrls.ts
type UrlParams (line 16) | type UrlParams = Readonly<Record<string, ReadonlyArray<number | string> ...
function cacheRoutes (line 18) | function cacheRoutes<T, R extends object, C extends object>(
function getRouteByName (line 41) | function getRouteByName<T, R extends object, C extends object>(
type StringifyQueryParams (line 58) | type StringifyQueryParams = (params: UrlParams) => string;
type GenerateUrlOptions (line 60) | type GenerateUrlOptions<T, R extends object, C extends object> = ParseOp...
type RouteCacheRecord (line 74) | type RouteCacheRecord = Readonly<{
type UrlGenerator (line 79) | type UrlGenerator = (routeName: string, params?: Params) => string;
function generateUrls (line 81) | function generateUrls<T = unknown, R extends object = EmptyObject, C ext...
FILE: src/resolver/matchPath.ts
type RegExpExecOptArray (line 15) | interface RegExpExecOptArray extends ReadonlyArray<string | undefined> {
type Matcher (line 21) | type Matcher = Readonly<{
type Match (line 26) | type Match = Readonly<{
function decodeParam (line 39) | function decodeParam(val: string): string {
function matchPath (line 48) | function matchPath(
FILE: src/resolver/matchRoute.ts
type MatchWithRoute (line 15) | type MatchWithRoute<T, R extends object, C extends object> = Match &
type RouteMatchIterator (line 20) | type RouteMatchIterator<T, R extends object, C extends object> = Iterator<
function matchRoute (line 72) | function matchRoute<T, R extends object, C extends object>(
FILE: src/resolver/resolveRoute.ts
function resolveRoute (line 14) | function resolveRoute<T, R extends object, C extends object>(
FILE: src/resolver/resolver.ts
function isDescendantRoute (line 15) | function isDescendantRoute<T, R extends object, C extends object>(
function isRouteContext (line 29) | function isRouteContext<T, R extends object, C extends object>(value: un...
type ResolutionErrorOptions (line 40) | interface ResolutionErrorOptions {
class ResolutionError (line 48) | class ResolutionError<T, R extends object = EmptyObject, C extends objec...
method constructor (line 64) | constructor(context: RouteContext<T, R, C>, options?: ResolutionErrorO...
method warn (line 79) | warn(): void {
function updateChainForRoute (line 84) | function updateChainForRoute<T, R extends object, C extends object>(
type ErrorHandlerCallback (line 108) | type ErrorHandlerCallback<T> = (error: unknown) => T;
type ResolveRouteCallback (line 114) | type ResolveRouteCallback<T, R extends object, C extends object> = (
type ResolverOptions (line 123) | type ResolverOptions<T, R extends object, C extends object> = Readonly<{
class Resolver (line 130) | class Resolver<T = unknown, R extends object = EmptyObject, C extends ob...
method constructor (line 144) | constructor(
method root (line 189) | get root(): Route<T, R, C> {
method context (line 196) | get context(): RouteContext<T, R, C> {
method __effectiveBaseUrl (line 207) | protected get __effectiveBaseUrl(): string {
method getRoutes (line 218) | getRoutes(): ReadonlyArray<Route<T, R, C>> {
method removeRoutes (line 227) | removeRoutes(): void {
method resolve (line 247) | async resolve(pathnameOrContext: ResolveContext<C> | string): Promise<...
method setRoutes (line 325) | setRoutes(routes: ReadonlyArray<Route<T, R, C>> | Route<T, R, C>): obj...
method __normalizePathname (line 338) | protected __normalizePathname(pathname: string): string | undefined {
method addRoutes (line 362) | protected addRoutes(routes: ReadonlyArray<Route<T, R, C>> | Route<T, R...
FILE: src/resolver/types.t.ts
type MaybePromise (line 14) | type MaybePromise<T> = Promise<T> | T;
type ActionResult (line 21) | type ActionResult<T> = T | NotFoundResult | null | undefined | void;
type ChildrenCallback (line 43) | type ChildrenCallback<T, R extends object, C extends object> = (
type Route (line 62) | type Route<T = unknown, R extends object = EmptyObject, C extends object...
type Match (line 111) | type Match<T, R extends object, C extends object> = Readonly<{
type ChainItem (line 129) | type ChainItem<T, R extends object, C extends object> = {
type ResolveContext (line 147) | type ResolveContext<C extends object = EmptyObject> = Readonly<{
type RouteContext (line 165) | type RouteContext<T, R extends object = EmptyObject, C extends object = ...
type RouteChildrenContext (line 235) | type RouteChildrenContext<T, R extends object = EmptyObject, C extends o...
type PrimitiveParamValue (line 240) | type PrimitiveParamValue = string | number | null;
type ParamValue (line 245) | type ParamValue = PrimitiveParamValue | readonly PrimitiveParamValue[];
type IndexedParams (line 257) | type IndexedParams = Readonly<Record<string, ParamValue>>;
type Params (line 259) | type Params = IndexedParams | ParamValue[];
FILE: src/resolver/utils.ts
type NotFoundResult (line 12) | type NotFoundResult = typeof notFoundResult;
class NotFoundError (line 17) | class NotFoundError<T, R extends object, C extends object> extends Error {
method constructor (line 28) | constructor(context: RouteContext<T, R, C>) {
function isObject (line 37) | function isObject(o: unknown): o is object {
function isFunction (line 43) | function isFunction<F extends (...args: readonly never[]) => unknown>(f:...
function isString (line 48) | function isString(s: unknown): s is string {
function toArray (line 53) | function toArray<T>(value: T | readonly T[] = []): readonly T[] {
function log (line 58) | function log(msg: string): string {
function getNotFoundError (line 63) | function getNotFoundError<T, R extends object, C extends object>(
function resolvePath (line 70) | function resolvePath(path?: string | readonly string[]): string {
function getRoutePath (line 75) | function getRoutePath<T, R extends object, C extends object>(route: Rout...
function unwrapChildren (line 80) | function unwrapChildren<T, R extends object, C extends object>(
FILE: src/router.ts
constant MAX_REDIRECT_COUNT (line 48) | const MAX_REDIRECT_COUNT = 256;
function prevent (line 50) | function prevent(): PreventResult {
method action (line 61) | action() {
method next (line 67) | async next() {
class Router (line 105) | class Router<R extends object = EmptyObject, C extends object = EmptyObj...
method constructor (line 152) | constructor(outlet?: Element | DocumentFragment | null, options?: Rout...
method #resolveRoute (line 167) | async #resolveRoute(context: RouteContext<R, C>): Promise<ActionResult...
method setOutlet (line 239) | setOutlet(outlet?: Element | DocumentFragment | null): void {
method getOutlet (line 251) | getOutlet(): Element | DocumentFragment | null | undefined {
method setRoutes (line 333) | override async setRoutes(
method addRoutes (line 347) | protected override addRoutes(routes: Route<R, C> | ReadonlyArray<Route...
method render (line 371) | async render(
method #doRender (line 387) | async #doRender(context: RouteContext<R, C>, shouldUpdateHistory: bool...
method #fullyResolveChain (line 484) | async #fullyResolveChain(
method #findComponentContextAfterAllRedirects (line 534) | async #findComponentContextAfterAllRedirects(context: RouteContext<R, ...
method #amendWithOnBeforeCallbacks (line 555) | async #amendWithOnBeforeCallbacks(contextWithFullChain: RouteContext<R...
method #runOnBeforeCallbacks (line 564) | async #runOnBeforeCallbacks(newContext: RouteContext<R, C>): Promise<R...
method #runOnBeforeLeaveCallbacks (line 658) | async #runOnBeforeLeaveCallbacks(
method #runOnBeforeEnterCallbacks (line 678) | async #runOnBeforeEnterCallbacks(
method #isReusableElement (line 693) | #isReusableElement(element?: unknown, otherElement?: unknown): boolean {
method #isLatestRender (line 702) | #isLatestRender(context: Partial<RouteContext<R, C>>): boolean {
method #redirect (line 710) | async #redirect(
method #ensureOutlet (line 728) | #ensureOutlet(outlet: Element | DocumentFragment | undefined | null = ...
method #updateBrowserHistory (line 737) | #updateBrowserHistory({ pathname, search = '', hash = '' }: ResolveCon...
method #copyUnchangedElements (line 745) | #copyUnchangedElements(
method #addAppearingContent (line 766) | #addAppearingContent(context: RouteContext<R, C>, previousContext?: Ro...
method #removeDisappearingContent (line 806) | #removeDisappearingContent(): void {
method #removeAppearingContent (line 816) | #removeAppearingContent(): void {
method #runOnAfterLeaveCallbacks (line 826) | #runOnAfterLeaveCallbacks(currentContext: RouteContext<R, C>, targetCo...
method #runOnAfterEnterCallbacks (line 854) | #runOnAfterEnterCallbacks(currentContext: RouteContext<R, C>): void {
method #animateIfNeeded (line 873) | async #animateIfNeeded(context: RouteContext<R, C>): Promise<RouteCont...
method subscribe (line 905) | subscribe(): void {
method unsubscribe (line 913) | unsubscribe(): void {
method #onNavigationEvent (line 917) | #onNavigationEvent(event?: Event): void {
method setTriggers (line 946) | static setTriggers(...triggers: readonly NavigationTrigger[]): void {
method urlForName (line 966) | urlForName(name: string, params?: Params | null): string {
method urlForPath (line 989) | urlForPath(path: string, params?: Params | null): string {
method go (line 1006) | static go(path: string | ResolveContext): boolean {
FILE: src/transitions/animate.ts
function animate (line 14) | async function animate(elem: Element, className: string): Promise<void> {
FILE: src/triggers/click.ts
function getAnchorOrigin (line 5) | function getAnchorOrigin(anchor: HTMLAnchorElement) {
function getNormalizedNodeName (line 18) | function getNormalizedNodeName(e: EventTarget): string | undefined {
type __Pathable (line 27) | type __Pathable = Readonly<{
function vaadinRouterGlobalClickHandler (line 34) | function vaadinRouterGlobalClickHandler(event: MouseEvent & __Pathable) {
constant CLICK (line 122) | const CLICK: NavigationTrigger = {
method activate (line 123) | activate() {
method inactivate (line 127) | inactivate() {
FILE: src/triggers/navigation.ts
constant DEFAULT_TRIGGERS (line 7) | const DEFAULT_TRIGGERS = {
function setNavigationTriggers (line 14) | function setNavigationTriggers(newTriggers: readonly NavigationTrigger[]...
FILE: src/triggers/popstate.ts
function vaadinRouterGlobalPopstateHandler (line 4) | function vaadinRouterGlobalPopstateHandler(event: PopStateEvent) {
constant POPSTATE (line 16) | const POPSTATE: NavigationTrigger = {
method activate (line 17) | activate() {
method inactivate (line 21) | inactivate() {
FILE: src/types.t.ts
type VaadinRouterLocationChangedEvent (line 23) | type VaadinRouterLocationChangedEvent<
type VaadinRouterErrorEvent (line 43) | type VaadinRouterErrorEvent<R extends object = EmptyObject, C extends ob...
type VaadinRouterGoEvent (line 56) | type VaadinRouterGoEvent = CustomEvent<ResolveContext>;
type WindowEventMap (line 59) | interface WindowEventMap {
type ArrayConstructor (line 65) | interface ArrayConstructor {
type RedirectContextInfo (line 73) | type RedirectContextInfo = Readonly<{
type RedirectResult (line 86) | interface RedirectResult {
type PreventResult (line 94) | interface PreventResult {
type NavigationTrigger (line 102) | interface NavigationTrigger {
type ActionValue (line 112) | type ActionValue = HTMLElement | PreventResult | RedirectResult;
type NextResult (line 117) | type NextResult<R extends object, C extends object> = _ActionResult<Rout...
type ActionResult (line 122) | type ActionResult = _ActionResult<ActionValue>;
type ChainItem (line 127) | type ChainItem<R extends object, C extends object> = _ChainItem<
type ContextExtension (line 143) | type ContextExtension<R extends object, C extends object> = Readonly<{
type ChildrenCallback (line 152) | type ChildrenCallback<R extends object = EmptyObject, C extends object =...
type RouteExtension (line 165) | type RouteExtension<R extends object, C extends object> = RequireAtLeast...
type RouteContext (line 194) | type RouteContext<R extends object = EmptyObject, C extends object = Emp...
type RouteChildrenContext (line 204) | type RouteChildrenContext<
type Route (line 213) | type Route<R extends object = EmptyObject, C extends object = EmptyObjec...
type RouterOptions (line 223) | type RouterOptions<R extends object = EmptyObject, C extends object = Em...
type RouterLocation (line 239) | interface RouterLocation<R extends object = EmptyObject, C extends objec...
type WebComponentInterface (line 414) | interface WebComponentInterface<R extends object = EmptyObject, C extend...
type ResolveContext (line 560) | type ResolveContext = Readonly<{
type Commands (line 567) | interface Commands {
type EmptyCommands (line 579) | type EmptyCommands = EmptyObject;
type PreventCommands (line 580) | type PreventCommands = Pick<Commands, 'prevent'>;
type PreventAndRedirectCommands (line 581) | type PreventAndRedirectCommands = Pick<Commands, 'prevent' | 'redirect'>;
type AnimateCustomClasses (line 583) | type AnimateCustomClasses = Readonly<{
FILE: src/utils.ts
function ensureRoute (line 17) | function ensureRoute<R extends object, C extends object>(route?: Route<R...
function ensureRoutes (line 54) | function ensureRoutes<R extends object, C extends object>(
function copyContextWithoutNext (line 61) | function copyContextWithoutNext<R extends object, C extends object>({
function getPathnameForRouter (line 69) | function getPathnameForRouter<T, R extends object, C extends object>(
function getMatchedPath (line 79) | function getMatchedPath(pathItems: ReadonlyArray<Readonly<{ path: string...
function getRoutePath (line 91) | function getRoutePath<R extends object, C extends object>(chain: Readonl...
type ResolverOnlyContext (line 96) | type ResolverOnlyContext<R extends object, C extends object> = Readonly<...
type PartialRouteContext (line 99) | type PartialRouteContext<R extends object, C extends object> = Readonly<{
function createLocation (line 117) | function createLocation<R extends object, C extends object>(
function createRedirect (line 143) | function createRedirect<R extends object, C extends object>(
function renderElement (line 158) | function renderElement<R extends object, C extends object, E extends Web...
function maybeCall (line 173) | function maybeCall<R, A extends unknown[], O extends object>(
function amend (line 186) | function amend<
function processNewChildren (line 201) | function processNewChildren<R extends object, C extends object>(
function fireRouterEvent (line 221) | function fireRouterEvent(type: string, detail: unknown): boolean {
function logValue (line 226) | function logValue(value: unknown): string {
FILE: src/v1-compat.t.ts
type ComponentResult (line 18) | type ComponentResult = HTMLElement;
type Context (line 25) | type Context = RouteContext;
type ActionFn (line 32) | type ActionFn = (
type ChildrenFn (line 43) | type ChildrenFn = ChildrenCallback;
type BeforeEnterObserver (line 50) | interface BeforeEnterObserver {
type BeforeLeaveObserver (line 62) | interface BeforeLeaveObserver {
type AfterEnterObserver (line 74) | interface AfterEnterObserver {
type AfterLeaveObserver (line 86) | interface AfterLeaveObserver {
FILE: test/resolver/generateUrls.spec.ts
method encode (line 199) | encode(str) {
FILE: test/resolver/matchRoute.spec.ts
function toArray (line 19) | function toArray<T>(iter: Iterator<T>): readonly T[] {
FILE: test/resolver/resolver.spec.ts
type CustomResolveOption (line 36) | type CustomResolveOption = Readonly<{
type RouteWithComponent (line 101) | type RouteWithComponent = Readonly<{
method action (line 408) | async action({ next }) {
method action (line 416) | action() {
method action (line 421) | async action({ next }) {
method action (line 429) | async action({ next }) {
method action (line 444) | action() {
method action (line 449) | action() {
method action (line 455) | action() {
method action (line 467) | action() {
method action (line 473) | action() {
method action (line 480) | action() {
method action (line 496) | async action({ next }) {
method action (line 505) | async action({ next }) {
method action (line 515) | action() {
method action (line 520) | async action({ next }) {
method action (line 529) | action() {
method action (line 538) | action() {
method action (line 664) | action() {
method action (line 780) | action() {
FILE: test/router/lifecycle-events.spec.ts
type HTMLElementTagNameMap (line 26) | interface HTMLElementTagNameMap {
class XSpy (line 34) | class XSpy extends HTMLElement implements WebComponentInterface {
method connectedCallback (line 38) | connectedCallback() {
method disconnectedCallback (line 41) | disconnectedCallback() {
method onBeforeEnter (line 44) | onBeforeEnter(): MaybePromise<undefined> {
method onAfterEnter (line 48) | onAfterEnter() {
method onBeforeLeave (line 51) | onBeforeLeave(): MaybePromise<undefined> {
method onAfterLeave (line 55) | onAfterLeave() {
function extractLifeCycleCallbackCallArgs (line 61) | function extractLifeCycleCallbackCallArgs(
method connectedCallback (line 341) | connectedCallback(): void {
method onBeforeLeave (line 344) | onBeforeLeave(_location: RouterLocation, commands: Commands) {
method connectedCallback (line 532) | connectedCallback(): void {
method onBeforeEnter (line 540) | onBeforeEnter(_location: RouterLocation, commands: Commands) {
function action (line 724) | function action() {
method action (line 872) | action() {
method action (line 1854) | async action() {
function registerSpyComponentAsync (line 1875) | async function registerSpyComponentAsync(tagname: string, name: string, ...
method action (line 1903) | async action() {
method action (line 1912) | async action() {
method action (line 2123) | async action(context: RouteContext) {
FILE: test/router/router.spec.ts
function expectException (line 21) | async function expectException(callback: Promise<unknown>, expectedConte...
method connectedCallback (line 730) | connectedCallback() {
method action (line 1413) | action(context) {
method action (line 1459) | async action(context) {
method action (line 1482) | action(_context, commands) {
FILE: test/router/test-utils.ts
function waitForNavigation (line 7) | async function waitForNavigation(): Promise<void> {
function cleanup (line 13) | function cleanup(element: Element): void {
function verifyActiveRoutes (line 17) | function verifyActiveRoutes(router: Router, expectedSegments: string[]):...
function createWebComponentAction (line 22) | function createWebComponentAction<T extends keyof WebComponentInterface>...
function checkOutletContents (line 45) | function checkOutletContents<T extends Element>(
FILE: test/transitions/animate.spec.ts
function registerElement (line 9) | function registerElement(element: `${string}-${string}`, template: strin...
function attach (line 23) | function attach(element: string) {
FILE: test/triggers/click.spec.ts
constant TEMPLATE (line 8) | const TEMPLATE = `<a id="home" href="">home</a>
function emulateClick (line 50) | function emulateClick(target: Element | null | undefined, button = Butto...
function onWindowClick (line 89) | function onWindowClick(event: Event) {
function onWindowNavigate (line 95) | function onWindowNavigate(event: CustomEvent) {
function expectClickIgnored (line 321) | function expectClickIgnored() {
FILE: test/triggers/setNavigationTriggers.spec.ts
function createTriggerMock (line 7) | function createTriggerMock() {
FILE: test/typescript/compile_fixture.ts
function expectTypeOfValue (line 23) | function expectTypeOfValue<T>(t: T): void {
type ActionFn (line 27) | type ActionFn = NonNullable<Route["action"]>;
type RouteMeta (line 29) | type RouteMeta = Readonly<{
method activate (line 64) | activate() {}
method inactivate (line 65) | inactivate() {}
method activate (line 68) | activate() {}
method inactivate (line 68) | inactivate() {}
method activate (line 69) | activate() {}
method inactivate (line 69) | inactivate() {}
method activate (line 69) | activate() {}
method inactivate (line 69) | inactivate() {}
method action (line 135) | action() {}
class MyViewWithLocation (line 199) | class MyViewWithLocation extends HTMLElement {
method connectedCallback (line 202) | connectedCallback() {
class MyViewWithBeforeEnter (line 211) | class MyViewWithBeforeEnter extends HTMLElement implements WebComponentI...
method onBeforeEnter (line 212) | onBeforeEnter(location: RouterLocation, commands: PreventAndRedirectCo...
class MyViewWithBeforeLeave (line 225) | class MyViewWithBeforeLeave extends HTMLElement implements WebComponentI...
method onBeforeLeave (line 226) | onBeforeLeave(location: RouterLocation, commands: PreventCommands, rou...
class MyViewWithAfterEnter (line 238) | class MyViewWithAfterEnter extends HTMLElement implements WebComponentIn...
method onAfterEnter (line 239) | onAfterEnter(location: RouterLocation, commands: EmptyCommands, router...
class MyViewWithAfterLeave (line 250) | class MyViewWithAfterLeave extends HTMLElement implements WebComponentIn...
method onAfterLeave (line 251) | onAfterLeave(location: RouterLocation, commands: EmptyCommands, router...
Condensed preview — 222 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (709K chars).
[
{
"path": ".editorconfig",
"chars": 276,
"preview": "# http://editorconfig.org\n\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninsert"
},
{
"path": ".eslintignore",
"chars": 23,
"preview": "src/documentation/*.js\n"
},
{
"path": ".eslintrc.json",
"chars": 630,
"preview": "{\n \"extends\": [\n \"vaadin/typescript-requiring-type-checking\",\n \"vaadin/imports-typescript\",\n \"vaadin/lit\",\n "
},
{
"path": ".github/dependabot.yml",
"chars": 140,
"preview": "version: 2\nupdates:\n - package-ecosystem: 'npm'\n directory: '/'\n schedule:\n interval: 'daily'\n versioning"
},
{
"path": ".github/workflows/docs.yml",
"chars": 889,
"preview": "name: Publish Docs\n\non:\n push:\n branches: ['main']\n workflow_dispatch:\n\npermissions:\n contents: read\n pages: writ"
},
{
"path": ".github/workflows/validation.yml",
"chars": 708,
"preview": "name: Validation\n\non:\n push:\n branches:\n - 'master'\n pull_request:\npermissions:\n contents: read\n\njobs:\n test"
},
{
"path": ".gitignore",
"chars": 71,
"preview": "bower_components\nnode_modules\nbuild\ndist\ncoverage\n\n.idea\n.vscode\n.docs\n"
},
{
"path": ".prettierrc.json",
"chars": 116,
"preview": "{\n \"bracketSpacing\": true,\n \"printWidth\": 120,\n \"trailingComma\": \"all\",\n \"tabWidth\": 2,\n \"singleQuote\": true\n}\n"
},
{
"path": ".run/All tests.run.xml",
"chars": 414,
"preview": "<component name=\"ProjectRunConfigurationManager\">\n <configuration default=\"false\" name=\"All tests\" type=\"JavaScriptTest"
},
{
"path": ".run/Template Karma.run.xml",
"chars": 396,
"preview": "<component name=\"ProjectRunConfigurationManager\">\n <configuration default=\"true\" type=\"JavaScriptTestRunnerKarma\">\n "
},
{
"path": ".stylelintrc.json",
"chars": 4264,
"preview": "{\n \"rules\": {\n \"at-rule-name-case\": \"lower\",\n \"at-rule-name-space-after\": \"always-single-line\",\n \"at-rule-semi"
},
{
"path": "LICENSE",
"chars": 10756,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 4409,
"preview": "[](https://www.npmjs.com/package/@vaadin/router)\n[![npm b"
},
{
"path": "analysis.json",
"chars": 61469,
"preview": "{\n \"schema_version\": \"1.0.0\",\n \"namespaces\": [\n {\n \"name\": \"Router\",\n \"description\": \"\",\n \"summary\":"
},
{
"path": "demo/.eslintrc.json",
"chars": 179,
"preview": "{\n \"extends\": [\"../.eslintrc.json\"],\n \"root\": true,\n \"parserOptions\": {\n \"project\": \"./tsconfig.json\"\n },\n \"rule"
},
{
"path": "demo/@debug/index.html",
"chars": 278,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Debug</title>\n <script async type=\"module\" src=\"./index.ts\"></sc"
},
{
"path": "demo/@debug/index.ts",
"chars": 676,
"preview": "/* eslint-disable import/no-duplicates, import/default */\nimport '@helpers/common.js';\nimport '@helpers/vaadin-presentat"
},
{
"path": "demo/@helpers/common.css",
"chars": 28,
"preview": ":host {\n display: block;\n}\n"
},
{
"path": "demo/@helpers/common.ts",
"chars": 164,
"preview": "import '@vaadin/vaadin-lumo-styles/badge-global.js';\nimport '@vaadin/vaadin-lumo-styles/color-global.js';\nimport '@vaadi"
},
{
"path": "demo/@helpers/iframe.script.ts",
"chars": 771,
"preview": "import './common.css';\nimport './common.js';\n\nimport { Router } from '@vaadin/router';\n\nhistory.replaceState(null, '', '"
},
{
"path": "demo/@helpers/nested-styles.css",
"chars": 214,
"preview": ":host {\n display: flex;\n flex-direction: column;\n background: #ddd;\n padding: 5px;\n min-height: 100vh;\n}\n\nmain {\n "
},
{
"path": "demo/@helpers/page.css",
"chars": 71,
"preview": "code {\n background: var(--code-background);\n padding: 0.2em 0.4em;\n}\n"
},
{
"path": "demo/@helpers/shared-styles.css",
"chars": 93,
"preview": ".note {\n background-color: #24c0ea;\n padding: 1em;\n color: white;\n border-radius: 4px;\n}\n"
},
{
"path": "demo/@helpers/theme-controller.ts",
"chars": 665,
"preview": "import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nexport default class ThemeController implements "
},
{
"path": "demo/@helpers/vaadin-demo-code-snippet-file.css",
"chars": 789,
"preview": "header {\n margin: 1.5em 0 0.5em;\n font-size: 0.75rem;\n display: flex;\n justify-content: space-between;\n align-items"
},
{
"path": "demo/@helpers/vaadin-demo-code-snippet-file.ts",
"chars": 2263,
"preview": "/* eslint-disable @typescript-eslint/unbound-method */\nimport '@vaadin/button/src/vaadin-button';\nimport '@vaadin/icon/s"
},
{
"path": "demo/@helpers/vaadin-demo-code-snippet.css",
"chars": 28,
"preview": ":host {\n display: block;\n}\n"
},
{
"path": "demo/@helpers/vaadin-demo-code-snippet.ts",
"chars": 1222,
"preview": "/* eslint-disable import/no-duplicates */\nimport { html, LitElement, type TemplateResult } from 'lit';\nimport { customEl"
},
{
"path": "demo/@helpers/vaadin-demo-layout.css",
"chars": 371,
"preview": ":host {\n --code-file-background: none;\n}\n\n:host([theme~='dark']) {\n --code-background: var(--lumo-shade);\n}\n\n:host([th"
},
{
"path": "demo/@helpers/vaadin-demo-layout.ts",
"chars": 3151,
"preview": "/* eslint-disable @typescript-eslint/unbound-method */\nimport '@vaadin/app-layout';\nimport '@vaadin/app-layout/vaadin-dr"
},
{
"path": "demo/@helpers/vaadin-presentation-addressbar.css",
"chars": 98,
"preview": ":host {\n display: flex;\n gap: 1rem;\n flex: 0 1 0;\n}\n\n:host > :last-child {\n flex: 1 0 auto;\n}\n"
},
{
"path": "demo/@helpers/vaadin-presentation-addressbar.ts",
"chars": 1740,
"preview": "/* eslint-disable @typescript-eslint/unbound-method */\nimport '@vaadin/button';\nimport '@vaadin/icon';\nimport '@vaadin/i"
},
{
"path": "demo/@helpers/vaadin-presentation.css",
"chars": 169,
"preview": ":host {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\nvaadin-presentation-addressbar,\niframe {\n width: 1"
},
{
"path": "demo/@helpers/vaadin-presentation.ts",
"chars": 2340,
"preview": "/* eslint-disable @typescript-eslint/unbound-method */\nimport { html, LitElement, type PropertyValues, type TemplateResu"
},
{
"path": "demo/@helpers/x-breadcrumbs.ts",
"chars": 1096,
"preview": "import { html, LitElement, nothing, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decor"
},
{
"path": "demo/@helpers/x-home-view.ts",
"chars": 448,
"preview": "import { LitElement, html, type TemplateResult } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport c"
},
{
"path": "demo/@helpers/x-image-view.css",
"chars": 167,
"preview": ".img-view {\n width: var(--x-image-view-width, 100%);\n height: var(--x-image-view-height, 100%);\n background-color: va"
},
{
"path": "demo/@helpers/x-image-view.ts",
"chars": 1038,
"preview": "import { LitElement, html, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js'"
},
{
"path": "demo/@helpers/x-knowledge-base.ts",
"chars": 623,
"preview": "import { html, LitElement, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js'"
},
{
"path": "demo/@helpers/x-login-view.ts",
"chars": 847,
"preview": "import { html, LitElement, type TemplateResult } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport c"
},
{
"path": "demo/@helpers/x-not-found-view.ts",
"chars": 477,
"preview": "import { html, LitElement, type TemplateResult } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport c"
},
{
"path": "demo/@helpers/x-profile-view.ts",
"chars": 715,
"preview": "import { LitElement, html, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js'"
},
{
"path": "demo/@helpers/x-user-list.ts",
"chars": 591,
"preview": "import { LitElement, html, type TemplateResult } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport c"
},
{
"path": "demo/@helpers/x-user-not-found-view.ts",
"chars": 744,
"preview": "import { html, LitElement, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js'"
},
{
"path": "demo/@helpers/x-user-numeric-view.ts",
"chars": 667,
"preview": "import { LitElement, html, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js'"
},
{
"path": "demo/@helpers/x-user-profile.ts",
"chars": 656,
"preview": "import { LitElement, html, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js'"
},
{
"path": "demo/animated-transitions/d1/iframe.html",
"chars": 406,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Animated Transitions</title>\n <link rel=\"stylesheet\" href=\"./sty"
},
{
"path": "demo/animated-transitions/d1/script.ts",
"chars": 637,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-image-view.js';\nimport '@helper"
},
{
"path": "demo/animated-transitions/d1/styles.css",
"chars": 277,
"preview": "#outlet > .leaving {\n animation: 1s fadeOut ease-in-out;\n}\n\n#outlet > .entering {\n animation: 1s fadeIn linear;\n}\n\n@ke"
},
{
"path": "demo/animated-transitions/d2/iframe.html",
"chars": 309,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Animated Transitions</title>\n <link rel=\"stylesheet\" href=\"style"
},
{
"path": "demo/animated-transitions/d2/script.ts",
"chars": 762,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-user-list.js';\nimport '@helpers/x-user-profile.js';\nimport { Rout"
},
{
"path": "demo/animated-transitions/d2/styles.css",
"chars": 687,
"preview": ".leaving {\n animation: 1s slideOutDown ease-in-out;\n}\n\n.entering {\n animation: 1s slideInDown linear;\n}\n\n.users-enteri"
},
{
"path": "demo/animated-transitions/d2/x-wrapper.ts",
"chars": 686,
"preview": "import { html, LitElement, type TemplateResult } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport s"
},
{
"path": "demo/animated-transitions/index.html",
"chars": 339,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Animated Transitions</title>\n\n <script async type=\"module\" src=\""
},
{
"path": "demo/animated-transitions/index.ts",
"chars": 4162,
"preview": "/* eslint-disable import/no-duplicates, import/default */\nimport '@helpers/common.js';\nimport '@helpers/vaadin-demo-layo"
},
{
"path": "demo/code-splitting/d1/iframe.html",
"chars": 323,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Code Splitting</title>\n <script async type=\"module\" src=\"script."
},
{
"path": "demo/code-splitting/d1/script.ts",
"chars": 451,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport { Router } from '@vaadin/router';\n\n// tag::"
},
{
"path": "demo/code-splitting/d1/user.bundle.ts",
"chars": 764,
"preview": "import { html, LitElement, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js'"
},
{
"path": "demo/code-splitting/d2/iframe.html",
"chars": 313,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Code Splitting</title>\n <script async type=\"module\" src=\"./scrip"
},
{
"path": "demo/code-splitting/d2/script.ts",
"chars": 521,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-image-view.js';\nimport '@helper"
},
{
"path": "demo/code-splitting/d2/user-routes.ts",
"chars": 319,
"preview": "import '@helpers/x-user-list.js';\nimport '@helpers/x-user-profile.js';\nimport type { Route } from '@vaadin/router';\n\n// "
},
{
"path": "demo/code-splitting/index.html",
"chars": 314,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Code Splitting</title>\n <script async type=\"module\" src=\"./index"
},
{
"path": "demo/code-splitting/index.ts",
"chars": 3941,
"preview": "/* eslint-disable import/no-duplicates, import/default */\nimport '@helpers/common.js';\nimport '@helpers/vaadin-demo-code"
},
{
"path": "demo/getting-started/d1/iframe.html",
"chars": 325,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Getting Started</title>\n <script async type=\"module\" src=\"script"
},
{
"path": "demo/getting-started/d1/script.ts",
"chars": 561,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-list.js';\nimport '@helpers"
},
{
"path": "demo/getting-started/index.html",
"chars": 323,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Getting Started</title>\n <script async type=\"module\" src=\"./inde"
},
{
"path": "demo/getting-started/index.ts",
"chars": 5237,
"preview": "/* eslint-disable import/no-duplicates, import/default */\nimport '@helpers/common.js';\nimport '@helpers/vaadin-demo-layo"
},
{
"path": "demo/getting-started/snippets/s1.html",
"chars": 269,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Snippets</title>\n </head>\n <body>\n <!-- tag::snippet[] -->\n "
},
{
"path": "demo/getting-started/snippets/s2.ts",
"chars": 114,
"preview": "// tag::snippet[]\nimport { Router } from '@vaadin/router';\n// end::snippet[]\n\nexport const router = new Router();\n"
},
{
"path": "demo/getting-started/snippets/s4.ts",
"chars": 617,
"preview": "/* eslint-disable import/order, import/no-duplicates */\n// tag::snippet[]\nimport type { RouterLocation } from '@vaadin/r"
},
{
"path": "demo/index.html",
"chars": 239,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <script>\n if (!window.location.pathname.endsWith('/getting-started/in"
},
{
"path": "demo/index.ts",
"chars": 219,
"preview": "import '@vaadin/vaadin-lumo-styles/all-imports.js';\nimport '@helpers/vaadin-demo-layout.js';\n\nif (window.matchMedia('(pr"
},
{
"path": "demo/lifecycle-callback/d1/iframe.html",
"chars": 321,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Lifecycle Callback</title>\n <script async type=\"module\" src=\"scr"
},
{
"path": "demo/lifecycle-callback/d1/script.ts",
"chars": 354,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport { Router } from '@vaadin/router';\nimport '."
},
{
"path": "demo/lifecycle-callback/d1/x-countdown.ts",
"chars": 1242,
"preview": "import { html, LitElement, render, type TemplateResult } from 'lit';\nimport { customElement } from 'lit/decorators.js';\n"
},
{
"path": "demo/lifecycle-callback/d2/iframe.html",
"chars": 324,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Lifecycle Callback</title>\n <script async type=\"module\" src=\"scr"
},
{
"path": "demo/lifecycle-callback/d2/script.ts",
"chars": 352,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport { Router } from '@vaadin/router';\nimport '."
},
{
"path": "demo/lifecycle-callback/d2/x-friend.ts",
"chars": 713,
"preview": "import { css, html, LitElement, render, type TemplateResult } from 'lit';\nimport { customElement } from 'lit/decorators."
},
{
"path": "demo/lifecycle-callback/d3/iframe.html",
"chars": 259,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Lifecycle Callback</title>\n <script async type=\"module\" src=\"scr"
},
{
"path": "demo/lifecycle-callback/d3/script.ts",
"chars": 432,
"preview": "import '@helpers/iframe.script.js';\nimport { Router } from '@vaadin/router';\nimport './x-user-deleted.js';\nimport './x-u"
},
{
"path": "demo/lifecycle-callback/d3/x-user-deleted.ts",
"chars": 532,
"preview": "import { html, LitElement, type TemplateResult } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport t"
},
{
"path": "demo/lifecycle-callback/d3/x-user-manage.ts",
"chars": 1153,
"preview": "import { html, LitElement, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js'"
},
{
"path": "demo/lifecycle-callback/d4/iframe.html",
"chars": 259,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Lifecycle Callback</title>\n <script async type=\"module\" src=\"scr"
},
{
"path": "demo/lifecycle-callback/d4/script.ts",
"chars": 357,
"preview": "import '@helpers/iframe.script.js';\nimport { Router } from '@vaadin/router';\nimport './x-main-page.js';\nimport './x-auto"
},
{
"path": "demo/lifecycle-callback/d4/x-autosave-view.ts",
"chars": 1078,
"preview": "/* eslint-disable @typescript-eslint/unbound-method */\nimport { html, LitElement, type TemplateResult } from 'lit';\nimpo"
},
{
"path": "demo/lifecycle-callback/d4/x-main-page.ts",
"chars": 407,
"preview": "import { html, LitElement, type TemplateResult } from 'lit';\nimport { customElement } from 'lit/decorators.js';\n\n// tag:"
},
{
"path": "demo/lifecycle-callback/d5/iframe.html",
"chars": 412,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Lifecycle Callback</title>\n <script async type=\"module\" src=\"scr"
},
{
"path": "demo/lifecycle-callback/d5/script.ts",
"chars": 765,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-list.js';\nimport '@helpers"
},
{
"path": "demo/lifecycle-callback/d6/iframe.html",
"chars": 394,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Lifecycle Callback</title>\n <script async type=\"module\" src=\"scr"
},
{
"path": "demo/lifecycle-callback/d6/script.ts",
"chars": 1650,
"preview": "/* eslint-disable import/no-duplicates */\nimport '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '"
},
{
"path": "demo/lifecycle-callback/index.html",
"chars": 332,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Lifecycle Callback</title>\n <script async type=\"module\" src=\"./i"
},
{
"path": "demo/lifecycle-callback/index.ts",
"chars": 14434,
"preview": "/* eslint-disable import/no-duplicates, import/default */\nimport '@helpers/common.js';\nimport '@helpers/vaadin-demo-layo"
},
{
"path": "demo/lifecycle-callback/snippets/my-view-with-after-enter.ts",
"chars": 616,
"preview": "/* eslint-disable @typescript-eslint/no-unused-vars */\nimport { LitElement } from 'lit';\nimport { customElement } from '"
},
{
"path": "demo/lifecycle-callback/snippets/my-view-with-after-leave.ts",
"chars": 616,
"preview": "/* eslint-disable @typescript-eslint/no-unused-vars */\nimport { LitElement } from 'lit';\nimport { customElement } from '"
},
{
"path": "demo/lifecycle-callback/snippets/my-view-with-before-enter.ts",
"chars": 668,
"preview": "/* eslint-disable @typescript-eslint/no-unused-vars */\nimport { LitElement } from 'lit';\nimport { customElement } from '"
},
{
"path": "demo/lifecycle-callback/snippets/my-view-with-before-leave.ts",
"chars": 625,
"preview": "/* eslint-disable @typescript-eslint/no-unused-vars */\nimport { LitElement } from 'lit';\nimport { customElement } from '"
},
{
"path": "demo/navigation-trigger/d1/iframe.html",
"chars": 259,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Navigation Trigger</title>\n <script async type=\"module\" src=\"scr"
},
{
"path": "demo/navigation-trigger/d1/script.ts",
"chars": 551,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-list.js';\nimport { Router "
},
{
"path": "demo/navigation-trigger/d2/iframe.html",
"chars": 315,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Navigation Trigger</title>\n <script async type=\"module\" src=\"scr"
},
{
"path": "demo/navigation-trigger/d2/script.ts",
"chars": 364,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-list.js';\nimport { Router "
},
{
"path": "demo/navigation-trigger/d3/iframe.html",
"chars": 352,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Navigation Trigger</title>\n <script async type=\"module\" src=\"scr"
},
{
"path": "demo/navigation-trigger/d3/script.ts",
"chars": 728,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-list.js';\nimport { DEFAULT"
},
{
"path": "demo/navigation-trigger/d4/iframe.html",
"chars": 315,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Navigation Trigger</title>\n <script async type=\"module\" src=\"scr"
},
{
"path": "demo/navigation-trigger/d4/script.ts",
"chars": 639,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-list.js';\nimport '@helpers"
},
{
"path": "demo/navigation-trigger/index.html",
"chars": 330,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Navigation Trigger</title>\n <script async type=\"module\" src=\"./i"
},
{
"path": "demo/navigation-trigger/index.ts",
"chars": 6750,
"preview": "/* eslint-disable import/no-duplicates, import/default */\nimport '@helpers/common.js';\nimport '@helpers/vaadin-demo-layo"
},
{
"path": "demo/redirect/d1/iframe.html",
"chars": 366,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Redirect</title>\n <script async type=\"module\" src=\"script.ts\"></"
},
{
"path": "demo/redirect/d1/script.ts",
"chars": 583,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-profile.js';\nimport '@help"
},
{
"path": "demo/redirect/d2/iframe.html",
"chars": 369,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Redirect</title>\n <script async type=\"module\" src=\"script.ts\"></"
},
{
"path": "demo/redirect/d2/script.ts",
"chars": 609,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-login-view.js';\nimport { Router"
},
{
"path": "demo/redirect/d2/x-admin-view.ts",
"chars": 830,
"preview": "import { html, LitElement, type TemplateResult } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport t"
},
{
"path": "demo/redirect/d3/iframe.html",
"chars": 320,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Redirect</title>\n <script async type=\"module\" src=\"script.ts\"></"
},
{
"path": "demo/redirect/d3/script.ts",
"chars": 485,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-profile.js';\nimport { Rout"
},
{
"path": "demo/redirect/index.html",
"chars": 302,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Redirect</title>\n <script async type=\"module\" src=\"./index.ts\"><"
},
{
"path": "demo/redirect/index.ts",
"chars": 5340,
"preview": "/* eslint-disable import/no-duplicates, import/default */\nimport '@helpers/common.js';\nimport '@helpers/vaadin-demo-layo"
},
{
"path": "demo/redirect/snippets/s1.ts",
"chars": 129,
"preview": "import { Router } from '@vaadin/router';\n\n// tag::snippet[]\nRouter.go('/to/path?paramName=value#sectionName');\n// end::s"
},
{
"path": "demo/redirect/snippets/s2.ts",
"chars": 200,
"preview": "import { Router } from '@vaadin/router';\n\n// tag::snippet[]\nRouter.go({\n pathname: '/to/path',\n // optional\n search: "
},
{
"path": "demo/redirect/snippets/s3.ts",
"chars": 298,
"preview": "// tag::snippet[]\nwindow.dispatchEvent(\n new CustomEvent('vaadin-router-go', {\n detail: {\n pathname: '/to/path'"
},
{
"path": "demo/route-actions/d1/iframe.html",
"chars": 401,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Route Actions</title>\n <script async type=\"module\" src=\"script.t"
},
{
"path": "demo/route-actions/d1/script.ts",
"chars": 1062,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-list.js';\nimport '@helpers"
},
{
"path": "demo/route-actions/d2/iframe.html",
"chars": 439,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Route Actions</title>\n <script async type=\"module\" src=\"script.t"
},
{
"path": "demo/route-actions/d2/script.ts",
"chars": 758,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-list.js';\nimport '@helpers"
},
{
"path": "demo/route-actions/d3/iframe.html",
"chars": 405,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Route Actions</title>\n <script async type=\"module\" src=\"script.t"
},
{
"path": "demo/route-actions/d3/script.ts",
"chars": 703,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-profile.js';\nimport { Rout"
},
{
"path": "demo/route-actions/d4/iframe.html",
"chars": 407,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Route Actions</title>\n <script async type=\"module\" src=\"script.t"
},
{
"path": "demo/route-actions/d4/script.ts",
"chars": 748,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-profile.js';\nimport { Rout"
},
{
"path": "demo/route-actions/d5/iframe.html",
"chars": 341,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Route Actions</title>\n <script async type=\"module\" src=\"script.t"
},
{
"path": "demo/route-actions/d5/script.ts",
"chars": 915,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-login-view.js';\nimport { Router"
},
{
"path": "demo/route-actions/index.html",
"chars": 310,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Route Actions</title>\n <script async type=\"module\" src=\"./index."
},
{
"path": "demo/route-actions/index.ts",
"chars": 9341,
"preview": "/* eslint-disable import/no-duplicates, import/default */\nimport '@helpers/common.js';\nimport '@helpers/vaadin-demo-layo"
},
{
"path": "demo/route-parameters/d1/iframe.html",
"chars": 489,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Route Parameters</title>\n <script async type=\"module\" src=\"scrip"
},
{
"path": "demo/route-parameters/d1/script.ts",
"chars": 732,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-profile.js';\nimport '@help"
},
{
"path": "demo/route-parameters/d2/iframe.html",
"chars": 361,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Route Parameters</title>\n <script async type=\"module\" src=\"scrip"
},
{
"path": "demo/route-parameters/d2/script.ts",
"chars": 375,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport { Router } from '@vaadin/router';\nimport '."
},
{
"path": "demo/route-parameters/d2/x-project-view.ts",
"chars": 762,
"preview": "import { LitElement, html, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js'"
},
{
"path": "demo/route-parameters/d3/iframe.html",
"chars": 344,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Route Parameters</title>\n <script async type=\"module\" src=\"scrip"
},
{
"path": "demo/route-parameters/d3/script.ts",
"chars": 452,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-list.js';\nimport '@helpers"
},
{
"path": "demo/route-parameters/d4/iframe.html",
"chars": 395,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Route Parameters</title>\n <script async type=\"module\" src=\"scrip"
},
{
"path": "demo/route-parameters/d4/script.ts",
"chars": 583,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-list.js';\nimport '@helpers"
},
{
"path": "demo/route-parameters/d5/iframe.html",
"chars": 383,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Route Parameters</title>\n <script async type=\"module\" src=\"scrip"
},
{
"path": "demo/route-parameters/d5/script.ts",
"chars": 284,
"preview": "import '@helpers/iframe.script.js';\nimport { Router } from '@vaadin/router';\nimport './x-page-number-view.js';\n\n// tag::"
},
{
"path": "demo/route-parameters/d5/x-page-number-view.ts",
"chars": 802,
"preview": "/* eslint-disable @typescript-eslint/class-methods-use-this */\nimport { html, LitElement, type TemplateResult } from 'li"
},
{
"path": "demo/route-parameters/d6/iframe.html",
"chars": 408,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Route Parameters</title>\n <script async type=\"module\" src=\"scrip"
},
{
"path": "demo/route-parameters/d6/script.ts",
"chars": 270,
"preview": "import '@helpers/iframe.script.js';\nimport './x-hash-view.js';\nimport { Router } from '@vaadin/router';\n\n// tag::snippet"
},
{
"path": "demo/route-parameters/d6/x-hash-view.ts",
"chars": 623,
"preview": "import { html, LitElement, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js'"
},
{
"path": "demo/route-parameters/index.html",
"chars": 322,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>Route Parameters</title>\n <script async type=\"module\" src=\"./ind"
},
{
"path": "demo/route-parameters/index.ts",
"chars": 8642,
"preview": "/* eslint-disable import/no-duplicates, import/default */\nimport '@helpers/common.js';\nimport '@helpers/vaadin-demo-layo"
},
{
"path": "demo/tsconfig.json",
"chars": 201,
"preview": "{\n \"extends\": [\"../tsconfig.json\"],\n \"compilerOptions\": {\n \"paths\": {\n \"@helpers/*\": [\"./@helpers/*\"],\n \""
},
{
"path": "demo/types.t.ts",
"chars": 418,
"preview": "/* eslint-disable import/unambiguous */\n// eslint-disable-next-line @typescript-eslint/triple-slash-reference\n/// <refer"
},
{
"path": "demo/url-generation/d1/iframe.html",
"chars": 255,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>URL Generation</title>\n <script async type=\"module\" src=\"script."
},
{
"path": "demo/url-generation/d1/script.ts",
"chars": 730,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-list.js';\nimport '@helpers"
},
{
"path": "demo/url-generation/d1/x-main-layout.ts",
"chars": 1054,
"preview": "import { html, LitElement, nothing, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decor"
},
{
"path": "demo/url-generation/d2/iframe.html",
"chars": 255,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>URL Generation</title>\n <script async type=\"module\" src=\"script."
},
{
"path": "demo/url-generation/d2/script.ts",
"chars": 715,
"preview": "import '@helpers/iframe.script.js';\nimport '@helpers/x-home-view.js';\nimport '@helpers/x-user-list.js';\nimport '@helpers"
},
{
"path": "demo/url-generation/d2/x-main-layout.ts",
"chars": 1043,
"preview": "import { html, LitElement, nothing, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decor"
},
{
"path": "demo/url-generation/d3/iframe.html",
"chars": 255,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>URL Generation</title>\n <script async type=\"module\" src=\"script."
},
{
"path": "demo/url-generation/d3/script.ts",
"chars": 455,
"preview": "import '@helpers/iframe.script.js';\nimport { Router } from '@vaadin/router';\nimport './x-user-layout-d3.js';\n\n// tag::sn"
},
{
"path": "demo/url-generation/d3/x-user-layout-d3.ts",
"chars": 1243,
"preview": "import '@helpers/x-user-profile.js';\nimport { html, LitElement, nothing, type TemplateResult } from 'lit';\nimport { cust"
},
{
"path": "demo/url-generation/d4/iframe.html",
"chars": 255,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>URL Generation</title>\n <script async type=\"module\" src=\"script."
},
{
"path": "demo/url-generation/d4/script.ts",
"chars": 528,
"preview": "import '@helpers/iframe.script.js';\nimport { Router } from '@vaadin/router';\nimport './x-user-layout-d4.js';\n\nhistory.pu"
},
{
"path": "demo/url-generation/d4/x-user-layout-d4.ts",
"chars": 1166,
"preview": "import '@helpers/x-user-profile.js';\nimport { html, LitElement, nothing, type TemplateResult } from 'lit';\nimport { cust"
},
{
"path": "demo/url-generation/d5/iframe.html",
"chars": 255,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>URL Generation</title>\n <script async type=\"module\" src=\"script."
},
{
"path": "demo/url-generation/d5/script.ts",
"chars": 272,
"preview": "import '@helpers/iframe.script.js';\nimport { Router } from '@vaadin/router';\nimport './x-pages-menu.js';\n\n// tag::snippe"
},
{
"path": "demo/url-generation/d5/x-pages-menu.ts",
"chars": 1492,
"preview": "import { css, html, LitElement, nothing, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/"
},
{
"path": "demo/url-generation/index.html",
"chars": 314,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <title>URL Generation</title>\n <script async type=\"module\" src=\"./index"
},
{
"path": "demo/url-generation/index.ts",
"chars": 6416,
"preview": "/* eslint-disable import/no-duplicates, import/default */\nimport '@helpers/common.js';\nimport '@helpers/vaadin-demo-layo"
},
{
"path": "demo/vite.config.ts",
"chars": 1177,
"preview": "import { dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { glob } from 'glob';\nimport { mer"
},
{
"path": "index.html",
"chars": 0,
"preview": ""
},
{
"path": "karma.config.cjs",
"chars": 1619,
"preview": "const karmaChromeLauncher = require('karma-chrome-launcher');\nconst karmaCoverage = require('karma-coverage');\nconst kar"
},
{
"path": "package.json",
"chars": 4244,
"preview": "{\n \"name\": \"@vaadin/router\",\n \"version\": \"2.0.1\",\n \"description\": \"Small and powerful client-side router for Web Comp"
},
{
"path": "polymer.json",
"chars": 540,
"preview": "{\n \"entrypoint\": \"bower_components/vaadin-router/index.html\",\n \"shell\": \"bower_components/iron-component-page/iron-com"
},
{
"path": "scripts/build.ts",
"chars": 1001,
"preview": "import { readFile } from 'node:fs/promises';\nimport { fileURLToPath } from 'node:url';\nimport { build } from 'esbuild';\n"
},
{
"path": "scripts/codeSnippet.ts",
"chars": 3827,
"preview": "import 'regexp.escape/auto';\nimport { extname } from 'node:path';\nimport hljs from 'highlight.js/lib/core';\nimport cssLa"
},
{
"path": "scripts/constructCss.ts",
"chars": 1087,
"preview": "import { readFile } from 'node:fs/promises';\nimport cssnanoPlugin from 'cssnano';\nimport postcss from 'postcss';\nimport "
},
{
"path": "scripts/copy-dts.ts",
"chars": 539,
"preview": "import { constants, copyFile, mkdir } from 'node:fs/promises';\nimport { glob } from 'glob';\n\nconst root = new URL('../',"
},
{
"path": "scripts/loadRegisterJs.ts",
"chars": 942,
"preview": "import { readFile } from 'node:fs/promises';\nimport MagicString from 'magic-string';\nimport type { Plugin } from 'vite';"
},
{
"path": "scripts/register.js",
"chars": 232,
"preview": "export function __REGISTER__(feature, vaadinObj = (window.Vaadin ??= {})) {\n vaadinObj.registrations ??= [];\n vaadinOb"
},
{
"path": "scripts/resolveHTMLImports.ts",
"chars": 707,
"preview": "import { fileURLToPath, pathToFileURL } from 'node:url';\nimport type { Plugin } from 'vite';\n\nconst searchParamsPattern "
},
{
"path": "scripts/types.d.ts",
"chars": 282,
"preview": "/* eslint-disable import/unambiguous */\n\ndeclare module 'regexp.escape/auto';\n\ninterface RegExpConstructor {\n escape(st"
},
{
"path": "src/index.ts",
"chars": 359,
"preview": "export * from './router.js';\nexport { DEFAULT_TRIGGERS } from './triggers/navigation.js';\nexport type * from './types.t."
},
{
"path": "src/mod.t.ts",
"chars": 229,
"preview": "// eslint-disable-next-line import/unambiguous\ndeclare module '@vaadin/vaadin-usage-statistics/vaadin-usage-statistics.j"
},
{
"path": "src/resolver/LICENSE.txt",
"chars": 1078,
"preview": "The MIT License\n\nCopyright (c) 2015-present Kriasoft.\n\nPermission is hereby granted, free of charge, to any person obtai"
},
{
"path": "src/resolver/generateUrls.ts",
"chars": 4741,
"preview": "/**\n * Universal Router (https://www.kriasoft.com/universal-router/)\n *\n * Copyright (c) 2015-present Kriasoft.\n *\n * Th"
},
{
"path": "src/resolver/matchPath.ts",
"chars": 2453,
"preview": "/**\n * Universal Router (https://www.kriasoft.com/universal-router/)\n *\n * Copyright (c) 2015-present Kriasoft.\n *\n * Th"
},
{
"path": "src/resolver/matchRoute.ts",
"chars": 5227,
"preview": "/**\n * Universal Router (https://www.kriasoft.com/universal-router/)\n *\n * Copyright (c) 2015-present Kriasoft.\n *\n * Th"
},
{
"path": "src/resolver/resolveRoute.ts",
"chars": 784,
"preview": "/**\n * Universal Router (https://www.kriasoft.com/universal-router/)\n *\n * Copyright (c) 2015-present Kriasoft.\n *\n * Th"
},
{
"path": "src/resolver/resolver.ts",
"chars": 11598,
"preview": "/**\n * Universal Router (https://www.kriasoft.com/universal-router/)\n *\n * Copyright (c) 2015-present Kriasoft.\n *\n * Th"
},
{
"path": "src/resolver/types.t.ts",
"chars": 7717,
"preview": "import type { EmptyObject } from 'type-fest';\nimport type Resolver from './resolver.js';\nimport type { NotFoundResult } "
},
{
"path": "src/resolver/utils.ts",
"chars": 2446,
"preview": "import type { ChildrenCallback, Route, RouteContext } from './types.t.js';\n\n/**\n * {@inheritDoc \"<internal>\".NotFoundErr"
},
{
"path": "src/router-config.ts",
"chars": 27,
"preview": "import './router-meta.js';\n"
},
{
"path": "src/router-meta.ts",
"chars": 233,
"preview": "import { usageStatistics } from '@vaadin/vaadin-usage-statistics/vaadin-usage-statistics.js';\n\n// @ts-expect-error: Gene"
},
{
"path": "src/router.ts",
"chars": 38811,
"preview": "/* eslint-disable @typescript-eslint/consistent-return */\nimport { compile } from 'path-to-regexp';\nimport type { EmptyO"
},
{
"path": "src/transitions/animate.ts",
"chars": 1023,
"preview": "const willAnimate = (elem: Element) => {\n const name = getComputedStyle(elem).getPropertyValue('animation-name');\n ret"
},
{
"path": "src/triggers/click.ts",
"chars": 4390,
"preview": "import type { NavigationTrigger } from '../types.t.js';\nimport { fireRouterEvent } from '../utils.js';\n\n/* istanbul igno"
},
{
"path": "src/triggers/navigation.ts",
"chars": 503,
"preview": "import type { NavigationTrigger } from '../types.t.js';\nimport CLICK from './click.js';\nimport POPSTATE from './popstate"
},
{
"path": "src/triggers/popstate.ts",
"chars": 733,
"preview": "import type { NavigationTrigger } from '../types.t.js';\nimport { fireRouterEvent } from '../utils.js';\n\nfunction vaadinR"
},
{
"path": "src/types.t.ts",
"chars": 20815,
"preview": "import type { EmptyObject, RequireAtLeastOne } from 'type-fest';\nimport type { ResolutionError, ResolverOptions } from '"
},
{
"path": "src/utils.ts",
"chars": 6871,
"preview": "import { compile } from 'path-to-regexp';\nimport type Resolver from './resolver/resolver.js';\nimport { isFunction, isObj"
},
{
"path": "src/v1-compat.t.ts",
"chars": 2180,
"preview": "/* eslint-disable max-classes-per-file */\n\nimport type { MaybePromise } from './resolver/types.t.js';\nimport type {\n Co"
},
{
"path": "test/resolver/LICENSE.txt",
"chars": 1078,
"preview": "The MIT License\n\nCopyright (c) 2015-present Kriasoft.\n\nPermission is hereby granted, free of charge, to any person obtai"
}
]
// ... and 22 more files (download for full content)
About this extraction
This page contains the full source code of the vaadin/vaadin-router GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 222 files (655.0 KB), approximately 164.6k tokens, and a symbol index with 459 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.