Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 761b0b678b | |||
| 12a7156928 | |||
| 59a870c3bc | |||
| 13fa654c0f |
19
changelog.md
19
changelog.md
@@ -1,5 +1,24 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-12-11 - 3.3.1 - fix(dees-pdf-viewer)
|
||||
Scroll active PDF thumbnail into view after rendering and on page changes; update dependency versions
|
||||
|
||||
- Ensure the active thumbnail is scrolled into view after thumbnails are rendered (improves sidebar navigation for dees-pdf-viewer).
|
||||
- Scroll the thumbnail into view when navigating pages if the sidebar is visible (prevents the active page from being off-screen).
|
||||
- Retain re-setup of the intersection observer for lazy-loading pages after thumbnail re-render.
|
||||
- Bumped dependencies in package.json: @design.estate/dees-wcctools -> ^2.0.1, lucide -> ^0.560.0, @git.zone/tswatch -> ^2.3.13, @types/node -> ^25.0.0.
|
||||
|
||||
## 2025-12-09 - 3.3.0 - feat(dees-appui-base)
|
||||
Add unified App UI API to dees-appui-base with ViewRegistry, AppRouter and StateManager
|
||||
|
||||
- Introduce ViewRegistry for declarative view registration and rendering (supports tag names, element classes and template functions).
|
||||
- Add AppRouter with hash/history/external/none modes, URL synchronization, navigate/back/forward and onRouteChange listener support.
|
||||
- Add StateManager to persist UI state (localStorage, sessionStorage or in-memory) with save/load/update/clear APIs.
|
||||
- Extend interfaces (interfaces/appconfig.ts) with IAppConfig, IViewDefinition, IRoutingConfig, IStatePersistenceConfig and IAppUIState.
|
||||
- Expose new public DeesAppuiBase methods: configure, navigateToView, getCurrentView, getUIState, restoreUIState, saveState, loadState, getViewRegistry, getRouter.
|
||||
- Maintain backward compatibility with existing property-based API and slot usage.
|
||||
- Export new modules (view.registry, app.router, state.manager) from dees-appui-base index and update element exports.
|
||||
|
||||
## 2025-12-08 - 3.2.0 - feat(dees-simple-appdash,dees-simple-login,dees-terminal)
|
||||
Revamp UI: dashboard & login styling, standardize icons to Lucide, and add terminal background/config
|
||||
|
||||
|
||||
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@design.estate/dees-catalog",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.1",
|
||||
"private": false,
|
||||
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
|
||||
"main": "dist_ts_web/index.js",
|
||||
@@ -18,7 +18,7 @@
|
||||
"dependencies": {
|
||||
"@design.estate/dees-domtools": "^2.3.6",
|
||||
"@design.estate/dees-element": "^2.1.3",
|
||||
"@design.estate/dees-wcctools": "^1.2.1",
|
||||
"@design.estate/dees-wcctools": "^2.0.1",
|
||||
"@fortawesome/fontawesome-svg-core": "^7.1.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^7.1.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^7.1.0",
|
||||
@@ -38,7 +38,7 @@
|
||||
"highlight.js": "11.11.1",
|
||||
"ibantools": "^4.5.1",
|
||||
"lit": "^3.3.1",
|
||||
"lucide": "^0.555.0",
|
||||
"lucide": "^0.560.0",
|
||||
"monaco-editor": "0.52.2",
|
||||
"pdfjs-dist": "^4.10.38",
|
||||
"xterm": "^5.3.0",
|
||||
@@ -48,10 +48,10 @@
|
||||
"@git.zone/tsbuild": "^3.1.2",
|
||||
"@git.zone/tsbundle": "^2.6.3",
|
||||
"@git.zone/tstest": "^3.1.3",
|
||||
"@git.zone/tswatch": "^2.3.5",
|
||||
"@git.zone/tswatch": "^2.3.13",
|
||||
"@push.rocks/projectinfo": "^5.0.2",
|
||||
"@push.rocks/tapbundle": "^6.0.3",
|
||||
"@types/node": "^24.10.1"
|
||||
"@types/node": "^25.0.0"
|
||||
},
|
||||
"files": [
|
||||
"ts/**/*",
|
||||
|
||||
136
pnpm-lock.yaml
generated
136
pnpm-lock.yaml
generated
@@ -15,8 +15,8 @@ importers:
|
||||
specifier: ^2.1.3
|
||||
version: 2.1.3
|
||||
'@design.estate/dees-wcctools':
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1
|
||||
'@fortawesome/fontawesome-svg-core':
|
||||
specifier: ^7.1.0
|
||||
version: 7.1.0
|
||||
@@ -75,8 +75,8 @@ importers:
|
||||
specifier: ^3.3.1
|
||||
version: 3.3.1
|
||||
lucide:
|
||||
specifier: ^0.555.0
|
||||
version: 0.555.0
|
||||
specifier: ^0.560.0
|
||||
version: 0.560.0
|
||||
monaco-editor:
|
||||
specifier: 0.52.2
|
||||
version: 0.52.2
|
||||
@@ -100,8 +100,8 @@ importers:
|
||||
specifier: ^3.1.3
|
||||
version: 3.1.3(@push.rocks/smartserve@1.4.0)(socks@2.8.7)(typescript@5.9.3)
|
||||
'@git.zone/tswatch':
|
||||
specifier: ^2.3.5
|
||||
version: 2.3.5(@tiptap/pm@2.27.1)
|
||||
specifier: ^2.3.13
|
||||
version: 2.3.13(@tiptap/pm@2.27.1)
|
||||
'@push.rocks/projectinfo':
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2
|
||||
@@ -109,8 +109,8 @@ importers:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3(socks@2.8.7)
|
||||
'@types/node':
|
||||
specifier: ^24.10.1
|
||||
version: 24.10.1
|
||||
specifier: ^25.0.0
|
||||
version: 25.0.0
|
||||
|
||||
packages:
|
||||
|
||||
@@ -322,6 +322,9 @@ packages:
|
||||
'@cloudflare/workers-types@4.20251205.0':
|
||||
resolution: {integrity: sha512-7pup7fYkuQW5XD8RUS/vkxF9SXlrGyCXuZ4ro3uVQvca/GTeSa+8bZ8T4wbq1Aea5lmLIGSlKbhl2msME7bRBA==}
|
||||
|
||||
'@cloudflare/workers-types@4.20251211.0':
|
||||
resolution: {integrity: sha512-e87o6KbelCz7dnI5ngrGT2ca15vJZ+COb2eqJ52iDHmOaujyC6aYZ71e2vor8X6V9T6tcDElC5sAqPR93j09EA==}
|
||||
|
||||
'@colors/colors@1.6.0':
|
||||
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
|
||||
engines: {node: '>=0.1.90'}
|
||||
@@ -332,8 +335,8 @@ packages:
|
||||
'@dabh/diagnostics@2.0.8':
|
||||
resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==}
|
||||
|
||||
'@design.estate/dees-catalog@3.1.2':
|
||||
resolution: {integrity: sha512-T4ZhoZzl6NAWGhrz00MDFla5syeMz53euZ+QcBftRMmHuqPHDU/JlBi+K2SmILtnRfWH/lGYWBKugYop6q0chg==}
|
||||
'@design.estate/dees-catalog@3.3.0':
|
||||
resolution: {integrity: sha512-iPT8QtMrheH8ZO6xmyZfAC+51smTsA9Og63BC2mKFixoGiAG7UScN1ywM/uHEmnKCd+KBY+dn4ZaHyR4UrrX2A==}
|
||||
|
||||
'@design.estate/dees-comms@1.0.30':
|
||||
resolution: {integrity: sha512-KchMlklJfKAjQiJiR0xmofXtQ27VgZtBIxcMwPE9d+h3jJRv+lPZxzBQVOM0eyM0uS44S5vJMZ11IeV4uDXSHg==}
|
||||
@@ -344,8 +347,11 @@ packages:
|
||||
'@design.estate/dees-element@2.1.3':
|
||||
resolution: {integrity: sha512-TjXWxVcdSPaT1IOk31ckfxvAZnJLuTxhFGsNCKoh63/UE2FVf6slp8//UFvN+ADigiA9ZsY0azkY99XbJCwDDA==}
|
||||
|
||||
'@design.estate/dees-wcctools@1.2.1':
|
||||
resolution: {integrity: sha512-ESFas1MPPwDUcXRssyHRsc63XPTBJSTBA+5RhYXDZx8mbV6HxEKiJR8Oz1Mv7DBdW+ZSuUTD/fA6Aa/fCxGYTQ==}
|
||||
'@design.estate/dees-wcctools@1.3.0':
|
||||
resolution: {integrity: sha512-+yd8c1gTIKNRQYCvG0xu6Am8dHsRm7ymluX2gnoBQN4aFOpZgIBi/v9CvGyPhTD1p/VRouIBz1wsUCejnwrFCA==}
|
||||
|
||||
'@design.estate/dees-wcctools@2.0.1':
|
||||
resolution: {integrity: sha512-1DaQtvoMmD+uH9cjSrL4szk7h0nbBlT/ZBmz+qvWCOqzZXE3wPOAdgASZ73NeQlehLx4KGbfJTCG15DSB0W3LQ==}
|
||||
|
||||
'@emnapi/core@1.7.1':
|
||||
resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==}
|
||||
@@ -555,8 +561,8 @@ packages:
|
||||
resolution: {integrity: sha512-t+/cKV21JHK8X7NGAmihs5M/eMm+V+jn4R5rzfwGG97WJFAcP5qE1Os9VYtyZw3tx/NZXA2yA4abo/ELluTuRA==}
|
||||
hasBin: true
|
||||
|
||||
'@git.zone/tswatch@2.3.5':
|
||||
resolution: {integrity: sha512-wYekG7Q/wg5uptXHPHhVi7dHq19QnLoevQpcpAzF6hMiNbOozN3+4zxOktyJBl6EHUYcFvHXA4fZ4bkJpo5TcA==}
|
||||
'@git.zone/tswatch@2.3.13':
|
||||
resolution: {integrity: sha512-43995OlWl8UzCA+cX3ehYba/ksm6CqHbMljHKjosrDRpx8EU+LY4bWTc8JT/Ldgwsw3iW9vur2bBqpgMmdeJJw==}
|
||||
hasBin: true
|
||||
|
||||
'@hapi/bourne@3.0.0':
|
||||
@@ -1023,8 +1029,8 @@ packages:
|
||||
'@push.rocks/smartversion@3.0.5':
|
||||
resolution: {integrity: sha512-8MZSo1yqyaKxKq0Q5N188l4un++9GFWVbhCAX5mXJwewZHn97ujffTeL+eOQYpWFTEpUhaq1QhL4NhqObBCt1Q==}
|
||||
|
||||
'@push.rocks/smartwatch@6.1.1':
|
||||
resolution: {integrity: sha512-wmhLKu9bdpvRcjOfitJOi4jsNKD7S2hVlVq6fAv3IhB2ZbRlSB+Hai4DwOlrdUZaWrg+dFIZU+/ifTOozOPiMg==}
|
||||
'@push.rocks/smartwatch@6.3.0':
|
||||
resolution: {integrity: sha512-TeZ1PGBoBMpC4/CK8StIj5InEiFfKp7xWJSm3aYMjB/uaoeRP0vXqv1ORIC/TKYGJuEDuAXUsit8tZVjn0qT1Q==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
'@push.rocks/smartxml@2.0.0':
|
||||
@@ -1798,8 +1804,8 @@ packages:
|
||||
'@types/node-forge@1.3.14':
|
||||
resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==}
|
||||
|
||||
'@types/node@24.10.1':
|
||||
resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==}
|
||||
'@types/node@25.0.0':
|
||||
resolution: {integrity: sha512-rl78HwuZlaDIUSeUKkmogkhebA+8K1Hy7tddZuJ3D0xV8pZSfsYGTsliGUol1JPzu9EKnTxPC4L1fiWouStRew==}
|
||||
|
||||
'@types/parse5@6.0.3':
|
||||
resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==}
|
||||
@@ -3309,6 +3315,9 @@ packages:
|
||||
lucide@0.555.0:
|
||||
resolution: {integrity: sha512-R7BkO2/XRpMADcMIRn1UOZOvirxr2Z6s/R82k0EUK71ZXXrlRbvkVwTAIf+9DRApeyH+zNMIGfiUdmrOhoAygQ==}
|
||||
|
||||
lucide@0.560.0:
|
||||
resolution: {integrity: sha512-w7++Pwdz0NxxMtC4ugLmsy66Ar95HnDIMjzJZdHl0kQKIHto3icgI+lbOZMlovZ1Mo4RGITWGhYn1ro7hcY/UA==}
|
||||
|
||||
make-dir@3.1.0:
|
||||
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -4712,8 +4721,8 @@ snapshots:
|
||||
'@api.global/typedrequest': 3.2.5
|
||||
'@api.global/typedrequest-interfaces': 3.0.19
|
||||
'@api.global/typedsocket': 4.1.0(@push.rocks/smartserve@1.4.0)
|
||||
'@cloudflare/workers-types': 4.20251205.0
|
||||
'@design.estate/dees-catalog': 3.1.2(@tiptap/pm@2.27.1)
|
||||
'@cloudflare/workers-types': 4.20251211.0
|
||||
'@design.estate/dees-catalog': 3.3.0(@tiptap/pm@2.27.1)
|
||||
'@design.estate/dees-comms': 1.0.30
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
@@ -4738,7 +4747,7 @@ snapshots:
|
||||
'@push.rocks/smartsitemap': 2.0.4
|
||||
'@push.rocks/smartstream': 3.2.5
|
||||
'@push.rocks/smarttime': 4.1.1
|
||||
'@push.rocks/smartwatch': 6.1.1
|
||||
'@push.rocks/smartwatch': 6.3.0
|
||||
'@push.rocks/taskbuffer': 3.5.0
|
||||
'@push.rocks/webrequest': 4.0.1
|
||||
'@push.rocks/webstore': 2.0.20
|
||||
@@ -5281,6 +5290,8 @@ snapshots:
|
||||
|
||||
'@cloudflare/workers-types@4.20251205.0': {}
|
||||
|
||||
'@cloudflare/workers-types@4.20251211.0': {}
|
||||
|
||||
'@colors/colors@1.6.0': {}
|
||||
|
||||
'@configvault.io/interfaces@1.0.17':
|
||||
@@ -5293,11 +5304,11 @@ snapshots:
|
||||
enabled: 2.0.0
|
||||
kuler: 2.0.0
|
||||
|
||||
'@design.estate/dees-catalog@3.1.2(@tiptap/pm@2.27.1)':
|
||||
'@design.estate/dees-catalog@3.3.0(@tiptap/pm@2.27.1)':
|
||||
dependencies:
|
||||
'@design.estate/dees-domtools': 2.3.6
|
||||
'@design.estate/dees-element': 2.1.3
|
||||
'@design.estate/dees-wcctools': 1.2.1
|
||||
'@design.estate/dees-wcctools': 1.3.0
|
||||
'@fortawesome/fontawesome-svg-core': 7.1.0
|
||||
'@fortawesome/free-brands-svg-icons': 7.1.0
|
||||
'@fortawesome/free-regular-svg-icons': 7.1.0
|
||||
@@ -5374,7 +5385,19 @@ snapshots:
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
'@design.estate/dees-wcctools@1.2.1':
|
||||
'@design.estate/dees-wcctools@1.3.0':
|
||||
dependencies:
|
||||
'@design.estate/dees-domtools': 2.3.6
|
||||
'@design.estate/dees-element': 2.1.3
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
lit: 3.3.1
|
||||
transitivePeerDependencies:
|
||||
- '@nuxt/kit'
|
||||
- react
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
'@design.estate/dees-wcctools@2.0.1':
|
||||
dependencies:
|
||||
'@design.estate/dees-domtools': 2.3.6
|
||||
'@design.estate/dees-element': 2.1.3
|
||||
@@ -5618,7 +5641,7 @@ snapshots:
|
||||
- utf-8-validate
|
||||
- vue
|
||||
|
||||
'@git.zone/tswatch@2.3.5(@tiptap/pm@2.27.1)':
|
||||
'@git.zone/tswatch@2.3.13(@tiptap/pm@2.27.1)':
|
||||
dependencies:
|
||||
'@api.global/typedserver': 7.11.1(@tiptap/pm@2.27.1)
|
||||
'@git.zone/tsbundle': 2.6.3
|
||||
@@ -5631,7 +5654,7 @@ snapshots:
|
||||
'@push.rocks/smartlog': 3.1.10
|
||||
'@push.rocks/smartlog-destination-local': 9.0.2
|
||||
'@push.rocks/smartshell': 3.3.0
|
||||
'@push.rocks/smartwatch': 6.1.1
|
||||
'@push.rocks/smartwatch': 6.3.0
|
||||
'@push.rocks/taskbuffer': 3.5.0
|
||||
transitivePeerDependencies:
|
||||
- '@nuxt/kit'
|
||||
@@ -5677,7 +5700,7 @@ snapshots:
|
||||
'@jest/schemas': 29.6.3
|
||||
'@types/istanbul-lib-coverage': 2.0.6
|
||||
'@types/istanbul-reports': 3.0.4
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
'@types/yargs': 17.0.35
|
||||
chalk: 4.1.2
|
||||
|
||||
@@ -6678,12 +6701,13 @@ snapshots:
|
||||
'@types/semver': 7.7.1
|
||||
semver: 7.7.3
|
||||
|
||||
'@push.rocks/smartwatch@6.1.1':
|
||||
'@push.rocks/smartwatch@6.3.0':
|
||||
dependencies:
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartenv': 6.0.0
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
chokidar: 5.0.0
|
||||
picomatch: 4.0.3
|
||||
|
||||
'@push.rocks/smartxml@2.0.0':
|
||||
@@ -7479,18 +7503,18 @@ snapshots:
|
||||
|
||||
'@types/accepts@1.3.7':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/babel__code-frame@7.0.6': {}
|
||||
|
||||
'@types/bn.js@5.2.0':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/body-parser@1.19.6':
|
||||
dependencies:
|
||||
'@types/connect': 3.4.38
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/buffer-json@2.0.3': {}
|
||||
|
||||
@@ -7507,17 +7531,17 @@ snapshots:
|
||||
|
||||
'@types/clean-css@4.2.11':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
source-map: 0.6.1
|
||||
|
||||
'@types/co-body@6.1.3':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
'@types/qs': 6.14.0
|
||||
|
||||
'@types/connect@3.4.38':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/content-disposition@0.5.9': {}
|
||||
|
||||
@@ -7528,11 +7552,11 @@ snapshots:
|
||||
'@types/connect': 3.4.38
|
||||
'@types/express': 5.0.6
|
||||
'@types/keygrip': 1.0.6
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/cors@2.8.19':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/debounce@1.2.4': {}
|
||||
|
||||
@@ -7544,7 +7568,7 @@ snapshots:
|
||||
|
||||
'@types/dns-packet@5.6.5':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/elliptic@6.4.18':
|
||||
dependencies:
|
||||
@@ -7552,7 +7576,7 @@ snapshots:
|
||||
|
||||
'@types/express-serve-static-core@5.1.0':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
'@types/qs': 6.14.0
|
||||
'@types/range-parser': 1.2.7
|
||||
'@types/send': 1.2.1
|
||||
@@ -7565,17 +7589,17 @@ snapshots:
|
||||
|
||||
'@types/from2@2.3.6':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/fs-extra@11.0.4':
|
||||
dependencies:
|
||||
'@types/jsonfile': 6.1.4
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/glob@8.1.0':
|
||||
dependencies:
|
||||
'@types/minimatch': 5.1.2
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/hast@3.0.4':
|
||||
dependencies:
|
||||
@@ -7609,7 +7633,7 @@ snapshots:
|
||||
|
||||
'@types/jsonfile@6.1.4':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/keygrip@1.0.6': {}
|
||||
|
||||
@@ -7626,7 +7650,7 @@ snapshots:
|
||||
'@types/http-errors': 2.0.5
|
||||
'@types/keygrip': 1.0.6
|
||||
'@types/koa-compose': 3.2.9
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/linkify-it@5.0.0': {}
|
||||
|
||||
@@ -7649,9 +7673,9 @@ snapshots:
|
||||
|
||||
'@types/node-forge@1.3.14':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/node@24.10.1':
|
||||
'@types/node@25.0.0':
|
||||
dependencies:
|
||||
undici-types: 7.16.0
|
||||
|
||||
@@ -7669,18 +7693,18 @@ snapshots:
|
||||
|
||||
'@types/s3rver@3.7.4':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/semver@7.7.1': {}
|
||||
|
||||
'@types/send@1.2.1':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/serve-static@2.2.0':
|
||||
dependencies:
|
||||
'@types/http-errors': 2.0.5
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/sinon-chai@3.2.12':
|
||||
dependencies:
|
||||
@@ -7699,11 +7723,11 @@ snapshots:
|
||||
|
||||
'@types/tar-stream@3.1.4':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/through2@2.0.41':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/triple-beam@1.3.5': {}
|
||||
|
||||
@@ -7729,11 +7753,11 @@ snapshots:
|
||||
|
||||
'@types/ws@7.4.7':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
|
||||
'@types/yargs-parser@21.0.3': {}
|
||||
|
||||
@@ -7743,7 +7767,7 @@ snapshots:
|
||||
|
||||
'@types/yauzl@2.10.3':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
optional: true
|
||||
|
||||
'@ungap/structured-clone@1.3.0': {}
|
||||
@@ -8379,7 +8403,7 @@ snapshots:
|
||||
engine.io@6.6.4:
|
||||
dependencies:
|
||||
'@types/cors': 2.8.19
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
accepts: 1.3.8
|
||||
base64id: 2.0.0
|
||||
cookie: 0.7.2
|
||||
@@ -9115,7 +9139,7 @@ snapshots:
|
||||
jest-util@29.7.0:
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 24.10.1
|
||||
'@types/node': 25.0.0
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.9.0
|
||||
graceful-fs: 4.2.11
|
||||
@@ -9324,6 +9348,8 @@ snapshots:
|
||||
|
||||
lucide@0.555.0: {}
|
||||
|
||||
lucide@0.560.0: {}
|
||||
|
||||
make-dir@3.1.0:
|
||||
dependencies:
|
||||
semver: 6.3.1
|
||||
|
||||
122
readme.hints.md
122
readme.hints.md
@@ -680,4 +680,124 @@ According to Lit's documentation (https://lit.dev/docs/components/decorators/#de
|
||||
- All unit tests passing
|
||||
- Manual testing of key components verified
|
||||
- No regressions detected
|
||||
- Focus management and interactions working correctly
|
||||
- Focus management and interactions working correctly
|
||||
|
||||
## Enhanced AppUI API (2025-12-08)
|
||||
|
||||
The `dees-appui-base` component has been enhanced with a unified configuration API for building real-world applications.
|
||||
|
||||
### New Modules:
|
||||
|
||||
1. **ViewRegistry** (`view.registry.ts`)
|
||||
- Manages view definitions and their lifecycle
|
||||
- Supports tag names, element classes, and template functions as view content
|
||||
- Methods: register, get, renderView, findByRoute
|
||||
|
||||
2. **AppRouter** (`app.router.ts`)
|
||||
- Built-in routing with hash or history mode
|
||||
- External router support for framework integration
|
||||
- Methods: navigate, back, forward, onRouteChange
|
||||
|
||||
3. **StateManager** (`state.manager.ts`)
|
||||
- Persists UI state (collapsed menus, selections, current view)
|
||||
- Supports localStorage, sessionStorage, or memory storage
|
||||
- Methods: save, load, update, clear
|
||||
|
||||
### New Interfaces (in `interfaces/appconfig.ts`):
|
||||
|
||||
```typescript
|
||||
interface IAppConfig {
|
||||
branding?: { logoIcon?: string; logoText?: string };
|
||||
appBar?: IAppBarConfig;
|
||||
views: IViewDefinition[];
|
||||
mainMenu?: IMainMenuConfig;
|
||||
routing?: IRoutingConfig;
|
||||
statePersistence?: IStatePersistenceConfig;
|
||||
onViewChange?: (viewId: string, view: IViewDefinition) => void;
|
||||
}
|
||||
|
||||
interface IViewDefinition {
|
||||
id: string;
|
||||
name: string;
|
||||
iconName?: string;
|
||||
content: string | (new () => HTMLElement) | (() => TemplateResult);
|
||||
secondaryMenu?: ISecondaryMenuGroup[];
|
||||
contentTabs?: ITab[];
|
||||
route?: string;
|
||||
}
|
||||
|
||||
interface IRoutingConfig {
|
||||
mode: 'hash' | 'history' | 'external' | 'none';
|
||||
basePath?: string;
|
||||
defaultView?: string;
|
||||
syncUrl?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### New Public Methods on DeesAppuiBase:
|
||||
|
||||
```typescript
|
||||
// Configure with unified config
|
||||
configure(config: IAppConfig): void
|
||||
|
||||
// Navigation
|
||||
navigateToView(viewId: string): boolean
|
||||
getCurrentView(): IViewDefinition | undefined
|
||||
|
||||
// State management
|
||||
getUIState(): IAppUIState
|
||||
restoreUIState(state: IAppUIState): void
|
||||
saveState(): void
|
||||
loadState(): boolean
|
||||
|
||||
// Access internals
|
||||
getViewRegistry(): ViewRegistry
|
||||
getRouter(): AppRouter | null
|
||||
```
|
||||
|
||||
### Usage Example (New Unified Config API):
|
||||
|
||||
```typescript
|
||||
import type { IAppConfig } from '@design.estate/dees-catalog';
|
||||
|
||||
const config: IAppConfig = {
|
||||
branding: { logoIcon: 'lucide:box', logoText: 'My App' },
|
||||
views: [
|
||||
{ id: 'dashboard', name: 'Dashboard', iconName: 'lucide:home', content: 'my-dashboard' },
|
||||
{ id: 'settings', name: 'Settings', iconName: 'lucide:settings', content: 'my-settings' },
|
||||
],
|
||||
mainMenu: {
|
||||
sections: [{ views: ['dashboard'] }],
|
||||
bottomItems: ['settings'],
|
||||
},
|
||||
routing: { mode: 'hash', defaultView: 'dashboard' },
|
||||
statePersistence: { enabled: true, storage: 'localStorage' },
|
||||
};
|
||||
|
||||
html`<dees-appui-base .config=${config}></dees-appui-base>`;
|
||||
```
|
||||
|
||||
### Backward Compatibility:
|
||||
|
||||
The existing property-based API still works:
|
||||
|
||||
```typescript
|
||||
html`
|
||||
<dees-appui-base
|
||||
.mainmenuGroups=${groups}
|
||||
.secondarymenuGroups=${secondaryGroups}
|
||||
@mainmenu-tab-select=${handler}
|
||||
>
|
||||
<div slot="maincontent">...</div>
|
||||
</dees-appui-base>
|
||||
`;
|
||||
```
|
||||
|
||||
### Key Features:
|
||||
|
||||
- **Declarative View Registry**: Map menu items to view components
|
||||
- **Built-in Routing**: Hash or history mode with URL synchronization
|
||||
- **External Router Support**: Integrate with Angular Router or other frameworks
|
||||
- **State Persistence**: Save/restore collapsed menus, selections, and current view
|
||||
- **View-specific Menus**: Each view can define its own secondary menu and tabs
|
||||
- **Full Backward Compatibility**: Existing code continues to work
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@design.estate/dees-catalog',
|
||||
version: '3.2.0',
|
||||
version: '3.3.1',
|
||||
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
||||
}
|
||||
|
||||
271
ts_web/elements/00group-appui/dees-appui-base/app.router.ts
Normal file
271
ts_web/elements/00group-appui/dees-appui-base/app.router.ts
Normal file
@@ -0,0 +1,271 @@
|
||||
import type { IRoutingConfig, IViewDefinition } from '../../interfaces/appconfig.js';
|
||||
import type { ViewRegistry } from './view.registry.js';
|
||||
|
||||
export type TRouteChangeCallback = (viewId: string, params?: Record<string, string>) => void;
|
||||
|
||||
/**
|
||||
* Router for managing view navigation and URL synchronization
|
||||
*/
|
||||
export class AppRouter {
|
||||
private config: Required<Omit<IRoutingConfig, 'notFound'>> & Pick<IRoutingConfig, 'notFound'>;
|
||||
private viewRegistry: ViewRegistry;
|
||||
private listeners: Set<TRouteChangeCallback> = new Set();
|
||||
private currentViewId: string | null = null;
|
||||
private isInitialized: boolean = false;
|
||||
|
||||
constructor(config: IRoutingConfig, viewRegistry: ViewRegistry) {
|
||||
this.config = {
|
||||
mode: config.mode,
|
||||
basePath: config.basePath || '',
|
||||
defaultView: config.defaultView || '',
|
||||
syncUrl: config.syncUrl ?? true,
|
||||
notFound: config.notFound,
|
||||
};
|
||||
this.viewRegistry = viewRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the router
|
||||
*/
|
||||
public init(): void {
|
||||
if (this.isInitialized) return;
|
||||
|
||||
if (this.config.mode === 'hash') {
|
||||
window.addEventListener('hashchange', this.handleHashChange);
|
||||
// Check initial hash
|
||||
const initialView = this.getViewFromHash();
|
||||
if (initialView) {
|
||||
this.navigate(initialView, { source: 'initial' });
|
||||
} else if (this.config.defaultView) {
|
||||
this.navigate(this.config.defaultView, { source: 'initial' });
|
||||
}
|
||||
} else if (this.config.mode === 'history') {
|
||||
window.addEventListener('popstate', this.handlePopState);
|
||||
// Check initial path
|
||||
const initialView = this.getViewFromPath();
|
||||
if (initialView) {
|
||||
this.navigate(initialView, { source: 'initial' });
|
||||
} else if (this.config.defaultView) {
|
||||
this.navigate(this.config.defaultView, { source: 'initial' });
|
||||
}
|
||||
} else if (this.config.mode === 'none' && this.config.defaultView) {
|
||||
this.navigate(this.config.defaultView, { source: 'initial' });
|
||||
}
|
||||
// For 'external' mode, we don't set up listeners - the external router handles it
|
||||
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a view by ID
|
||||
*/
|
||||
public navigate(
|
||||
viewId: string,
|
||||
options: {
|
||||
source?: 'navigation' | 'popstate' | 'initial' | 'programmatic';
|
||||
replace?: boolean;
|
||||
params?: Record<string, string>;
|
||||
} = {}
|
||||
): boolean {
|
||||
const { source = 'programmatic', replace = false, params } = options;
|
||||
|
||||
const view = this.viewRegistry.get(viewId);
|
||||
if (!view) {
|
||||
console.warn(`Cannot navigate to unknown view: ${viewId}`);
|
||||
if (this.config.notFound) {
|
||||
if (typeof this.config.notFound === 'function') {
|
||||
this.config.notFound();
|
||||
} else {
|
||||
return this.navigate(this.config.notFound, { source, replace: true });
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const previousViewId = this.currentViewId;
|
||||
this.currentViewId = viewId;
|
||||
|
||||
// Update URL if configured
|
||||
if (this.config.syncUrl && this.config.mode !== 'none' && this.config.mode !== 'external') {
|
||||
this.updateUrl(view, replace);
|
||||
}
|
||||
|
||||
// Notify listeners
|
||||
this.notifyListeners(viewId, params);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate back in history
|
||||
*/
|
||||
public back(): void {
|
||||
if (this.config.mode === 'hash' || this.config.mode === 'history') {
|
||||
window.history.back();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate forward in history
|
||||
*/
|
||||
public forward(): void {
|
||||
if (this.config.mode === 'hash' || this.config.mode === 'history') {
|
||||
window.history.forward();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current view ID
|
||||
*/
|
||||
public getCurrentViewId(): string | null {
|
||||
return this.currentViewId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a route change listener
|
||||
*/
|
||||
public onRouteChange(callback: TRouteChangeCallback): () => void {
|
||||
this.listeners.add(callback);
|
||||
return () => this.listeners.delete(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle external navigation (for external router mode)
|
||||
*/
|
||||
public handleExternalNavigation(viewId: string, params?: Record<string, string>): void {
|
||||
if (this.config.mode !== 'external') {
|
||||
console.warn('handleExternalNavigation should only be used in external mode');
|
||||
}
|
||||
|
||||
const previousViewId = this.currentViewId;
|
||||
this.currentViewId = viewId;
|
||||
this.notifyListeners(viewId, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync state with URL (for external router integration)
|
||||
*/
|
||||
public syncWithUrl(): string | null {
|
||||
if (this.config.mode === 'hash') {
|
||||
return this.getViewFromHash();
|
||||
} else if (this.config.mode === 'history') {
|
||||
return this.getViewFromPath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current route from the URL
|
||||
*/
|
||||
public getCurrentRoute(): string {
|
||||
if (this.config.mode === 'hash') {
|
||||
return window.location.hash.slice(1) || '';
|
||||
} else if (this.config.mode === 'history') {
|
||||
let path = window.location.pathname;
|
||||
if (this.config.basePath && path.startsWith(this.config.basePath)) {
|
||||
path = path.slice(this.config.basePath.length);
|
||||
}
|
||||
return path.replace(/^\//, '');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a URL for a view
|
||||
*/
|
||||
public buildUrl(viewId: string): string {
|
||||
const view = this.viewRegistry.get(viewId);
|
||||
const route = view?.route || viewId;
|
||||
|
||||
if (this.config.mode === 'hash') {
|
||||
return `#${route}`;
|
||||
} else if (this.config.mode === 'history') {
|
||||
return `${this.config.basePath}/${route}`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the router
|
||||
*/
|
||||
public destroy(): void {
|
||||
if (this.config.mode === 'hash') {
|
||||
window.removeEventListener('hashchange', this.handleHashChange);
|
||||
} else if (this.config.mode === 'history') {
|
||||
window.removeEventListener('popstate', this.handlePopState);
|
||||
}
|
||||
this.listeners.clear();
|
||||
this.isInitialized = false;
|
||||
}
|
||||
|
||||
// Private methods
|
||||
|
||||
private handleHashChange = (): void => {
|
||||
const viewId = this.getViewFromHash();
|
||||
if (viewId && viewId !== this.currentViewId) {
|
||||
this.navigate(viewId, { source: 'popstate' });
|
||||
}
|
||||
};
|
||||
|
||||
private handlePopState = (): void => {
|
||||
const viewId = this.getViewFromPath();
|
||||
if (viewId && viewId !== this.currentViewId) {
|
||||
this.navigate(viewId, { source: 'popstate' });
|
||||
}
|
||||
};
|
||||
|
||||
private getViewFromHash(): string | null {
|
||||
const hash = window.location.hash.slice(1); // Remove #
|
||||
if (!hash) return null;
|
||||
|
||||
// Try to find view by route
|
||||
const view = this.viewRegistry.findByRoute(hash);
|
||||
return view?.id || null;
|
||||
}
|
||||
|
||||
private getViewFromPath(): string | null {
|
||||
let path = window.location.pathname;
|
||||
|
||||
// Remove base path if configured
|
||||
if (this.config.basePath) {
|
||||
if (path.startsWith(this.config.basePath)) {
|
||||
path = path.slice(this.config.basePath.length);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove leading slash
|
||||
path = path.replace(/^\//, '');
|
||||
|
||||
if (!path) return null;
|
||||
|
||||
const view = this.viewRegistry.findByRoute(path);
|
||||
return view?.id || null;
|
||||
}
|
||||
|
||||
private updateUrl(view: IViewDefinition, replace: boolean): void {
|
||||
const route = view.route || view.id;
|
||||
|
||||
if (this.config.mode === 'hash') {
|
||||
const newHash = `#${route}`;
|
||||
if (replace) {
|
||||
window.history.replaceState(null, '', newHash);
|
||||
} else {
|
||||
window.history.pushState(null, '', newHash);
|
||||
}
|
||||
} else if (this.config.mode === 'history') {
|
||||
const basePath = this.config.basePath || '';
|
||||
const newPath = `${basePath}/${route}`;
|
||||
if (replace) {
|
||||
window.history.replaceState({ viewId: view.id }, '', newPath);
|
||||
} else {
|
||||
window.history.pushState({ viewId: view.id }, '', newPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private notifyListeners(viewId: string, params?: Record<string, string>): void {
|
||||
for (const listener of this.listeners) {
|
||||
listener(viewId, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,11 @@ import type { DeesAppuiMaincontent } from '../dees-appui-maincontent/dees-appui-
|
||||
import type { DeesAppuiActivitylog } from '../dees-appui-activitylog/dees-appui-activitylog.js';
|
||||
import { demoFunc } from './dees-appui-base.demo.js';
|
||||
|
||||
// New module imports
|
||||
import { ViewRegistry } from './view.registry.js';
|
||||
import { AppRouter } from './app.router.js';
|
||||
import { StateManager } from './state.manager.js';
|
||||
|
||||
// Import child components
|
||||
import '../dees-appui-appbar/index.js';
|
||||
import '../dees-appui-mainmenu/dees-appui-mainmenu.js';
|
||||
@@ -116,6 +121,19 @@ export class DeesAppuiBase extends DeesElement {
|
||||
@state()
|
||||
accessor activitylog: DeesAppuiActivitylog | undefined = undefined;
|
||||
|
||||
// NEW: Unified config property
|
||||
@property({ type: Object })
|
||||
accessor config: interfaces.IAppConfig | undefined = undefined;
|
||||
|
||||
// NEW: Current view state
|
||||
@state()
|
||||
accessor currentView: interfaces.IViewDefinition | undefined = undefined;
|
||||
|
||||
// NEW: Internal services (not reactive, managed internally)
|
||||
private viewRegistry: ViewRegistry = new ViewRegistry();
|
||||
private router: AppRouter | null = null;
|
||||
private stateManager: StateManager | null = null;
|
||||
|
||||
public static styles = [
|
||||
cssManager.defaultStyles,
|
||||
css`
|
||||
@@ -155,6 +173,15 @@ export class DeesAppuiBase extends DeesElement {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* View container for dynamically loaded views */
|
||||
.view-container {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.view-container:empty {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -200,6 +227,7 @@ export class DeesAppuiBase extends DeesElement {
|
||||
<dees-appui-maincontent
|
||||
.tabs=${this.maincontentTabs}
|
||||
>
|
||||
<div class="view-container"></div>
|
||||
<slot name="maincontent"></slot>
|
||||
</dees-appui-maincontent>
|
||||
<dees-appui-activitylog></dees-appui-activitylog>
|
||||
@@ -214,6 +242,24 @@ export class DeesAppuiBase extends DeesElement {
|
||||
this.secondarymenu = this.shadowRoot.querySelector('dees-appui-secondarymenu');
|
||||
this.maincontent = this.shadowRoot.querySelector('dees-appui-maincontent');
|
||||
this.activitylog = this.shadowRoot.querySelector('dees-appui-activitylog');
|
||||
|
||||
// Initialize from config if provided
|
||||
if (this.config) {
|
||||
this.applyConfig(this.config);
|
||||
|
||||
// Restore state if enabled
|
||||
if (this.config.statePersistence?.enabled) {
|
||||
this.loadState();
|
||||
}
|
||||
|
||||
// Initialize router after state restore
|
||||
this.router?.init();
|
||||
}
|
||||
}
|
||||
|
||||
async disconnectedCallback() {
|
||||
await super.disconnectedCallback();
|
||||
this.router?.destroy();
|
||||
}
|
||||
|
||||
// Event handlers for appbar
|
||||
@@ -295,4 +341,242 @@ export class DeesAppuiBase extends DeesElement {
|
||||
composed: true
|
||||
}));
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// NEW: Public methods for unified config API
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* Configure the app shell with a unified config object
|
||||
*/
|
||||
public configure(config: interfaces.IAppConfig): void {
|
||||
this.config = config;
|
||||
this.applyConfig(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a view by ID
|
||||
*/
|
||||
public navigateToView(viewId: string): boolean {
|
||||
if (this.router) {
|
||||
return this.router.navigate(viewId);
|
||||
}
|
||||
|
||||
// Fallback for non-routed mode
|
||||
const view = this.viewRegistry.get(viewId);
|
||||
if (view) {
|
||||
this.loadView(view);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current view
|
||||
*/
|
||||
public getCurrentView(): interfaces.IViewDefinition | undefined {
|
||||
return this.currentView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get UI state for serialization
|
||||
*/
|
||||
public getUIState(): interfaces.IAppUIState {
|
||||
return {
|
||||
currentViewId: this.currentView?.id,
|
||||
mainMenuCollapsed: this.mainmenuCollapsed,
|
||||
secondaryMenuCollapsed: this.secondarymenuCollapsed,
|
||||
secondaryMenuSelectedKey: this.secondarymenuSelectedItem?.key,
|
||||
collapsedGroups: [], // TODO: Get from secondarymenu if needed
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore UI state from a state object
|
||||
*/
|
||||
public restoreUIState(state: interfaces.IAppUIState): void {
|
||||
if (state.mainMenuCollapsed !== undefined) {
|
||||
this.mainmenuCollapsed = state.mainMenuCollapsed;
|
||||
}
|
||||
if (state.secondaryMenuCollapsed !== undefined) {
|
||||
this.secondarymenuCollapsed = state.secondaryMenuCollapsed;
|
||||
}
|
||||
if (state.currentViewId) {
|
||||
this.navigateToView(state.currentViewId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current UI state
|
||||
*/
|
||||
public saveState(): void {
|
||||
this.stateManager?.save(this.getUIState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and restore saved UI state
|
||||
*/
|
||||
public loadState(): boolean {
|
||||
const state = this.stateManager?.load();
|
||||
if (state) {
|
||||
this.restoreUIState(state);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get access to the view registry
|
||||
*/
|
||||
public getViewRegistry(): ViewRegistry {
|
||||
return this.viewRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get access to the router
|
||||
*/
|
||||
public getRouter(): AppRouter | null {
|
||||
return this.router;
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// NEW: Private helper methods
|
||||
// ==========================================
|
||||
|
||||
private applyConfig(config: interfaces.IAppConfig): void {
|
||||
// Register views
|
||||
if (config.views) {
|
||||
this.viewRegistry.clear();
|
||||
this.viewRegistry.registerAll(config.views);
|
||||
}
|
||||
|
||||
// Apply branding
|
||||
if (config.branding) {
|
||||
this.mainmenuLogoIcon = config.branding.logoIcon || '';
|
||||
this.mainmenuLogoText = config.branding.logoText || '';
|
||||
}
|
||||
|
||||
// Apply app bar config
|
||||
if (config.appBar) {
|
||||
this.appbarMenuItems = config.appBar.menuItems || [];
|
||||
this.appbarBreadcrumbs = config.appBar.breadcrumbs || '';
|
||||
this.appbarBreadcrumbSeparator = config.appBar.breadcrumbSeparator || ' > ';
|
||||
this.appbarShowWindowControls = config.appBar.showWindowControls ?? true;
|
||||
this.appbarShowSearch = config.appBar.showSearch ?? false;
|
||||
this.appbarUser = config.appBar.user;
|
||||
this.appbarProfileMenuItems = config.appBar.profileMenuItems || [];
|
||||
}
|
||||
|
||||
// Build main menu from view references
|
||||
if (config.mainMenu) {
|
||||
this.mainmenuGroups = this.buildMainMenuGroups(config);
|
||||
this.mainmenuBottomTabs = this.buildBottomTabs(config);
|
||||
}
|
||||
|
||||
// Initialize state manager
|
||||
if (config.statePersistence) {
|
||||
this.stateManager = new StateManager(config.statePersistence);
|
||||
}
|
||||
|
||||
// Initialize router
|
||||
if (config.routing && config.routing.mode !== 'none') {
|
||||
this.router = new AppRouter(config.routing, this.viewRegistry);
|
||||
this.router.onRouteChange((viewId) => {
|
||||
const view = this.viewRegistry.get(viewId);
|
||||
if (view) {
|
||||
this.loadView(view);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Bind event callbacks
|
||||
if (config.onViewChange) {
|
||||
this.addEventListener('view-change', ((e: CustomEvent) => {
|
||||
config.onViewChange!(e.detail.viewId, e.detail.view);
|
||||
}) as EventListener);
|
||||
}
|
||||
|
||||
if (config.onSearch) {
|
||||
this.addEventListener('appbar-search-click', () => {
|
||||
config.onSearch!();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private buildMainMenuGroups(config: interfaces.IAppConfig): interfaces.IMenuGroup[] {
|
||||
if (!config.mainMenu?.sections) return [];
|
||||
|
||||
return config.mainMenu.sections.map((section) => ({
|
||||
name: section.name,
|
||||
tabs: section.views
|
||||
.map((viewId) => {
|
||||
const view = this.viewRegistry.get(viewId);
|
||||
if (!view) {
|
||||
console.warn(`View "${viewId}" not found in registry`);
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
key: view.name,
|
||||
iconName: view.iconName,
|
||||
action: () => this.navigateToView(viewId),
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as interfaces.ITab[],
|
||||
}));
|
||||
}
|
||||
|
||||
private buildBottomTabs(config: interfaces.IAppConfig): interfaces.ITab[] {
|
||||
if (!config.mainMenu?.bottomItems) return [];
|
||||
|
||||
return config.mainMenu.bottomItems
|
||||
.map((viewId) => {
|
||||
const view = this.viewRegistry.get(viewId);
|
||||
if (!view) {
|
||||
console.warn(`View "${viewId}" not found in registry`);
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
key: view.name,
|
||||
iconName: view.iconName,
|
||||
action: () => this.navigateToView(viewId),
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as interfaces.ITab[];
|
||||
}
|
||||
|
||||
private loadView(view: interfaces.IViewDefinition): void {
|
||||
const previousView = this.currentView;
|
||||
this.currentView = view;
|
||||
|
||||
// Update secondary menu
|
||||
if (view.secondaryMenu) {
|
||||
this.secondarymenuGroups = view.secondaryMenu;
|
||||
this.secondarymenuHeading = view.name;
|
||||
}
|
||||
|
||||
// Update content tabs
|
||||
if (view.contentTabs) {
|
||||
this.maincontentTabs = view.contentTabs;
|
||||
}
|
||||
|
||||
// Render view content into the view container
|
||||
const viewContainer = this.maincontent?.shadowRoot?.querySelector('.view-container')
|
||||
|| this.shadowRoot?.querySelector('.view-container');
|
||||
if (viewContainer) {
|
||||
this.viewRegistry.renderView(view.id, viewContainer as HTMLElement);
|
||||
}
|
||||
|
||||
// Save state if configured
|
||||
this.stateManager?.update({ currentViewId: view.id });
|
||||
|
||||
// Dispatch event
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('view-change', {
|
||||
detail: { viewId: view.id, view, previousView },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
export * from './dees-appui-base.js';
|
||||
export * from './view.registry.js';
|
||||
export * from './app.router.js';
|
||||
export * from './state.manager.js';
|
||||
|
||||
185
ts_web/elements/00group-appui/dees-appui-base/state.manager.ts
Normal file
185
ts_web/elements/00group-appui/dees-appui-base/state.manager.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import type { IStatePersistenceConfig, IAppUIState } from '../../interfaces/appconfig.js';
|
||||
|
||||
/**
|
||||
* Manager for persisting and restoring UI state
|
||||
*/
|
||||
export class StateManager {
|
||||
private config: Required<IStatePersistenceConfig>;
|
||||
private memoryStorage: Map<string, string> = new Map();
|
||||
|
||||
constructor(config: IStatePersistenceConfig = { enabled: false }) {
|
||||
this.config = {
|
||||
enabled: config.enabled,
|
||||
storageKey: config.storageKey || 'dees-appui-state',
|
||||
storage: config.storage || 'localStorage',
|
||||
persist: {
|
||||
mainMenuCollapsed: true,
|
||||
secondaryMenuCollapsed: true,
|
||||
selectedView: true,
|
||||
secondaryMenuSelection: true,
|
||||
collapsedGroups: true,
|
||||
...config.persist,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if state persistence is enabled
|
||||
*/
|
||||
public isEnabled(): boolean {
|
||||
return this.config.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current UI state
|
||||
*/
|
||||
public save(state: Partial<IAppUIState>): void {
|
||||
if (!this.config.enabled) return;
|
||||
|
||||
const existingState = this.load() || {};
|
||||
const newState: IAppUIState = {
|
||||
...existingState,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
// Only save what's configured
|
||||
if (this.config.persist.selectedView && state.currentViewId !== undefined) {
|
||||
newState.currentViewId = state.currentViewId;
|
||||
}
|
||||
if (this.config.persist.mainMenuCollapsed && state.mainMenuCollapsed !== undefined) {
|
||||
newState.mainMenuCollapsed = state.mainMenuCollapsed;
|
||||
}
|
||||
if (this.config.persist.secondaryMenuCollapsed && state.secondaryMenuCollapsed !== undefined) {
|
||||
newState.secondaryMenuCollapsed = state.secondaryMenuCollapsed;
|
||||
}
|
||||
if (this.config.persist.secondaryMenuSelection && state.secondaryMenuSelectedKey !== undefined) {
|
||||
newState.secondaryMenuSelectedKey = state.secondaryMenuSelectedKey;
|
||||
}
|
||||
if (this.config.persist.collapsedGroups && state.collapsedGroups !== undefined) {
|
||||
newState.collapsedGroups = state.collapsedGroups;
|
||||
}
|
||||
|
||||
this.setItem(this.config.storageKey, JSON.stringify(newState));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load persisted UI state
|
||||
*/
|
||||
public load(): IAppUIState | null {
|
||||
if (!this.config.enabled) return null;
|
||||
|
||||
try {
|
||||
const data = this.getItem(this.config.storageKey);
|
||||
if (!data) return null;
|
||||
return JSON.parse(data) as IAppUIState;
|
||||
} catch (e) {
|
||||
console.warn('Failed to load UI state:', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear persisted state
|
||||
*/
|
||||
public clear(): void {
|
||||
this.removeItem(this.config.storageKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if state exists
|
||||
*/
|
||||
public hasState(): boolean {
|
||||
return this.getItem(this.config.storageKey) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get state age in milliseconds
|
||||
*/
|
||||
public getStateAge(): number | null {
|
||||
const state = this.load();
|
||||
if (!state?.timestamp) return null;
|
||||
return Date.now() - state.timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update specific state properties
|
||||
*/
|
||||
public update(updates: Partial<IAppUIState>): void {
|
||||
const currentState = this.load() || {};
|
||||
this.save({ ...currentState, ...updates });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the storage key being used
|
||||
*/
|
||||
public getStorageKey(): string {
|
||||
return this.config.storageKey;
|
||||
}
|
||||
|
||||
// Storage abstraction methods
|
||||
|
||||
private getItem(key: string): string | null {
|
||||
switch (this.config.storage) {
|
||||
case 'localStorage':
|
||||
try {
|
||||
return localStorage.getItem(key);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
case 'sessionStorage':
|
||||
try {
|
||||
return sessionStorage.getItem(key);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
case 'memory':
|
||||
return this.memoryStorage.get(key) || null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private setItem(key: string, value: string): void {
|
||||
switch (this.config.storage) {
|
||||
case 'localStorage':
|
||||
try {
|
||||
localStorage.setItem(key, value);
|
||||
} catch (e) {
|
||||
console.warn('Failed to save to localStorage:', e);
|
||||
}
|
||||
break;
|
||||
case 'sessionStorage':
|
||||
try {
|
||||
sessionStorage.setItem(key, value);
|
||||
} catch (e) {
|
||||
console.warn('Failed to save to sessionStorage:', e);
|
||||
}
|
||||
break;
|
||||
case 'memory':
|
||||
this.memoryStorage.set(key, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private removeItem(key: string): void {
|
||||
switch (this.config.storage) {
|
||||
case 'localStorage':
|
||||
try {
|
||||
localStorage.removeItem(key);
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
break;
|
||||
case 'sessionStorage':
|
||||
try {
|
||||
sessionStorage.removeItem(key);
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
break;
|
||||
case 'memory':
|
||||
this.memoryStorage.delete(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
167
ts_web/elements/00group-appui/dees-appui-base/view.registry.ts
Normal file
167
ts_web/elements/00group-appui/dees-appui-base/view.registry.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import { html, render, type TemplateResult } from '@design.estate/dees-element';
|
||||
import type { IViewDefinition } from '../../interfaces/appconfig.js';
|
||||
|
||||
/**
|
||||
* Registry for managing views and their lifecycle
|
||||
*/
|
||||
export class ViewRegistry {
|
||||
private views: Map<string, IViewDefinition> = new Map();
|
||||
private instances: Map<string, HTMLElement> = new Map();
|
||||
private currentViewId: string | null = null;
|
||||
|
||||
/**
|
||||
* Register a single view
|
||||
*/
|
||||
public register(view: IViewDefinition): void {
|
||||
if (this.views.has(view.id)) {
|
||||
console.warn(`View with id "${view.id}" already registered. Overwriting.`);
|
||||
}
|
||||
this.views.set(view.id, view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register multiple views
|
||||
*/
|
||||
public registerAll(views: IViewDefinition[]): void {
|
||||
views.forEach((view) => this.register(view));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a view definition by ID
|
||||
*/
|
||||
public get(viewId: string): IViewDefinition | undefined {
|
||||
return this.views.get(viewId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered view IDs
|
||||
*/
|
||||
public getViewIds(): string[] {
|
||||
return Array.from(this.views.keys());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all views
|
||||
*/
|
||||
public getAll(): IViewDefinition[] {
|
||||
return Array.from(this.views.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get route for a view
|
||||
*/
|
||||
public getRoute(viewId: string): string {
|
||||
const view = this.views.get(viewId);
|
||||
return view?.route || view?.id || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Find view by route
|
||||
*/
|
||||
public findByRoute(route: string): IViewDefinition | undefined {
|
||||
for (const view of this.views.values()) {
|
||||
const viewRoute = view.route || view.id;
|
||||
if (viewRoute === route) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a view's content into a container
|
||||
*/
|
||||
public renderView(viewId: string, container: HTMLElement): HTMLElement | null {
|
||||
const view = this.views.get(viewId);
|
||||
if (!view) {
|
||||
console.error(`View "${viewId}" not found in registry`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Clear container
|
||||
container.innerHTML = '';
|
||||
|
||||
let element: HTMLElement;
|
||||
|
||||
if (typeof view.content === 'string') {
|
||||
// Tag name string
|
||||
element = document.createElement(view.content);
|
||||
} else if (typeof view.content === 'function') {
|
||||
// Check if it's a class constructor or template function
|
||||
if (view.content.prototype instanceof HTMLElement) {
|
||||
// Element class constructor
|
||||
element = new (view.content as new () => HTMLElement)();
|
||||
} else {
|
||||
// Template function - wrap in a container and use Lit's render
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'view-content-wrapper';
|
||||
wrapper.style.cssText = 'display: contents;';
|
||||
const template = (view.content as () => TemplateResult)();
|
||||
render(template, wrapper);
|
||||
element = wrapper;
|
||||
}
|
||||
} else {
|
||||
console.error(`Invalid content type for view "${viewId}"`);
|
||||
return null;
|
||||
}
|
||||
|
||||
container.appendChild(element);
|
||||
this.instances.set(viewId, element);
|
||||
this.currentViewId = viewId;
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get currently active view ID
|
||||
*/
|
||||
public getCurrentViewId(): string | null {
|
||||
return this.currentViewId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cached instance of a view
|
||||
*/
|
||||
public getInstance(viewId: string): HTMLElement | undefined {
|
||||
return this.instances.get(viewId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all instances
|
||||
*/
|
||||
public clearInstances(): void {
|
||||
this.instances.clear();
|
||||
this.currentViewId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a view
|
||||
*/
|
||||
public unregister(viewId: string): boolean {
|
||||
this.instances.delete(viewId);
|
||||
return this.views.delete(viewId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the registry
|
||||
*/
|
||||
public clear(): void {
|
||||
this.views.clear();
|
||||
this.instances.clear();
|
||||
this.currentViewId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a view is registered
|
||||
*/
|
||||
public has(viewId: string): boolean {
|
||||
return this.views.has(viewId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of registered views
|
||||
*/
|
||||
public get size(): number {
|
||||
return this.views.size;
|
||||
}
|
||||
}
|
||||
@@ -299,6 +299,8 @@ export class DeesPdfViewer extends DeesElement {
|
||||
await this.renderThumbnails();
|
||||
// Re-setup intersection observer for lazy loading of pages
|
||||
this.setupIntersectionObserver();
|
||||
// Scroll to active thumbnail after rendering
|
||||
this.scrollThumbnailIntoView(this.currentPage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,6 +580,11 @@ export class DeesPdfViewer extends DeesElement {
|
||||
// Update current page
|
||||
this.currentPage = pageNum;
|
||||
|
||||
// Scroll thumbnail into view if sidebar is visible
|
||||
if (this.showSidebar) {
|
||||
this.scrollThumbnailIntoView(pageNum);
|
||||
}
|
||||
|
||||
// Ensure the page is rendered
|
||||
await this.renderPageIfNeeded(pageNum);
|
||||
}
|
||||
|
||||
189
ts_web/elements/interfaces/appconfig.ts
Normal file
189
ts_web/elements/interfaces/appconfig.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import type { TemplateResult } from '@design.estate/dees-element';
|
||||
import type { IAppBarMenuItem } from './appbarmenuitem.js';
|
||||
import type { ITab } from './tab.js';
|
||||
import type { ISecondaryMenuGroup } from './secondarymenu.js';
|
||||
|
||||
/**
|
||||
* User configuration for the app bar
|
||||
*/
|
||||
export interface IAppUser {
|
||||
name: string;
|
||||
email?: string;
|
||||
avatar?: string;
|
||||
status?: 'online' | 'offline' | 'busy' | 'away';
|
||||
}
|
||||
|
||||
/**
|
||||
* View definition for the view registry
|
||||
*/
|
||||
export interface IViewDefinition {
|
||||
/** Unique identifier for routing */
|
||||
id: string;
|
||||
/** Display name */
|
||||
name: string;
|
||||
/** Optional icon */
|
||||
iconName?: string;
|
||||
/** The view content - can be a tag name, element class, or template function */
|
||||
content: string | (new () => HTMLElement) | (() => TemplateResult);
|
||||
/** Secondary menu items specific to this view */
|
||||
secondaryMenu?: ISecondaryMenuGroup[];
|
||||
/** Content tabs specific to this view */
|
||||
contentTabs?: ITab[];
|
||||
/** Optional route path (defaults to id) */
|
||||
route?: string;
|
||||
/** Badge to show on menu item */
|
||||
badge?: string | number;
|
||||
badgeVariant?: 'default' | 'success' | 'warning' | 'error';
|
||||
}
|
||||
|
||||
/**
|
||||
* Main menu section with view references
|
||||
*/
|
||||
export interface IMainMenuSection {
|
||||
/** Section name (optional for ungrouped) */
|
||||
name?: string;
|
||||
/** Views in this section (by ID reference) */
|
||||
views: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Main menu configuration
|
||||
*/
|
||||
export interface IMainMenuConfig {
|
||||
/** Menu sections with view references */
|
||||
sections?: IMainMenuSection[];
|
||||
/** Bottom pinned items (view IDs) */
|
||||
bottomItems?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* App bar configuration
|
||||
*/
|
||||
export interface IAppBarConfig {
|
||||
menuItems?: IAppBarMenuItem[];
|
||||
breadcrumbs?: string;
|
||||
breadcrumbSeparator?: string;
|
||||
showWindowControls?: boolean;
|
||||
showSearch?: boolean;
|
||||
user?: IAppUser;
|
||||
profileMenuItems?: IAppBarMenuItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Branding configuration
|
||||
*/
|
||||
export interface IBrandingConfig {
|
||||
logoIcon?: string;
|
||||
logoText?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing configuration
|
||||
*/
|
||||
export interface IRoutingConfig {
|
||||
/** Routing mode */
|
||||
mode: 'hash' | 'history' | 'external' | 'none';
|
||||
/** Base path for history mode */
|
||||
basePath?: string;
|
||||
/** Default view ID to show on startup */
|
||||
defaultView?: string;
|
||||
/** Sync URL on view change */
|
||||
syncUrl?: boolean;
|
||||
/** Handle 404s - show view ID or callback */
|
||||
notFound?: string | (() => void);
|
||||
}
|
||||
|
||||
/**
|
||||
* State persistence configuration
|
||||
*/
|
||||
export interface IStatePersistenceConfig {
|
||||
/** Enable state persistence */
|
||||
enabled: boolean;
|
||||
/** Storage key prefix */
|
||||
storageKey?: string;
|
||||
/** Storage type */
|
||||
storage?: 'localStorage' | 'sessionStorage' | 'memory';
|
||||
/** What to persist */
|
||||
persist?: {
|
||||
mainMenuCollapsed?: boolean;
|
||||
secondaryMenuCollapsed?: boolean;
|
||||
selectedView?: boolean;
|
||||
secondaryMenuSelection?: boolean;
|
||||
collapsedGroups?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity log configuration
|
||||
*/
|
||||
export interface IActivityLogConfig {
|
||||
enabled?: boolean;
|
||||
width?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main unified configuration interface for dees-appui-base
|
||||
*/
|
||||
export interface IAppConfig {
|
||||
/** Application branding */
|
||||
branding?: IBrandingConfig;
|
||||
|
||||
/** App bar configuration */
|
||||
appBar?: IAppBarConfig;
|
||||
|
||||
/** View definitions (the registry) */
|
||||
views: IViewDefinition[];
|
||||
|
||||
/** Main menu structure */
|
||||
mainMenu?: IMainMenuConfig;
|
||||
|
||||
/** Routing configuration */
|
||||
routing?: IRoutingConfig;
|
||||
|
||||
/** State persistence configuration */
|
||||
statePersistence?: IStatePersistenceConfig;
|
||||
|
||||
/** Activity log configuration */
|
||||
activityLog?: IActivityLogConfig;
|
||||
|
||||
/** Event callbacks (optional shorthand) */
|
||||
onViewChange?: (viewId: string, view: IViewDefinition) => void;
|
||||
onSearch?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialized UI state for persistence
|
||||
*/
|
||||
export interface IAppUIState {
|
||||
/** Current view ID */
|
||||
currentViewId?: string;
|
||||
/** Main menu collapsed state */
|
||||
mainMenuCollapsed?: boolean;
|
||||
/** Secondary menu collapsed state */
|
||||
secondaryMenuCollapsed?: boolean;
|
||||
/** Selected secondary menu item key */
|
||||
secondaryMenuSelectedKey?: string;
|
||||
/** Collapsed group names in secondary menu */
|
||||
collapsedGroups?: string[];
|
||||
/** Timestamp of last save */
|
||||
timestamp?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route change event detail
|
||||
*/
|
||||
export interface IRouteChangeEvent {
|
||||
viewId: string;
|
||||
previousViewId: string | null;
|
||||
params?: Record<string, string>;
|
||||
source: 'navigation' | 'popstate' | 'initial' | 'programmatic';
|
||||
}
|
||||
|
||||
/**
|
||||
* View change event detail
|
||||
*/
|
||||
export interface IViewChangeEvent {
|
||||
viewId: string;
|
||||
view: IViewDefinition;
|
||||
previousView?: IViewDefinition;
|
||||
}
|
||||
@@ -3,3 +3,4 @@ export * from './selectionoption.js';
|
||||
export * from './appbarmenuitem.js';
|
||||
export * from './menugroup.js';
|
||||
export * from './secondarymenu.js';
|
||||
export * from './appconfig.js';
|
||||
|
||||
Reference in New Issue
Block a user