@nutin/cli 1.3.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +89 -9
- package/README.md +20 -35
- package/bin/add-feature.mjs +9 -0
- package/bin/cli.mjs +8 -0
- package/bin/create-app.mjs +5 -4
- package/bin/update-app.mjs +9 -0
- package/lib/index.mjs +76 -25
- package/lib/src/add-feature/feature-adder.mjs +83 -0
- package/lib/src/add-feature/feature-context-builder.mjs +25 -0
- package/lib/src/add-feature/nutin-config-updater.mjs +29 -0
- package/lib/src/add-feature/package-json-updater.mjs +49 -0
- package/lib/src/common/feature-registry.mjs +11 -0
- package/lib/src/common/file-generator.mjs +159 -0
- package/lib/src/common/meta-bootstrap-prompt.mjs +33 -0
- package/lib/src/common/package-data.mjs +10 -0
- package/lib/src/common/package-json-helper.mjs +103 -0
- package/lib/src/common/project-meta.mjs +54 -0
- package/lib/src/create-app/context-builder.mjs +21 -0
- package/lib/src/{git-manager.mjs → create-app/git-manager.mjs} +2 -2
- package/lib/src/create-app/json-manager.mjs +73 -0
- package/lib/src/{prompts.mjs → create-app/new-app-prompt.mjs} +6 -6
- package/lib/src/{project-generator.mjs → create-app/project-generator.mjs} +10 -8
- package/lib/src/create-app/success-message.mjs +14 -0
- package/lib/src/update-app/conflict-report-writer.mjs +31 -0
- package/lib/src/update-app/fetch-old-templates.mjs +61 -0
- package/lib/src/update-app/project-updater.mjs +136 -0
- package/lib/src/update-app/template-differ.mjs +124 -0
- package/lib/src/update-app/update-context-builder.mjs +25 -0
- package/lib/src/update-app/update-summary-printer.mjs +22 -0
- package/lib/src/update-app/version-compare.mjs +12 -0
- package/lib/src/utils/print.mjs +36 -0
- package/lib/src/utils/promise-exec-alias.mjs +4 -0
- package/package.json +17 -12
- package/templates/base/.gitignore.hbs +6 -1
- package/templates/base/AGENTS.md.hbs +169 -0
- package/templates/base/GETTING_STARTED.md.hbs +227 -0
- package/templates/base/README.md.hbs +8 -17
- package/templates/base/config/languages.json.hbs +6 -0
- package/templates/base/config/seo.json.hbs +12 -0
- package/templates/base/nutin.config.js.hbs +51 -0
- package/templates/base/src/app/components/index.ts.hbs +1 -2
- package/templates/base/src/app/globals.d.ts.hbs +3 -19
- package/templates/base/src/{libs/guards → app}/guards.ts.hbs +1 -5
- package/templates/base/src/app/main.ts.hbs +7 -26
- package/templates/base/src/app/services/index.ts.hbs +1 -1
- package/templates/base/src/app/views/home/home.view.html.hbs +7 -0
- package/templates/base/src/app/views/home/home.view.ts.hbs +1 -56
- package/templates/base/src/app/views/index.ts.hbs +1 -0
- package/templates/base/src/app/views/not-found/not-found.view.html.hbs +8 -0
- package/templates/base/src/app/views/not-found/not-found.view.ts.hbs +4 -37
- package/templates/base/src/core/base-classes/base-component/base-component.test.js.hbs +158 -0
- package/templates/base/src/core/base-classes/base-component/base-component.ts.hbs +68 -35
- package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.test.js.hbs +124 -0
- package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.ts.hbs +9 -8
- package/templates/base/src/core/base-classes/base-component/helpers/children.helper.test.js.hbs +74 -0
- package/templates/base/src/core/base-classes/base-component/helpers/children.helper.ts.hbs +1 -1
- package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.test.js.hbs +145 -0
- package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.ts.hbs +7 -1
- package/templates/base/src/core/base-classes/base-component/helpers/event.helper.test.js.hbs +88 -0
- package/templates/base/src/core/base-classes/base-component/helpers/event.helper.ts.hbs +2 -2
- package/templates/base/src/core/base-classes/base-component/helpers/i18n.helper.test.js.hbs +62 -0
- package/templates/base/src/core/base-classes/base-component/helpers/pipe.helper.test.js.hbs +82 -0
- package/templates/base/src/core/base-classes/base-component/helpers/security.helper.test.js.hbs +142 -0
- package/templates/base/src/core/base-classes/base-component/helpers/security.helper.ts.hbs +67 -22
- package/templates/base/src/core/base-classes/base-component/helpers/token.helper.test.js.hbs +183 -0
- package/templates/base/src/core/base-classes/component/component.test.js.hbs +85 -0
- package/templates/base/src/core/base-classes/component/component.ts.hbs +24 -33
- package/templates/base/src/core/base-classes/component/helpers/config.helper.test.js.hbs +58 -0
- package/templates/base/src/core/base-classes/component/helpers/data-binding.helper.test.js.hbs +75 -0
- package/templates/base/src/core/base-classes/service/service.test.js.hbs +150 -0
- package/templates/base/src/core/base-classes/service/service.ts.hbs +26 -20
- package/templates/base/src/core/base-classes/view/view.test.js.hbs +74 -0
- package/templates/base/src/core/base-classes/view/view.ts.hbs +17 -41
- package/templates/base/src/core/config.ts.hbs +7 -0
- package/templates/base/src/core/index.ts.hbs +0 -3
- package/templates/base/src/core/internals.d.ts.hbs +27 -0
- package/templates/base/src/core/nutin-config.d.ts.hbs +4 -0
- package/templates/base/src/core/services/event-bus/event-bus.test.js.hbs +67 -3
- package/templates/base/src/core/services/event-bus/event-bus.ts.hbs +9 -4
- package/templates/base/src/core/services/event-bus/index.ts.hbs +3 -0
- package/templates/base/src/core/services/event-bus/lifecycle.facade.test.js.hbs +123 -0
- package/templates/base/src/core/services/event-bus/lifecycle.facade.ts.hbs +61 -0
- package/templates/base/src/core/services/event-bus/navigation.facade.test.js.hbs +46 -0
- package/templates/base/src/core/services/event-bus/navigation.facade.ts.hbs +25 -0
- package/templates/base/src/core/services/globals/globals.test.js.hbs +149 -0
- package/templates/base/src/core/services/globals/globals.ts.hbs +95 -0
- package/templates/base/src/core/services/http-client/helpers/http-builder.helper.test.js.hbs +25 -0
- package/templates/base/src/core/services/http-client/helpers/http-manager.helper.test.js.hbs +20 -0
- package/templates/base/src/core/services/http-client/http-client.test.js.hbs +43 -0
- package/templates/base/src/core/services/i18n/i18n.test.js.hbs +270 -0
- package/templates/{features/i18n → base}/src/core/services/i18n/i18n.ts.hbs +22 -4
- package/templates/base/src/core/services/index.ts.hbs +5 -6
- package/templates/base/src/core/services/pipe-registry/pipe-registry.test.js.hbs +60 -1
- package/templates/base/src/core/services/pipe-registry/pipe-registry.ts.hbs +1 -1
- package/templates/base/src/core/services/pipe-registry/pipes.test.js.hbs +128 -0
- package/templates/base/src/{libs/pipes → core/services/pipe-registry}/pipes.ts.hbs +8 -5
- package/templates/base/src/core/services/router/helpers/navigation-manager.helper.test.js.hbs +149 -6
- package/templates/base/src/core/services/router/helpers/navigation-manager.helper.ts.hbs +40 -29
- package/templates/base/src/core/services/router/helpers/route-guard-manager.helper.test.js.hbs +27 -0
- package/templates/base/src/core/services/router/helpers/view-render-manager.helper.test.js.hbs +83 -17
- package/templates/base/src/core/services/router/helpers/view-render-manager.helper.ts.hbs +14 -38
- package/templates/base/src/core/services/router/router.test.js.hbs +226 -2
- package/templates/base/src/core/services/router/router.ts.hbs +40 -8
- package/templates/base/src/index.html.hbs +2 -6
- package/templates/base/src/styles/_styles.scss.hbs +102 -43
- package/templates/base/src/styles/base/_variables.scss.hbs +1 -86
- package/templates/base/src/styles/main.scss.hbs +1 -18
- package/templates/base/src/styles/tailwind.css.hbs +2 -0
- package/templates/base/tools/builder/builder.config.js.hbs +40 -0
- package/templates/base/tools/builder/builder.js.hbs +24 -26
- package/templates/base/tools/builder/core/app/binary-extensions.test.js.hbs +16 -0
- package/templates/base/tools/builder/core/app/compile-ts.js.hbs +16 -0
- package/templates/base/tools/builder/core/{copy-static.js.hbs → app/copy-static.js.hbs} +27 -34
- package/templates/base/tools/builder/core/app/validate-routes.js.hbs +77 -0
- package/templates/base/tools/builder/core/finalize-build.js.hbs +16 -7
- package/templates/base/tools/builder/core/{add-tags.js.hbs → html-index/add-tags.js.hbs} +2 -2
- package/templates/base/tools/builder/core/html-index/add-tags.test.js.hbs +56 -0
- package/templates/base/tools/builder/core/{validate-html.js.hbs → html-index/validate-html.js.hbs} +5 -11
- package/templates/base/tools/builder/core/html-templates/merge-templates.js.hbs +152 -0
- package/templates/base/tools/builder/core/html-templates/minify-html.js.hbs +15 -0
- package/templates/base/tools/builder/core/html-templates/process-html-templates.js.hbs +8 -0
- package/templates/{features/i18n/tools/builder/core → base/tools/builder/core/i18n}/build-i18n.js.hbs +6 -17
- package/templates/base/tools/builder/core/prod-bundle/compress-files.js.hbs +37 -0
- package/templates/base/tools/builder/core/prod-bundle/esbuild.js.hbs +30 -0
- package/templates/base/tools/builder/core/{hash-files.js.hbs → prod-bundle/hash-files.js.hbs} +14 -11
- package/templates/base/tools/builder/core/seo/generate-robots-txt.js.hbs +51 -0
- package/templates/base/tools/builder/core/seo/generate-seo-files.js.hbs +16 -0
- package/templates/base/tools/builder/core/seo/generate-seo-html.js.hbs +128 -0
- package/templates/base/tools/builder/core/seo/generate-sitemap-xml.js.hbs +43 -0
- package/templates/base/tools/builder/core/seo/html-substitutions.js.hbs +99 -0
- package/templates/base/tools/builder/core/seo/i18n-resolution.js.hbs +66 -0
- package/templates/base/tools/builder/core/seo/route-output.js.hbs +64 -0
- package/templates/base/tools/builder/core/seo/ssr/ssr-bundle.js.hbs +54 -0
- package/templates/base/tools/builder/core/seo/ssr/ssr-polyfills.js.hbs +132 -0
- package/templates/base/tools/builder/core/seo/ssr/ssr-render.js.hbs +95 -0
- package/templates/base/tools/builder/core/styles/sass.js.hbs +40 -0
- package/templates/base/tools/builder/core/styles/tailwind.js.hbs +84 -0
- package/templates/base/tools/dev/dev-serve.js.hbs +26 -18
- package/templates/base/tools/dev/serve.js.hbs +11 -1
- package/templates/base/tools/dev/watcher.js.hbs +14 -6
- package/templates/base/tools/generator/generator.js.hbs +64 -47
- package/templates/base/tools/generator/handle-file.js.hbs +17 -44
- package/templates/base/tools/generator/handle-file.test.js.hbs +72 -0
- package/templates/base/tools/generator/templates/{component.template.js.hbs → component/component.template.js.hbs} +3 -2
- package/templates/base/tools/generator/templates/component/component.template.test.js.hbs +23 -0
- package/templates/base/tools/generator/templates/html/html.template.js.hbs +2 -0
- package/templates/base/tools/generator/templates/html/html.template.test.js.hbs +7 -0
- package/templates/base/tools/generator/templates/index.js.hbs +7 -12
- package/templates/base/tools/generator/templates/locale/locale.template.js.hbs +4 -0
- package/templates/base/tools/generator/templates/locale/locale.template.test.js.hbs +7 -0
- package/templates/base/tools/generator/templates/scss/scss.template.js.hbs +1 -0
- package/templates/base/tools/generator/templates/scss/scss.template.test.js.hbs +7 -0
- package/templates/base/tools/generator/templates/service/service.template.js.hbs +17 -0
- package/templates/base/tools/generator/templates/service/service.template.test.js.hbs +22 -0
- package/templates/base/tools/generator/templates/test/test.template.js.hbs +10 -0
- package/templates/base/tools/generator/templates/test/test.template.test.js.hbs +27 -0
- package/templates/base/tools/generator/templates/{view.template.js.hbs → view/view.template.js.hbs} +3 -2
- package/templates/base/tools/generator/templates/view/view.template.test.js.hbs +24 -0
- package/templates/base/tools/testin-nutin/core/coverage/collect-coverage.js.hbs +39 -0
- package/templates/base/tools/testin-nutin/core/coverage/compute-coverage.js.hbs +115 -0
- package/templates/base/tools/testin-nutin/core/coverage/compute-coverage.test.js.hbs +74 -0
- package/templates/base/tools/testin-nutin/core/coverage/write-summary-report.js.hbs +54 -0
- package/templates/base/tools/testin-nutin/core/coverage/write-uncovered-report.js.hbs +44 -0
- package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/assertion-lib.js.hbs +62 -24
- package/templates/base/tools/testin-nutin/core/globals/clock.js.hbs +206 -0
- package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/jsdom-setup.js.hbs +19 -6
- package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/register-test-globals.js.hbs +13 -4
- package/templates/base/tools/testin-nutin/core/globals/silence-console.js.hbs +23 -0
- package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/spyon.js.hbs +2 -0
- package/templates/base/tools/testin-nutin/core/index.js.hbs +9 -0
- package/templates/base/tools/testin-nutin/core/printer.js.hbs +85 -0
- package/templates/base/tools/testin-nutin/core/queue/queue.test.js.hbs +46 -0
- package/templates/base/tools/testin-nutin/core/queue/test-discovery.test.js.hbs +46 -0
- package/templates/base/tools/testin-nutin/core/queue/test-queue.js.hbs +154 -0
- package/templates/{features/testinNutin → base/tools}/testin-nutin/core/tests/assertions.test.js.hbs +27 -0
- package/templates/base/tools/testin-nutin/core/tests/clock.test.js.hbs +118 -0
- package/templates/base/tools/testin-nutin/core/tests/globals-each-all.test.js.hbs +28 -0
- package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/create-mock-method.js.hbs +2 -1
- package/templates/base/tools/testin-nutin/mocks/create-mock-method.test.js.hbs +64 -0
- package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-event-bus.js.hbs +3 -2
- package/templates/base/tools/testin-nutin/mocks/mock-event-bus.test.js.hbs +66 -0
- package/templates/base/tools/testin-nutin/mocks/mock-http-client.test.js.hbs +26 -0
- package/templates/base/tools/testin-nutin/mocks/mock-i18n.test.js.hbs +61 -0
- package/templates/base/tools/testin-nutin/mocks/mock-router.test.js.hbs +29 -0
- package/templates/base/tools/testin-nutin/runner.js.hbs +100 -0
- package/templates/base/tools/testin-nutin/watch-tests.js.hbs +37 -0
- package/templates/base/tools/utils/error-exit.js.hbs +8 -0
- package/templates/base/tools/utils/format.js.hbs +10 -10
- package/templates/base/tools/utils/format.test.js.hbs +45 -0
- package/templates/base/tools/utils/get-files-recursive.js.hbs +6 -1
- package/templates/base/tools/utils/get-files-recursive.test.js.hbs +49 -0
- package/templates/base/tools/utils/get-rel-to-core.test.js.hbs +18 -0
- package/templates/base/tools/utils/index.js.hbs +1 -3
- package/templates/base/tools/utils/languages.js.hbs +15 -0
- package/templates/base/tools/utils/languages.test.js.hbs +16 -0
- package/templates/base/tools/utils/print.js.hbs +19 -13
- package/templates/base/tools/utils/print.test.js.hbs +60 -0
- package/templates/base/tools/utils/prompt-boolean.js.hbs +7 -0
- package/templates/base/tools/utils/prompt-boolean.test.js.hbs +54 -0
- package/templates/base/tools/utils/run-command.js.hbs +4 -2
- package/templates/base/tools/utils/run-command.test.js.hbs +19 -0
- package/templates/base/tools/utils/run-script.js.hbs +14 -3
- package/templates/base/tools/utils/run-script.test.js.hbs +40 -0
- package/templates/features/{deployHelper → docker}/.dockerignore.hbs +4 -4
- package/templates/features/docker/tools/docker/Dockerfile.template.hbs +51 -0
- package/templates/features/{deployHelper/tools/deployment/nginx.conf.hbs → docker/tools/docker/nginx.conf.template} +4 -2
- package/templates/features/docker/tools/docker/scripts/docker-build.js.hbs +20 -0
- package/templates/features/docker/tools/docker/scripts/docker-ports.js.hbs +48 -0
- package/templates/features/docker/tools/docker/scripts/docker-ports.test.js.hbs +52 -0
- package/templates/features/docker/tools/docker/scripts/docker-run.js.hbs +28 -0
- package/templates/features/docker/tools/docker/scripts/render-docker-template.js.hbs +23 -0
- package/templates/features/docker/tools/docker/scripts/render-docker-template.test.js.hbs +60 -0
- package/templates/features/docker/tools/docker/scripts/validate-docker.js.hbs +30 -0
- package/lib/src/context-builder.mjs +0 -40
- package/lib/src/file-generator.mjs +0 -109
- package/lib/src/json-generator.mjs +0 -31
- package/lib/src/package-manager.mjs +0 -139
- package/lib/src/print.mjs +0 -32
- package/lib/src/utils.mjs +0 -18
- package/lib/src/version.mjs +0 -1
- package/templates/base/builder.config.js.hbs +0 -36
- package/templates/base/src/app/components/common/anchor/anchor.component.ts.hbs +0 -32
- package/templates/base/src/app/components/common/button/button.component.ts.hbs +0 -40
- package/templates/base/src/app/dom.test.js.hbs +0 -13
- package/templates/base/src/app/services/example/example.service.ts.hbs +0 -18
- package/templates/base/src/app/views/home/home.view.test.js.hbs +0 -26
- package/templates/base/src/core/services/store/store.test.js.hbs +0 -8
- package/templates/base/src/core/services/store/store.ts.hbs +0 -39
- package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.test.js.hbs +0 -0
- package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.ts.hbs +0 -113
- package/templates/base/src/core/utils/button-manager/button-manager.utils.test.js.hbs +0 -249
- package/templates/base/src/core/utils/button-manager/button-manager.utils.ts.hbs +0 -131
- package/templates/base/src/core/utils/helpers/attributes.helper.ts.hbs +0 -50
- package/templates/base/src/core/utils/index.ts.hbs +0 -2
- package/templates/base/src/libs/index.ts.hbs +0 -4
- package/templates/base/src/libs/popover/helpers/focus-trap.helper.ts.hbs +0 -197
- package/templates/base/src/libs/popover/helpers/popover-dom.helper.ts.hbs +0 -75
- package/templates/base/src/libs/popover/popover.ts.hbs +0 -138
- package/templates/base/src/libs/snackbar/snackbar.ts.hbs +0 -75
- package/templates/base/src/styles/base/_functions.scss.hbs +0 -9
- package/templates/base/src/styles/core/_anchors.scss.hbs +0 -10
- package/templates/base/src/styles/core/_index.scss.hbs +0 -6
- package/templates/base/src/styles/core/_popover.scss.hbs +0 -96
- package/templates/base/src/styles/core/_snackbar.scss.hbs +0 -50
- package/templates/base/tools/builder/core/esbuild.js.hbs +0 -25
- package/templates/base/tools/builder/core/minify-html.js.hbs +0 -60
- package/templates/base/tools/builder/core/sass.js.hbs +0 -22
- package/templates/base/tools/builder/core/tsc.js.hbs +0 -12
- package/templates/base/tools/generator/templates/service.template.js.hbs +0 -17
- package/templates/base/tools/utils/export-flags.js.hbs +0 -6
- package/templates/features/deployHelper/tools/builder/core/compress-files.js.hbs +0 -56
- package/templates/features/deployHelper/tools/deployment/Dockerfile.hbs +0 -30
- package/templates/features/i18n/src/app/views/home/locales/en.json.hbs +0 -9
- package/templates/features/i18n/src/app/views/not-found/locales/en.json +0 -8
- package/templates/features/i18n/src/app/views/not-found/locales/fr.json +0 -8
- package/templates/features/i18n/src/core/config.ts.hbs +0 -3
- package/templates/features/i18n/src/core/services/i18n/i18n.test.js.hbs +0 -125
- package/templates/features/i18n/tools/generator/templates/json.template.js.hbs +0 -8
- package/templates/features/i18n/tools/utils/languages.js.hbs +0 -7
- package/templates/features/stylinNutin/src/styles/_custom-classes.scss.hbs +0 -9
- package/templates/features/stylinNutin/src/styles/base/_mixins.scss.hbs +0 -372
- package/templates/features/stylinNutin/src/styles/components/_index.scss.hbs +0 -3
- package/templates/features/stylinNutin/src/styles/core/_utilities.scss.hbs +0 -254
- package/templates/features/stylinNutin/src/styles/views/_index.scss.hbs +0 -0
- package/templates/features/stylinNutin/tools/generator/templates/scss.template.js.hbs +0 -4
- package/templates/features/template/src/app/views/home/home.view.html.hbs +0 -56
- package/templates/features/template/src/app/views/not-found/not-found.view.html.hbs +0 -19
- package/templates/features/template/tools/builder/core/merge-templates.js.hbs +0 -49
- package/templates/features/template/tools/generator/templates/html.template.js.hbs +0 -2
- package/templates/features/testinNutin/testin-nutin/core/index.js.hbs +0 -5
- package/templates/features/testinNutin/testin-nutin/core/src/utils/print.js.hbs +0 -64
- package/templates/features/testinNutin/testin-nutin/core/src/utils/test-queue.js.hbs +0 -64
- package/templates/features/testinNutin/testin-nutin/core/tests/globals-each-all.test.js.hbs +0 -28
- package/templates/features/testinNutin/testin-nutin/mocks/mock-store.js.hbs +0 -38
- package/templates/features/testinNutin/testin-nutin/runner.js.hbs +0 -36
- package/templates/features/testinNutin/testin-nutin/watch-tests.js.hbs +0 -21
- package/templates/features/testinNutin/testin-nutin.config.js.hbs +0 -14
- /package/lib/src/{template-compiler.mjs → common/template-compiler.mjs} +0 -0
- /package/templates/{features/i18n → base}/src/core/base-classes/base-component/helpers/i18n.helper.ts.hbs +0 -0
- /package/templates/{features/i18n → base}/src/core/services/i18n/languages.ts.hbs +0 -0
- /package/templates/base/tools/builder/{variables → core/app}/binary-extensions.js.hbs +0 -0
- /package/templates/base/tools/builder/core/{paths.js.hbs → app/paths.js.hbs} +0 -0
- /package/templates/{features/testinNutin/testin-nutin/core/src/utils → base/tools/testin-nutin/core/queue}/queue.js.hbs +0 -0
- /package/templates/{features/testinNutin/testin-nutin/core/src/utils → base/tools/testin-nutin/core/queue}/test-discovery.js.hbs +0 -0
- /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-http-client.js.hbs +0 -0
- /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-i18n.js.hbs +0 -0
- /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-router.js.hbs +0 -0
package/templates/base/src/core/services/router/helpers/view-render-manager.helper.test.js.hbs
CHANGED
|
@@ -26,24 +26,11 @@ describe('ViewRenderManager', () => {
|
|
|
26
26
|
return el;
|
|
27
27
|
},
|
|
28
28
|
destroy: () => {},
|
|
29
|
+
onEnter: () => {},
|
|
30
|
+
onExit: () => {},
|
|
29
31
|
};
|
|
30
32
|
};
|
|
31
33
|
|
|
32
|
-
{{#if animation}}
|
|
33
|
-
it('should add exit class and destroy current view on transition out', async () => {
|
|
34
|
-
const view = mockView();
|
|
35
|
-
const el = view.render();
|
|
36
|
-
container.appendChild(el);
|
|
37
|
-
|
|
38
|
-
// Spy on destroy
|
|
39
|
-
const spy = spyOn(view, 'destroy');
|
|
40
|
-
|
|
41
|
-
await ViewRenderManager.transitionOutCurrentView(view);
|
|
42
|
-
|
|
43
|
-
expect(spy).toHaveBeenCalled();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
{{/if}}
|
|
47
34
|
it('should do nothing if no current view is passed to transitionOutCurrentView', async () => {
|
|
48
35
|
const result = await ViewRenderManager.transitionOutCurrentView(null);
|
|
49
36
|
expect(result).toBe(null);
|
|
@@ -51,7 +38,8 @@ describe('ViewRenderManager', () => {
|
|
|
51
38
|
|
|
52
39
|
it('should emit view-unmount event on transition out', async () => {
|
|
53
40
|
const emitted = [];
|
|
54
|
-
|
|
41
|
+
const emitSpy = spyOn(AppEventBus, 'emit');
|
|
42
|
+
emitSpy.andCallFake((event, data) => emitted.push({ event, data }));
|
|
55
43
|
|
|
56
44
|
const view = mockView('SettingsView');
|
|
57
45
|
const el = view.render();
|
|
@@ -59,6 +47,84 @@ describe('ViewRenderManager', () => {
|
|
|
59
47
|
|
|
60
48
|
await ViewRenderManager.transitionOutCurrentView(view);
|
|
61
49
|
|
|
62
|
-
expect(JSON.stringify(emitted)).toContain(JSON.stringify({ event: 'view-unmount', data: 'SettingsView' }));
|
|
50
|
+
expect(JSON.stringify(emitted)).toContain(JSON.stringify({ event: 'view-unmount', data: { viewName: 'SettingsView' } }));
|
|
51
|
+
emitSpy.restore();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should call destroy() and onExit() on the current view when transitioning out', async () => {
|
|
55
|
+
let destroyed = false;
|
|
56
|
+
let exited = false;
|
|
57
|
+
const view = mockView('SettingsView');
|
|
58
|
+
view.destroy = () => { destroyed = true; };
|
|
59
|
+
view.onExit = () => { exited = true; };
|
|
60
|
+
|
|
61
|
+
await ViewRenderManager.transitionOutCurrentView(view);
|
|
62
|
+
|
|
63
|
+
expect(destroyed).toBe(true);
|
|
64
|
+
expect(exited).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should set route params, render, call onEnter and emit view-mount when rendering a new view', () => {
|
|
68
|
+
const emitted = [];
|
|
69
|
+
const emitSpy = spyOn(AppEventBus, 'emit');
|
|
70
|
+
emitSpy.andCallFake((event, data) => emitted.push({ event, data }));
|
|
71
|
+
|
|
72
|
+
const calls = [];
|
|
73
|
+
const viewElement = document.createElement('div');
|
|
74
|
+
const view = {
|
|
75
|
+
viewName: 'HomeView',
|
|
76
|
+
getElement: () => viewElement,
|
|
77
|
+
setRouteParams: (params) => calls.push(['setRouteParams', params]),
|
|
78
|
+
render: () => { calls.push(['render']); return viewElement; },
|
|
79
|
+
onEnter: () => calls.push(['onEnter']),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
ViewRenderManager.renderNewView(() => view, { id: '42' });
|
|
83
|
+
|
|
84
|
+
expect(calls).toEqual([
|
|
85
|
+
['setRouteParams', { id: '42' }],
|
|
86
|
+
['render'],
|
|
87
|
+
['onEnter'],
|
|
88
|
+
]);
|
|
89
|
+
expect(JSON.stringify(emitted)).toContain(JSON.stringify({ event: 'view-mount', data: { viewName: 'HomeView' } }));
|
|
90
|
+
emitSpy.restore();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should not attempt to clear stale content when the new view has no parent element yet', () => {
|
|
94
|
+
const viewElement = document.createElement('div');
|
|
95
|
+
// Note: viewElement is intentionally never attached to the container.
|
|
96
|
+
const view = {
|
|
97
|
+
viewName: 'HomeView',
|
|
98
|
+
getElement: () => viewElement,
|
|
99
|
+
setRouteParams: () => {},
|
|
100
|
+
render: () => viewElement,
|
|
101
|
+
onEnter: () => {},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
expect(() => ViewRenderManager.renderNewView(() => view)).not.toThrow();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should clear stale mount content before rendering the new view', () => {
|
|
108
|
+
const stale = document.createElement('div');
|
|
109
|
+
stale.className = 'stale-seo-content';
|
|
110
|
+
container.appendChild(stale);
|
|
111
|
+
|
|
112
|
+
const viewElement = document.createElement('div');
|
|
113
|
+
viewElement.className = 'mock-view';
|
|
114
|
+
container.appendChild(viewElement);
|
|
115
|
+
|
|
116
|
+
const view = {
|
|
117
|
+
viewName: 'HomeView',
|
|
118
|
+
getElement: () => viewElement,
|
|
119
|
+
setRouteParams: () => {},
|
|
120
|
+
render: () => viewElement,
|
|
121
|
+
onEnter: () => {},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
ViewRenderManager.renderNewView(() => view);
|
|
125
|
+
|
|
126
|
+
expect(container.children.length).toBe(1);
|
|
127
|
+
expect(container.contains(stale)).toBe(false);
|
|
128
|
+
expect(container.contains(viewElement)).toBe(true);
|
|
63
129
|
});
|
|
64
130
|
});
|
|
@@ -1,35 +1,14 @@
|
|
|
1
|
-
import { View,
|
|
1
|
+
import { View, Lifecycle } from '../../../index.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* handles all view rendering
|
|
4
|
+
* handles all view rendering.
|
|
5
5
|
*/
|
|
6
6
|
export class ViewRenderManager {
|
|
7
|
-
{{#if transition}}
|
|
8
|
-
public static renderAnimatedView(view: View): void {
|
|
9
|
-
const container = document.getElementById('app');
|
|
10
|
-
const el = view.render();
|
|
11
|
-
el.classList.add('app-view-transition');
|
|
12
|
-
container?.appendChild(el);
|
|
13
|
-
|
|
14
|
-
requestAnimationFrame(() => {
|
|
15
|
-
el.classList.add('enter-active');
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
{{/if}}
|
|
20
7
|
public static async transitionOutCurrentView(currentView: View | null): Promise<null> {
|
|
21
8
|
if (!currentView) return null;
|
|
22
|
-
{{#if transition}}
|
|
23
|
-
const container = document.getElementById('app');
|
|
24
|
-
const el = container?.firstElementChild as HTMLElement;
|
|
25
|
-
|
|
26
|
-
if (el) {
|
|
27
|
-
el.classList.add('exit-active');
|
|
28
|
-
await this.delay(300); // match transition duration in _libs.scss
|
|
29
|
-
}
|
|
30
|
-
{{/if}}
|
|
31
9
|
currentView.destroy();
|
|
32
|
-
|
|
10
|
+
currentView.onExit();
|
|
11
|
+
Lifecycle.viewUnmount(currentView.viewName);
|
|
33
12
|
return null;
|
|
34
13
|
}
|
|
35
14
|
|
|
@@ -38,28 +17,25 @@ export class ViewRenderManager {
|
|
|
38
17
|
params: Record<string, string> = {}
|
|
39
18
|
): View {
|
|
40
19
|
const view = viewConstructor();
|
|
20
|
+
ViewRenderManager.clearStaleMountContent(view);
|
|
41
21
|
|
|
42
22
|
// Set route parameters before rendering
|
|
43
23
|
view.setRouteParams(params);
|
|
44
24
|
|
|
45
|
-
{{#if transition}}
|
|
46
|
-
this.renderAnimatedView(view);
|
|
47
|
-
|
|
48
|
-
{{else}}
|
|
49
25
|
view.render();
|
|
26
|
+
view.onEnter();
|
|
50
27
|
|
|
51
|
-
|
|
52
|
-
this.emitEvent('view-mount', view.viewName);
|
|
28
|
+
Lifecycle.viewMount(view.viewName);
|
|
53
29
|
return view;
|
|
54
30
|
}
|
|
55
31
|
|
|
56
|
-
private static
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
32
|
+
private static clearStaleMountContent(view: View): void {
|
|
33
|
+
const viewElement = view.getElement();
|
|
34
|
+
const container = viewElement.parentElement;
|
|
35
|
+
if (!container) return;
|
|
60
36
|
|
|
61
|
-
|
|
62
|
-
|
|
37
|
+
Array.from(container.childNodes).forEach((node) => {
|
|
38
|
+
if (node !== viewElement) node.remove();
|
|
39
|
+
});
|
|
63
40
|
}
|
|
64
|
-
{{/if}}
|
|
65
41
|
}
|
|
@@ -1,7 +1,231 @@
|
|
|
1
|
-
import { AppRouter } from '#root/dist/src/core/services/index.js'
|
|
1
|
+
import { AppRouter, Navigation, I18nService } from '#root/dist/src/core/services/index.js';
|
|
2
|
+
import { CONFIG } from '#root/dist/src/core/config.js';
|
|
3
|
+
|
|
4
|
+
function makeView(name) {
|
|
5
|
+
const calls = [];
|
|
6
|
+
const el = document.createElement('div');
|
|
7
|
+
return {
|
|
8
|
+
viewName: name,
|
|
9
|
+
calls,
|
|
10
|
+
getElement: () => el,
|
|
11
|
+
setRouteParams: (params) => calls.push(['setRouteParams', params]),
|
|
12
|
+
render: () => { calls.push(['render']); return el; },
|
|
13
|
+
onEnter: () => calls.push(['onEnter']),
|
|
14
|
+
onExit: () => calls.push(['onExit']),
|
|
15
|
+
destroy: () => calls.push(['destroy']),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
2
18
|
|
|
3
19
|
describe('Router', () => {
|
|
20
|
+
let router = null;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
CONFIG.i18n = false;
|
|
24
|
+
window.history.pushState({}, '', '/');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
if (router) {
|
|
29
|
+
router.dispose();
|
|
30
|
+
router = null;
|
|
31
|
+
}
|
|
32
|
+
CONFIG.i18n = false;
|
|
33
|
+
window.history.pushState({}, '', '/');
|
|
34
|
+
});
|
|
35
|
+
|
|
4
36
|
it('should be defined', () => {
|
|
5
37
|
expect(AppRouter).toBeDefined();
|
|
6
38
|
});
|
|
7
|
-
|
|
39
|
+
|
|
40
|
+
it('AppRouter returns the same singleton instance on repeated getInstance() calls', async () => {
|
|
41
|
+
const home = makeView('home');
|
|
42
|
+
router = AppRouter({ '/': () => home });
|
|
43
|
+
await flushPromises();
|
|
44
|
+
|
|
45
|
+
const again = router.constructor.getInstance();
|
|
46
|
+
expect(again).toBe(router);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('navigate() matches a route and renders its view (via the initial construction-time navigate)', async () => {
|
|
50
|
+
const home = makeView('home');
|
|
51
|
+
router = AppRouter({ '/': () => home });
|
|
52
|
+
await flushPromises();
|
|
53
|
+
|
|
54
|
+
expect(home.calls).toEqual([
|
|
55
|
+
['setRouteParams', {}],
|
|
56
|
+
['render'],
|
|
57
|
+
['onEnter'],
|
|
58
|
+
]);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('navigate() passes matched route params to the rendered view', async () => {
|
|
62
|
+
const home = makeView('home');
|
|
63
|
+
const userView = makeView('user');
|
|
64
|
+
router = AppRouter({ '/': () => home, '/users/:id': () => userView });
|
|
65
|
+
await flushPromises();
|
|
66
|
+
|
|
67
|
+
await router.navigate('/users/42');
|
|
68
|
+
|
|
69
|
+
expect(userView.calls[0]).toEqual(['setRouteParams', { id: '42' }]);
|
|
70
|
+
expect(router.getCurrentParams()).toEqual({ id: '42' });
|
|
71
|
+
expect(router.getParam('id')).toBe('42');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('navigate() renders the /404 route when no pattern matches', async () => {
|
|
75
|
+
const home = makeView('home');
|
|
76
|
+
const notFound = makeView('not-found');
|
|
77
|
+
router = AppRouter({ '/': () => home, '/404': () => notFound });
|
|
78
|
+
await flushPromises();
|
|
79
|
+
|
|
80
|
+
await router.navigate('/nope');
|
|
81
|
+
|
|
82
|
+
expect(notFound.calls.some(c => c[0] === 'render')).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('navigate() logs an error and renders nothing when no route matches and there is no /404 route', async () => {
|
|
86
|
+
const home = makeView('home');
|
|
87
|
+
router = AppRouter({ '/': () => home });
|
|
88
|
+
await flushPromises();
|
|
89
|
+
|
|
90
|
+
const errSpy = spyOn(console, 'error');
|
|
91
|
+
errSpy.andCallFake(() => {});
|
|
92
|
+
|
|
93
|
+
await router.navigate('/nope');
|
|
94
|
+
|
|
95
|
+
expect(errSpy.callCount).toBe(1);
|
|
96
|
+
errSpy.restore();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('navigate() blocks navigation when a guard returns false', async () => {
|
|
100
|
+
const home = makeView('home');
|
|
101
|
+
const secret = makeView('secret');
|
|
102
|
+
router = AppRouter({
|
|
103
|
+
'/': () => home,
|
|
104
|
+
'/secret': { view: () => secret, guards: [() => false] },
|
|
105
|
+
});
|
|
106
|
+
await flushPromises();
|
|
107
|
+
|
|
108
|
+
await router.navigate('/secret');
|
|
109
|
+
|
|
110
|
+
expect(secret.calls.length).toBe(0);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('navigate() redirects when a guard returns a redirect path', async () => {
|
|
114
|
+
const home = makeView('home');
|
|
115
|
+
const login = makeView('login');
|
|
116
|
+
const secret = makeView('secret');
|
|
117
|
+
router = AppRouter({
|
|
118
|
+
'/': () => home,
|
|
119
|
+
'/login': () => login,
|
|
120
|
+
'/secret': { view: () => secret, guards: [() => '/login'] },
|
|
121
|
+
});
|
|
122
|
+
await flushPromises();
|
|
123
|
+
|
|
124
|
+
await router.navigate('/secret');
|
|
125
|
+
|
|
126
|
+
expect(login.calls.some(c => c[0] === 'render')).toBe(true);
|
|
127
|
+
expect(secret.calls.length).toBe(0);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('navigate() renders the view when all guards pass', async () => {
|
|
131
|
+
const home = makeView('home');
|
|
132
|
+
const dashboard = makeView('dashboard');
|
|
133
|
+
router = AppRouter({
|
|
134
|
+
'/': () => home,
|
|
135
|
+
'/dashboard': { view: () => dashboard, guards: [() => true] },
|
|
136
|
+
});
|
|
137
|
+
await flushPromises();
|
|
138
|
+
|
|
139
|
+
await router.navigate('/dashboard');
|
|
140
|
+
|
|
141
|
+
expect(dashboard.calls.some(c => c[0] === 'render')).toBe(true);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('reload() re-navigates to the current path without pushing a new history entry', async () => {
|
|
145
|
+
const home = makeView('home');
|
|
146
|
+
router = AppRouter({ '/': () => home });
|
|
147
|
+
await flushPromises();
|
|
148
|
+
home.calls.length = 0;
|
|
149
|
+
|
|
150
|
+
await router.reload();
|
|
151
|
+
|
|
152
|
+
expect(home.calls.some(c => c[0] === 'render')).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('responds to a popstate event by re-navigating to the new current path', async () => {
|
|
156
|
+
const home = makeView('home');
|
|
157
|
+
const about = makeView('about');
|
|
158
|
+
router = AppRouter({ '/': () => home, '/about': () => about });
|
|
159
|
+
await flushPromises();
|
|
160
|
+
|
|
161
|
+
window.history.pushState({}, '', '/about');
|
|
162
|
+
window.dispatchEvent(new window.PopStateEvent('popstate'));
|
|
163
|
+
await flushPromises();
|
|
164
|
+
|
|
165
|
+
expect(about.calls.some(c => c[0] === 'render')).toBe(true);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('switches the language on popstate when the URL locale differs from the current one', async () => {
|
|
169
|
+
CONFIG.i18n = true;
|
|
170
|
+
I18nService['_LANGUAGES'] = ['en', 'fr'];
|
|
171
|
+
I18nService['_currentLanguage'] = 'en';
|
|
172
|
+
|
|
173
|
+
const home = makeView('home');
|
|
174
|
+
router = AppRouter({ '/': () => home });
|
|
175
|
+
await flushPromises();
|
|
176
|
+
|
|
177
|
+
const setLangSpy = spyOn(I18nService, 'setCurrentLanguage');
|
|
178
|
+
setLangSpy.andCallFake(async (lang) => { I18nService['_currentLanguage'] = lang; });
|
|
179
|
+
|
|
180
|
+
window.history.pushState({}, '', '/fr');
|
|
181
|
+
window.dispatchEvent(new window.PopStateEvent('popstate'));
|
|
182
|
+
await flushPromises();
|
|
183
|
+
|
|
184
|
+
expect(setLangSpy).toHaveBeenCalledWith('fr');
|
|
185
|
+
|
|
186
|
+
setLangSpy.restore();
|
|
187
|
+
I18nService['_currentLanguage'] = 'en';
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('wires Navigation.navigateTo() through to router.navigate()', async () => {
|
|
191
|
+
const home = makeView('home');
|
|
192
|
+
const about = makeView('about');
|
|
193
|
+
router = AppRouter({ '/': () => home, '/about': () => about });
|
|
194
|
+
await flushPromises();
|
|
195
|
+
|
|
196
|
+
Navigation.navigateTo('/about');
|
|
197
|
+
await flushPromises();
|
|
198
|
+
|
|
199
|
+
expect(about.calls.some(c => c[0] === 'render')).toBe(true);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('wires Navigation.reload() through to router.reload()', async () => {
|
|
203
|
+
const home = makeView('home');
|
|
204
|
+
router = AppRouter({ '/': () => home });
|
|
205
|
+
await flushPromises();
|
|
206
|
+
home.calls.length = 0;
|
|
207
|
+
|
|
208
|
+
Navigation.reload();
|
|
209
|
+
await flushPromises();
|
|
210
|
+
|
|
211
|
+
expect(home.calls.some(c => c[0] === 'render')).toBe(true);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('removeEventListeners() stops the router from responding to further navigate/popstate events', async () => {
|
|
215
|
+
const home = makeView('home');
|
|
216
|
+
const about = makeView('about');
|
|
217
|
+
router = AppRouter({ '/': () => home, '/about': () => about });
|
|
218
|
+
await flushPromises();
|
|
219
|
+
|
|
220
|
+
router.removeEventListeners();
|
|
221
|
+
|
|
222
|
+
Navigation.navigateTo('/about');
|
|
223
|
+
await flushPromises();
|
|
224
|
+
expect(about.calls.length).toBe(0);
|
|
225
|
+
|
|
226
|
+
window.history.pushState({}, '', '/about');
|
|
227
|
+
window.dispatchEvent(new window.PopStateEvent('popstate'));
|
|
228
|
+
await flushPromises();
|
|
229
|
+
expect(about.calls.length).toBe(0);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { View, AppEventBus } from '../../index.js';
|
|
1
|
+
import { View, Navigation, Service, I18nService, AppEventBus } from '../../index.js';
|
|
2
|
+
import { Language } from '../i18n/languages.js';
|
|
2
3
|
import { Routes, RouteConfig, RouteGuardsManager, GuardResult } from './helpers/route-guard-manager.helper.js';
|
|
3
4
|
import { ViewRenderManager } from './helpers/view-render-manager.helper.js';
|
|
4
5
|
import { NavigationManager } from './helpers/navigation-manager.helper.js';
|
|
@@ -16,13 +17,27 @@ export interface RouteMatch {
|
|
|
16
17
|
params: Record<string, string>;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
class Router {
|
|
20
|
+
class Router extends Service<Router> {
|
|
20
21
|
private _currentView: View | null = null;
|
|
21
22
|
private _currentParams: Record<string, string> = {};
|
|
23
|
+
private onPopState = () => this.handlePopState();
|
|
24
|
+
private onNavigate = (data: { path: string }) => this.navigate(data.path);
|
|
25
|
+
private onReload = () => this.reload();
|
|
26
|
+
private _busSubscriptions: Array<() => void> = [];
|
|
22
27
|
|
|
23
28
|
constructor(private routes: Routes) {
|
|
29
|
+
super();
|
|
24
30
|
this.initializeEventListeners();
|
|
25
31
|
this.navigate(NavigationManager.getCurrentPath());
|
|
32
|
+
this.registerCleanup(this.removeEventListeners);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public removeEventListeners(): void {
|
|
36
|
+
this._busSubscriptions.forEach((callback) => {
|
|
37
|
+
callback();
|
|
38
|
+
});
|
|
39
|
+
this._busSubscriptions = [];
|
|
40
|
+
window.removeEventListener('popstate', this.onPopState);
|
|
26
41
|
}
|
|
27
42
|
|
|
28
43
|
public async reload(): Promise<void> {
|
|
@@ -58,8 +73,8 @@ class Router {
|
|
|
58
73
|
routeMatch.params
|
|
59
74
|
);
|
|
60
75
|
|
|
61
|
-
NavigationManager.updateMetaContent(this._currentView);
|
|
62
76
|
NavigationManager.updateHistory(normalizedPath, currentPath, pushState);
|
|
77
|
+
window.scrollTo({ top: 0 });
|
|
63
78
|
}
|
|
64
79
|
|
|
65
80
|
public getCurrentParams(): Record<string, string> {
|
|
@@ -71,12 +86,29 @@ class Router {
|
|
|
71
86
|
}
|
|
72
87
|
|
|
73
88
|
private initializeEventListeners(): void {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
89
|
+
this.initializeNavigationEvents();
|
|
90
|
+
this.initializeI18nEvents();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private initializeNavigationEvents(): void {
|
|
94
|
+
window.addEventListener('popstate', this.onPopState);
|
|
95
|
+
const unsubOnNav = Navigation.onNavigate(this.onNavigate);
|
|
96
|
+
const unsubOnReload = Navigation.onReload(this.onReload);
|
|
97
|
+
this._busSubscriptions.push(unsubOnNav, unsubOnReload);
|
|
77
98
|
}
|
|
78
99
|
|
|
79
|
-
private
|
|
100
|
+
private initializeI18nEvents(): void {
|
|
101
|
+
const onLangChanged = () => NavigationManager.updateLocaleInUrl();
|
|
102
|
+
AppEventBus.subscribe('language-changed', onLangChanged);
|
|
103
|
+
const unsubOnLang = () => AppEventBus.off('language-changed', onLangChanged);
|
|
104
|
+
this._busSubscriptions.push(unsubOnLang);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private async handlePopState(): Promise<void> {
|
|
108
|
+
const newLocale = NavigationManager.getCurrentLocale();
|
|
109
|
+
if (newLocale && newLocale !== I18nService.currentLanguage) {
|
|
110
|
+
await I18nService.setCurrentLanguage(newLocale as Language);
|
|
111
|
+
}
|
|
80
112
|
this.navigate(NavigationManager.getCurrentPath(), false);
|
|
81
113
|
}
|
|
82
114
|
|
|
@@ -141,4 +173,4 @@ class Router {
|
|
|
141
173
|
}
|
|
142
174
|
}
|
|
143
175
|
|
|
144
|
-
export const AppRouter = (routes: Routes) =>
|
|
176
|
+
export const AppRouter = (routes: Routes) => Router.getInstance(routes);
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
2
|
+
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
|
-
<meta name="description" content="{{projectName}}">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<link rel="stylesheet" href="/main.css">
|
|
8
5
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
|
9
|
-
<title>{{projectName}}</title>
|
|
10
6
|
</head>
|
|
11
7
|
<body>
|
|
12
8
|
<main id="app"></main>
|
|
13
9
|
</body>
|
|
14
|
-
</html>
|
|
10
|
+
</html>
|
|
@@ -1,54 +1,113 @@
|
|
|
1
1
|
@use "variables" as *;
|
|
2
|
-
{{#if stylinNutin}}
|
|
3
|
-
@use "sass:map";
|
|
4
|
-
@use "mixins";
|
|
5
|
-
{{/if}}
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
/* ===========
|
|
4
|
+
CSS Reset
|
|
5
|
+
=========== */
|
|
6
|
+
|
|
7
|
+
/* Box sizing */
|
|
8
|
+
|
|
9
|
+
*,
|
|
10
|
+
*::before,
|
|
11
|
+
*::after {
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* Remove default spacing */
|
|
16
|
+
|
|
17
|
+
* {
|
|
9
18
|
margin: 0;
|
|
10
19
|
padding: 0;
|
|
11
|
-
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Document */
|
|
23
|
+
|
|
24
|
+
html {
|
|
12
25
|
font-size: 100%;
|
|
26
|
+
-webkit-text-size-adjust: 100%;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
body {
|
|
30
|
+
min-height: 100vh;
|
|
13
31
|
font: inherit;
|
|
14
|
-
|
|
32
|
+
line-height: 1.5;
|
|
15
33
|
}
|
|
16
34
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
35
|
+
/* Media */
|
|
36
|
+
|
|
37
|
+
img,
|
|
38
|
+
picture,
|
|
39
|
+
svg,
|
|
40
|
+
canvas,
|
|
41
|
+
video {
|
|
42
|
+
display: block;
|
|
43
|
+
max-width: 100%;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Forms */
|
|
47
|
+
|
|
48
|
+
button,
|
|
49
|
+
input,
|
|
50
|
+
select,
|
|
51
|
+
textarea {
|
|
52
|
+
font: inherit;
|
|
53
|
+
color: inherit;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
button {
|
|
57
|
+
background: none;
|
|
58
|
+
border: none;
|
|
59
|
+
cursor: pointer;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
textarea {
|
|
63
|
+
resize: vertical;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Tables */
|
|
67
|
+
|
|
68
|
+
table {
|
|
69
|
+
border-collapse: collapse;
|
|
70
|
+
border-spacing: 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Lists */
|
|
74
|
+
|
|
75
|
+
ul,
|
|
76
|
+
ol {
|
|
77
|
+
list-style: none;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Links */
|
|
81
|
+
|
|
82
|
+
a {
|
|
83
|
+
color: inherit;
|
|
84
|
+
text-decoration: none;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Quotes */
|
|
88
|
+
|
|
89
|
+
blockquote,
|
|
90
|
+
q {
|
|
91
|
+
quotes: none;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
blockquote::before,
|
|
95
|
+
blockquote::after,
|
|
96
|
+
q::before,
|
|
97
|
+
q::after {
|
|
98
|
+
content: "";
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Miscellaneous */
|
|
102
|
+
|
|
103
|
+
hr {
|
|
104
|
+
border: none;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
fieldset {
|
|
108
|
+
border: none;
|
|
21
109
|
}
|
|
22
110
|
|
|
23
|
-
|
|
24
|
-
:
|
|
25
|
-
body {
|
|
26
|
-
background-color: $background-color;
|
|
27
|
-
color: $text-color;
|
|
28
|
-
min-height: 100vh;
|
|
29
|
-
cursor: default;
|
|
30
|
-
{{#if stylinNutin}}
|
|
31
|
-
line-height: 1.6;
|
|
32
|
-
font-family: $primary-font;
|
|
33
|
-
font-size: map.get($font-sizes, medium);
|
|
34
|
-
{{else}}
|
|
35
|
-
font-family: Arial, Helvetica, sans-serif;
|
|
36
|
-
font-size: 1rem;
|
|
37
|
-
{{/if}}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
{{#if stylinNutin}}
|
|
41
|
-
h1 {
|
|
42
|
-
font-size: map.get($font-sizes, largest);
|
|
43
|
-
font-family: $secondary-font;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
a, button {
|
|
47
|
-
cursor: pointer;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
a {
|
|
51
|
-
text-decoration: none;
|
|
52
|
-
}
|
|
53
|
-
{{/if}}
|
|
111
|
+
:where([hidden]) {
|
|
112
|
+
display: none !important;
|
|
54
113
|
}
|