Compare commits

...

25 Commits

Author SHA1 Message Date
26ca16a284 v3.43.3
Some checks failed
Default (tags) / security (push) Failing after 2s
Default (tags) / test (push) Failing after 2s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-02-24 18:25:54 +00:00
3ab3eb5e5e fix(dees-table): use lucide icon identifier for Search action in dees-table 2026-02-24 18:25:54 +00:00
da5dbc70e2 v3.43.2
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-02-21 21:12:18 +00:00
19b7981542 fix(dees-chart-log): avoid duplicate log entries, optimize incremental updates, enforce maxEntries, and respect filters when writing logs 2026-02-21 21:12:18 +00:00
bad105074e v3.43.1
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-02-21 18:06:42 +00:00
f124091784 fix(dees-chart-log): replay buffered log entries when terminal becomes ready and sync logEntries updates to re-render filtered logs 2026-02-21 18:06:42 +00:00
68790a26ed v3.43.0
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-02-17 17:34:53 +00:00
2abf7e356d feat(dees-form): add layout styles to dees-form and standardize demo input grouping 2026-02-17 17:34:53 +00:00
ecd35683e6 v3.42.2
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-02-16 17:16:49 +00:00
93cb632448 fix(dees-chart-area): add ApexAxisChartSeries type to dees-chart-area component to improve typing for ApexCharts series data 2026-02-16 17:16:49 +00:00
047e42c6a3 v3.42.1
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-02-16 16:03:23 +00:00
59efa8cff0 fix(dees-table): Guard against undefined action.type in dees-table by using optional chaining and update several dependencies 2026-02-16 16:03:23 +00:00
09f0aa97dd v3.42.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-02-02 14:32:20 +00:00
7c62f45d77 feat(dees-form-submit): forward button properties to internal dees-button, use property bindings, add demo and styles 2026-02-02 14:32:20 +00:00
b123768474 v3.41.6
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-02-02 09:27:53 +00:00
f292e7a7f4 fix(dees-simple-appdash): respect selectedView when loading initial view, falling back to the first tab 2026-02-02 09:27:53 +00:00
d82e5603a7 v3.41.5
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-02-01 23:51:53 +00:00
7e2386bcdf fix(dees-service-lib-loader): prevent horizontal scrollbar by offsetting xterm WidthCache measurement container 2026-02-01 23:51:53 +00:00
eba2a03355 v3.41.4
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-01-29 13:21:30 +00:00
06c01f0690 fix(): no changes 2026-01-29 13:21:30 +00:00
91e03eb9c4 v3.41.3
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-01-29 07:46:19 +00:00
b7f3f47c61 fix(dees-pdf-viewer): use in-memory PDF data for download and print; add robust print wrapper, cleanup and error handling 2026-01-29 07:46:19 +00:00
0a83f0e136 update 2026-01-28 17:10:37 +00:00
2b048cf34f update 2026-01-28 17:02:52 +00:00
7e50b8cb3f update 2026-01-28 16:46:07 +00:00
23 changed files with 1179 additions and 446 deletions

View File

@@ -1,5 +1,88 @@
# Changelog
## 2026-02-24 - 3.43.3 - fix(dees-table)
use lucide icon identifier for Search action in dees-table
- Replaced iconName 'magnifyingGlass' with 'lucide:Search' in ts_web/elements/00group-dataview/dees-table/dees-table.ts
- Updates the icon identifier for the header 'Search' action; no functional behavior changed
## 2026-02-21 - 3.43.2 - fix(dees-chart-log)
avoid duplicate log entries, optimize incremental updates, enforce maxEntries, and respect filters when writing logs
- Prevent property-bound logEntries from duplicating entries already present in logBuffer by deduplicating on timestamp|message
- Call updateMetrics() when replaying or appending log entries so metrics stay accurate
- Skip processing entirely when the incoming logEntries array is unchanged
- Optimize append-only updates by writing only the new tail entries instead of full re-render
- Enforce maxEntries when appending to the logBuffer to maintain buffer size
- Respect filterMode and searchQuery when deciding whether to write appended entries to the terminal
## 2026-02-21 - 3.43.1 - fix(dees-chart-log)
replay buffered log entries when terminal becomes ready and sync logEntries updates to re-render filtered logs
- Replay entries stored in logBuffer after terminal initialization to avoid losing entries that arrived early
- Add updated() lifecycle hook to copy logEntries into logBuffer and call reRenderFilteredLogs when terminalReady
- Call super.updated(changedProperties) to preserve base class behavior
## 2026-02-17 - 3.43.0 - feat(dees-form)
add layout styles to dees-form and standardize demo input grouping
- Add static CSS to dees-form: default column layout with gap and support for [horizontal-layout] (row wrapping, alignment and gap).
- Remove inline <style> from dees-form render to centralize styling.
- Simplify dees-input-base styles by removing host margins and making spacing container-driven.
- Update multiple demo files to wrap related inputs in a new .input-group container and include .input-group CSS for consistent vertical spacing.
## 2026-02-16 - 3.42.2 - fix(dees-chart-area)
add ApexAxisChartSeries type to dees-chart-area component to improve typing for ApexCharts series data
- Introduces ApexAxisChartSeries type alias with support for number arrays, [x,y] tuples, and object {x,y,...} series entries
- Type-only change — no runtime or API behavior modified
- File changed: ts_web/elements/00group-chart/dees-chart-area/component.ts
## 2026-02-16 - 3.42.1 - fix(dees-table)
Guard against undefined action.type in dees-table by using optional chaining and update several dependencies
- Use optional chaining (action.type?.includes(...)) in ts_web/elements/.../dees-table.ts to prevent runtime errors when action.type is undefined
- Bump dependency apexcharts from ^5.3.6 to ^5.5.0
- Bump dependency lucide from ^0.563.0 to ^0.564.0
- Bump devDependency @git.zone/tswatch from ^3.0.1 to ^3.1.0
- Bump devDependency @types/node from ^25.0.10 to ^25.2.3
## 2026-02-02 - 3.42.0 - feat(dees-form-submit)
forward button properties to internal dees-button, use property bindings, add demo and styles
- Added forwarded properties: type, size, icon, iconPosition (with defaults) and preserved text/status/disabled
- Changed template to use property bindings (.prop) for dees-button instead of string attributes
- Switched internal event handler to listen for dees-button's @clicked event (was @click)
- Added component styles (:host display and dees-button width:100%) and improved layout
- Expanded demo with multiple usage examples (basic, icons, types, sizes, states, and a form context)
## 2026-02-02 - 3.41.6 - fix(dees-simple-appdash)
respect selectedView when loading initial view, falling back to the first tab
- firstUpdated now loads this.selectedView if set, otherwise loads the first view tab
- Prevents always loading the first tab and preserves a previously selected view on initial render
## 2026-02-01 - 3.41.5 - fix(dees-service-lib-loader)
prevent horizontal scrollbar by offsetting xterm WidthCache measurement container
- Injects additional CSS into DeesServiceLibLoader to move xterm.js WidthCache measurement div off-screen horizontally (selector: body > div[style*="top: -50000px"][style*="width: 50000px"])
- Fixes root cause where xterm creates a large-width measurement container (width: 50000px) on document.body that expands scrollWidth and causes a horizontal scrollbar
- Change applied in ts_web/services/DeesServiceLibLoader.ts by concatenating the fix CSS into the injected stylesheet
## 2026-01-29 - 3.41.4 - fix()
no changes
- No files changed in this commit; no code or documentation modified
- No release required
## 2026-01-29 - 3.41.3 - fix(dees-pdf-viewer)
use in-memory PDF data for download and print; add robust print wrapper, cleanup and error handling
- Download and Print now use pdfDocument.getData() to create Blob URLs so in-memory PDFs (pdf.js) can be saved/printed.
- Print flow now opens an HTML wrapper with an iframe to allow onafterprint handling, auto-close, popup-fallback and timed cleanup of Blob URLs.
- Added try/catch logging, URL.revokeObjectURL calls and safety timeouts to avoid resource leaks.
- Removed context menu items that relied on the raw PDF URL (Open in New Tab, Copy PDF URL); Download/Print actions now await the async handlers.
## 2026-01-28 - 3.41.2 - fix(dees-pdf-viewer)
account for devicePixelRatio when setting canvas dimensions and scale 2D context to render crisp PDF pages and thumbnails on high-DPI displays

View File

@@ -1,6 +1,6 @@
{
"name": "@design.estate/dees-catalog",
"version": "3.41.2",
"version": "3.43.3",
"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",
@@ -34,10 +34,10 @@
"@tiptap/extension-underline": "^2.23.0",
"@tiptap/starter-kit": "^2.23.0",
"@tsclass/tsclass": "^9.3.0",
"apexcharts": "^5.3.6",
"apexcharts": "^5.5.0",
"highlight.js": "11.11.1",
"ibantools": "^4.5.1",
"lucide": "^0.563.0",
"lucide": "^0.564.0",
"monaco-editor": "0.55.1",
"pdfjs-dist": "^4.10.38",
"xterm": "^5.3.0",
@@ -47,9 +47,9 @@
"@git.zone/tsbuild": "^4.1.2",
"@git.zone/tsbundle": "^2.8.3",
"@git.zone/tstest": "^3.1.8",
"@git.zone/tswatch": "^3.0.1",
"@git.zone/tswatch": "^3.1.0",
"@push.rocks/projectinfo": "^5.0.2",
"@types/node": "^25.0.10"
"@types/node": "^25.2.3"
},
"files": [
"ts/**/*",

251
pnpm-lock.yaml generated
View File

@@ -63,8 +63,8 @@ importers:
specifier: ^9.3.0
version: 9.3.0
apexcharts:
specifier: ^5.3.6
version: 5.3.6
specifier: ^5.5.0
version: 5.5.0
highlight.js:
specifier: 11.11.1
version: 11.11.1
@@ -72,8 +72,8 @@ importers:
specifier: ^4.5.1
version: 4.5.1
lucide:
specifier: ^0.563.0
version: 0.563.0
specifier: ^0.564.0
version: 0.564.0
monaco-editor:
specifier: 0.55.1
version: 0.55.1
@@ -97,14 +97,14 @@ importers:
specifier: ^3.1.8
version: 3.1.8(@push.rocks/smartserve@2.0.1)(socks@2.8.7)(typescript@5.9.3)
'@git.zone/tswatch':
specifier: ^3.0.1
version: 3.0.1(@tiptap/pm@2.27.2)
specifier: ^3.1.0
version: 3.1.0(@tiptap/pm@2.27.2)
'@push.rocks/projectinfo':
specifier: ^5.0.2
version: 5.0.2
'@types/node':
specifier: ^25.0.10
version: 25.0.10
specifier: ^25.2.3
version: 25.2.3
packages:
@@ -117,6 +117,9 @@ packages:
'@api.global/typedrequest@3.2.5':
resolution: {integrity: sha512-LM/sUTuYnU5xY4gNZrN6ERMiKr+SpDZuSxJkAZz1YazC7ymGfo6uQ8sCnN8eNNQNFqIOkC+BtfYRayfbGwYLLg==}
'@api.global/typedrequest@3.2.6':
resolution: {integrity: sha512-CnvbjYjnGGw3rwL+7bTHSgRHEpDujzhs3cv7l1xgCXMPQe3DcPg74+9ep1Y5cu21T/w0pxNnDCJpbb0SHqHzAw==}
'@api.global/typedserver@3.0.80':
resolution: {integrity: sha512-dcp0oXsjBL+XdFg1wUUP08uJQid5bQ0Yv3V3Y3lnI2QCbat0FU+Tsb0TZRnZ4+P150Vj/ITBqJUgDzFsF34grA==}
@@ -337,17 +340,17 @@ packages:
'@cfworker/json-schema@4.1.1':
resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==}
'@cloudflare/workers-types@4.20260124.0':
resolution: {integrity: sha512-h6TJlew6AtGuEXFc+k5ifalk+tg3fkg0lla6XbMAb2AKKfJGwlFNTwW2xyT/Ha92KY631CIJ+Ace08DPdFohdA==}
'@cloudflare/workers-types@4.20260127.0':
resolution: {integrity: sha512-4M1HLcWViSdT/pAeDGEB5x5P3sqW7UIi34QrBRnxXbqjAY9if8vBU/lWRWnM+UqKzxWGB2LYjEVOzZrp0jZL+w==}
'@cloudflare/workers-types@4.20260214.0':
resolution: {integrity: sha512-qb8rgbAdJR4BAPXolXhFL/wuGtecHLh1veOyZ1mK6QqWuCdI3vK1biKC0i3lzmzdLR/DZvsN3mNtpUE8zpWGEg==}
'@configvault.io/interfaces@1.0.17':
resolution: {integrity: sha512-bEcCUR2VBDJsTin8HQh8Uw/mlYl2v8A3jMIaQ+MTB9Hrqd6CZL2dL7iJdWyFl/3EIX+LDxWFR+Oq7liIq7w+1Q==}
'@design.estate/dees-catalog@3.37.0':
resolution: {integrity: sha512-c6q+yK2FwMsMK72GykUhZnvKUgTzjFO9vdbn6OBxas2/eY/6Wi6BC5i9YONN0UYcW8yqjHIDjN9nP7yE1Ai4PA==}
'@design.estate/dees-catalog@3.42.0':
resolution: {integrity: sha512-pArkafnrhRsHsSxKUMUM2YP5ei/AbcchPEKZY2PyHHAdXcNxyT3pE2Oh1FPcs1pqF2LpEgJRq8KFQbFhvhp8Nw==}
'@design.estate/dees-comms@1.0.30':
resolution: {integrity: sha512-KchMlklJfKAjQiJiR0xmofXtQ27VgZtBIxcMwPE9d+h3jJRv+lPZxzBQVOM0eyM0uS44S5vJMZ11IeV4uDXSHg==}
@@ -566,8 +569,8 @@ packages:
resolution: {integrity: sha512-nmiLGeOkKMkLDyIk5BUBLx5ExskFbKHKlPdrWCARPVFkU4cAAiuIyJWVfLwISoS0TO/zSInLqArPwIc76yvaNw==}
hasBin: true
'@git.zone/tswatch@3.0.1':
resolution: {integrity: sha512-vrAkKM5ff/e1BLNkrIRXnTIkMyjl/uW49c1cYaw2nYGloM6/wT1FSwYjwh6BcDkHIYMnzS30SOy9jSYRptW/iw==}
'@git.zone/tswatch@3.1.0':
resolution: {integrity: sha512-R2ZI+j1OKVgd0zTbtGtJjyt7r2kF0Z4nl8neolHuQL+jpr16i2NHVfVK92uIeeZDnJSqo5vf7Syt0XeQ4rz2HA==}
hasBin: true
'@happy-dom/global-registrator@15.11.7':
@@ -945,6 +948,9 @@ packages:
'@push.rocks/smartlog@3.1.10':
resolution: {integrity: sha512-5pf5JyzOE2WTCUislNIW4EHePo1a7hiXB+jbil38+N5hW71AEwcPFe6oGxbp5w9ALlz66hV2+E+25R0SsxN+fQ==}
'@push.rocks/smartlog@3.1.11':
resolution: {integrity: sha512-zyLH8pQD2UD7l76wJBESEWXU1FSTBLOuRI0/DN139EYyMkwMq1+pdQKptTkJhhVL/OIj56oMg9SpJb4bJB7uKg==}
'@push.rocks/smartmanifest@2.0.2':
resolution: {integrity: sha512-QGc5C9vunjfUbYsPGz5bynV/mVmPHkrQDkWp8ZO8VJtK1GZe+njgbrNyxn2SUHR0IhSAbSXl1j4JvBqYf5eTVg==}
@@ -1044,6 +1050,9 @@ packages:
'@push.rocks/smarttime@4.1.1':
resolution: {integrity: sha512-Ha/3J/G+zfTl4ahpZgF6oUOZnUjpLhrBja0OQ2cloFxF9sKT8I1COaSqIfBGDtoK2Nly4UD4aTJ3JcJNOg/kgA==}
'@push.rocks/smarttime@4.2.3':
resolution: {integrity: sha512-8gMg8RUkrCG4p9NcEUZV7V6KpL24+jAMK02g7qyhfA6giz/JJWD0+8w8xjSR+G7qe16KVQ2y3RbvAL9TxmO36g==}
'@push.rocks/smartunique@3.0.9':
resolution: {integrity: sha512-q6DYQgT7/dqdWi9HusvtWCjdsFzLFXY9LTtaZV6IYNJt6teZOonoygxTdNt9XLn6niBSbLYrHSKvJNTRH/uK+g==}
@@ -1069,6 +1078,9 @@ packages:
'@push.rocks/taskbuffer@3.5.0':
resolution: {integrity: sha512-Y9WwIEIyp6oVFdj06j84tfrZIvjhbMb3DF52rYxlTeYLk3W7RPhSg1bGPCbtkXWeKdBrSe37V90BkOG7Qq8Pqg==}
'@push.rocks/taskbuffer@4.2.1':
resolution: {integrity: sha512-F3aizWLGWdAz7wSJqOzjwVgo1VQJcxTbHUjDN/Pqxw0WMQUwODRGbhgy4zLag7bOyE4tc8Jv7yid7Bjmn5hKdg==}
'@push.rocks/webrequest@3.0.37':
resolution: {integrity: sha512-fLN7kP6GeHFxE4UH4r9C9pjcQb0QkJxHeAMwXvbOqB9hh0MFNKhtGU7GoaTn8SVRGRMPc9UqZVNwo6u5l8Wn0A==}
@@ -1495,31 +1507,6 @@ packages:
'@socket.io/component-emitter@3.1.2':
resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
'@svgdotjs/svg.draggable.js@3.0.6':
resolution: {integrity: sha512-7iJFm9lL3C40HQcqzEfezK2l+dW2CpoVY3b77KQGqc8GXWa6LhhmX5Ckv7alQfUXBuZbjpICZ+Dvq1czlGx7gA==}
peerDependencies:
'@svgdotjs/svg.js': ^3.2.4
'@svgdotjs/svg.filter.js@3.0.9':
resolution: {integrity: sha512-/69XMRCDoam2HgC4ldHIaDgeQf1ViHIsa0Ld4uWgiXtZ+E24DWHe/9Ib6kbNiZ7WRIdlVokUDR1Fg0kjIpkfbw==}
engines: {node: '>= 0.8.0'}
'@svgdotjs/svg.js@3.2.5':
resolution: {integrity: sha512-/VNHWYhNu+BS7ktbYoVGrCmsXDh+chFMaONMwGNdIBcFHrWqk2jY8fNyr3DLdtQUIalvkPfM554ZSFa3dm3nxQ==}
'@svgdotjs/svg.resize.js@2.0.5':
resolution: {integrity: sha512-4heRW4B1QrJeENfi7326lUPYBCevj78FJs8kfeDxn5st0IYPIRXoTtOSYvTzFWgaWWXd3YCDE6ao4fmv91RthA==}
engines: {node: '>= 14.18'}
peerDependencies:
'@svgdotjs/svg.js': ^3.2.4
'@svgdotjs/svg.select.js': ^4.0.1
'@svgdotjs/svg.select.js@4.0.3':
resolution: {integrity: sha512-qkMgso1sd2hXKd1FZ1weO7ANq12sNmQJeGDjs46QwDVsxSRcHmvWKL2NDF7Yimpwf3sl5esOLkPqtV2bQ3v/Jg==}
engines: {node: '>= 14.18'}
peerDependencies:
'@svgdotjs/svg.js': ^3.2.4
'@szmarczak/http-timer@5.0.1':
resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
engines: {node: '>=14.16'}
@@ -1785,11 +1772,11 @@ packages:
'@types/node-forge@1.3.14':
resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==}
'@types/node@22.19.7':
resolution: {integrity: sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==}
'@types/node@22.19.11':
resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==}
'@types/node@25.0.10':
resolution: {integrity: sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==}
'@types/node@25.2.3':
resolution: {integrity: sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==}
'@types/ping@0.4.4':
resolution: {integrity: sha512-ifvo6w2f5eJYlXm+HiVx67iJe8WZp87sfa683nlqED5Vnt9Z93onkokNoWqOG21EaE8fMxyKPobE+mkPEyxsdw==}
@@ -1907,8 +1894,8 @@ packages:
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
engines: {node: '>=12'}
apexcharts@5.3.6:
resolution: {integrity: sha512-sVEPw+J0Gp0IHQabKu8cfdsxlfME0e36Wid7RIaPclGM2OUt+O7O4+6mfAmTUYhy5bDk8cNHzEhPfVtLCIXEJA==}
apexcharts@5.5.0:
resolution: {integrity: sha512-r0GzBUmIAihVDHiPTWrKzd2I+T2Dw+oZTDBRJeBExUuCyqEaCe2pAMEKZnTbJQXyDAhCBzPgkM2SeeKQuW4Ddw==}
argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
@@ -2167,6 +2154,10 @@ packages:
crelt@1.0.6:
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
croner@10.0.1:
resolution: {integrity: sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==}
engines: {node: '>=18.0'}
croner@9.1.0:
resolution: {integrity: sha512-p9nwwR4qyT5W996vBZhdvBCnMhicY5ytZkR4D1Xj0wuTDEiMnjwR57Q3RXYY/s0EpX6Ay3vgIcfaR+ewGHsi+g==}
engines: {node: '>=18.0'}
@@ -2860,12 +2851,12 @@ packages:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'}
lucide@0.562.0:
resolution: {integrity: sha512-k1Fb8ZMnRQovWRlea7Jr0b9UKA29IM7/cu79+mJrhVohvA2YC/Ti3Sk+G+h/SIu3IlrKT4RAbWMHUBBQd1O6XA==}
lucide@0.563.0:
resolution: {integrity: sha512-2zBzDJ5n2Plj3d0ksj6h9TWPOSiKu9gtxJxnBAye11X/8gfWied6IYJn6ADYBp1NPoJmgpyOYP3wMrVx69+2AA==}
lucide@0.564.0:
resolution: {integrity: sha512-FasyXKHWon773WIl3HeCQpd5xS6E0aLjqxiQStlHNKktni+HDncc1sqY+6vRUbCfmDsIaKQz43EEQLAUDLZO0g==}
make-dir@3.1.0:
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
engines: {node: '>=8'}
@@ -4046,6 +4037,18 @@ snapshots:
'@push.rocks/webrequest': 3.0.37
'@push.rocks/webstream': 1.0.10
'@api.global/typedrequest@3.2.6':
dependencies:
'@api.global/typedrequest-interfaces': 3.0.19
'@push.rocks/isounique': 1.0.5
'@push.rocks/lik': 6.2.2
'@push.rocks/smartbuffer': 3.0.5
'@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartguard': 3.1.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/webrequest': 4.0.1
'@push.rocks/webstream': 1.0.10
'@api.global/typedserver@3.0.80(@push.rocks/smartserve@2.0.1)':
dependencies:
'@api.global/typedrequest': 3.2.5
@@ -4096,11 +4099,11 @@ snapshots:
'@api.global/typedserver@8.3.0(@tiptap/pm@2.27.2)':
dependencies:
'@api.global/typedrequest': 3.2.5
'@api.global/typedrequest': 3.2.6
'@api.global/typedrequest-interfaces': 3.0.19
'@api.global/typedsocket': 4.1.0(@push.rocks/smartserve@2.0.1)
'@cloudflare/workers-types': 4.20260124.0
'@design.estate/dees-catalog': 3.37.0(@tiptap/pm@2.27.2)
'@cloudflare/workers-types': 4.20260214.0
'@design.estate/dees-catalog': 3.42.0(@tiptap/pm@2.27.2)
'@design.estate/dees-comms': 1.0.30
'@push.rocks/lik': 6.2.2
'@push.rocks/smartdelay': 3.0.5
@@ -4109,7 +4112,7 @@ snapshots:
'@push.rocks/smartfile': 13.1.2
'@push.rocks/smartfs': 1.3.1
'@push.rocks/smartjson': 5.2.0
'@push.rocks/smartlog': 3.1.10
'@push.rocks/smartlog': 3.1.11
'@push.rocks/smartlog-destination-devtools': 1.0.12
'@push.rocks/smartlog-interfaces': 3.0.2
'@push.rocks/smartmanifest': 2.0.2
@@ -4124,7 +4127,7 @@ snapshots:
'@push.rocks/smartserve': 2.0.1
'@push.rocks/smartsitemap': 2.0.4
'@push.rocks/smartstream': 3.2.5
'@push.rocks/smarttime': 4.1.1
'@push.rocks/smarttime': 4.2.3
'@push.rocks/smartwatch': 6.3.0
'@push.rocks/taskbuffer': 3.5.0
'@push.rocks/webrequest': 4.0.1
@@ -4162,7 +4165,7 @@ snapshots:
'@api.global/typedsocket@4.1.0(@push.rocks/smartserve@2.0.1)':
dependencies:
'@api.global/typedrequest': 3.2.5
'@api.global/typedrequest': 3.2.6
'@api.global/typedrequest-interfaces': 3.0.19
'@push.rocks/isohash': 2.0.1
'@push.rocks/smartdelay': 3.0.5
@@ -4720,15 +4723,15 @@ snapshots:
'@cfworker/json-schema@4.1.1': {}
'@cloudflare/workers-types@4.20260124.0': {}
'@cloudflare/workers-types@4.20260127.0': {}
'@cloudflare/workers-types@4.20260214.0': {}
'@configvault.io/interfaces@1.0.17':
dependencies:
'@api.global/typedrequest-interfaces': 3.0.19
'@design.estate/dees-catalog@3.37.0(@tiptap/pm@2.27.2)':
'@design.estate/dees-catalog@3.42.0(@tiptap/pm@2.27.2)':
dependencies:
'@design.estate/dees-domtools': 2.3.8
'@design.estate/dees-element': 2.1.6
@@ -4748,10 +4751,10 @@ snapshots:
'@tiptap/extension-underline': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))
'@tiptap/starter-kit': 2.27.2
'@tsclass/tsclass': 9.3.0
apexcharts: 5.3.6
apexcharts: 5.5.0
highlight.js: 11.11.1
ibantools: 4.5.1
lucide: 0.562.0
lucide: 0.563.0
monaco-editor: 0.55.1
pdfjs-dist: 4.10.38
xterm: 5.3.0
@@ -5056,7 +5059,7 @@ snapshots:
- utf-8-validate
- vue
'@git.zone/tswatch@3.0.1(@tiptap/pm@2.27.2)':
'@git.zone/tswatch@3.1.0(@tiptap/pm@2.27.2)':
dependencies:
'@api.global/typedserver': 8.3.0(@tiptap/pm@2.27.2)
'@git.zone/tsbundle': 2.8.3
@@ -5068,11 +5071,11 @@ snapshots:
'@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartfs': 1.3.1
'@push.rocks/smartinteract': 2.0.16
'@push.rocks/smartlog': 3.1.10
'@push.rocks/smartlog': 3.1.11
'@push.rocks/smartlog-destination-local': 9.0.2
'@push.rocks/smartshell': 3.3.0
'@push.rocks/smartwatch': 6.3.0
'@push.rocks/taskbuffer': 3.5.0
'@push.rocks/taskbuffer': 4.2.1
transitivePeerDependencies:
- '@nuxt/kit'
- '@swc/helpers'
@@ -5105,7 +5108,7 @@ snapshots:
'@inquirer/figures': 1.0.15
'@inquirer/type': 2.0.0
'@types/mute-stream': 0.0.4
'@types/node': 22.19.7
'@types/node': 22.19.11
'@types/wrap-ansi': 3.0.0
ansi-escapes: 4.3.2
cli-width: 4.1.0
@@ -5833,6 +5836,19 @@ snapshots:
'@push.rocks/webrequest': 3.0.37
'@tsclass/tsclass': 9.3.0
'@push.rocks/smartlog@3.1.11':
dependencies:
'@api.global/typedrequest-interfaces': 3.0.19
'@push.rocks/consolecolor': 2.0.3
'@push.rocks/isounique': 1.0.5
'@push.rocks/smartclickhouse': 2.0.17
'@push.rocks/smartfile': 11.2.7
'@push.rocks/smarthash': 3.2.6
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smarttime': 4.2.3
'@push.rocks/webrequest': 4.0.1
'@tsclass/tsclass': 9.3.0
'@push.rocks/smartmanifest@2.0.2': {}
'@push.rocks/smartmarkdown@3.0.3':
@@ -6038,11 +6054,11 @@ snapshots:
'@push.rocks/smartserve@2.0.1':
dependencies:
'@api.global/typedrequest': 3.2.5
'@api.global/typedrequest': 3.2.6
'@cfworker/json-schema': 4.1.1
'@push.rocks/lik': 6.2.2
'@push.rocks/smartenv': 6.0.0
'@push.rocks/smartlog': 3.1.10
'@push.rocks/smartlog': 3.1.11
'@push.rocks/smartpath': 6.0.0
ws: 8.19.0
transitivePeerDependencies:
@@ -6141,6 +6157,17 @@ snapshots:
is-nan: 1.3.2
pretty-ms: 9.3.0
'@push.rocks/smarttime@4.2.3':
dependencies:
'@push.rocks/lik': 6.2.2
'@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartpromise': 4.2.3
croner: 10.0.1
date-fns: 4.1.0
dayjs: 1.11.19
is-nan: 1.3.2
pretty-ms: 9.3.0
'@push.rocks/smartunique@3.0.9':
dependencies:
'@types/uuid': 9.0.8
@@ -6192,6 +6219,22 @@ snapshots:
- supports-color
- vue
'@push.rocks/taskbuffer@4.2.1':
dependencies:
'@design.estate/dees-element': 2.1.6
'@push.rocks/lik': 6.2.2
'@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartlog': 3.1.11
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrx': 3.0.10
'@push.rocks/smarttime': 4.2.3
'@push.rocks/smartunique': 3.0.9
transitivePeerDependencies:
- '@nuxt/kit'
- react
- supports-color
- vue
'@push.rocks/webrequest@3.0.37':
dependencies:
'@push.rocks/smartdelay': 3.0.5
@@ -6716,25 +6759,6 @@ snapshots:
'@socket.io/component-emitter@3.1.2': {}
'@svgdotjs/svg.draggable.js@3.0.6(@svgdotjs/svg.js@3.2.5)':
dependencies:
'@svgdotjs/svg.js': 3.2.5
'@svgdotjs/svg.filter.js@3.0.9':
dependencies:
'@svgdotjs/svg.js': 3.2.5
'@svgdotjs/svg.js@3.2.5': {}
'@svgdotjs/svg.resize.js@2.0.5(@svgdotjs/svg.js@3.2.5)(@svgdotjs/svg.select.js@4.0.3(@svgdotjs/svg.js@3.2.5))':
dependencies:
'@svgdotjs/svg.js': 3.2.5
'@svgdotjs/svg.select.js': 4.0.3(@svgdotjs/svg.js@3.2.5)
'@svgdotjs/svg.select.js@4.0.3(@svgdotjs/svg.js@3.2.5)':
dependencies:
'@svgdotjs/svg.js': 3.2.5
'@szmarczak/http-timer@5.0.1':
dependencies:
defer-to-connect: 2.0.1
@@ -6919,27 +6943,27 @@ snapshots:
'@types/bn.js@5.2.0':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/body-parser@1.19.6':
dependencies:
'@types/connect': 3.4.38
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/buffer-json@2.0.3': {}
'@types/clean-css@4.2.11':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
source-map: 0.6.1
'@types/connect@3.4.38':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/cors@2.8.19':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/debug@4.1.12':
dependencies:
@@ -6947,7 +6971,7 @@ snapshots:
'@types/dns-packet@5.6.5':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/elliptic@6.4.18':
dependencies:
@@ -6955,7 +6979,7 @@ snapshots:
'@types/express-serve-static-core@5.1.1':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/qs': 6.14.0
'@types/range-parser': 1.2.7
'@types/send': 1.2.1
@@ -6968,17 +6992,17 @@ snapshots:
'@types/from2@2.3.6':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/fs-extra@11.0.4':
dependencies:
'@types/jsonfile': 6.1.4
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/glob@8.1.0':
dependencies:
'@types/minimatch': 5.1.2
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/hast@3.0.4':
dependencies:
@@ -7000,7 +7024,7 @@ snapshots:
'@types/jsonfile@6.1.4':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/linkify-it@5.0.0': {}
@@ -7023,17 +7047,17 @@ snapshots:
'@types/mute-stream@0.0.4':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/node-forge@1.3.14':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/node@22.19.7':
'@types/node@22.19.11':
dependencies:
undici-types: 6.21.0
'@types/node@25.0.10':
'@types/node@25.2.3':
dependencies:
undici-types: 7.16.0
@@ -7051,22 +7075,22 @@ snapshots:
'@types/send@1.2.1':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/serve-static@2.2.0':
dependencies:
'@types/http-errors': 2.0.5
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/symbol-tree@3.2.5': {}
'@types/tar-stream@3.1.4':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/through2@2.0.41':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/trusted-types@2.0.7': {}
@@ -7092,11 +7116,11 @@ snapshots:
'@types/ws@8.18.1':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
'@types/yauzl@2.10.3':
dependencies:
'@types/node': 25.0.10
'@types/node': 25.2.3
optional: true
'@ungap/structured-clone@1.3.0': {}
@@ -7145,13 +7169,8 @@ snapshots:
ansi-styles@6.2.3: {}
apexcharts@5.3.6:
apexcharts@5.5.0:
dependencies:
'@svgdotjs/svg.draggable.js': 3.0.6(@svgdotjs/svg.js@3.2.5)
'@svgdotjs/svg.filter.js': 3.0.9
'@svgdotjs/svg.js': 3.2.5
'@svgdotjs/svg.resize.js': 2.0.5(@svgdotjs/svg.js@3.2.5)(@svgdotjs/svg.select.js@4.0.3(@svgdotjs/svg.js@3.2.5))
'@svgdotjs/svg.select.js': 4.0.3(@svgdotjs/svg.js@3.2.5)
'@yr/monotone-cubic-spline': 1.0.3
argparse@1.0.10:
@@ -7403,6 +7422,8 @@ snapshots:
crelt@1.0.6: {}
croner@10.0.1: {}
croner@9.1.0: {}
cross-spawn@7.0.6:
@@ -7528,7 +7549,7 @@ snapshots:
engine.io@6.6.4:
dependencies:
'@types/cors': 2.8.19
'@types/node': 25.0.10
'@types/node': 25.2.3
accepts: 1.3.8
base64id: 2.0.0
cookie: 0.7.2
@@ -8194,10 +8215,10 @@ snapshots:
lru-cache@7.18.3: {}
lucide@0.562.0: {}
lucide@0.563.0: {}
lucide@0.564.0: {}
make-dir@3.1.0:
dependencies:
semver: 6.3.1

View File

@@ -0,0 +1,79 @@
import { expect, tap, webhelpers } from '@git.zone/tstest/tapbundle';
import * as deesCatalog from '../ts_web/index.js';
tap.test('PDF viewer should render text layer', async () => {
const viewer = await webhelpers.fixture(
webhelpers.html`
<dees-pdf-viewer
pdfUrl="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf"
initialZoom="page-fit"
style="height: 600px; width: 100%;"
></dees-pdf-viewer>
`
) as deesCatalog.DeesPdfViewer;
// Wait for PDF to load and render
await new Promise(resolve => setTimeout(resolve, 5000));
await viewer.updateComplete;
expect(viewer.totalPages).toBeGreaterThan(0);
const textLayer = viewer.shadowRoot?.querySelector('.text-layer[data-page="1"]');
expect(textLayer).toBeTruthy();
const textSpans = textLayer?.querySelectorAll('span');
expect(textSpans?.length).toBeGreaterThan(0);
console.log(`Text layer has ${textSpans?.length} spans`);
});
tap.test('Text should be selectable', async () => {
const viewer = await webhelpers.fixture(
webhelpers.html`
<dees-pdf-viewer
pdfUrl="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf"
initialZoom="page-fit"
style="height: 600px; width: 100%;"
></dees-pdf-viewer>
`
) as deesCatalog.DeesPdfViewer;
// Wait for PDF to load and render
await new Promise(resolve => setTimeout(resolve, 5000));
const textLayer = viewer.shadowRoot?.querySelector('.text-layer[data-page="1"]');
const firstSpan = textLayer?.querySelector('span') as HTMLElement;
if (firstSpan?.textContent) {
const range = document.createRange();
const textNode = firstSpan.firstChild;
if (textNode) {
range.setStart(textNode, 0);
range.setEnd(textNode, Math.min(5, textNode.textContent?.length || 0));
const selection = window.getSelection();
selection?.removeAllRanges();
selection?.addRange(range);
expect(selection?.toString().length).toBeGreaterThan(0);
console.log('Selected text:', selection?.toString());
}
}
});
tap.test('endOfContent element exists for selection boundary', async () => {
const viewer = await webhelpers.fixture(
webhelpers.html`
<dees-pdf-viewer
pdfUrl="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf"
initialZoom="page-fit"
style="height: 600px; width: 100%;"
></dees-pdf-viewer>
`
) as deesCatalog.DeesPdfViewer;
// Wait for PDF to load and render
await new Promise(resolve => setTimeout(resolve, 5000));
const endOfContent = viewer.shadowRoot?.querySelector('.text-layer[data-page="1"] .endOfContent');
expect(endOfContent).toBeTruthy();
});
export default tap.start();

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@design.estate/dees-catalog',
version: '3.41.2',
version: '3.43.3',
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
}

View File

@@ -12,6 +12,16 @@ import { chartAreaStyles } from './styles.js';
import { renderChartArea } from './template.js';
import type ApexCharts from 'apexcharts';
type ApexAxisChartSeries = {
name?: string;
type?: string;
color?: string;
group?: string;
hidden?: boolean;
zIndex?: number;
data: (number | null)[] | { x: any; y: any; [key: string]: any }[] | [number, number | null][] | number[][];
}[];
import { DeesServiceLibLoader } from '../../../services/index.js';
declare global {

View File

@@ -385,11 +385,23 @@ export class DeesChartLog extends DeesElement {
this.domtoolsInstance = await this.domtoolsPromise;
await this.initializeTerminal();
// Process any initial log entries
if (this.logEntries.length > 0) {
// initializeTerminal() already replayed logBuffer (from addLog/updateLog).
// Now handle logEntries set via property binding before terminal was ready.
if (this.logEntries.length > 0 && this.logBuffer.length === 0) {
this.logBuffer = [...this.logEntries];
for (const entry of this.logEntries) {
this.updateMetrics(entry.level);
this.writeLogEntry(entry);
}
} else if (this.logEntries.length > 0 && this.logBuffer.length > 0) {
const bufferSet = new Set(this.logBuffer.map(e => `${e.timestamp}|${e.message}`));
for (const entry of this.logEntries) {
if (!bufferSet.has(`${entry.timestamp}|${entry.message}`)) {
this.logBuffer.push(entry);
this.updateMetrics(entry.level);
this.writeLogEntry(entry);
}
}
}
}
@@ -445,6 +457,58 @@ export class DeesChartLog extends DeesElement {
this.rateInterval = setInterval(() => this.calculateRate(), 1000);
this.terminalReady = true;
// Replay any entries that arrived via updateLog()/addLog() before terminal was ready
for (const entry of this.logBuffer) {
this.writeLogEntry(entry);
}
}
public updated(changedProperties: Map<string, any>) {
super.updated(changedProperties);
if (changedProperties.has('logEntries') && this.terminalReady && this.logEntries.length > 0) {
const oldEntries: ILogEntry[] = changedProperties.get('logEntries') || [];
const newEntries = this.logEntries;
// Same content? Skip entirely.
if (
oldEntries.length === newEntries.length &&
oldEntries.length > 0 &&
oldEntries[oldEntries.length - 1].timestamp === newEntries[newEntries.length - 1].timestamp &&
oldEntries[oldEntries.length - 1].message === newEntries[newEntries.length - 1].message
) {
return;
}
// Append-only? Write only the new tail entries incrementally.
if (
newEntries.length > oldEntries.length &&
oldEntries.length > 0 &&
oldEntries[oldEntries.length - 1].timestamp === newEntries[oldEntries.length - 1].timestamp &&
oldEntries[oldEntries.length - 1].message === newEntries[oldEntries.length - 1].message
) {
const tailEntries = newEntries.slice(oldEntries.length);
for (const entry of tailEntries) {
this.logBuffer.push(entry);
this.updateMetrics(entry.level);
// Enforce maxEntries
if (this.logBuffer.length > this.maxEntries) {
this.logBuffer.shift();
}
// Respect filter mode
if (!this.filterMode || !this.searchQuery || this.entryMatchesFilter(entry)) {
this.writeLogEntry(entry);
}
}
return;
}
// Different content — full re-render
this.logBuffer = [...newEntries];
this.reRenderFilteredLogs();
}
}
private getTerminalTheme() {

View File

@@ -232,7 +232,7 @@ export class DeesTable<T> extends DeesElement {
${directives.resolveExec(async () => {
const resultArray: TemplateResult[] = [];
for (const action of this.dataActions) {
if (!action.type.includes('header')) continue;
if (!action.type?.includes('header')) continue;
resultArray.push(
html`<div
class="headerAction"
@@ -450,7 +450,7 @@ export class DeesTable<T> extends DeesElement {
<td
@dblclick=${(e: Event) => {
const dblAction = this.dataActions.find((actionArg) =>
actionArg.type.includes('doubleClick')
actionArg.type?.includes('doubleClick')
);
if (this.editableFields.includes(editKey)) {
this.handleCellEditing(e, itemArg, editKey);
@@ -506,7 +506,7 @@ export class DeesTable<T> extends DeesElement {
${directives.resolveExec(async () => {
const resultArray: TemplateResult[] = [];
for (const action of this.dataActions) {
if (!action.type.includes('footer')) continue;
if (!action.type?.includes('footer')) continue;
resultArray.push(
html`<div
class="footerAction"
@@ -540,11 +540,11 @@ export class DeesTable<T> extends DeesElement {
super.updated(changedProperties);
this.determineColumnWidths();
if (this.searchable) {
const existing = this.dataActions.find((actionArg) => actionArg.type.includes('header') && actionArg.name === 'Search');
const existing = this.dataActions.find((actionArg) => actionArg.type?.includes('header') && actionArg.name === 'Search');
if (!existing) {
this.dataActions.unshift({
name: 'Search',
iconName: 'magnifyingGlass',
iconName: 'lucide:Search',
type: ['header'],
actionFunc: async () => {
console.log('open search');
@@ -623,7 +623,7 @@ export class DeesTable<T> extends DeesElement {
const width = window.getComputedStyle(cell).width;
if (cell.textContent.includes('Actions')) {
const neededWidth =
this.dataActions.filter((actionArg) => actionArg.type.includes('inRow')).length * 36;
this.dataActions.filter((actionArg) => actionArg.type?.includes('inRow')).length * 36;
cell.style.width = `${Math.max(neededWidth, 68)}px`;
} else {
cell.style.width = width;
@@ -795,7 +795,7 @@ export class DeesTable<T> extends DeesElement {
getActionsForType(typeArg: ITableAction['type'][0]) {
const actions: ITableAction[] = [];
for (const action of this.dataActions) {
if (!action.type.includes(typeArg)) continue;
if (!action.type?.includes(typeArg)) continue;
actions.push(action);
}
return actions;

View File

@@ -1,3 +1,85 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html`<dees-form-submit>Submit Form</dees-form-submit>`;
export const demoFunc = () => html`
<style>
.demo-container {
display: flex;
flex-direction: column;
gap: 24px;
padding: 24px;
}
.demo-section {
display: flex;
flex-direction: column;
gap: 12px;
}
.demo-section h3 {
margin: 0 0 8px 0;
font-size: 14px;
font-weight: 500;
color: #888;
}
.demo-row {
display: flex;
gap: 12px;
flex-wrap: wrap;
align-items: center;
}
</style>
<div class="demo-container">
<div class="demo-section">
<h3>Basic Usage</h3>
<div class="demo-row">
<dees-form-submit>Submit Form</dees-form-submit>
<dees-form-submit text="With Text Property"></dees-form-submit>
</div>
</div>
<div class="demo-section">
<h3>With Icons (inherited from DeesButton)</h3>
<div class="demo-row">
<dees-form-submit icon="lucide:send">Submit</dees-form-submit>
<dees-form-submit icon="lucide:save" iconPosition="left">Save Form</dees-form-submit>
<dees-form-submit icon="lucide:arrow-right" iconPosition="right">Continue</dees-form-submit>
</div>
</div>
<div class="demo-section">
<h3>Button Types</h3>
<div class="demo-row">
<dees-form-submit type="highlighted" icon="lucide:send">Highlighted</dees-form-submit>
<dees-form-submit type="normal" icon="lucide:send">Normal</dees-form-submit>
<dees-form-submit type="discreet" icon="lucide:send">Discreet</dees-form-submit>
</div>
</div>
<div class="demo-section">
<h3>Sizes</h3>
<div class="demo-row">
<dees-form-submit size="small" icon="lucide:send">Small</dees-form-submit>
<dees-form-submit size="normal" icon="lucide:send">Normal</dees-form-submit>
<dees-form-submit size="large" icon="lucide:send">Large</dees-form-submit>
</div>
</div>
<div class="demo-section">
<h3>States</h3>
<div class="demo-row">
<dees-form-submit status="normal" icon="lucide:send">Normal</dees-form-submit>
<dees-form-submit status="pending" icon="lucide:send">Pending</dees-form-submit>
<dees-form-submit status="success" icon="lucide:check">Success</dees-form-submit>
<dees-form-submit status="error" icon="lucide:x">Error</dees-form-submit>
<dees-form-submit disabled icon="lucide:send">Disabled</dees-form-submit>
</div>
</div>
<div class="demo-section">
<h3>In a Form Context</h3>
<dees-form>
<dees-input-text label="Name" key="name"></dees-input-text>
<dees-input-text label="Email" key="email"></dees-input-text>
<dees-form-submit icon="lucide:send" type="highlighted">Submit Form</dees-form-submit>
</dees-form>
</div>
</div>
`;

View File

@@ -6,6 +6,7 @@ import {
css,
cssManager,
property,
type TemplateResult,
} from '@design.estate/dees-element';
import type { DeesForm } from '../dees-form/dees-form.js';
import { themeDefaultStyles } from '../../00theme.js';
@@ -21,38 +22,61 @@ export class DeesFormSubmit extends DeesElement {
public static demo = demoFunc;
public static demoGroups = ['Form', 'Button'];
@property({
type: Boolean,
reflect: true,
})
// =============================================
// Properties forwarded to internal dees-button
// =============================================
@property({ type: Boolean, reflect: true })
accessor disabled = false;
@property({
type: String,
})
@property({ type: String })
accessor text: string;
@property({
type: String,
})
@property({ type: String })
accessor status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
@property({ type: String, reflect: true })
accessor type: 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost' | 'link' | 'normal' | 'highlighted' | 'discreet' | 'big' = 'default';
@property({ type: String, reflect: true })
accessor size: 'sm' | 'default' | 'lg' | 'icon' | 'small' | 'normal' | 'large' = 'default';
@property({ type: String })
accessor icon: string;
@property({ type: String })
accessor iconPosition: 'left' | 'right' = 'left';
constructor() {
super();
}
public static styles = [themeDefaultStyles, cssManager.defaultStyles, css`
/* TODO: Migrate hardcoded values to --dees-* CSS variables */
`];
public render() {
public static styles = [
themeDefaultStyles,
cssManager.defaultStyles,
css`
:host {
display: inline-block;
}
dees-button {
width: 100%;
}
`,
];
public render(): TemplateResult {
return html`
<dees-button
status="${this.status}"
@click="${this.submit}"
?disabled="${this.disabled}"
.status=${this.status}
.type=${this.type}
.size=${this.size}
.icon=${this.icon}
.iconPosition=${this.iconPosition}
.text=${this.text}
?disabled=${this.disabled}
@clicked=${this.submit}
>
${this.text || html`<slot></slot>`}
<slot></slot>
</dees-button>
`;
}

View File

@@ -1,6 +1,7 @@
import {
customElement,
html,
css,
type TemplateResult,
DeesElement,
type CSSResult,
@@ -81,13 +82,25 @@ export class DeesForm extends DeesElement {
@property({ type: Boolean, reflect: true, attribute: 'horizontal-layout' })
accessor horizontalLayout: boolean = false;
public static styles = [
css`
:host {
display: flex;
flex-direction: column;
gap: 16px;
}
:host([horizontal-layout]) {
flex-direction: row;
flex-wrap: wrap;
align-items: flex-start;
gap: 16px;
}
`,
];
public render(): TemplateResult {
return html`
<style>
:host {
display: contents;
}
</style>
<slot></slot>
`;
}

View File

@@ -54,37 +54,20 @@ export abstract class DeesInputBase<T = any> extends DeesElement {
/* CSS Variables for consistent spacing */
:host {
--dees-input-spacing-unit: 8px;
--dees-input-vertical-gap: calc(var(--dees-input-spacing-unit) * 2); /* 16px */
--dees-input-horizontal-gap: calc(var(--dees-input-spacing-unit) * 2); /* 16px */
--dees-input-label-gap: var(--dees-input-spacing-unit); /* 8px */
}
/* Default vertical stacking mode (for forms) */
/* Default block display with no margins - spacing is container-driven */
:host {
display: block;
margin: 0;
margin-bottom: var(--dees-input-vertical-gap);
}
/* Last child in container should have no bottom margin */
:host(:last-child) {
margin-bottom: 0;
}
/* Horizontal layout mode - activated by attribute */
:host([layout-mode="horizontal"]) {
display: inline-block;
margin: 0;
margin-right: var(--dees-input-horizontal-gap);
margin-bottom: 0;
}
:host([layout-mode="horizontal"]:last-child) {
margin-right: 0;
}
/* Auto mode - inherit from parent dees-form if present */
/* Label position variations */
:host([label-position="left"]) .input-wrapper {
display: grid;

View File

@@ -31,6 +31,12 @@ export const demoFunc = () => html`
flex-wrap: wrap;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.spacer {
height: 200px;
display: flex;
@@ -63,30 +69,32 @@ export const demoFunc = () => html`
}
}}>
<dees-panel .title=${'1. Basic Dropdowns'} .subtitle=${'Standard dropdown with search functionality and various options'}>
<dees-input-dropdown
.label=${'Select Country'}
.options=${[
{ option: 'United States', key: 'us' },
{ option: 'Canada', key: 'ca' },
{ option: 'Germany', key: 'de' },
{ option: 'France', key: 'fr' },
{ option: 'United Kingdom', key: 'uk' },
{ option: 'Australia', key: 'au' },
{ option: 'Japan', key: 'jp' },
{ option: 'Brazil', key: 'br' }
]}
.selectedOption=${{ option: 'United States', key: 'us' }}
></dees-input-dropdown>
<dees-input-dropdown
.label=${'Select Role'}
.options=${[
{ option: 'Administrator', key: 'admin' },
{ option: 'Editor', key: 'editor' },
{ option: 'Viewer', key: 'viewer' },
{ option: 'Guest', key: 'guest' }
]}
></dees-input-dropdown>
<div class="input-group">
<dees-input-dropdown
.label=${'Select Country'}
.options=${[
{ option: 'United States', key: 'us' },
{ option: 'Canada', key: 'ca' },
{ option: 'Germany', key: 'de' },
{ option: 'France', key: 'fr' },
{ option: 'United Kingdom', key: 'uk' },
{ option: 'Australia', key: 'au' },
{ option: 'Japan', key: 'jp' },
{ option: 'Brazil', key: 'br' }
]}
.selectedOption=${{ option: 'United States', key: 'us' }}
></dees-input-dropdown>
<dees-input-dropdown
.label=${'Select Role'}
.options=${[
{ option: 'Administrator', key: 'admin' },
{ option: 'Editor', key: 'editor' },
{ option: 'Viewer', key: 'viewer' },
{ option: 'Guest', key: 'guest' }
]}
></dees-input-dropdown>
</div>
</dees-panel>
</dees-demowrapper>
@@ -176,24 +184,26 @@ export const demoFunc = () => html`
}
}}>
<dees-panel .title=${'4. States'} .subtitle=${'Different states and configurations'}>
<dees-input-dropdown
.label=${'Required Field'}
.required=${true}
.options=${[
{ option: 'Option A', key: 'a' },
{ option: 'Option B', key: 'b' },
{ option: 'Option C', key: 'c' }
]}
></dees-input-dropdown>
<dees-input-dropdown
.label=${'Disabled Dropdown'}
.disabled=${true}
.options=${[
{ option: 'Cannot Select', key: 'disabled' }
]}
.selectedOption=${{ option: 'Cannot Select', key: 'disabled' }}
></dees-input-dropdown>
<div class="input-group">
<dees-input-dropdown
.label=${'Required Field'}
.required=${true}
.options=${[
{ option: 'Option A', key: 'a' },
{ option: 'Option B', key: 'b' },
{ option: 'Option C', key: 'c' }
]}
></dees-input-dropdown>
<dees-input-dropdown
.label=${'Disabled Dropdown'}
.disabled=${true}
.options=${[
{ option: 'Cannot Select', key: 'disabled' }
]}
.selectedOption=${{ option: 'Cannot Select', key: 'disabled' }}
></dees-input-dropdown>
</div>
</dees-panel>
</dees-demowrapper>

View File

@@ -13,6 +13,12 @@ export const demoFunc = () => html`
margin: 0 auto;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.payment-group {
display: flex;
align-items: center;
@@ -24,16 +30,18 @@ export const demoFunc = () => html`
<div class="demo-container">
<dees-panel .title=${'Basic IBAN Input'} .subtitle=${'International Bank Account Number with automatic formatting'}>
<dees-input-iban
.label=${'Bank Account IBAN'}
.description=${'Enter your International Bank Account Number'}
></dees-input-iban>
<dees-input-iban
.label=${'Verified IBAN'}
.description=${'This IBAN has been verified'}
.value=${'DE89370400440532013000'}
></dees-input-iban>
<div class="input-group">
<dees-input-iban
.label=${'Bank Account IBAN'}
.description=${'Enter your International Bank Account Number'}
></dees-input-iban>
<dees-input-iban
.label=${'Verified IBAN'}
.description=${'This IBAN has been verified'}
.value=${'DE89370400440532013000'}
></dees-input-iban>
</div>
</dees-panel>
<dees-panel .title=${'Payment Information'} .subtitle=${'IBAN input with horizontal layout for payment forms'}>
@@ -53,18 +61,20 @@ export const demoFunc = () => html`
</dees-panel>
<dees-panel .title=${'Validation & States'} .subtitle=${'Required fields and disabled states'}>
<dees-input-iban
.label=${'Payment Account'}
.description=${'Required for processing payments'}
.required=${true}
></dees-input-iban>
<dees-input-iban
.label=${'Locked IBAN'}
.description=${'This IBAN cannot be changed'}
.value=${'FR1420041010050500013M02606'}
.disabled=${true}
></dees-input-iban>
<div class="input-group">
<dees-input-iban
.label=${'Payment Account'}
.description=${'Required for processing payments'}
.required=${true}
></dees-input-iban>
<dees-input-iban
.label=${'Locked IBAN'}
.description=${'This IBAN cannot be changed'}
.value=${'FR1420041010050500013M02606'}
.disabled=${true}
></dees-input-iban>
</div>
</dees-panel>
<dees-panel .title=${'Bank Transfer Form'} .subtitle=${'Complete form example with IBAN validation'}>

View File

@@ -13,6 +13,12 @@ export const demoFunc = () => html`
margin: 0 auto;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.horizontal-group {
display: flex;
align-items: center;
@@ -24,18 +30,20 @@ export const demoFunc = () => html`
<div class="demo-container">
<dees-panel .title=${'Basic Phone Input'} .subtitle=${'Automatic formatting for phone numbers'}>
<dees-input-phone
.label=${'Phone Number'}
.description=${'Enter your phone number with country code'}
.value=${'5551234567'}
></dees-input-phone>
<dees-input-phone
.label=${'Contact Phone'}
.description=${'Required for account verification'}
.required=${true}
.placeholder=${'+1 (555) 000-0000'}
></dees-input-phone>
<div class="input-group">
<dees-input-phone
.label=${'Phone Number'}
.description=${'Enter your phone number with country code'}
.value=${'5551234567'}
></dees-input-phone>
<dees-input-phone
.label=${'Contact Phone'}
.description=${'Required for account verification'}
.required=${true}
.placeholder=${'+1 (555) 000-0000'}
></dees-input-phone>
</div>
</dees-panel>
<dees-panel .title=${'Horizontal Layout'} .subtitle=${'Phone inputs arranged horizontally'}>
@@ -55,17 +63,19 @@ export const demoFunc = () => html`
</dees-panel>
<dees-panel .title=${'International Numbers'} .subtitle=${'Supports formatting for numbers with country codes'}>
<dees-input-phone
.label=${'International Contact'}
.description=${'Automatically formats international numbers'}
.value=${'441234567890'}
></dees-input-phone>
<dees-input-phone
.label=${'Emergency Contact'}
.value=${'911'}
.disabled=${true}
></dees-input-phone>
<div class="input-group">
<dees-input-phone
.label=${'International Contact'}
.description=${'Automatically formats international numbers'}
.value=${'441234567890'}
></dees-input-phone>
<dees-input-phone
.label=${'Emergency Contact'}
.value=${'911'}
.disabled=${true}
></dees-input-phone>
</div>
</dees-panel>
<dees-panel .title=${'Form Integration'} .subtitle=${'Phone input as part of a contact form'}>

View File

@@ -14,6 +14,12 @@ export const demoFunc = () => html`
margin: 0 auto;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.shopping-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
@@ -60,17 +66,19 @@ export const demoFunc = () => html`
<div class="demo-container">
<dees-panel .title=${'Basic Quantity Selector'} .subtitle=${'Simple quantity input with increment/decrement buttons'}>
<dees-input-quantityselector
.label=${'Quantity'}
.description=${'Select the desired quantity'}
.value=${1}
></dees-input-quantityselector>
<dees-input-quantityselector
.label=${'Items in Cart'}
.description=${'Adjust the quantity of items'}
.value=${3}
></dees-input-quantityselector>
<div class="input-group">
<dees-input-quantityselector
.label=${'Quantity'}
.description=${'Select the desired quantity'}
.value=${1}
></dees-input-quantityselector>
<dees-input-quantityselector
.label=${'Items in Cart'}
.description=${'Adjust the quantity of items'}
.value=${3}
></dees-input-quantityselector>
</div>
</dees-panel>
<dees-panel .title=${'Shopping Cart'} .subtitle=${'Modern e-commerce product cards with interactive quantity selectors'} .runAfterRender=${async (elementArg: HTMLElement) => {
@@ -169,19 +177,21 @@ export const demoFunc = () => html`
</dees-panel>
<dees-panel .title=${'Required & Disabled States'} .subtitle=${'Different states for validation and restrictions'}>
<dees-input-quantityselector
.label=${'Number of Licenses'}
.description=${'Select how many licenses you need'}
.required=${true}
.value=${1}
></dees-input-quantityselector>
<dees-input-quantityselector
.label=${'Fixed Quantity'}
.description=${'This quantity cannot be changed'}
.disabled=${true}
.value=${5}
></dees-input-quantityselector>
<div class="input-group">
<dees-input-quantityselector
.label=${'Number of Licenses'}
.description=${'Select how many licenses you need'}
.required=${true}
.value=${1}
></dees-input-quantityselector>
<dees-input-quantityselector
.label=${'Fixed Quantity'}
.description=${'This quantity cannot be changed'}
.disabled=${true}
.value=${5}
></dees-input-quantityselector>
</div>
</dees-panel>
<dees-panel .title=${'Order Form'} .subtitle=${'Complete order form with quantity selection'}>

View File

@@ -23,6 +23,12 @@ export const demoFunc = () => html`
margin-bottom: 0;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.demo-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
@@ -60,20 +66,22 @@ export const demoFunc = () => html`
</dees-panel>
<dees-panel .title=${'2. Horizontal Layout'} .subtitle=${'Radio groups with horizontal arrangement'}>
<dees-input-radiogroup
.label=${'Do you agree with the terms?'}
.options=${['Yes', 'No', 'Maybe']}
.direction=${'horizontal'}
.selectedOption=${'Yes'}
></dees-input-radiogroup>
<dees-input-radiogroup
.label=${'Experience Level'}
.options=${['Beginner', 'Intermediate', 'Expert']}
.direction=${'horizontal'}
.selectedOption=${'Intermediate'}
.description=${'Select your experience level with web development'}
></dees-input-radiogroup>
<div class="input-group">
<dees-input-radiogroup
.label=${'Do you agree with the terms?'}
.options=${['Yes', 'No', 'Maybe']}
.direction=${'horizontal'}
.selectedOption=${'Yes'}
></dees-input-radiogroup>
<dees-input-radiogroup
.label=${'Experience Level'}
.options=${['Beginner', 'Intermediate', 'Expert']}
.direction=${'horizontal'}
.selectedOption=${'Intermediate'}
.description=${'Select your experience level with web development'}
></dees-input-radiogroup>
</div>
</dees-panel>
<dees-panel .title=${'3. Advanced Options'} .subtitle=${'Using object format with keys and payloads'}>
@@ -132,30 +140,32 @@ export const demoFunc = () => html`
</dees-panel>
<dees-panel .title=${'6. Settings Example'} .subtitle=${'Common patterns in application settings'}>
<dees-input-radiogroup
.label=${'Theme Preference'}
.options=${[
{ option: 'Light Theme', key: 'light', payload: 'light' },
{ option: 'Dark Theme', key: 'dark', payload: 'dark' },
{ option: 'System Default', key: 'system', payload: 'auto' }
]}
.selectedOption=${'dark'}
.description=${'Choose how the application should appear'}
></dees-input-radiogroup>
<dees-input-radiogroup
.label=${'Notification Frequency'}
.options=${['All Notifications', 'Important Only', 'None']}
.selectedOption=${'Important Only'}
.description=${'Control how often you receive notifications'}
></dees-input-radiogroup>
<dees-input-radiogroup
.label=${'Language'}
.options=${['English', 'German', 'French', 'Spanish', 'Japanese']}
.selectedOption=${'English'}
.direction=${'horizontal'}
></dees-input-radiogroup>
<div class="input-group">
<dees-input-radiogroup
.label=${'Theme Preference'}
.options=${[
{ option: 'Light Theme', key: 'light', payload: 'light' },
{ option: 'Dark Theme', key: 'dark', payload: 'dark' },
{ option: 'System Default', key: 'system', payload: 'auto' }
]}
.selectedOption=${'dark'}
.description=${'Choose how the application should appear'}
></dees-input-radiogroup>
<dees-input-radiogroup
.label=${'Notification Frequency'}
.options=${['All Notifications', 'Important Only', 'None']}
.selectedOption=${'Important Only'}
.description=${'Control how often you receive notifications'}
></dees-input-radiogroup>
<dees-input-radiogroup
.label=${'Language'}
.options=${['English', 'German', 'French', 'Spanish', 'Japanese']}
.selectedOption=${'English'}
.direction=${'horizontal'}
></dees-input-radiogroup>
</div>
</dees-panel>
<dees-panel .title=${'7. Form Integration'} .subtitle=${'Works seamlessly with dees-form'}>

View File

@@ -30,6 +30,12 @@ export const demoFunc = () => html`
flex-wrap: wrap;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.grid-layout {
display: grid;
grid-template-columns: 1fr 1fr;
@@ -83,25 +89,27 @@ export const demoFunc = () => html`
}
}}>
<dees-panel .title=${'Basic Text Inputs'} .subtitle=${'Standard text inputs with labels and descriptions'}>
<dees-input-text
.label=${'Username'}
.value=${'johndoe'}
.key=${'username'}
></dees-input-text>
<dees-input-text
.label=${'Email Address'}
.value=${'john@example.com'}
.description=${'We will never share your email with anyone'}
.key=${'email'}
></dees-input-text>
<dees-input-text
.label=${'Password'}
.isPasswordBool=${true}
.value=${'secret123'}
.key=${'password'}
></dees-input-text>
<div class="input-group">
<dees-input-text
.label=${'Username'}
.value=${'johndoe'}
.key=${'username'}
></dees-input-text>
<dees-input-text
.label=${'Email Address'}
.value=${'john@example.com'}
.description=${'We will never share your email with anyone'}
.key=${'email'}
></dees-input-text>
<dees-input-text
.label=${'Password'}
.isPasswordBool=${true}
.value=${'secret123'}
.key=${'password'}
></dees-input-text>
</div>
</dees-panel>
</dees-demowrapper>
@@ -172,31 +180,33 @@ export const demoFunc = () => html`
}
}}>
<dees-panel .title=${'Label Positions'} .subtitle=${'Different label positioning options for various layouts'}>
<dees-input-text
.label=${'Label on Top (Default)'}
.value=${'Standard layout'}
.labelPosition=${'top'}
></dees-input-text>
<dees-input-text
.label=${'Label on Left'}
.value=${'Inline label'}
.labelPosition=${'left'}
></dees-input-text>
<div class="grid-layout">
<div class="input-group">
<dees-input-text
.label=${'Label on Top (Default)'}
.value=${'Standard layout'}
.labelPosition=${'top'}
></dees-input-text>
<dees-input-text
.label=${'Label on Left'}
.value=${'Inline label'}
.labelPosition=${'left'}
></dees-input-text>
<div class="grid-layout">
<dees-input-text
.label=${'City'}
.value=${'New York'}
.labelPosition=${'left'}
></dees-input-text>
<dees-input-text
.label=${'ZIP Code'}
.value=${'10001'}
<dees-input-text
.label=${'ZIP Code'}
.value=${'10001'}
.labelPosition=${'left'}
></dees-input-text>
</div>
</div>
</dees-panel>
</dees-demowrapper>
@@ -234,24 +244,26 @@ export const demoFunc = () => html`
}
}}>
<dees-panel .title=${'Validation & States'} .subtitle=${'Different validation states and input configurations'}>
<dees-input-text
.label=${'Required Field'}
.required=${true}
.key=${'requiredField'}
></dees-input-text>
<dees-input-text
.label=${'Disabled Field'}
.value=${'Cannot edit this'}
.disabled=${true}
></dees-input-text>
<dees-input-text
.label=${'Field with Error'}
.value=${'invalid@'}
.validationText=${'Please enter a valid email address'}
.validationState=${'invalid'}
></dees-input-text>
<div class="input-group">
<dees-input-text
.label=${'Required Field'}
.required=${true}
.key=${'requiredField'}
></dees-input-text>
<dees-input-text
.label=${'Disabled Field'}
.value=${'Cannot edit this'}
.disabled=${true}
></dees-input-text>
<dees-input-text
.label=${'Field with Error'}
.value=${'invalid@'}
.validationText=${'Please enter a valid email address'}
.validationState=${'invalid'}
></dees-input-text>
</div>
</dees-panel>
</dees-demowrapper>
@@ -279,19 +291,21 @@ export const demoFunc = () => html`
});
}}>
<dees-panel .title=${'Advanced Features'} .subtitle=${'Password visibility toggle and other advanced features'}>
<dees-input-text
.label=${'Password with Toggle'}
.isPasswordBool=${true}
.value=${'mySecurePassword123'}
.description=${'Click the eye icon to show/hide password'}
></dees-input-text>
<dees-input-text
.label=${'API Key'}
.isPasswordBool=${true}
.value=${'sk-1234567890abcdef'}
.description=${'Keep this key secure and never share it'}
></dees-input-text>
<div class="input-group">
<dees-input-text
.label=${'Password with Toggle'}
.isPasswordBool=${true}
.value=${'mySecurePassword123'}
.description=${'Click the eye icon to show/hide password'}
></dees-input-text>
<dees-input-text
.label=${'API Key'}
.isPasswordBool=${true}
.value=${'sk-1234567890abcdef'}
.description=${'Keep this key secure and never share it'}
></dees-input-text>
</div>
</dees-panel>
</dees-demowrapper>

View File

@@ -13,6 +13,12 @@ export const demoFunc = () => html`
margin: 0 auto;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.horizontal-group {
display: flex;
gap: 24px;
@@ -39,27 +45,30 @@ export const demoFunc = () => html`
<div class="demo-container">
<dees-panel .title=${'Basic Type List'} .subtitle=${'Add and remove items from a list'}>
<dees-input-typelist
.label=${'Tags'}
.description=${'Add tags by typing and pressing Enter'}
.value=${['javascript', 'typescript', 'web-components']}
></dees-input-typelist>
<dees-input-typelist
.label=${'Team Members'}
.description=${'Add email addresses of team members'}
.value=${['alice@example.com', 'bob@example.com']}
></dees-input-typelist>
<div class="input-group">
<dees-input-typelist
.label=${'Tags'}
.description=${'Add tags by typing and pressing Enter'}
.value=${['javascript', 'typescript', 'web-components']}
></dees-input-typelist>
<dees-input-typelist
.label=${'Team Members'}
.description=${'Add email addresses of team members'}
.value=${['alice@example.com', 'bob@example.com']}
></dees-input-typelist>
</div>
</dees-panel>
<dees-panel .title=${'Skills & Keywords'} .subtitle=${'Manage lists of skills and keywords'}>
<dees-input-typelist
.label=${'Your Skills'}
.description=${'List your professional skills'}
.value=${['HTML', 'CSS', 'JavaScript', 'Node.js', 'React']}
></dees-input-typelist>
<div class="horizontal-group">
<div class="input-group">
<dees-input-typelist
.label=${'Your Skills'}
.description=${'List your professional skills'}
.value=${['HTML', 'CSS', 'JavaScript', 'Node.js', 'React']}
></dees-input-typelist>
<div class="horizontal-group">
<dees-input-typelist
.label=${'Categories'}
.layoutMode=${'horizontal'}
@@ -72,22 +81,25 @@ export const demoFunc = () => html`
.value=${['innovation', 'startup', 'growth']}
></dees-input-typelist>
</div>
</div>
</dees-panel>
<dees-panel .title=${'Required & Disabled States'} .subtitle=${'Different input states for validation'}>
<dees-input-typelist
.label=${'Project Dependencies'}
.description=${'List all required npm packages'}
.required=${true}
.value=${['@design.estate/dees-element', '@design.estate/dees-domtools']}
></dees-input-typelist>
<dees-input-typelist
.label=${'System Tags'}
.description=${'These tags are managed by the system'}
.disabled=${true}
.value=${['system', 'protected', 'readonly']}
></dees-input-typelist>
<div class="input-group">
<dees-input-typelist
.label=${'Project Dependencies'}
.description=${'List all required npm packages'}
.required=${true}
.value=${['@design.estate/dees-element', '@design.estate/dees-domtools']}
></dees-input-typelist>
<dees-input-typelist
.label=${'System Tags'}
.description=${'These tags are managed by the system'}
.disabled=${true}
.value=${['system', 'protected', 'readonly']}
></dees-input-typelist>
</div>
</dees-panel>
<dees-panel .title=${'Article Publishing Form'} .subtitle=${'Complete form with tag management'}>

View File

@@ -52,7 +52,7 @@ export class DeesPdfViewer extends DeesElement {
accessor thumbnailData: Array<{page: number, rendered: boolean}> = [];
@property({ type: Array })
accessor pageData: Array<{page: number, rendered: boolean, rendering: boolean}> = [];
accessor pageData: Array<{page: number, rendered: boolean, rendering: boolean, textLayerRendered: boolean}> = [];
private pdfDocument: any;
private renderState: RenderState = 'idle';
@@ -63,6 +63,7 @@ export class DeesPdfViewer extends DeesElement {
private currentRenderPromise: Promise<void> | null = null;
private thumbnailRenderTasks: any[] = [];
private pageRenderTasks: Map<number, any> = new Map();
private textLayerRenderTasks: Map<number, any> = new Map();
private canvas: HTMLCanvasElement | undefined;
private ctx: CanvasRenderingContext2D | undefined;
private viewerMain: HTMLElement | null = null;
@@ -230,6 +231,7 @@ export class DeesPdfViewer extends DeesElement {
<div class="page-wrapper" data-page="${item.page}">
<div class="canvas-container">
<canvas class="page-canvas" data-page="${item.page}"></canvas>
<div class="text-layer" data-page="${item.page}"></div>
</div>
</div>
`
@@ -330,7 +332,8 @@ export class DeesPdfViewer extends DeesElement {
this.pageData = Array.from({length: this.totalPages}, (_, i) => ({
page: i + 1,
rendered: false,
rendering: false
rendering: false,
textLayerRendered: false,
}));
// Set loading to false to render the pages
@@ -476,6 +479,9 @@ export class DeesPdfViewer extends DeesElement {
pageInfo.rendering = false;
this.pageRenderTasks.delete(pageNum);
// Render text layer for selection
await this.renderTextLayer(pageNum);
// Update page data to reflect rendered state
this.requestUpdate('pageData');
} catch (error: any) {
@@ -487,6 +493,132 @@ export class DeesPdfViewer extends DeesElement {
}
}
private async renderTextLayer(pageNum: number): Promise<void> {
const pageInfo = this.pageData.find(p => p.page === pageNum);
if (!pageInfo || pageInfo.textLayerRendered) return;
try {
const textLayerDiv = this.shadowRoot?.querySelector(
`.text-layer[data-page="${pageNum}"]`
) as HTMLElement;
if (!textLayerDiv) return;
textLayerDiv.innerHTML = '';
const page = await this.pdfDocument.getPage(pageNum);
const textContent = await page.getTextContent();
const viewport = this.computeViewport(page);
// @ts-ignore - Dynamic import of pdfjs
const pdfjs = await import('https://cdn.jsdelivr.net/npm/pdfjs-dist@4.0.379/+esm');
textLayerDiv.style.width = `${viewport.width}px`;
textLayerDiv.style.height = `${viewport.height}px`;
// Set the scale factor CSS variable - required by PDF.js text layer
textLayerDiv.style.setProperty('--scale-factor', String(viewport.scale));
const textLayerRenderTask = pdfjs.renderTextLayer({
textContentSource: textContent,
container: textLayerDiv,
viewport: viewport,
});
this.textLayerRenderTasks.set(pageNum, textLayerRenderTask);
await textLayerRenderTask.promise;
// Add endOfContent for selection boundary
const endOfContent = document.createElement('div');
endOfContent.className = 'endOfContent';
textLayerDiv.appendChild(endOfContent);
// Custom drag selection for Shadow DOM compatibility
// caretRangeFromPoint doesn't pierce shadow DOM, so we find spans manually
let isDragging = false;
let anchorNode: Node | null = null;
let anchorOffset = 0;
const getTextPositionFromPoint = (x: number, y: number): { node: Node; offset: number } | null => {
// Find span at coordinates by checking bounding rects
const spans = Array.from(textLayerDiv.querySelectorAll('span'));
for (const span of spans) {
const rect = span.getBoundingClientRect();
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
const textNode = span.firstChild;
if (textNode && textNode.nodeType === Node.TEXT_NODE) {
// Calculate character offset based on x position
const text = textNode.textContent || '';
const charWidth = rect.width / text.length;
const relativeX = x - rect.left;
const offset = Math.min(Math.round(relativeX / charWidth), text.length);
return { node: textNode, offset };
}
}
}
return null;
};
const handleMouseUp = () => {
if (isDragging) {
isDragging = false;
anchorNode = null;
textLayerDiv.classList.remove('selecting');
}
document.removeEventListener('mouseup', handleMouseUp);
document.removeEventListener('mousemove', handleMouseMove);
};
const handleMouseMove = (e: MouseEvent) => {
if (!isDragging || !anchorNode) return;
e.preventDefault();
const pos = getTextPositionFromPoint(e.clientX, e.clientY);
if (pos) {
const selection = window.getSelection();
if (selection) {
try {
selection.setBaseAndExtent(anchorNode, anchorOffset, pos.node, pos.offset);
} catch (err) {
// Ignore errors from invalid selections
}
}
}
};
textLayerDiv.addEventListener('mousedown', (e: MouseEvent) => {
if (e.button !== 0) return;
const pos = getTextPositionFromPoint(e.clientX, e.clientY);
if (pos) {
// Prevent native selection behavior
e.preventDefault();
isDragging = true;
anchorNode = pos.node;
anchorOffset = pos.offset;
textLayerDiv.classList.add('selecting');
// Clear existing selection
const selection = window.getSelection();
selection?.removeAllRanges();
// Add document-level listeners for drag
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
}
});
pageInfo.textLayerRendered = true;
page.cleanup?.();
this.textLayerRenderTasks.delete(pageNum);
} catch (error: any) {
if (error?.name !== 'RenderingCancelledException') {
console.error(`Error rendering text layer for page ${pageNum}:`, error);
}
this.textLayerRenderTasks.delete(pageNum);
}
}
private handleScroll = () => {
// Throttle scroll events
if (this.scrollThrottleTimeout) {
@@ -771,6 +903,7 @@ export class DeesPdfViewer extends DeesElement {
this.pageData.forEach(page => {
page.rendered = false;
page.rendering = false;
page.textLayerRendered = false;
});
// Cancel any ongoing render tasks
@@ -783,6 +916,16 @@ export class DeesPdfViewer extends DeesElement {
});
this.pageRenderTasks.clear();
// Cancel text layer render tasks
this.textLayerRenderTasks.forEach(task => {
try {
task.cancel?.();
} catch (error) {
// Ignore cancellation errors
}
});
this.textLayerRenderTasks.clear();
// Request update to re-render pages
this.requestUpdate();
@@ -792,52 +935,138 @@ export class DeesPdfViewer extends DeesElement {
});
}
private downloadPdf() {
const link = document.createElement('a');
link.href = this.pdfUrl;
link.download = this.pdfUrl.split('/').pop() || 'document.pdf';
link.click();
private async downloadPdf() {
if (!this.pdfDocument) return;
try {
// Get raw PDF data from the loaded document
const data = await this.pdfDocument.getData();
const blob = new Blob([data.buffer], { type: 'application/pdf' });
const blobUrl = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = blobUrl;
link.download = this.pdfUrl ? this.pdfUrl.split('/').pop() || 'document.pdf' : 'document.pdf';
link.click();
// Clean up blob URL after short delay
setTimeout(() => URL.revokeObjectURL(blobUrl), 1000);
} catch (error) {
console.error('Error downloading PDF:', error);
}
}
private printPdf() {
window.open(this.pdfUrl, '_blank')?.print();
private async printPdf() {
if (!this.pdfDocument) return;
try {
// Get raw PDF data from the loaded document
const data = await this.pdfDocument.getData();
const blob = new Blob([data.buffer], { type: 'application/pdf' });
const pdfUrl = URL.createObjectURL(blob);
// Create an HTML wrapper page that embeds the PDF and handles print/close
// This gives us control over the afterprint event (direct PDF URLs don't support it)
const htmlContent = `
<!DOCTYPE html>
<html>
<head>
<title>Print PDF</title>
<style>
* { margin: 0; padding: 0; }
html, body { width: 100%; height: 100%; overflow: hidden; }
iframe { width: 100%; height: 100%; border: none; }
@media print {
html, body, iframe { width: 100%; height: 100%; }
}
</style>
</head>
<body>
<iframe src="${pdfUrl}" type="application/pdf"></iframe>
<script>
window.onload = function() {
setTimeout(function() {
window.focus();
window.print();
}, 500);
};
window.onafterprint = function() {
window.close();
};
// Safety close after 2 minutes
setTimeout(function() { window.close(); }, 120000);
</script>
</body>
</html>
`;
const htmlBlob = new Blob([htmlContent], { type: 'text/html' });
const htmlUrl = URL.createObjectURL(htmlBlob);
const printWindow = window.open(htmlUrl, '_blank', 'width=800,height=600');
if (printWindow) {
// Cleanup blob URLs when window closes
const checkClosed = setInterval(() => {
if (printWindow.closed) {
clearInterval(checkClosed);
URL.revokeObjectURL(pdfUrl);
URL.revokeObjectURL(htmlUrl);
}
}, 500);
// Safety cleanup after 2 minutes
setTimeout(() => {
clearInterval(checkClosed);
URL.revokeObjectURL(pdfUrl);
URL.revokeObjectURL(htmlUrl);
}, 120000);
} else {
// Popup blocked - fall back to direct navigation
window.open(pdfUrl, '_blank');
setTimeout(() => URL.revokeObjectURL(pdfUrl), 60000);
URL.revokeObjectURL(htmlUrl);
}
} catch (error) {
console.error('Error printing PDF:', error);
}
}
/**
* Provide context menu items for right-click functionality
*/
public getContextMenuItems() {
return [
{
name: 'Open PDF in New Tab',
iconName: 'lucide:ExternalLink',
action: async () => {
window.open(this.pdfUrl, '_blank');
}
},
{ divider: true },
{
name: 'Copy PDF URL',
const items: any[] = [];
// Add copy option if text is selected
const selection = window.getSelection();
const selectedText = selection?.toString() || '';
if (selectedText) {
items.push({
name: 'Copy',
iconName: 'lucide:Copy',
action: async () => {
await navigator.clipboard.writeText(this.pdfUrl);
await navigator.clipboard.writeText(selectedText);
}
},
});
items.push({ divider: true });
}
items.push(
{
name: 'Download PDF',
iconName: 'lucide:Download',
action: async () => {
this.downloadPdf();
await this.downloadPdf();
}
},
{
name: 'Print PDF',
iconName: 'lucide:Printer',
action: async () => {
this.printPdf();
await this.printPdf();
}
}
];
);
return items;
}
private get canZoomIn(): boolean {
@@ -996,6 +1225,16 @@ export class DeesPdfViewer extends DeesElement {
});
this.pageRenderTasks.clear();
// Cancel text layer render tasks
this.textLayerRenderTasks.forEach(task => {
try {
task.cancel?.();
} catch (error) {
// Ignore cancellation errors
}
});
this.textLayerRenderTasks.clear();
// Cancel any thumbnail render tasks
for (const task of (this.thumbnailRenderTasks || [])) {
try {

View File

@@ -276,6 +276,7 @@ export const viewerStyles = [
border-radius: 4px;
overflow: hidden;
display: inline-block;
position: relative;
}
.page-canvas {
@@ -284,6 +285,52 @@ export const viewerStyles = [
image-rendering: crisp-edges;
}
/* Text layer for selection */
.text-layer {
position: absolute;
inset: 0;
overflow: visible;
line-height: 1;
text-size-adjust: none;
forced-color-adjust: none;
transform-origin: 0 0;
z-index: 1;
user-select: text;
-webkit-user-select: text;
}
.text-layer span,
.text-layer br {
color: transparent;
position: absolute;
white-space: pre;
cursor: text;
transform-origin: 0% 0%;
user-select: text;
-webkit-user-select: text;
}
.text-layer ::selection {
background: rgba(0, 100, 200, 0.3);
}
.text-layer br::selection {
background: transparent;
}
.text-layer .endOfContent {
display: block;
position: absolute;
inset: 100% 0 0;
z-index: 0;
cursor: default;
user-select: none;
}
.text-layer.selecting .endOfContent {
top: 0;
}
.pdf-viewer.with-sidebar .viewer-main {
margin-left: 0;
}

View File

@@ -390,7 +390,8 @@ export class DeesSimpleAppDash extends DeesElement {
const domtools = await this.domtoolsPromise;
super.firstUpdated(_changedProperties);
if (this.viewTabs && this.viewTabs.length > 0) {
await this.loadView(this.viewTabs[0]);
const viewToLoad = this.selectedView || this.viewTabs[0];
await this.loadView(viewToLoad);
}
}

View File

@@ -193,9 +193,20 @@ export class DeesServiceLibLoader {
const response = await fetch(cssUrl);
const cssText = await response.text();
// Fix for xterm.js WidthCache measurement container causing horizontal scrollbar
// xterm.js creates this on document.body with width: 50000px, top: -50000px
// Moving it off-screen horizontally prevents scrollWidth expansion
const xtermMeasurementFix = `
/* Fix xterm.js WidthCache measurement container causing horizontal scrollbar */
/* xterm creates this on document.body - move it off-screen horizontally too */
body > div[style*="top: -50000px"][style*="width: 50000px"] {
left: -50000px !important;
}
`;
const style = document.createElement('style');
style.id = styleId;
style.textContent = cssText;
style.textContent = cssText + xtermMeasurementFix;
document.head.appendChild(style);
}