Compare commits

...

155 Commits

Author SHA1 Message Date
4eef9fc731 1.8.1
Some checks failed
Default (tags) / security (push) Failing after 25s
Default (tags) / test (push) Failing after 13s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-06-10 18:58:05 +00:00
cd86001713 fix(dees-statsgrid): Adjust stats grid styling for better alignment and improved visualizations in gauge and trend tiles. 2025-06-10 18:58:05 +00:00
f7e4582fde feat(dees-statsgrid): Add dees-statsgrid component with demo and integration in the main export 2025-06-10 18:29:37 +00:00
4635e3fce5 1.8.0
Some checks failed
Default (tags) / security (push) Failing after 20s
Default (tags) / test (push) Failing after 9s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-25 14:27:51 +00:00
af3dc5c466 feat(dees-pagination): Add new pagination component to the library along with its demo and integration in the main export. 2025-04-25 14:27:51 +00:00
12861b2230 1.7.0
Some checks failed
Default (tags) / security (push) Failing after 17s
Default (tags) / test (push) Failing after 8s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-22 12:49:57 +00:00
b7f672e0f2 feat(dees-searchbar): Add dees-searchbar component with live search and filter demo 2025-04-22 12:49:57 +00:00
fcb44dfd24 1.6.0
Some checks failed
Default (tags) / security (push) Failing after 20s
Default (tags) / test (push) Failing after 9s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-22 12:30:22 +00:00
f17b880b59 feat(documentation/dees-heading): Add codex documentation overview and dees-heading component demo 2025-04-22 12:30:22 +00:00
68785d9a72 1.5.6
Some checks failed
Default (tags) / security (push) Failing after 20s
Default (tags) / test (push) Failing after 8s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-18 17:07:43 +00:00
ab4396297a fix(dependencies): Bump dependency versions and update demo code references 2025-04-18 17:07:43 +00:00
ef369f2955 update 2025-04-13 17:32:44 +00:00
1e73a9527b 1.5.5
Some checks failed
Default (tags) / security (push) Failing after 19s
Default (tags) / test (push) Failing after 9s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-12 14:35:56 +00:00
23a4faa5d1 fix(catalog): No code or documentation changes were detected. This commit records an empty update in commit information and confirms that the current state remains stable. 2025-04-12 14:35:55 +00:00
b0020ace16 1.5.4
Some checks failed
Default (tags) / security (push) Failing after 12s
Default (tags) / test (push) Failing after 12s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-11 11:19:58 +00:00
bb78d32dbf fix(readme): Update readme with company and trademark guidelines, clarifying legal usage without exposing licensing details. 2025-04-11 11:19:58 +00:00
e83ad8d504 1.5.3
Some checks failed
Default (tags) / security (push) Failing after 17s
Default (tags) / test (push) Failing after 8s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-11 11:19:24 +00:00
765b01afe0 fix(readme): Update readme.md: remove redundant usage section and refine component documentation with improved examples. 2025-04-11 11:19:23 +00:00
00e34e7e6c 1.5.2
Some checks failed
Default (tags) / security (push) Failing after 9s
Default (tags) / test (push) Failing after 10s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-11 10:24:28 +00:00
bf2ee25390 fix(ci): Remove obsolete GitLab CI configuration file 2025-04-11 10:24:28 +00:00
bf6d8d0bc6 1.5.1
Some checks failed
Default (tags) / security (push) Failing after 18s
Default (tags) / test (push) Failing after 11s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-11 10:20:35 +00:00
3399004e75 fix(readme): Update readme with comprehensive reference documentation: add a usage snippet for components like DeesButton, introduce a detailed overview table of all component categories, and enhance documentation sections for each component group. 2025-04-11 10:20:35 +00:00
6c2f36f020 1.5.0
Some checks failed
Default (tags) / security (push) Failing after 14s
Default (tags) / test (push) Failing after 14s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-11 10:15:00 +00:00
71f4d44782 feat(badge): Add dees-badge component with demo file and update packageManager field in package.json 2025-04-11 10:15:00 +00:00
6df2eb5acc 1.4.1
Some checks failed
Default (tags) / security (push) Failing after 17s
Default (tags) / test (push) Failing after 12s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-20 03:59:23 +01:00
469f8e0f21 fix(dependencies): Update dependency versions for smartpromise, webcontainer/api, tapbundle, and @types/node 2025-01-20 03:59:23 +01:00
3712f6ef90 1.4.0
Some checks failed
Default (tags) / security (push) Failing after 17s
Default (tags) / test (push) Failing after 12s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-20 03:43:21 +01:00
d2646cd62c feat(dees-terminal): Enhanced the dees-terminal component to support environment variable settings and improved setup command execution. 2025-01-20 03:43:20 +01:00
f29ca0ba0b 1.3.4
Some checks failed
Default (tags) / security (push) Failing after 18s
Default (tags) / test (push) Failing after 12s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-15 16:00:03 +01:00
0c273a818d fix(chart): Fix chart rendering and appearance issues in the DeesChartArea component. 2025-01-15 16:00:03 +01:00
6e8099c6f4 1.3.3
Some checks failed
Default (tags) / security (push) Failing after 18s
Default (tags) / test (push) Failing after 12s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-12-17 19:44:47 +01:00
07c68b82a4 fix(dees-input-multitoggle): Add missing TypeScript declaration for dees-input-multitoggle 2024-12-17 19:44:47 +01:00
afd19dc912 1.3.2
Some checks failed
Default (tags) / security (push) Failing after 17s
Default (tags) / test (push) Failing after 12s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-12-09 19:14:21 +01:00
f02572665f fix(metadata): Updated package metadata and readme for better project description and structure. 2024-12-09 19:14:21 +01:00
f93082e9b0 1.3.1 2024-11-07 01:16:54 +01:00
08f3bad5f9 fix(DeesSimpleAppDash): Fix: add border to controlbar in DeesSimpleAppDash 2024-11-07 01:16:54 +01:00
563958813e 1.3.0 2024-11-07 01:14:37 +01:00
1ae1703133 feat(dees-simple-appdash): Enhance responsive styling and terminal setup command 2024-11-07 01:14:36 +01:00
d2771dfc31 1.2.0 2024-10-07 13:43:15 +02:00
dd46d3e2f4 feat(index.ts): Add export for colors module in index.ts 2024-10-07 13:43:15 +02:00
ae641801e1 1.1.13 2024-10-06 01:18:13 +02:00
719e63c667 fix(dees-button): Fix styling issue in button component. 2024-10-06 01:18:13 +02:00
e26da1bb8f 1.1.12 2024-10-06 01:12:01 +02:00
f5fad07038 fix(dees-button): Fix reflect attribute for disabled property on dees-button component 2024-10-06 01:12:00 +02:00
68992301ff 1.1.11 2024-10-05 00:01:38 +02:00
d529c27a68 fix(DeesStepper): Adjusted CSS properties in DeesStepper component 2024-10-05 00:01:37 +02:00
56e8d868c9 1.1.10 2024-10-04 15:34:00 +02:00
14e1d4a9f5 fix(dependencies): Reverted @webcontainer/api version 2024-10-04 15:33:59 +02:00
efe49469aa 1.1.9 2024-10-04 15:30:14 +02:00
39500f802a fix(dependencies): Update various dependencies for compatibility and stability. 2024-10-04 15:30:13 +02:00
a64d38dbb1 1.1.8 2024-09-02 22:09:11 +02:00
e2d3ae8949 fix(dees-simple-appdash): Corrected viewTab active background color 2024-09-02 22:09:10 +02:00
1c4fd0a2da 1.1.7 2024-09-02 22:07:43 +02:00
d0e85da308 fix(dependencies): Update dependencies to their latest versions 2024-09-02 22:07:42 +02:00
6a0b0aa741 1.1.6 2024-07-01 11:59:31 +02:00
4c620f480c fix(dees-dataview-codebox): Corrected the font-family order for better font rendering. 2024-07-01 11:59:30 +02:00
b7ebcc4cef 1.1.5 2024-07-01 11:58:19 +02:00
e6275bfeb3 fix(dees-dataview-codebox): Adjusted line number font weight in codebox 2024-07-01 11:58:18 +02:00
d4585654b6 1.1.4 2024-07-01 11:57:52 +02:00
1a8562dc6f fix(UI): Fixed font-family order for code and value elements 2024-07-01 11:57:51 +02:00
2148959c35 1.1.3 2024-07-01 11:52:56 +02:00
28a84f7c1a fix(dees-dataview-codebox): Adjusted codebox font weight and font family. 2024-07-01 11:52:56 +02:00
e935dc3983 1.1.2 2024-06-30 21:33:23 +02:00
ab30b51c9e fix(elements): Fix various UI components and improve styles 2024-06-30 21:33:22 +02:00
8377d94f81 1.1.1 2024-06-30 10:37:53 +02:00
14ebeeaee8 fix(build): Fix build script by updating source folders. 2024-06-30 10:37:52 +02:00
fdc8cb44ab 1.1.0 2024-06-30 10:37:12 +02:00
f72936d1a7 feat(project dependencies): Updated various dependencies and internal fonts 2024-06-30 10:37:12 +02:00
04170d4834 update documentation 2024-04-20 23:16:19 +02:00
9006e023bd 1.0.289 2024-02-13 23:31:27 +01:00
8c8d5e9329 fix(core): update 2024-02-13 23:31:26 +01:00
f4fee46439 1.0.288 2024-02-05 13:56:50 +01:00
af2ee9a6a5 fix(core): update 2024-02-05 13:56:49 +01:00
a3012b5ec1 1.0.287 2024-02-05 13:17:45 +01:00
0f414ae24a fix(core): update 2024-02-05 13:17:44 +01:00
1fa9b410ed 1.0.286 2024-02-05 13:13:02 +01:00
90c7890cbf fix(core): update 2024-02-05 13:13:02 +01:00
55e601b806 1.0.285 2024-02-05 13:12:27 +01:00
45952914ba fix(core): update 2024-02-05 13:12:26 +01:00
5f83b01f0e 1.0.284 2024-02-05 13:11:06 +01:00
c03563f2fc fix(core): update 2024-02-05 13:11:05 +01:00
edf50d2f8d 1.0.283 2024-02-05 10:07:50 +01:00
d0789ab279 fix(core): update 2024-02-05 10:07:49 +01:00
55bac1bf7b 1.0.282 2024-02-03 14:49:26 +01:00
2957121473 fix(core): update 2024-02-03 14:49:25 +01:00
020a1bfb75 1.0.281 2024-02-03 14:42:47 +01:00
bb246c4f8e fix(core): update 2024-02-03 14:42:46 +01:00
cf258a8d59 1.0.280 2024-02-03 14:42:21 +01:00
38b4df1ce3 fix(core): update 2024-02-03 14:42:20 +01:00
2f7fdc16c7 1.0.279 2024-02-03 12:44:12 +01:00
f4b426bc7f fix(core): update 2024-02-03 12:44:12 +01:00
9577cc9ebf 1.0.278 2024-02-03 11:26:16 +01:00
be5124217a fix(core): update 2024-02-03 11:26:15 +01:00
d029a6c346 1.0.277 2024-01-24 12:18:38 +01:00
69bb03dcdf fix(core): update 2024-01-24 12:18:37 +01:00
5eb2f4cebc 1.0.276 2024-01-24 00:59:11 +01:00
c021a84788 fix(core): update 2024-01-24 00:59:11 +01:00
321ce99338 1.0.275 2024-01-22 22:30:45 +01:00
6cfe89645c fix(core): update 2024-01-22 22:30:44 +01:00
11f900beeb 1.0.274 2024-01-22 20:51:51 +01:00
39ca21e57c fix(core): update 2024-01-22 20:51:50 +01:00
87872191e2 1.0.273 2024-01-22 19:40:09 +01:00
2d3fd74333 fix(core): update 2024-01-22 19:40:08 +01:00
eebe898dcc 1.0.272 2024-01-22 19:27:54 +01:00
ca73d00f68 fix(core): update 2024-01-22 19:27:54 +01:00
14bdc46073 1.0.271 2024-01-22 19:27:31 +01:00
bf04ce6a8f fix(core): update 2024-01-22 19:27:30 +01:00
9dc6dab3a5 1.0.270 2024-01-22 19:23:23 +01:00
a21a3b6918 fix(core): update 2024-01-22 19:23:22 +01:00
6ae21d73aa 1.0.269 2024-01-22 18:39:31 +01:00
941871991f fix(core): update 2024-01-22 18:39:31 +01:00
497c38f426 1.0.268 2024-01-22 18:30:36 +01:00
bfaa1623d9 fix(core): update 2024-01-22 18:30:35 +01:00
5ffa5a2adc 1.0.267 2024-01-22 18:30:01 +01:00
16d1375e47 fix(core): update 2024-01-22 18:30:00 +01:00
30eda34f37 1.0.266 2024-01-22 17:32:58 +01:00
2cc7eb1ead fix(core): update 2024-01-22 17:32:58 +01:00
64f5bee801 1.0.265 2024-01-22 17:29:53 +01:00
9d57f983dd fix(core): update 2024-01-22 17:29:53 +01:00
8ccd21df36 1.0.264 2024-01-22 17:25:55 +01:00
c22c994d12 fix(core): update 2024-01-22 17:25:55 +01:00
01369444b7 1.0.263 2024-01-22 17:22:33 +01:00
4beb15f70c fix(core): update 2024-01-22 17:22:33 +01:00
30170bcd2e 1.0.262 2024-01-22 17:12:59 +01:00
f29a8de504 fix(core): update 2024-01-22 17:12:58 +01:00
965ab02315 1.0.261 2024-01-22 01:29:28 +01:00
991784eae6 fix(core): update 2024-01-22 01:29:27 +01:00
b474b7986c 1.0.260 2024-01-22 01:26:13 +01:00
98542252cb fix(core): update 2024-01-22 01:26:13 +01:00
0d78deadf2 1.0.259 2024-01-22 01:11:29 +01:00
5108f47e56 fix(core): update 2024-01-22 01:11:28 +01:00
7d4e5f2ca7 1.0.258 2024-01-22 00:59:26 +01:00
c0ad0f4570 fix(core): update 2024-01-22 00:59:25 +01:00
d56eb602a9 1.0.257 2024-01-21 22:37:39 +01:00
f1c791eb12 fix(core): update 2024-01-21 22:37:39 +01:00
e872188be7 1.0.256 2024-01-21 17:14:16 +01:00
2d712e78b0 fix(core): update 2024-01-21 17:14:15 +01:00
ebeecd0686 1.0.255 2024-01-21 14:25:03 +01:00
1d09f994c6 fix(core): update 2024-01-21 14:25:02 +01:00
686c3714fc 1.0.254 2024-01-21 14:14:57 +01:00
e29086036c fix(core): update 2024-01-21 14:14:57 +01:00
78e24aa720 1.0.253 2024-01-21 13:57:46 +01:00
7c8876c835 fix(core): update 2024-01-21 13:57:46 +01:00
2222fb1e01 1.0.252 2024-01-21 13:57:27 +01:00
46b30a1ef0 fix(core): update 2024-01-21 13:57:26 +01:00
d49d5f70ef 1.0.251 2024-01-21 13:36:48 +01:00
76a950e2ba fix(core): update 2024-01-21 13:36:47 +01:00
077aba5e58 1.0.250 2024-01-21 13:27:29 +01:00
857362423f fix(core): update 2024-01-21 13:27:29 +01:00
e7232b6a53 1.0.249 2024-01-21 01:45:36 +01:00
2eb26544b3 fix(core): update 2024-01-21 01:45:35 +01:00
a40834d1cf 1.0.248 2024-01-21 01:42:07 +01:00
0c1159d4c7 fix(core): update 2024-01-21 01:42:06 +01:00
4584765046 1.0.247 2024-01-21 01:12:57 +01:00
5817068cb5 fix(core): update 2024-01-21 01:12:57 +01:00
65 changed files with 15031 additions and 4801 deletions

View File

@ -1,128 +0,0 @@
# gitzone ci_default
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
cache:
paths:
- .npmci_cache/
key: '$CI_BUILD_STAGE'
stages:
- security
- test
- release
- metadata
before_script:
- pnpm install -g pnpm
- pnpm install -g @shipzone/npmci
- npmci npm prepare
# ====================
# security stage
# ====================
# ====================
# security stage
# ====================
auditProductionDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci command npm config set registry https://registry.npmjs.org
- npmci command pnpm audit --audit-level=high --prod
tags:
- lossless
- docker
allow_failure: true
auditDevDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci command npm config set registry https://registry.npmjs.org
- npmci command pnpm audit --audit-level=high --dev
tags:
- lossless
- docker
allow_failure: true
# ====================
# test stage
# ====================
testStable:
stage: test
script:
- npmci node install stable
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
testBuild:
stage: test
script:
- npmci node install stable
- npmci npm install
- npmci npm build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
release:
stage: release
script:
- npmci node install stable
- npmci npm publish
only:
- tags
tags:
- lossless
- docker
- notpriv
# ====================
# metadata stage
# ====================
codequality:
stage: metadata
allow_failure: true
only:
- tags
script:
- npmci command npm install -g typescript
- npmci npm prepare
- npmci npm install
tags:
- lossless
- docker
- priv
trigger:
stage: metadata
script:
- npmci trigger
only:
- tags
tags:
- lossless
- docker
- notpriv
pages:
stage: metadata
script:
- npmci node install stable
- npmci npm install
- npmci command npm run buildDocs
tags:
- lossless
- docker
- notpriv
only:
- tags
artifacts:
expire_in: 1 week
paths:
- public
allow_failure: true

509
changelog.md Normal file
View File

@ -0,0 +1,509 @@
# Changelog
## 2025-06-10 - 1.8.1 - fix(dees-statsgrid)
Adjust stats grid styling for better alignment and improved visualizations in gauge and trend tiles.
- Center-align tile header elements by setting align-items to center and ensuring full width.
- Increase tile content height to 90px and center its content.
- Update gauge visualization: reduce circle radius from 40 to 30, adjust stroke dasharray (from 251.2 to 188.5), and decrease gauge text font size.
- Refine trend chart layout: set trend-svg height to 40px, center trend value and adjust typography to larger, bolder text.
- Ensure overall grid responsiveness with adjusted gap and column sizing.
## 2025-04-25 - 1.8.0 - feat(dees-pagination)
Add new pagination component to the library along with its demo and integration in the main export.
- Introduced dees-pagination component with support for various page range scenarios.
- Created demo file to showcase pagination with both small and large sets of pages.
- Updated the module's index to export the new pagination component.
## 2025-04-22 - 1.7.0 - feat(dees-searchbar)
Add dees-searchbar component with live search and filter demo
- Introduces a new dees-searchbar element with an input field, a search button, and filters
- Wires up events for 'search-changed' and 'search-submit' to provide realtime feedback
- Adds a demo file to showcase usage and logging of search events
## 2025-04-22 - 1.6.0 - feat(documentation/dees-heading)
Add codex documentation overview and dees-heading component demo
- Introduce 'codex.md' to provide a high-level overview of project layout, component patterns, and build workflow
- Add and update dees-heading component with demo to support multiple heading levels and horizontal rule styles
- Update component export index to include dees-heading
## 2025-04-18 - 1.5.6 - fix(dependencies)
Bump dependency versions and update demo code references
- Upgrade @design.estate/dees-element from ^2.0.39 to ^2.0.41
- Upgrade @tsclass/tsclass from ^4.4.0 to ^9.0.0
- Upgrade lucide from ^0.488.0 to ^0.501.0
- Update @types/node from ^22.10.7 to ^22.14.1
- Update dees-icon demo: scope search to demo container and adjust hover scaling
- Replace resolveExec with directives.resolveExec in dees-table for proper rendering
## 2025-04-12 - 1.5.5 - fix(catalog)
No code or documentation changes were detected. This commit records an empty update in commit information and confirms that the current state remains stable.
- Verified that there are no modifications in source, documentation, or demos
- Commit metadata and build configuration remain unchanged
## 2025-04-11 - 1.5.4 - fix(readme)
Update readme with company and trademark guidelines, clarifying legal usage without exposing licensing details.
- Added sections detailing company information and trademark guidelines.
- Outlined legal disclaimers for trademark usage.
## 2025-04-11 - 1.5.3 - fix(readme)
Update readme.md: remove redundant usage section and refine component documentation with improved examples.
- Removed the standalone manual import and usage example for components.
- Added refined examples demonstrating both basic and option-based usage (e.g. for DeesButton).
- Improved markdown formatting and consistency across component documentation.
## 2025-04-11 - 1.5.3 - fix(readme)
Update readme.md for clearer documentation: removed redundant 'Usage' section and refined component examples (e.g., DeesButton's basic and options usage) for improved clarity and consistency.
- Removed standalone usage example showing manual import and creation of components
- Added refined examples demonstrating both basic and option-based usage of components
- Improved overall readme formatting and consistency across component documentation
## 2025-04-11 - 1.5.2 - fix(ci)
Remove obsolete GitLab CI configuration file
- Deleted .gitlab-ci.yml as the CI pipeline configuration is now managed elsewhere.
- Cleaned up CI stages for security, testing, release, and metadata.
## 2025-04-11 - 1.5.1 - fix(readme)
Update readme with comprehensive reference documentation: add a usage snippet for components like DeesButton, introduce a detailed overview table of all component categories, and enhance documentation sections for each component group.
- Added a code example showing how to import and use DeesButton.
- Introduced a components overview table that categorizes Core UI, Forms, Layout, Data Display, Visualization, Dialogs & Overlays, Navigation, and Development components.
- Expanded detailed documentation with usage examples for each component type.
- Reorganized content to improve clarity and ease of navigation for developers.
## 2025-04-11 - 1.5.0 - feat(badge)
Add dees-badge component with demo file and update packageManager field in package.json
- Introduce a new badge component allowing different types (default, primary, success, warning, error) with an optional rounded style
- Provide a demo for the badge component
- Export the badge component in the main elements index
- Update package.json to include an explicit packageManager field
## 2025-01-20 - 1.4.1 - fix(dependencies)
Update dependency versions for smartpromise, webcontainer/api, tapbundle, and @types/node
- Update @push.rocks/smartpromise to version ^4.2.0
- Downgrade @webcontainer/api to version 1.2.0
- Update @push.rocks/tapbundle to version ^5.5.6
- Update @types/node to version ^22.10.7
## 2025-01-20 - 1.4.0 - feat(dees-terminal)
Enhanced the dees-terminal component to support environment variable settings and improved setup command execution.
- Added environment property to pass custom environment variables.
- Introduced webcontainerDeferred to handle the promise for web container creation.
- Enhanced demo to illustrate environment variable usage.
- Improved async interaction with the terminal for setting environment variables and executing setup commands.
## 2025-01-15 - 1.3.4 - fix(chart)
Fix chart rendering and appearance issues in the DeesChartArea component.
- Resolved issues with chart dimensions calculation based on padding.
- Adjusted grid and axis lines appearance for better visibility.
- Updated tooltip and grid line styling for better accessibility.
- Improved series data representation as time-series for more accurate display.
## 2024-12-17 - 1.3.3 - fix(dees-input-multitoggle)
Add missing TypeScript declaration for dees-input-multitoggle
- Added a missing declaration to the HTMLElementTagNameMap for 'dees-input-multitoggle' element.
## 2024-12-09 - 1.3.2 - fix(metadata)
Updated package metadata and readme for better project description and structure.
- Updated package.json and npmextra.json with a detailed project description and list of keywords.
- Enhanced readme.md with installation instructions, component usage examples, and detailed component descriptions for clarity.
## 2024-11-07 - 1.3.1 - fix(DeesSimpleAppDash)
Fix: add border to controlbar in DeesSimpleAppDash
- Fixed the missing border at the top of the controlbar in DeesSimpleAppDash.
## 2024-11-07 - 1.3.0 - feat(dees-simple-appdash)
Enhance responsive styling and terminal setup command
- Added a new property `terminalSetupCommand` for configuring terminal setup commands.
- Improved responsive styling and positioning for components to achieve a fluid layout.
- Fixed layout shifts by switching positions to `absolute` for `appbar` and `appcontent`.
## 2024-10-07 - 1.2.0 - feat(index.ts)
Add export for colors module in index.ts
- The index.ts file now exports the colors module, making color utilities available for external use.
## 2024-10-06 - 1.1.13 - fix(dees-button)
Fix styling issue in button component.
- Moved the .button.disabled styling block to its correct position after the .button.highlighted block.
## 2024-10-06 - 1.1.12 - fix(dees-button)
Fix reflect attribute for disabled property on dees-button component
- Added reflect: true to the 'disabled' property ensuring changes reflect in the DOM attribute.
## 2024-10-05 - 1.1.11 - fix(DeesStepper)
Adjusted CSS properties in DeesStepper component
- Increased border-radius from 8px to 16px for step container elements
- Adjusted font-size and font-weight for the title in the step container to improve readability
## 2024-10-04 - 1.1.10 - fix(dependencies)
Reverted @webcontainer/api version
- Changed @webcontainer/api version from ^1.3.0 to 1.2.0 in package.json
## 2024-10-04 - 1.1.9 - fix(dependencies)
Update various dependencies for compatibility and stability.
- Update @design.estate/dees-domtools to version ^2.0.61
- Update @design.estate/dees-element to version ^2.0.39
- Update @webcontainer/api to version ^1.3.0
- Update apexcharts to version ^3.54.0
- Update monaco-editor to version ^0.52.0
- Update pdfjs-dist to version ^4.6.82
- Update @push.rocks/tapbundle to version ^5.3.0
- Update @types/node to version ^22.7.4
## 2024-09-02 - 1.1.8 - fix(dees-simple-appdash)
Corrected viewTab active background color
- Corrected the background color of the viewTab in active state for better visual consistency.
## 2024-09-02 - 1.1.7 - fix(dependencies)
Update dependencies to their latest versions
- Update dependency versions in package.json:
- - @design.estate/dees-element from ^2.0.34 to ^2.0.36
- - @fortawesome/fontawesome-svg-core from ^6.5.2 to ^6.6.0
- - @fortawesome/free-brands-svg-icons from ^6.5.2 to ^6.6.0
- - @fortawesome/free-regular-svg-icons from ^6.5.2 to ^6.6.0
- - @fortawesome/free-solid-svg-icons from ^6.5.2 to ^6.6.0
- - @tsclass/tsclass from ^4.0.63 to ^4.1.2
- - apexcharts from ^3.49.2 to ^3.53.0
- - highlight.js from 11.9.0 to 11.10.0
- - monaco-editor from ^0.50.0 to ^0.51.0
- - pdfjs-dist from ^4.3.136 to ^4.5.136
- - @git.zone/tsbuild from ^2.1.82 to ^2.1.84
- - @push.rocks/tapbundle from ^5.0.23 to ^5.0.24
- - @types/node from ^20.14.9 to ^22.5.2
## 2024-07-01 - 1.1.6 - fix(dees-dataview-codebox)
Corrected the font-family order for better font rendering.
- Corrected the font-family order in dees-dataview-codebox.ts to ensure 'Intel One Mono' is prioritized over 'Geist Mono'.
## 2024-07-01 - 1.1.5 - fix(dees-dataview-codebox)
Adjusted line number font weight in codebox
- Changed the line number font weight from 400 to 200 in the codebox for better visual alignment.
## 2024-07-01 - 1.1.4 - fix(UI)
Fixed font-family order for code and value elements
- Updated font-family order in dees-dataview-codebox.ts
- Updated font-family order in dees-dataview-statusobject.ts
## 2024-07-01 - 1.1.3 - fix(dees-dataview-codebox)
Adjusted codebox font weight and font family.
- Changed font weight from 200 to 400 for better readability.
- Updated font-family to include 'Geist Mono' for uniform appearance.
## 2024-06-30 - 1.1.2 - fix(elements)
Fix various UI components and improve styles
- Updated styles for multiple components for better appearance in dark mode.
- Added missing event handling in `dees-input-fileupload` component.
- Implemented tooltip support in `dees-label` component.
- Fixed alignment issues in `dees-appui-*` components.
- Resolved various accessibility issues in form elements.
## 2024-06-30 - 1.1.1 - fix(build)
Fix build script by updating source folders.
- Updated build script to correctly reference 'tsfolders' folder.
## 2024-06-30 - 1.1.0 - feat(project dependencies)
Updated various dependencies and internal fonts
- Updated dependencies in package.json with the latest versions.
- Updated font-family references from 'Roboto' and 'Inter' to 'Geist Sans' across multiple components.
## 2024-04-20 - 1.0.289 - Documentation Update
Updated the project documentation.
## 2024-02-05 to 2024-02-13 - 1.0.277 to 1.0.288 - Core Fixes
Series of core updates and patches.
## 2024-01-22 to 2024-02-05 - 1.0.257 to 1.0.276 - Core Fixes
More core updates and patches.
## 2024-01-21 - 1.0.256 to 1.0.257 - Core Fixes
Minor bugfixes for core functionalities.
## 2024-01-18 - 1.0.246 - Core Fixes
Bugfixes for core functionalities.
## 2024-01-15 - 1.0.242 to 1.0.245 - Core Fixes
Various core bugfixes.
## 2024-01-11 - 1.0.241 to 1.0.242 - Core Fixes
Bugfixes for core functionalities.
## 2024-01-10 - 1.0.240 to 1.0.241 - Core Fixes
Minor core fixes and improvements.
## 2024-01-09 - 1.0.239 to 1.0.240 - Core Fixes
Core updates and patches.
## 2023-12-26 - 1.0.238 to 1.0.239 - Core Fixes
Updates to core functionalities.
## 2023-12-20 - 1.0.236 to 1.0.238 - Core Fixes
Bugfixes and updates for core functionalities.
## 2023-12-08 - 1.0.233 to 1.0.236 - Core Fixes
Multiple updates to core functionalities.
## 2023-11-29 - 1.0.231 to 1.0.232 - Core Fixes
Minor core patches.
## 2023-10-31 - 1.0.229 to 1.0.230 - Core Fixes
Various updates and fixes for core.
## 2023-10-24 - 1.0.228 to 1.0.229 - Core Fixes
More core updates and patches.
## 2023-10-23 - 1.0.226 to 1.0.228 - Core Fixes
Series of updates and fixes for core functionalities.
## 2023-10-20 - 1.0.223 to 1.0.225 - Core Fixes
Minor updates and fixes for core.
## 2023-10-18 - 1.0.222 to 1.0.223 - Core Fixes
Core patches and bugfixes.
## 2023-10-17 - 1.0.221 to 1.0.222 - Core Fixes
Further updates to core functionalities.
## 2023-10-12 - 1.0.220 to 1.0.221 - Core Fixes
Minor core updates.
## 2023-10-11 - 1.0.219 to 1.0.220 - Core Fixes
Bugfixes and updates for core.
## 2023-10-07 - 1.0.217 to 1.0.219 - Core Fixes
Series of core updates and patches.
## 2023-10-05 - 1.0.216 to 1.0.217 - Core Fixes
Minor updates for core functionalities.
## 2023-09-22 - 1.0.212 to 1.0.215 - Core Fixes
Various updates and fixes for core.
## 2023-09-20 - 1.0.210 to 1.0.211 - Core Fixes
Series of core patches.
## 2023-09-18 - 1.0.209 to 1.0.210 - Core Fixes
Bugfixes and improvements to core.
## 2023-09-17 - 1.0.207 to 1.0.209 - Core Fixes
Minor updates and fixes.
## 2023-09-15 - 1.0.203 to 1.0.206 - Core Fixes
Series of core improvements and fixes.
## 2023-09-14 - 1.0.199 to 1.0.202 - Core Fixes
Patches for core functionalities.
## 2023-09-13 - 1.0.194 to 1.0.198 - Core Fixes
Various bugfixes and updates for core.
## 2023-09-12 - 1.0.190 to 1.0.193 - Core Fixes
Minor core patches.
## 2023-09-09 - 1.0.188 to 1.0.189 - Core Fixes
Updates and bugfixes for core.
## 2023-09-07 - 1.0.186 to 1.0.187 - Core Fixes
Core patches and improvements.
## 2023-09-04 - 1.0.184 to 1.0.185 - Core Fixes
Series of updates and bugfixes for core.
## 2023-09-01 - 1.0.180 to 1.0.183 - Core Fixes
Various core updates and fixes.
## 2023-08-28 - 1.0.177 to 1.0.179 - Core Fixes
Minor bugfixes and updates.
## 2023-08-26 - 1.0.176 to 1.0.177 - Core Fixes
Bugfixes for core functionalities.
## 2023-08-20 - 1.0.173 to 1.0.175 - Core Fixes
Series of updates and fixes for core.
## 2023-08-08 - 1.0.172 to 1.0.173 - Core Fixes
Minor core updates.
## 2023-08-07 - 1.0.171 to 1.0.172 - Core Fixes
Various updates to core.
## 2023-04-12 - 1.0.169 to 1.0.170 - Core Fixes
Bugfixes for core functionalities.
## 2023-04-10 - 1.0.163 to 1.0.168 - Core Fixes
Series of core updates and patches.
## 2023-04-07 - 1.0.160 to 1.0.162 - Core Fixes
Various updates for core functionalities.
## 2023-04-06 - 1.0.155 to 1.0.159 - Core Fixes
Bugfixes and improvements to core.
## 2023-03-27 - 1.0.150 to 1.0.154 - Core Fixes
Minor updates and patches for core functionalities.
## 2023-03-09 - 1.0.149 to 1.0.150 - Core Fixes
Bugfixes for core functionalities.
## 2023-01-17 - 1.0.146 to 1.0.148 - Core Fixes
Updates and fixes for core.
## 2023-01-16 - 1.0.144 to 1.0.145 - Core Fixes
Series of updates and bugfixes for core.
## 2023-01-13 - 1.0.134 to 1.0.143 - Core Fixes
Various core patches and improvements.
## 2023-01-12 - 1.0.132 to 1.0.133 - Core Fixes
Minor fixes for core functionalities.
## 2023-01-11 - 1.0.126 to 1.0.131 - Core Fixes
Series of updates and fixes for core.
## 2023-01-09 - 1.0.122 to 1.0.123 - Core Fixes
Minor core patches and updates.
## 2023-01-07 - 1.0.113 to 1.0.121 - Core Fixes
Updates and bugfixes for core functionalities.
## 2023-01-03 - 1.0.107 to 1.0.110 - Core Fixes
Series of core updates and fixes.
## 2022-12-12 - 1.0.105 to 1.0.106 - Core Fixes
Various core patches.
## 2022-12-07 - 1.0.104 to 1.0.105 - Core Fixes
Bugfixes and improvements to core functionalities.
## 2022-12-06 - 1.0.103 to 1.0.104 - Core Fixes
Series of updates and patches for core.
## 2022-08-18 - 1.0.98 to 1.0.102 - Core Fixes
Updates and bugfixes for core functionalities.
## 2022-07-14 - 1.0.93 to 1.0.94 - Core Fixes
Minor core updates.
## 2022-06-26 - 1.0.92 to 1.0.93 - Core Fixes
Bugfixes for core functionalities.
## 2022-06-10 - 1.0.91 to 1.0.92 - Core Fixes
Updates and patches for core.
## 2022-05-30 - 1.0.90 to 1.0.91 - Core Fixes
Minor updates and bugfixes.
## 2022-05-26 - 1.0.87 to 1.0.89 - Core Fixes
Series of core updates and patches.
## 2022-05-24 to 2022-05-20 - 1.0.83 to 1.0.86 - Core Fixes
Various updates and bugfixes for core functionalities.
## 2022-05-03 - 1.0.79 to 1.0.80 - Core Fixes
Updates and patches for core functionalities.
## 2022-03-18 - 1.0.74 to 1.0.77 - Core Fixes
Bugfixes and updates for core functionalities.
## 2022-01-24 - 1.0.73 to 1.0.74 - Core Fixes
Minor core patches.
## 2021-12-10 - 1.0.71 to 1.0.72 - Core Fixes
Series of updates and fixes for core functionalities.
## 2021-11-27 - 1.0.70 to 1.0.71 - Core Fixes
Bugfixes and improvements to core.
## 2021-10-08 - 1.0.68 to 1.0.69 - Core Fixes
Various core updates and patches.
## 2021-10-07 - 1.0.67 to 1.0.68 - Core Fixes
Minor updates and improvements for core.
## 2021-09-15 - 1.0.64 to 1.0.66 - Core Fixes
Bugfixes and improvements to core functionalities.
## 2021-09-14 - 1.0.61 to 1.0.62 - Core Fixes
Minor patches and updates for core.
## 2021-09-10 - 1.0.57 to 1.0.60 - Core Fixes
Series of updates and fixes for core functionalities.
## 2021-09-09 - 1.0.54 to 1.0.56 - Core Fixes
Various core updates and patches.
## 2021-09-01 - 1.0.51 to 1.0.53 - Core Fixes
Minor updates for core functionalities.
## 2021-08-29 - 1.0.49 to 1.0.50 - Core Fixes
Bugfixes and patches for core.
## 2021-08-27 - 1.0.44 to 1.0.48 - Core Fixes
Series of updates and improvements for core functionalities.
## 2021-08-26 - 1.0.42 to 1.0.43 - Core Fixes
Minor updates and bugfixes for core.
## 2021-08-25 - 1.0.38 to 1.0.41 - Core Fixes
Extracted series of core updates and patches.
## 2021-08-24 - 1.0.37 - Core Fix
Bugfixes for core functionalities.
## 2021-08-20 - 1.0.34 to 1.0.36 - Core Fixes
Core functionalities updates and patches.
## 2021-05-05 - 1.0.32 to 1.0.33 - Core Fixes
Series of core bugfixes.
## 2021-03-06 - 1.0.29 to 1.0.31 - Core Fixes
Updates and improvements for core functionalities.
## 2021-02-13 - 1.0.27 to 1.0.28 - Core Fixes
Bugfixes and patches for core functionalities.
## 2020-12-10 - 1.0.25 to 1.0.26 - Core Fixes
Various core updates and improvements.
## 2020-12-02 - 1.0.23 to 1.0.24 - Core Fixes
Minor updates for core functionalities.
## 2020-12-01 - 1.0.21 to 1.0.22 - Core Fixes
Bugfixes and patches for core.
## 2020-09-13 - 1.0.19 to 1.0.20 - Core Fixes
Updates and bugfixes for core functionalities.
## 2020-05-11 - 1.0.17 to 1.0.18 - Core Fixes
Core functionalities updates.

43
codex.md Normal file
View File

@ -0,0 +1,43 @@
# Codex: Project Overview and Codebase Structure
## Project Overview
- Package: `@design.estate/dees-catalog`
- Focus: Web Components library providing UI elements and layouts for modern web apps.
## Directory Layout
- ts_web/: TypeScript source files
- elements/: Individual Web Component definitions
- pages/: Page-level templates for composite layouts
- html/: Demo/app entry point loading the bundled scripts
- dist_bundle/: Bundled browser JS and source maps
- dist_ts_web/: ES module outputs for TypeScript/web consumers
- dist_watch/: Watch-mode development bundle with live reload
- test/: Browser-based tests using `@push.rocks/tapbundle`
## Component Patterns
- Each component in ts_web/elements/:
- Decorated with `@customElement('tag-name')`
- Extends `DeesElement` from `@design.estate/dees-element`
- Uses `@property` for reactive, reflected attributes
- Defines `static styles = [cssManager.defaultStyles, css`...`]`
- Implements `render()` returning a Lit `html` template with slots or markup
- Exposes a demo via `public static demo` linking to `.demo.ts` files
## Build & Development Workflow
- Install dependencies: `npm install` or `pnpm install`
- Build production bundle: `npm run build`
- Start dev watch mode: `npm run watch`
- Run tests: `npm test` (launches browser fixtures)
## Theming & Utilities
- Default global styles via `cssManager.defaultStyles`
- Theme-aware values with `cssManager.bdTheme(light, dark)`
- DOM utilities set up in `html/index.ts` using `@design.estate/dees-domtools`
## Documentation
- `readme.md` provides an overview of all components and basic usage
- Live examples in `.demo.ts` files
accessible via component `demo` static property
## Updates to this file
If you have pattern insisights or general changes to the codebase, please update this file.

View File

@ -2,17 +2,46 @@
"gitzone": {
"projectType": "wcc",
"module": {
"githost": "gitlab.com",
"githost": "code.foss.global",
"gitscope": "design.estate",
"gitrepo": "dees-catalog",
"description": "a webcomponents catalog for handling daily stuff on the web",
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
"npmPackagename": "@design.estate/dees-catalog",
"license": "MIT",
"projectDomain": "design.estate"
"projectDomain": "design.estate",
"keywords": [
"Web Components",
"User Interface",
"UI Library",
"Component Library",
"JavaScript",
"TypeScript",
"Dynamic Components",
"Modular Architecture",
"Reusable Components",
"Web Development",
"Application UI",
"Custom Elements",
"Shadow DOM",
"UI Elements",
"Dashboard Interfaces",
"Form Handling",
"Data Display",
"Visualization",
"Charting",
"Interactive Components",
"Responsive Design",
"Web Applications",
"Modern Web",
"Frontend Development"
]
}
},
"npmci": {
"npmGlobalTools": [],
"npmAccessLevel": "public"
},
"tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
}
}

View File

@ -1,43 +1,49 @@
{
"name": "@design.estate/dees-catalog",
"version": "1.0.246",
"version": "1.8.1",
"private": false,
"description": "website for lossless.com",
"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",
"typings": "dist_ts_web/index.d.ts",
"type": "module",
"scripts": {
"test": "tstest test/ --web",
"build": "tsbuild element --allowimplicitany && tsbundle element --production",
"build": "tsbuild tsfolders --allowimplicitany && tsbundle element --production",
"watch": "tswatch element",
"buildDocs": "tsdoc"
},
"author": "Lossless GmbH",
"license": "MIT",
"dependencies": {
"@design.estate/dees-domtools": "^2.0.55",
"@design.estate/dees-element": "^2.0.33",
"@design.estate/dees-wcctools": "^1.0.87",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
"@fortawesome/free-brands-svg-icons": "^6.5.1",
"@fortawesome/free-regular-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@design.estate/dees-domtools": "^2.1.1",
"@design.estate/dees-element": "^2.0.41",
"@design.estate/dees-wcctools": "^1.0.90",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-brands-svg-icons": "^6.7.2",
"@fortawesome/free-regular-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@push.rocks/smarti18n": "^1.0.4",
"@push.rocks/smartpromise": "^4.0.3",
"@push.rocks/smartstring": "^4.0.13",
"@tsclass/tsclass": "^4.0.46",
"highlight.js": "11.9.0",
"ibantools": "^4.3.9",
"pdfjs-dist": "^4.0.379"
"@push.rocks/smartpromise": "^4.2.0",
"@push.rocks/smartstring": "^4.0.15",
"@tsclass/tsclass": "^9.0.0",
"@webcontainer/api": "1.2.0",
"apexcharts": "^4.3.0",
"highlight.js": "11.11.1",
"ibantools": "^4.5.1",
"lucide": "^0.501.0",
"monaco-editor": "^0.52.2",
"pdfjs-dist": "^4.10.38",
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0"
},
"devDependencies": {
"@git.zone/tsbuild": "^2.1.72",
"@git.zone/tsbuild": "^2.1.84",
"@git.zone/tsbundle": "^2.0.15",
"@git.zone/tstest": "^1.0.77",
"@git.zone/tswatch": "^2.0.21",
"@git.zone/tstest": "^1.0.90",
"@git.zone/tswatch": "^2.0.37",
"@push.rocks/projectinfo": "^5.0.2",
"@push.rocks/tapbundle": "^5.0.15",
"@types/node": "^20.11.5"
"@push.rocks/tapbundle": "^5.5.6",
"@types/node": "^22.14.1"
},
"files": [
"ts/**/*",
@ -53,5 +59,32 @@
],
"browserslist": [
"last 1 chrome versions"
]
],
"keywords": [
"Web Components",
"User Interface",
"UI Library",
"Component Library",
"JavaScript",
"TypeScript",
"Dynamic Components",
"Modular Architecture",
"Reusable Components",
"Web Development",
"Application UI",
"Custom Elements",
"Shadow DOM",
"UI Elements",
"Dashboard Interfaces",
"Form Handling",
"Data Display",
"Visualization",
"Charting",
"Interactive Components",
"Responsive Design",
"Web Applications",
"Modern Web",
"Frontend Development"
],
"packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6"
}

12836
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

4
readme.hints.md Normal file
View File

@ -0,0 +1,4 @@
!!! Please pay attention to the following points when writing the readme: !!!
* Give a short rundown of components and a few points abputspecific features on each.
* Try to list all components in a summary.
* Then list all components with a short description.

1396
readme.md

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
/**
* autocreated commitinfo by @pushrocks/commitinfo
* autocreated commitinfo by @push.rocks/commitinfo
*/
export const commitinfo = {
name: '@design.estate/dees-catalog',
version: '1.0.246',
description: 'website for lossless.com'
version: '1.8.1',
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
}

View File

@ -1,11 +1,13 @@
export const dark = {
blue: '#0050b9',
blueActive: '#0069f2',
blueMuted: '#012452',
text: '#ffffff',
}
export const bright = {
blue: '#0050b9',
blueActive: '#0069f2',
blueMuted: '#0069f2',
text: '#333333',
}

View File

@ -0,0 +1,239 @@
import * as plugins from './00plugins.js';
import {
DeesElement,
type TemplateResult,
property,
customElement,
html,
css,
cssManager,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { DeesContextmenu } from './dees-contextmenu.js';
@customElement('dees-appui-activitylog')
export class DeesAppuiActivitylog extends DeesElement {
// STATIC
public static demo = () => html`<dees-appui-activitylog></dees-appui-activitylog>`;
// INSTANCE
public static styles = [
cssManager.defaultStyles,
css`
:host {
color: #fff;
position: relative;
display: block;
width: 100%;
max-width: 300px;
height: 100%;
background: #111c28;
font-family: 'Intel One Mono', sans-serif;
border-left: 1px solid #202020;
cursor: default;
}
.maincontainer {
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
}
.topbar {
position: absolute;
top: 0px;
height: 32px;
width: 100%;
padding: 0px 12px 0px 12px;
background: #0e151f;
}
.topbar .heading {
text-align: left;
line-height: 24px;
padding-top: 8px;
font-weight: 500;
font-size: 14px;
font-family: 'Geist Sans', sans-serif;
}
.activityContainer {
position: absolute;
top: 32px;
bottom: 40px;
width: 100%;
padding: 8px 0px;
overflow-y: scroll;
}
.streamingIndicator {
font-size: 12px;
text-align: center;
padding-top: 16px;
color: #888
}
.streamingIndicator.bottom {
padding-top: 0px;
padding-bottom: 16px;
}
.activityentry {
min-height: 30px;
font-size: 12px;
padding: 8px 16px;
border-bottom: 1px dotted #ffffff20;
}
.activityentry:last-of-type {
border-bottom: 1px solid #ffffff00;
}
.activityentry:hover {
background: #00000080;
}
.timestamp {
color: #ff8787;
}
.searchbox {
position: absolute;
bottom: 0px;
width: 100%;
height: 40px;
background: #0e151f;
}
.searchbox input {
color: #fff;
background: none;
width: 100%;
height: 40px;
line-height: 32px;
border: none;
padding: 4px 12px;
font-family: 'Intel One Mono', sans-serif;
}
.searchbox input:focus {
outline: none;
}
.bottomShadow {
position: absolute;
width: 100%;
height: 32px;
bottom: 40px;
background: linear-gradient(180deg, #111c2800 0%, #0e151f 100%);
pointer-events: none;
}
.topShadow {
position: absolute;
width: 100%;
height: 32px;
top: 32px;
background: linear-gradient(0deg, #111c2800 0%, #0e151f 100%);
pointer-events: none;
}
`,
];
public render(): TemplateResult {
return html`
${domtools.elementBasic.styles}
<style></style>
<div class="maincontainer">
<div class="topbar">
<div class="heading">Activity Log</div>
</div>
<div class="activityContainer">
<div class="streamingIndicator">streaming...</div>
<div class="activityentry" @contextmenu=${async eventArg => {
DeesContextmenu.openContextMenuWithOptions(eventArg, [
{
name: 'app settings',
action: async () => {},
},
{
name: 'account settings',
action: async () => {},
},
{
name: 'logout',
action: async () => {},
},
]);
}}>
<span class="timestamp">22:01:</span> Max Mustermann logged in
</div>
<div class="activityentry">
<span class="timestamp">22:02:</span> Max Mustermann viewed an invoice
</div>
<div class="activityentry">
<span class="timestamp">22:03:</span> Max Mustermann added a new contact
</div>
<div class="activityentry">
<span class="timestamp">22:04:</span> Max Mustermann updated account settings
</div>
<div class="activityentry">
<span class="timestamp">22:05:</span> Max Mustermann logged out
</div>
<div class="activityentry">
<span class="timestamp">22:06:</span> Max Mustermann logged in
</div>
<div class="activityentry">
<span class="timestamp">22:07:</span> Max Mustermann created a new invoice
</div>
<div class="activityentry">
<span class="timestamp">22:08:</span> Max Mustermann sent an invoice
</div>
<div class="activityentry">
<span class="timestamp">22:09:</span> Max Mustermann viewed reports
</div>
<div class="activityentry">
<span class="timestamp">22:10:</span> Max Mustermann logged out
</div>
<div class="activityentry">
<span class="timestamp">22:11:</span> Max Mustermann logged in
</div>
<div class="activityentry">
<span class="timestamp">22:12:</span> Max Mustermann deleted an invoice
</div>
<div class="activityentry">
<span class="timestamp">22:13:</span> Max Mustermann contacted support
</div>
<div class="activityentry">
<span class="timestamp">22:14:</span> Max Mustermann added a new user
</div>
<div class="activityentry">
<span class="timestamp">22:15:</span> Max Mustermann changed password
</div>
<div class="activityentry">
<span class="timestamp">22:16:</span> Max Mustermann logged out
</div>
<div class="activityentry">
<span class="timestamp">22:17:</span> Max Mustermann logged in
</div>
<div class="activityentry">
<span class="timestamp">22:18:</span> Max Mustermann archived an invoice
</div>
<div class="activityentry">
<span class="timestamp">22:19:</span> Max Mustermann approved a payment
</div>
<div class="activityentry">
<span class="timestamp">22:20:</span> Max Mustermann logged out
</div>
<div class="streamingIndicator bottom">loading more...</div>
</div>
<div class="searchbox">
<input type="text" placeholder="Search" />
</div>
<div class="topShadow"></div>
<div class="bottomShadow"></div>
</div>
`;
}
}

View File

@ -0,0 +1,77 @@
import {
DeesElement,
type TemplateResult,
property,
customElement,
html,
css,
cssManager,
} from '@design.estate/dees-element';
@customElement('dees-appui-appbar')
export class DeesAppuiBar extends DeesElement {
public static demo = () => html`<dees-appui-appbar></dees-appui-appbar>`;
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
position: relative;
height: 100%;
width: 100%;
height: 40px;
border-bottom: 1px solid #202020;
background: #000000;
color: #ffffff80;
font-size: 12px;
display: grid;
grid-template-columns: ${cssManager.cssGridColumns(3, 20)};
-webkit-app-region: drag;
}
.menus {
display: flex;
padding-left: 8px;
cursor: default;
}
.menuItem {
line-height: 24px;
padding: 0px 8px;
margin: 8px 0px;
border-radius: 4px;
-webkit-app-region: no-drag;
}
.menuItem:hover {
background: #ffffff20;
}
.breadcrumbs {
height: 24px;
line-height: 24px;
margin: 8px;
border-radius: 8px;
text-align: center;
}
`,
];
// INSTANCE
public render(): TemplateResult {
return html`
<div class="menus">
<dees-windowcontrols></dees-windowcontrols>
<div class="menuItem">File</div>
<div class="menuItem">View</div>
<div class="menuItem">Help</div>
<div class="menuItem">Terminal</div>
</div>
<div class="breadcrumbs">
tool:social.io > org:design.estate > prop:lossless.com
</div>
<div class="account"></div>
`;
}
}

View File

@ -0,0 +1,47 @@
import {
DeesElement,
type TemplateResult,
property,
customElement,
html,
css,
cssManager,
} from '@design.estate/dees-element';
@customElement('dees-appui-base')
export class DeesAppuiBase extends DeesElement {
public static demo = () => html`<dees-appui-base></dees-appui-base>`;
public static styles = [
cssManager.defaultStyles,
css`
:host {
position: absolute;
height: 100%;
width: 100%;
}
.maingrid {
position: absolute;
top: 40px;
height: calc(100% - 40px);
width: 100%;
display: grid;
grid-template-columns: 60px 240px auto 240px;
}
`,
];
// INSTANCE
public render(): TemplateResult {
return html`
<style></style>
<dees-appui-appbar></dees-appui-appbar>
<div class="maingrid">
<dees-appui-mainmenu></dees-appui-mainmenu>
<dees-appui-mainselector></dees-appui-mainselector>
<dees-appui-maincontent></dees-appui-maincontent>
<dees-appui-activitylog></dees-appui-activitylog>
</div>
`;
}
}

View File

@ -0,0 +1,161 @@
import * as interfaces from './interfaces/index.js';
import {
DeesElement,
type TemplateResult,
property,
customElement,
html,
css,
cssManager,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
@customElement('dees-appui-maincontent')
export class DeesAppuiMaincontent extends DeesElement {
public static demo = () => html`<dees-appui-maincontent></dees-appui-maincontent>`;
// INSTANCE
@property({
type: Array,
})
public tabs: interfaces.ITab[] = [
{ key: 'option 1', action: () => {} },
{ key: 'a very long option', action: () => {} },
{ key: 'reminder: set your tabs', action: () => {} },
{ key: 'option 4', action: () => {} },
];
@property()
public selectedTab = null;
public static styles = [
cssManager.defaultStyles,
css`
:host {
color: #fff;
display: block;
width: 100%;
height: 100%;
position: relative;
background: #161616;
}
.maincontainer {
position: absolute;
height: 100%;
right: 0px;
top: 0px;
width: 100%;
}
.topbar {
position: absolute;
width: 100%;
background: #000000;
user-select: none;
}
.topbar .tabsContainer {
padding-top: 20px;
padding-bottom: 0px;
position: relative;
z-index: 1;
display: grid;
margin-left: 24px;
font-size: 14px;
}
.topbar .tabsContainer .tab {
color: #a0a0a0;
white-space: nowrap;
margin-right: 30px;
padding-top: 4px;
padding-bottom: 12px;
transition: color 0.1s;
}
.topbar .tabsContainer .tab:hover {
color: #ffffff;
}
.topbar .tabsContainer .tab.selectedTab {
color: #e0e0e0;
}
.topbar .tabIndicator {
position: absolute;
z-index: 0;
left: 40px;
bottom: 0px;
height: 40px;
width: 40px;
background: #161616;
transition: all 0.1s;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
border-top: 1px solid #444444;
}
.mainicon {
}
`,
];
public render(): TemplateResult {
return html`
<style>
.topbar .tabsContainer {
grid-template-columns: repeat(${this.tabs.length}, min-content);
}
</style>
<div class="maincontainer">
<div class="topbar">
<div class="tabsContainer">
${this.tabs.map((tabArg) => {
return html`
<div
class="tab ${tabArg === this.selectedTab ? 'selectedTab' : null}"
@click="${() => {
this.selectedTab = tabArg;
this.updateTabIndicator();
tabArg.action();
}}"
>
${tabArg.key}
</div>
`;
})}
</div>
<div class="tabIndicator"></div>
</div>
</div>
`;
}
/**
* updates the indicator
*/
private updateTabIndicator() {
let selectedTab = this.selectedTab;
const tabIndex = this.tabs.indexOf(selectedTab);
const selectedTabElement: HTMLElement = this.shadowRoot.querySelector(
`.tabsContainer .tab:nth-child(${tabIndex + 1})`
);
const tabsContainer: HTMLElement = this.shadowRoot.querySelector('.tabsContainer');
const marginLeft = parseInt(window.getComputedStyle(tabsContainer).getPropertyValue("margin-left"));
const tabIndicator: HTMLElement = this.shadowRoot.querySelector('.tabIndicator');
tabIndicator.style.width = selectedTabElement.clientWidth + 24 + 'px';
tabIndicator.style.left = selectedTabElement.offsetLeft + marginLeft - 12 + 'px';
}
private updateTab(tabArg: interfaces.ITab) {
this.selectedTab = tabArg;
this.updateTabIndicator();
this.selectedTab.action();
}
firstUpdated() {
this.updateTab(this.tabs[0]);
}
}

View File

@ -0,0 +1,142 @@
import * as plugins from './00plugins.js';
import * as interfaces from './interfaces/index.js';
import {
DeesElement,
type TemplateResult,
property,
customElement,
html,
css,
cssManager,
} from '@design.estate/dees-element';
import { DeesContextmenu } from './dees-contextmenu.js';
/**
* the most left menu
* usually used as organization selector
*/
@customElement('dees-appui-mainmenu')
export class DeesAppuiMainmenu extends DeesElement {
public static demo = () => html`<dees-appui-mainmenu></dees-appui-mainmenu>`;
// INSTANCE
// INSTANCE
@property()
public tabs: interfaces.ITab[] = [
{ key: 'option 1', iconName: 'building', action: () => {} },
{ key: 'option 2', iconName: 'building', action: () => {} },
{ key: 'option 3', iconName: 'building', action: () => {} },
{ key: 'option 4', iconName: 'building', action: () => {} },
];
@property()
public selectedTab: interfaces.ITab;
public static styles = [
cssManager.defaultStyles,
css`
.mainContainer {
--menuSize: 60px;
color: #ccc;
z-index: 10;
display: block;
position: relative;
width: var(--menuSize);
height: 100%;
background: #000000;
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5);
user-select: none;
border-right: 1px solid #202020;
}
.tabsContainer {
}
.tab {
padding-top: 18px;
font-size: 24px;
width: var(--menuSize);
height: var(--menuSize);
text-align: center;
transition: color 0.1s, background 0.2s;
}
.tab:hover {
background: rgba(255, 255, 255, 0.15);
}
.tab.selectedTab {
color: #fff;
background: rgba(255, 255, 255, 0.1);
}
.tabIndicator {
opacity: 0;
background: #4e729a;
position: absolute;
width: 5px;
height: calc((var(--menuSize) / 3) * 2);
left: 0px;
top: calc(var(--menuSize) - (((var(--menuSize) / 3) * 2) / 2));
border-top-right-radius: 7px;
border-bottom-right-radius: 7px;
transition: all 0.1s;
}
`,
];
public render(): TemplateResult {
return html`
<div class="mainContainer" @contextmenu=${(eventArg) => {
DeesContextmenu.openContextMenuWithOptions(eventArg, [{
name: 'app settings',
action: async () => {},
iconName: 'gear',
}])
}}>
<div class="tabsContainer">
${this.tabs.map((tabArg) => {
return html`
<div
class="tab ${tabArg === this.selectedTab ? 'selectedTab' : null}"
@click="${() => {
this.updateTab(tabArg);
}}"
>
<dees-icon iconFA="${tabArg.iconName as any}"></dees-icon>
</div>
`;
})}
</div>
<div class="tabIndicator"></div>
</div>
`;
}
private async updateTabIndicator() {
let selectedTab = this.selectedTab;
if (!selectedTab) {
selectedTab = this.tabs[0];
}
const tabIndex = this.tabs.indexOf(selectedTab);
const selectedTabElement: HTMLElement = this.shadowRoot.querySelector(
`.tabsContainer .tab:nth-child(${tabIndex + 1})`
);
const tabIndicator: HTMLElement = this.shadowRoot.querySelector('.tabIndicator');
const offsetTop = selectedTabElement.offsetTop;
tabIndicator.style.opacity = `1`;
tabIndicator.style.top = `calc(${offsetTop}px + (var(--menuSize) / 6))`;
}
updateTab(tabArg: interfaces.ITab) {
this.selectedTab = tabArg;
this.updateTabIndicator();
this.selectedTab.action();
}
firstUpdated() {
this.updateTab(this.tabs[0]);
}
}

View File

@ -0,0 +1,160 @@
import * as plugins from './00plugins.js';
import * as interfaces from './interfaces/index.js';
import { DeesContextmenu } from './dees-contextmenu.js';
import {
DeesElement,
type TemplateResult,
property,
customElement,
html,
css,
cssManager,
} from '@design.estate/dees-element';
/**
* the property selector menu
* mainly used to select assets within in an organization
*/
@customElement('dees-appui-mainselector')
export class DeesAppuiMainselector extends DeesElement {
public static demo = () => html`<dees-appui-mainselector></dees-appui-mainselector>`;
// INSTANCE
@property()
public selectionOptions: interfaces.ISelectionOption[] = [
{
key: 'Overview',
action: () => {},
},
{
key: 'option 1',
action: () => {},
},
{ key: 'option 2', action: () => {} },
{ key: 'option 3', action: () => {} },
{ key: 'option 4', action: () => {} },
];
@property()
public selectedOption: interfaces.ISelectionOption = null;
public static styles = [
cssManager.defaultStyles,
css`
:host {
color: #fff;
position: relative;
display: block;
width: 100%;
max-width: 300px;
height: 100%;
background: #000000;
border-right: 1px solid #222222;
}
.maincontainer {
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
}
.topbar {
position: absolute;
height: 32px;
width: 100%;
}
.topbar .heading {
padding-left: 16px;
padding-top: 8px;
line-height: 24px;
font-family: 'Geist Sans', sans-serif;
font-weight: 600;
font-size: 14px;
}
.selectionOptions {
position: absolute;
top: 32px;
padding-top: 8px;
left: 0px;
width: 100%;
font-family: 'Geist Sans', sans-serif;
font-size: 14px;
}
.selectionOptions .selectionOption {
cursor: default;
margin-left: 16px;
margin-right: 16px;
padding-top: 8px;
padding-bottom: 8px;
border-top: 1px dotted #303030;
border-left: 0px solid rgba(0, 0, 0, 0);
transition: all 0.1s;
}
.selectionOptions .selectionOption:hover {
border-left: 2px solid #26a69a50;
padding-left: 8px;
}
.selectionOptions .selectionOption:first-child {
border-top: 1px solid rgba(0, 0, 0, 0);
}
.selectionOptions .selectionOption.selectedOption {
border-left: 4px solid #26a69a;
padding-left: 10px;
}
`,
];
public render(): TemplateResult {
return html`
<style></style>
<div class="maincontainer">
<div class="topbar">
<div class="heading">Properties</div>
</div>
<div class="selectionOptions">
${this.selectionOptions.map((selectionOptionArg) => {
return html`
<div
class="selectionOption ${this.selectedOption === selectionOptionArg
? 'selectedOption'
: null}"
@click="${() => {
this.selectOption(selectionOptionArg);
}}"
@contextmenu="${(eventArg: MouseEvent) => {
DeesContextmenu.openContextMenuWithOptions(eventArg, [
{
name: 'property settings',
action: async () => {},
iconName: 'gear',
},
]);
}}"
>
${selectionOptionArg.key}
</div>
`;
})}
</div>
</div>
`;
}
private selectOption(optionArg: interfaces.ISelectionOption) {
this.selectedOption = optionArg;
this.selectedOption.action();
}
firstUpdated() {
this.selectOption(this.selectionOptions[0]);
}
}

View File

@ -0,0 +1,12 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html`
<div style="display: flex; gap: 8px; align-items: center;">
<dees-badge .text=${'Default'}></dees-badge>
<dees-badge .type=${'primary'} .text=${'Primary'}></dees-badge>
<dees-badge .type=${'success'} .text=${'Success'}></dees-badge>
<dees-badge .type=${'warning'} .text=${'Warning'}></dees-badge>
<dees-badge .type=${'error'} .text=${'Error'}></dees-badge>
<dees-badge .type=${'primary'} .rounded=${true} .text=${'Rounded'}></dees-badge>
</div>
`;

View File

@ -0,0 +1,96 @@
import {
DeesElement,
css,
cssManager,
customElement,
html,
property,
type CSSResult,
type TemplateResult,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { demoFunc } from './dees-badge.demo.js';
declare global {
interface HTMLElementTagNameMap {
'dees-badge': DeesBadge;
}
}
@customElement('dees-badge')
export class DeesBadge extends DeesElement {
public static demo = demoFunc;
@property({ type: String })
public type: 'default' | 'primary' | 'success' | 'warning' | 'error' = 'default';
@property({ type: String })
public text: string = '';
@property({ type: Boolean })
public rounded: boolean = false;
constructor() {
super();
domtools.elementBasic.setup();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: inline-block;
}
.badge {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 2px 8px;
font-size: 12px;
font-weight: 500;
line-height: 1.5;
border-radius: 4px;
white-space: nowrap;
}
.badge.rounded {
border-radius: 12px;
}
.badge.default {
background: ${cssManager.bdTheme('#f5f5f5', '#333')};
color: ${cssManager.bdTheme('#666', '#ccc')};
}
.badge.primary {
background: #0050b9;
color: #ffffff;
}
.badge.success {
background: #2e7d32;
color: #ffffff;
}
.badge.warning {
background: #ed6c02;
color: #ffffff;
}
.badge.error {
background: #e4002b;
color: #ffffff;
}
`,
];
public render(): TemplateResult {
return html`
<div class="badge ${this.type} ${this.rounded ? 'rounded' : ''}">
${this.text}
</div>
`;
}
}

View File

@ -35,7 +35,8 @@ export class DeesButton extends DeesElement {
public eventDetailData: string;
@property({
type: Boolean
type: Boolean,
reflect: true,
})
public disabled = false;
@ -61,11 +62,10 @@ export class DeesButton extends DeesElement {
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
box-sizing: border-box;
font-family: 'Roboto', 'monospace';
font-family: 'Geist Sans', 'monospace';
}
:host([hidden]) {
display: none;
@ -84,7 +84,7 @@ export class DeesButton extends DeesElement {
border: 1px solid ${cssManager.bdTheme('#eee', '#333')};
border-top: ${cssManager.bdTheme('1px solid #eee', '1px solid #444')};
border-radius: 4px;
line-height: 40px;
height: 40px;
padding: 0px 8px;
min-width: 100px;
user-select: none;
@ -104,13 +104,6 @@ export class DeesButton extends DeesElement {
border-top: 1px solid #0069f2;
}
.button.disabled {
background: ${cssManager.bdTheme('#ffffff00', '#11111100')};
border: 1px dashed ${cssManager.bdTheme('#666666', '#666666')};
color: #9b9b9e;
cursor: default;
}
.button.highlighted {
background: #e4002b;
border: none;
@ -132,6 +125,14 @@ export class DeesButton extends DeesElement {
.button.discreet:hover {
background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.1)', 'rgba(255, 255, 255, 0.1)')};
}
.button.disabled {
background: ${cssManager.bdTheme('#ffffff00', '#11111100')};
border: 1px dashed ${cssManager.bdTheme('#666666', '#666666')};
color: #9b9b9e;
cursor: default;
}
.button.hidden {
display: none;
}

View File

@ -0,0 +1,21 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => {
return html`
<style>
.demoBox {
position: relative;
background: #000000;
height: 100%;
width: 100%;
padding: 40px;
box-sizing: border-box;
}
</style>
<div class="demoBox">
<dees-chart-area
.label=${'System Usage'}
></dees-chart-area>
</div>
`;
};

View File

@ -0,0 +1,266 @@
import {
DeesElement,
css,
cssManager,
customElement,
html,
property,
state,
type CSSResult,
type TemplateResult,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { demoFunc } from './dees-chart-area.demo.js';
import ApexCharts from 'apexcharts';
declare global {
interface HTMLElementTagNameMap {
'dees-chart-area': DeesChartArea;
}
}
@customElement('dees-chart-area')
export class DeesChartArea extends DeesElement {
public static demo = demoFunc;
// instance
@state()
public chart: ApexCharts;
@property()
public label: string = 'Untitled Chart';
private resizeObserver: ResizeObserver;
constructor() {
super();
domtools.elementBasic.setup();
this.resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
if (entry.target.classList.contains('mainbox')) {
this.resizeChart(); // Call resizeChart when the .mainbox size changes
}
}
});
this.registerStartupFunction(async () => {
this.updateComplete.then(() => {
const mainbox = this.shadowRoot.querySelector('.mainbox');
if (mainbox) {
this.resizeObserver.observe(mainbox); // Start observing the .mainbox element
}
});
});
this.registerGarbageFunction(async () => {
this.resizeObserver.disconnect();
});
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
font-family: 'Geist Sans', sans-serif;
color: #ccc;
font-weight: 600;
font-size: 12px;
}
.mainbox {
position: relative;
width: 100%;
height: 400px;
background: #111;
border-radius: 8px;
}
.chartTitle {
position: absolute;
top: 0;
left: 0;
width: 100%;
text-align: center;
padding-top: 16px;
}
.chartContainer {
position: absolute;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
padding: 32px 16px 16px 0px;
}
`,
];
public render(): TemplateResult {
return html`
<div class="mainbox">
<div class="chartTitle">${this.label}</div>
<div class="chartContainer"></div>
</div>
`;
}
public async firstUpdated() {
const domtoolsInstance = await this.domtoolsPromise;
var options: ApexCharts.ApexOptions = {
series: [
{
name: 'cpu',
data: [
{ x: '2025-01-15T03:00:00', y: 25 },
{ x: '2025-01-15T07:00:00', y: 30 },
{ x: '2025-01-15T11:00:00', y: 20 },
{ x: '2025-01-15T15:00:00', y: 35 },
{ x: '2025-01-15T19:00:00', y: 25 },
],
},
{
name: 'memory',
data: [
{ x: '2025-01-15T03:00:00', y: 10 },
{ x: '2025-01-15T07:00:00', y: 12 },
{ x: '2025-01-15T11:00:00', y: 10 },
{ x: '2025-01-15T15:00:00', y: 30 },
{ x: '2025-01-15T19:00:00', y: 40 },
],
},
],
chart: {
width: 0, // Adjusted for responsive width
height: 0, // Adjusted for responsive height
type: 'area',
toolbar: {
show: false, // This line disables the toolbar
},
},
dataLabels: {
enabled: false,
},
stroke: {
width: 1,
curve: 'smooth',
},
xaxis: {
type: 'datetime', // Time-series data
labels: {
format: 'hh:mm A', // Time formatting
style: {
colors: '#9e9e9e', // Label color
fontSize: '12px',
},
},
axisBorder: {
show: false, // Hide x-axis border
},
axisTicks: {
show: false, // Hide x-axis ticks
},
},
yaxis: {
min: 0,
labels: {
formatter: function (val: number) {
return `${val} Mbps`; // Format Y-axis labels
},
style: {
colors: '#9e9e9e', // Label color
fontSize: '12px',
},
},
axisBorder: {
show: false, // Hide y-axis border
},
axisTicks: {
show: false, // Hide y-axis ticks
},
},
tooltip: {
shared: true, // Enables the tooltip to display across series
intersect: false, // Allows hovering anywhere on the chart
followCursor: true, // Makes tooltip follow mouse even between points
x: {
format: 'dd/MM/yy HH:mm',
},
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
// Get the x value
const xValue = w.globals.labels[dataPointIndex];
// Iterate through each series and get its value
let tooltipContent = `<div style="padding: 10px; background: #1e1e2f; color: white; border-radius: 5px;">`;
tooltipContent += ``; // `<strong>Time:</strong> ${xValue}<br/>`;
series.forEach((s, index) => {
const label = w.globals.seriesNames[index]; // Get series label
const value = s[dataPointIndex]; // Get value at data point
tooltipContent += `<strong>${label}:</strong> ${value} Mbps<br/>`;
});
tooltipContent += `</div>`;
return tooltipContent;
},
},
grid: {
xaxis: {
lines: {
show: true, // This enables the grid lines along the x-axis
},
},
yaxis: {
lines: {
show: true,
},
},
borderColor: '#333', // Set the color of the grid lines
strokeDashArray: 0, // Solid line
row: {
colors: [], // This can be used to alternate the shading of the horizontal rows
opacity: 0.1,
},
column: {
colors: [], // For vertical column bands, not needed here but available for customization
opacity: 0.1,
},
},
fill: {
type: 'gradient', // Gradient fill for the area
gradient: {
shade: 'dark',
type: 'vertical',
gradientToColors: ['#9c27b0'], // Gradient color ending
stops: [0, 100],
},
},
};
this.chart = new ApexCharts(this.shadowRoot.querySelector('.chartContainer'), options);
await this.chart.render();
await this.resizeChart();
}
public async resizeChart() {
const mainbox: HTMLDivElement = this.shadowRoot.querySelector('.mainbox');
const chartContainer: HTMLDivElement = this.shadowRoot.querySelector('.chartContainer');
// Get computed style of the element
const styleMainbox = window.getComputedStyle(mainbox);
const styleChartContainer = window.getComputedStyle(chartContainer);
// Extract padding values
const paddingTop = parseInt(styleChartContainer.paddingTop, 10);
const paddingBottom = parseInt(styleChartContainer.paddingBottom, 10);
const paddingLeft = parseInt(styleChartContainer.paddingLeft, 10);
const paddingRight = parseInt(styleChartContainer.paddingRight, 10);
// Calculate the actual width and height to use, subtracting padding
const actualWidth = mainbox.clientWidth - paddingLeft - paddingRight;
const actualHeight = mainbox.offsetHeight - paddingTop - paddingBottom;
await this.chart.updateOptions({
chart: {
width: actualWidth,
height: actualHeight,
},
});
}
}

View File

@ -0,0 +1,20 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => {
return html`
<style>
.demoBox {
position: relative;
background: #000000;
height: 100%;
width: 100%;
padding: 40px;
}
</style>
<div class="demoBox">
<dees-chart-log
.label=${'Event Log'}
></dees-chart-log>
</div>
`;
};

View File

@ -0,0 +1,89 @@
import {
DeesElement,
css,
cssManager,
customElement,
html,
property,
state,
type CSSResult,
type TemplateResult,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { demoFunc } from './dees-chart-log.demo.js';
import ApexCharts from 'apexcharts';
declare global {
interface HTMLElementTagNameMap {
'dees-chart-log': DeesChartLog;
}
}
@customElement('dees-chart-log')
export class DeesChartLog extends DeesElement {
public static demo = demoFunc;
// instance
@state()
public chart: ApexCharts;
@property()
public label: string = 'Untitled Chart';
constructor() {
super();
domtools.elementBasic.setup();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
font-family: 'Geist Sans', sans-serif;
color: #ccc;
font-weight: 600;
font-size: 12px;
}
.mainbox {
position: relative;
width: 100%;
height: 400px;
background: #222;
border-radius: 8px;
padding: 32px 16px 16px 0px;
}
.chartTitle {
position: absolute;
top: 0;
left: 0;
width: 100%;
text-align: center;
padding-top: 16px;
}
.chartContainer {
position: relative;
width: 100%;
height: 100%;
}
`,
];
public render(): TemplateResult {
return html` <div class="mainbox">
<div class="chartTitle">${this.label}</div>
<div class="chartContainer"></div>
</div> `;
}
public async firstUpdated() {
const domtoolsInstance = await this.domtoolsPromise;
}
public async updateLog() {
}
}

View File

@ -159,7 +159,7 @@ export class DeesContextmenu extends DeesElement {
this.destroy();
}}>
<dees-icon .iconFA=${'xmark'}></dees-icon
>Deactivate Contextmenu globally.
>allow native context
</div>
` : html``}
</div>

View File

@ -48,7 +48,7 @@ export class DeesDataviewCodebox extends DeesElement {
display: block;
text-align: left;
font-size: 16px;
font-family: 'Roboto', 'Inter', sans-serif;
font-family: 'Geist Sans', sans-serif;
}
.mainbox {
position: relative;
@ -142,7 +142,7 @@ export class DeesDataviewCodebox extends DeesElement {
.lineNumbers {
line-height: 1.4em;
font-weight: 200;
font-family: 'Intel One Mono', 'monospace';
font-family: 'Intel One Mono', 'Geist Mono', 'monospace';
}
.hljs-string {

View File

@ -1,39 +1,49 @@
import { html } from '@design.estate/dees-element';
import { html, cssManager } from '@design.estate/dees-element';
import * as tsclass from '@tsclass/tsclass';
export const demoFunc = () => html`<dees-dataview-statusobject
.statusObject=${{
id: '1',
name: 'Demo Item',
combinedStatus: 'partly_ok',
combinedStatusText: 'partly_ok',
details: [
{
name: 'Detail 1',
value: 'Value 1',
status: 'ok',
statusText: 'OK',
},
{
name: 'Detail 2',
value: 'Value 2',
status: 'partly_ok',
statusText: 'partly_ok',
},
{
name: 'Detail 3',
value: 'Value 3',
status: 'not_ok',
statusText: 'not_ok',
},
{
name: 'Detail 4',
value:
'Value 4 jhdkfjhalskdfjhfdjskalsdkfjhfdjskalskdjfhjdkslaksjdhfjdkslaskdfjhfjdkslaskdjfhjdskalskdjhfdjskalskdjfhdjskl',
status: 'ok',
statusText: 'OK',
},
],
} as tsclass.code.IStatusObject}
>
</dees-dataview-statusobject>`;
export const demoFunc = () => html` <style>
.demo {
background: ${cssManager.bdTheme('#eeeeeb', '#000000')};
display: block;
content: '';
padding: 40px;
}
</style>
<div class="demo">
<dees-dataview-statusobject
.statusObject=${{
id: '1',
name: 'Demo Item',
combinedStatus: 'partly_ok',
combinedStatusText: 'partly_ok',
details: [
{
name: 'Detail 1',
value: 'Value 1',
status: 'ok',
statusText: 'OK',
},
{
name: 'Detail 2',
value: 'Value 2',
status: 'partly_ok',
statusText: 'partly_ok',
},
{
name: 'Detail 3',
value: 'Value 3',
status: 'not_ok',
statusText: 'not_ok',
},
{
name: 'Detail 4',
value:
'Value 4 jhdkfjhalskdfjhfdjskalsdkfjhfdjskalskdjfhjdkslaksjdhfjdkslaskdfjhfjdkslaskdjfhjdskalskdjhfdjskalskdjfhdjskl',
status: 'ok',
statusText: 'OK',
},
],
} as tsclass.code.IStatusObject}
>
</dees-dataview-statusobject>
</div>`;

View File

@ -37,6 +37,7 @@ export class DeesDataviewStatusobject extends DeesElement {
box-shadow: 0px 1px 3px #00000030;
min-height: 48px;
color: ${cssManager.bdTheme('#000', '#fff')};
border-top: ${cssManager.bdTheme('none', '1px solid #ffffff10')};
cursor: default;
}
@ -68,12 +69,12 @@ export class DeesDataviewStatusobject extends DeesElement {
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
border: 1px solid ${cssManager.bdTheme('#999', '#444')};
border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#444')};
text-align: center;
padding: 4px;
border-radius: 3px;
margin-right: 16px;
color: #ffffff80;
color: ${cssManager.bdTheme('#333', '#ffffff80')};
user-select: none;
}
@ -106,7 +107,7 @@ export class DeesDataviewStatusobject extends DeesElement {
align-items: center;
display: grid;
grid-template-columns: 40px auto;
border-top: 1px dotted ${cssManager.bdTheme('#999', '#282828')};
border-top: 1px dotted ${cssManager.bdTheme('#e0e0e0', '#282828')};
transition: all 0.2s;
}
@ -114,6 +115,10 @@ export class DeesDataviewStatusobject extends DeesElement {
background: #ffffff05;
}
.detail:active {
background: #ffffff10;
}
.detail .detailsText {
padding-top: 8px;
padding-bottom: 8px;
@ -128,7 +133,7 @@ export class DeesDataviewStatusobject extends DeesElement {
.detail .detailsText .value {
font-size: 14px;
font-family: 'Intel One Mono';
font-family: 'Intel One Mono', 'Geist Mono';
}
`,
];

View File

@ -0,0 +1,98 @@
import {
DeesElement,
property,
html,
customElement,
type TemplateResult,
css,
cssManager,
domtools
} from '@design.estate/dees-element';
const deferred = domtools.plugins.smartpromise.defer();
declare global {
interface HTMLElementTagNameMap {
'dees-editormarkdown': DeesEditorMarkdown;
}
}
@customElement('dees-editormarkdown')
export class DeesEditorMarkdown extends DeesElement {
public static demo = () => html`<dees-editormarkdown></dees-editormarkdown>`;
public static styles = [
cssManager.defaultStyles,
css`
.gridcontainer {
position: absolute;
height: 100%;
width: 100%;
display: grid;
grid-template-columns: 60% 40%;
}
.editorContainer {
position: relative;
}
.outletContainer {
background: #111;
color: #fff;
font-family: 'Roboto Slab';
padding: 20px;
overflow-y: scroll;
}
`,
];
public render() {
return html`
<div class="gridcontainer">
<div class="editorContainer">
<dees-editor
.language=${'markdown'}
.content=${`# a test content
This is test content that is of longer form an hopefully starts to wrap when I need it. And yes, it does perfectly. nice.
Test | Hello
--- | ---
Yeah | So good
This is real asset I think. Why would we want to leave that on the table? Can you tell my that?
Why are we here?
Do you know?
> note:
There is something going on.
\`\`\`typescript
const hello = 'yes'
\`\`\`
`}
wordWrap="bounded"
></dees-editor>
</div>
<div class="outletContainer">
<dees-editormarkdownoutlet></dees-editormarkdownoutlet>
</div>
</div>
`;
}
public async firstUpdated(_changedPropertiesArg) {
await super.firstUpdated(_changedPropertiesArg);
const editor = this.shadowRoot.querySelector('dees-editor');
// lets care about wiring the markdown stuff.
const markdownOutlet = this.shadowRoot.querySelector('dees-editormarkdownoutlet');
const smartmarkdownInstance = new domtools.plugins.smartmarkdown.SmartMarkdown();
const mdParsedResult = await smartmarkdownInstance.getMdParsedResultFromMarkdown('loading...')
editor.contentSubject.subscribe(async contentArg => {
await mdParsedResult.updateFromMarkdownString(contentArg)
const html = mdParsedResult.html;
markdownOutlet.updateHtmlText(html);
})
}
}

View File

@ -0,0 +1,42 @@
import { customElement, DeesElement, html, type TemplateResult } from '@design.estate/dees-element';
declare global {
interface HTMLElementTagNameMap {
'dees-editormarkdownoutlet': DeesEditorMarkdownOutlet;
}
}
@customElement('dees-editormarkdownoutlet')
export class DeesEditorMarkdownOutlet extends DeesElement {
// DEMO
public static demo = () => html`<dees-editormarkdownoutlet></dees-editormarkdownoutlet>`;
// INSTANCE
private outlet: HTMLElement;
public render(): TemplateResult {
return html`
<div class="outlet markdown-body">
<h1>Hi there</h1>
</div>
`;
}
public async firstUpdated(_changedProperties: Map<string | number | symbol, unknown>) {
await super.firstUpdated(_changedProperties);
const styleElement = document.createElement('style');
const cssText = await (
await fetch('https://unpkg.com/github-markdown-css@5.1.0/github-markdown-dark.css')
).text();
styleElement.textContent = cssText;
this.shadowRoot.append(styleElement);
}
public async updateHtmlText(htmlTextArg: string) {
await this.updateComplete;
if (!this.outlet) {
this.outlet = this.shadowRoot.querySelector('.outlet');
}
this.outlet.innerHTML = htmlTextArg;
}
}

View File

@ -0,0 +1,126 @@
import {
DeesElement,
property,
html,
customElement,
type TemplateResult,
css,
cssManager,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import type * as monaco from 'monaco-editor';
declare global {
interface HTMLElementTagNameMap {
'dees-editor': DeesEditor;
}
}
@customElement('dees-editor')
export class DeesEditor extends DeesElement {
// DEMO
public static demo = () => html` <dees-editor></dees-editor> `;
// STATIC
public static monacoDeferred: ReturnType<typeof domtools.plugins.smartpromise.defer>;
// INSTANCE
public editorDeferred = domtools.plugins.smartpromise.defer<monaco.editor.IStandaloneCodeEditor>();
public language = 'typescript';
@property({
type: String
})
public content = "function hello() {\n\talert('Hello world!');\n}";
@property({
type: Object
})
public contentSubject = new domtools.plugins.smartrx.rxjs.Subject<string>();
@property({
type: Boolean
})
public wordWrap: monaco.editor.IStandaloneEditorConstructionOptions['wordWrap'] = 'off';
constructor() {
super();
domtools.DomTools.setupDomTools();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
}
* {
box-sizing: border-box;
}
#container {
position: absolute;
height: 100%;
width: 100%;
}
`,
];
public render(): TemplateResult {
return html`
<div class="mainbox">
<div id="container"></div>
</div>
`;
}
public async firstUpdated(
_changedProperties: Map<string | number | symbol, unknown>
): Promise<void> {
super.firstUpdated(_changedProperties);
const container = this.shadowRoot.getElementById('container');
if (!DeesEditor.monacoDeferred) {
DeesEditor.monacoDeferred = domtools.plugins.smartpromise.defer();
const scriptUrl = `https://cdn.jsdelivr.net/npm/monaco-editor/min/vs/loader.js`;
const script = document.createElement('script');
script.src = scriptUrl;
script.onload = () => {
DeesEditor.monacoDeferred.resolve();
};
document.head.appendChild(script);
}
await DeesEditor.monacoDeferred.promise;
(window as any).require.config({
paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor/min/vs' },
});
(window as any).require(['vs/editor/editor.main'], async () => {
const editor = ((window as any).monaco.editor as typeof monaco.editor).create(container, {
value: this.content,
language: this.language,
theme: 'vs-dark',
useShadowDOM: true,
fontSize: 16,
automaticLayout: true,
wordWrap: this.wordWrap
});
this.editorDeferred.resolve(editor);
});
const css = await (
await fetch('https://cdn.jsdelivr.net/npm/monaco-editor/min/vs/editor/editor.main.css')
).text();
const styleElement = document.createElement('style');
styleElement.textContent = css;
this.shadowRoot.append(styleElement);
// editor is setup let do the rest
const editor = await this.editorDeferred.promise;
editor.onDidChangeModelContent(async eventArg => {
this.contentSubject.next(editor.getValue());
});
this.contentSubject.next(editor.getValue());
}
}

View File

@ -1,13 +1,14 @@
import { html, domtools } from '@design.estate/dees-element';
import { html, domtools, cssManager } from '@design.estate/dees-element';
import type { DeesForm } from './dees-form.js';
export const demoFunc = () => html`
<style>
.demoContainer {
max-width: 400px;
margin: auto;
margin: 24px auto;
padding: 16px;
background: #111;
background: ${cssManager.bdTheme('#eeeeeb', '#111')};
box-shadow: 0px 1px 3px #00000030;
}
</style>
<div class="demoContainer">
@ -38,7 +39,9 @@ export const demoFunc = () => html`
<dees-input-typelist
.label=${'a type list'}
></dees-input-typelist>
<dees-input-text .required="${true}" key="hello1" label="a text"></dees-input-text>
<dees-input-text .required="${true}" key="hello1" label="a text" .description=${`
This is an awesome description.
`}></dees-input-text>
<dees-input-text .required="${true}" key="hello2" label="also a text"></dees-input-text>
<dees-input-text
.required="${true}"

View File

@ -101,6 +101,10 @@ export class DeesForm extends DeesElement {
}
}
/**
* collects the form data
* @returns
*/
public async collectFormData() {
const children = this.getFormElements();
const valueObject: { [key: string]: string | number | boolean | any[] } = {};

View File

@ -0,0 +1,14 @@
import { html } from '@design.estate/dees-element';
export function demoFunc() {
return html`
<dees-heading level="1">This is a H1 heading</dees-heading>
<dees-heading level="2">This is a H2 heading</dees-heading>
<dees-heading level="3">This is a H3 heading</dees-heading>
<dees-heading level="4">This is a H4 heading</dees-heading>
<dees-heading level="5">This is a H5 heading</dees-heading>
<dees-heading level="6">This is a H6 heading</dees-heading>
<dees-heading level="hr">This is an hr heading</dees-heading>
<dees-heading level="hr-small">This is an hr small heading</dees-heading>
`;
}

View File

@ -0,0 +1,115 @@
import {
customElement,
html,
css,
property,
cssManager,
type TemplateResult,
DeesElement,
type CSSResult,
} from '@design.estate/dees-element';
import { demoFunc } from './dees-heading.demo.js';
declare global {
interface HTMLElementTagNameMap {
'dees-heading': DeesHeading;
}
}
@customElement('dees-heading')
export class DeesHeading extends DeesElement {
// demo
public static demo = demoFunc;
// properties
/**
* Heading level: 1-6 for h1-h6, or 'hr' for horizontal rule style
*/
@property({ type: String, reflect: true })
public level: '1' | '2' | '3' | '4' | '5' | '6' | 'hr' | 'hr-small' = '1';
// STATIC STYLES
public static styles: CSSResult[] = [
cssManager.defaultStyles,
css`
/* Heading styles */
h1, h2, h3, h4, h5, h6 {
margin: 16px 0 8px;
font-weight: 600;
color: ${cssManager.bdTheme('#000', '#fff')};
}
h1 { font-size: 32px; font-family: 'Cal Sans'; letter-spacing: 0.025em;}
h2 { font-size: 28px; }
h3 { font-size: 24px; }
h4 { font-size: 20px; }
h5 { font-size: 16px; }
h6 { font-size: 14px; }
/* Horizontal rule style heading */
.heading-hr {
display: flex;
align-items: center;
text-align: center;
margin: 16px 0;
color: ${cssManager.bdTheme('#000', '#fff')};
}
/* Fade lines toward and away from text for hr style */
.heading-hr::before {
content: '';
flex: 1;
height: 1px;
/* fade in toward center */
background: ${cssManager.bdTheme(
'linear-gradient(to right, transparent, #ccc)',
'linear-gradient(to right, transparent, #333)'
)};
margin: 0 8px;
}
.heading-hr::after {
content: '';
flex: 1;
height: 1px;
/* fade out away from center */
background: ${cssManager.bdTheme(
'linear-gradient(to right, #ccc, transparent)',
'linear-gradient(to right, #333, transparent)'
)};
margin: 0 8px;
}
/* Small hr variant with reduced margins */
.heading-hr.heading-hr-small {
margin: 8px 0;
font-size: 12px;
}
.heading-hr.heading-hr-small::before,
.heading-hr.heading-hr-small::after {
margin: 0 8px;
}
`,
];
// INSTANCE
public render(): TemplateResult {
switch (this.level) {
case '1':
return html`<h1><slot></slot></h1>`;
case '2':
return html`<h2><slot></slot></h2>`;
case '3':
return html`<h3><slot></slot></h3>`;
case '4':
return html`<h4><slot></slot></h4>`;
case '5':
return html`<h5><slot></slot></h5>`;
case '6':
return html`<h6><slot></slot></h6>`;
case 'hr':
return html`<div class="heading-hr"><slot></slot></div>`;
case 'hr-small':
return html`<div class="heading-hr heading-hr-small"><slot></slot></div>`;
default:
return html`<h1><slot></slot></h1>`;
}
}
}

View File

@ -1,31 +1,155 @@
import { html } from '@design.estate/dees-element';
import { icons, type IconWithPrefix } from './dees-icon.js';
import * as lucideIcons from 'lucide';
import { faIcons } from './dees-icon.js';
export const demoFunc = () => {
// Group FontAwesome icons by type
const faIcons = Object.keys(icons.fa);
// Extract Lucide icons from the lucideIcons object directly
// Log the first few keys to understand the structure
console.log('First few Lucide keys:', Object.keys(lucideIcons).slice(0, 5));
// Get all icon functions from lucideIcons (they have PascalCase names)
const lucideIconsList = Object.keys(lucideIcons)
.filter(key => {
// Skip utility functions and focus on icon components (first letter is uppercase)
const isUppercaseFirst = key[0] === key[0].toUpperCase() && key[0] !== key[0].toLowerCase();
const isFunction = typeof lucideIcons[key] === 'function';
const notUtility = !['createElement', 'createIcons', 'default'].includes(key);
return isFunction && isUppercaseFirst && notUtility;
})
.map(pascalName => {
// Convert PascalCase to camelCase
return pascalName.charAt(0).toLowerCase() + pascalName.slice(1);
});
// Log how many icons we found
console.log(`Found ${lucideIconsList.length} Lucide icons`);
// If we didn't find any, try an alternative approach
if (lucideIconsList.length === 0) {
console.log('Trying alternative approach to find Lucide icons');
// Try to get icon names from a known property if available
if (lucideIcons.icons) {
const iconSource = lucideIcons.icons || {};
lucideIconsList.push(...Object.keys(iconSource));
console.log(`Found ${lucideIconsList.length} icons via alternative method`);
}
}
export const demoFunc = () => html`
// Define the functions in TS scope instead of script tags
const searchIcons = (event: InputEvent) => {
const searchTerm = (event.target as HTMLInputElement).value.toLowerCase().trim();
// Get the demo container first, then search within it
const demoContainer = (event.target as HTMLElement).closest('.demoContainer');
const containers = demoContainer.querySelectorAll('.iconContainer');
containers.forEach(container => {
const iconName = container.getAttribute('data-name');
if (searchTerm === '') {
container.classList.remove('hidden');
} else if (iconName && iconName.includes(searchTerm)) {
container.classList.remove('hidden');
} else {
container.classList.add('hidden');
}
});
// Update counts - search within demoContainer
demoContainer.querySelectorAll('.section-container').forEach(section => {
const visibleIcons = section.querySelectorAll('.iconContainer:not(.hidden)').length;
const countElement = section.querySelector('.icon-count');
if (countElement) {
const totalIconsCount = section.classList.contains('fa-section')
? faIcons.length
: lucideIconsList.length;
countElement.textContent = visibleIcons === totalIconsCount
? `${totalIconsCount} icons`
: `${visibleIcons} of ${totalIconsCount} icons`;
}
});
};
const copyIconName = (iconNameToCopy: string, type: 'fa' | 'lucide') => {
// Use the new prefix format
const textToCopy = `${type}:${iconNameToCopy}`;
navigator.clipboard.writeText(textToCopy).then(() => {
// Find the event target
const currentEvent = window.event as MouseEvent;
const currentTarget = currentEvent.currentTarget as HTMLElement;
// Show feedback
const tooltip = currentTarget.querySelector('.copy-tooltip');
if (tooltip) {
tooltip.textContent = 'Copied!';
setTimeout(() => {
tooltip.textContent = 'Click to copy';
}, 2000);
}
});
};
return html`
<style>
.demoContainer {
width: 100%;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
background: #111111;
padding: 10px; font-size: 30px;
padding: 20px;
font-size: 30px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
.search-container {
width: 100%;
margin-bottom: 20px;
display: flex;
}
#iconSearch {
flex: 1;
padding: 12px 16px;
font-size: 16px;
border: none;
border-radius: 4px;
background: #222;
color: #fff;
border: 1px solid #333;
}
#iconSearch:focus {
outline: none;
border-color: #e4002b;
}
dees-icon {
transition: color 0.02s;
transition: all 0.2s ease;
color: #ffffff;
}
dees-icon:hover {
color: #e4002b;
}
.iconContainer {
display: block;
padding: 16px 16px 0px 16px;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 16px 0px 16px;
border: 1px solid #333333;
margin-right: 8px;
margin-bottom: 8px;
margin-right: 10px;
margin-bottom: 10px;
border-radius: 4px;
transition: background-color 0.2s;
cursor: pointer;
position: relative;
}
.iconContainer:hover {
background-color: #222;
}
.iconName {
@ -33,23 +157,136 @@ export const demoFunc = () => html`
text-align: center;
color: #ccc;
background: #333333;
padding: 4px 8px;
padding-bottom: 4px;
padding: 6px 10px;
margin-left: -16px;
margin-right: -16px;
margin-top: 16px;
margin-top: 20px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 120px;
border-radius: 0 0 4px 4px;
}
.section-title {
width: 100%;
color: #ffffff;
font-size: 24px;
margin: 20px 0;
padding-bottom: 10px;
border-bottom: 1px solid #333333;
display: flex;
justify-content: space-between;
align-items: center;
}
.api-note {
font-size: 14px;
color: #e4002b;
margin-bottom: 20px;
padding: 10px;
border: 1px solid #e4002b;
border-radius: 4px;
background: rgba(228, 0, 43, 0.1);
}
.icon-count {
font-size: 14px;
color: #888;
font-weight: normal;
background: #222;
padding: 5px 10px;
border-radius: 20px;
}
.icons-grid {
display: flex;
flex-wrap: wrap;
width: 100%;
}
.section-container {
width: 100%;
margin-bottom: 30px;
}
.copy-tooltip {
position: absolute;
background: #333;
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
top: -30px;
opacity: 0;
transition: opacity 0.3s;
pointer-events: none;
}
.iconContainer:hover .copy-tooltip {
opacity: 1;
}
.iconContainer:hover dees-icon {
transform: scale(1.1);
}
.hidden {
display: none !important;
}
</style>
<div class="demoContainer">
${Object.keys(faIcons).map(
(iconName) => html`
<div class="iconContainer">
<dees-icon .iconFA=${iconName as any}></dees-icon>
<div class="iconName">${iconName}</div>
</div>
`
)}
<div class="demoContainer">
<div class="search-container">
<input type="text" id="iconSearch" placeholder="Search icons..." @input=${searchIcons}>
</div>
`;
<div class="api-note">
New API: Use <code>icon="fa:iconName"</code> or <code>icon="lucide:iconName"</code> instead of <code>iconFA</code>.
Click any icon to copy its new format to clipboard.
</div>
<div class="section-container fa-section">
<div class="section-title">
FontAwesome Icons
<span class="icon-count">${faIcons.length} icons</span>
</div>
<div class="icons-grid">
${faIcons.map(
(iconName) => {
const prefixedName = `fa:${iconName}`;
return html`
<div class="iconContainer fa-icon" data-name=${iconName.toLowerCase()} @click=${() => copyIconName(iconName, 'fa')}>
<dees-icon .icon=${prefixedName as IconWithPrefix} iconSize="24"></dees-icon>
<div class="iconName">${iconName}</div>
<span class="copy-tooltip">Click to copy</span>
</div>
`;
}
)}
</div>
</div>
<div class="section-container lucide-section">
<div class="section-title">
Lucide Icons
<span class="icon-count">${lucideIconsList.length} icons</span>
</div>
<div class="icons-grid">
${lucideIconsList.map(
(iconName) => {
const prefixedName = `lucide:${iconName}`;
return html`
<div class="iconContainer lucide-icon" data-name=${iconName.toLowerCase()} @click=${() => copyIconName(iconName, 'lucide')}>
<dees-icon .icon=${prefixedName as IconWithPrefix} iconSize="24"></dees-icon>
<div class="iconName">${iconName}</div>
<span class="copy-tooltip">Click to copy</span>
</div>
`;
}
)}
</div>
</div>
</div>
`;
};

View File

@ -52,10 +52,12 @@ import {
faFileInvoiceDollar as faFileInvoiceDollarSolid,
faGear as faGearSolid,
faGrip as faGripSolid,
faMagnifyingGlass as faMagnifyingGlassSolid,
faMessage as faMessageSolid,
faMoneyCheckDollar as faMoneyCheckDollarSolid,
faMugHot as faMugHotSolid,
faMinus as faMinusSolid,
faNetworkWired as faNetworkWiredSolid,
faPaperclip as faPaperclipSolid,
faPaste as faPasteSolid,
faPenToSquare as faPenToSquareSolid,
@ -65,6 +67,7 @@ import {
faUsers as faUsersSolid,
faShare as faShareSolid,
faSun as faSunSolid,
faTerminal as faTerminalSolid,
faTrash as faTrashSolid,
faTrashCan as faTrashCanSolid,
faWallet as faWalletSolid,
@ -72,7 +75,12 @@ import {
} from '@fortawesome/free-solid-svg-icons';
import { demoFunc } from './dees-icon.demo.js';
export const faIcons = {
// Import Lucide icons and the createElement function
import * as lucideIcons from 'lucide';
import { createElement } from 'lucide';
// Collect FontAwesome icons
const faIcons = {
// normal
arrowRight: faArrowRightSolid,
arrowUpRightFromSquare: faArrowUpRightFromSquareSolid,
@ -97,11 +105,13 @@ export const faIcons = {
fileInvoiceDoller: faFileInvoiceDollarSolid,
gear: faGearSolid,
grip: faGripSolid,
magnifyingGlass: faMagnifyingGlassSolid,
message: faMessageRegular,
messageSolid: faMessageSolid,
moneyCheckDollar: faMoneyCheckDollarSolid,
mugHot: faMugHotSolid,
minus: faMinusSolid,
networkWired: faNetworkWiredSolid,
paperclip: faPaperclipSolid,
paste: faPasteRegular,
pasteSolid: faPasteSolid,
@ -112,6 +122,7 @@ export const faIcons = {
share: faShareSolid,
sun: faSunRegular,
sunSolid: faSunSolid,
terminal: faTerminalSolid,
trash: faTrashSolid,
trashSolid: faTrashSolid,
trashCan: faTrashCanRegular,
@ -130,7 +141,32 @@ export const faIcons = {
twitter: faTwitter,
};
export type TIconKey = keyof typeof faIcons;
// Create a string literal type for all FA icons
type FAIconKey = keyof typeof faIcons;
// Create union types for the icons with prefixes
export type IconWithPrefix = `fa:${FAIconKey}` | `lucide:${string}`;
// Export only FontAwesome icons directly
export const icons = {
fa: faIcons
};
// Legacy type for backward compatibility
export type TIconKey = FAIconKey | `lucide:${string}`;
// Use a global static cache for all icons to reduce rendering
const iconCache = new Map<string, string>();
// Clear cache items occasionally to prevent memory leaks
const MAX_CACHE_SIZE = 500;
function limitCacheSize() {
if (iconCache.size > MAX_CACHE_SIZE) {
// Remove oldest entries (first 20% of items)
const keysToDelete = Array.from(iconCache.keys()).slice(0, MAX_CACHE_SIZE / 5);
keysToDelete.forEach(key => iconCache.delete(key));
}
}
declare global {
interface HTMLElementTagNameMap {
@ -142,31 +178,170 @@ declare global {
export class DeesIcon extends DeesElement {
public static demo = demoFunc;
/**
* @deprecated Use the `icon` property instead with format "fa:iconName" or "lucide:iconName"
*/
@property({
type: String
type: String,
converter: {
// Convert attribute string to property (for reflected attributes)
fromAttribute: (value: string): TIconKey => value as TIconKey,
// Convert property to attribute (for reflection)
toAttribute: (value: TIconKey): string => value
}
})
public iconFA: keyof typeof faIcons;
public iconFA?: TIconKey;
@property()
/**
* The preferred icon property. Use format "fa:iconName" or "lucide:iconName"
* Examples: "fa:check", "lucide:menu"
*/
@property({
type: String,
converter: {
fromAttribute: (value: string): IconWithPrefix => value as IconWithPrefix,
toAttribute: (value: IconWithPrefix): string => value
}
})
public icon?: IconWithPrefix;
@property({ type: Number })
public iconSize: number;
@property({ type: String })
public color: string = 'currentColor';
@property({ type: Number })
public strokeWidth: number = 2;
// For tracking when we need to re-render
private lastIcon: IconWithPrefix | TIconKey | null = null;
private lastIconSize: number | null = null;
private lastColor: string | null = null;
private lastStrokeWidth: number | null = null;
constructor() {
super();
domtools.elementBasic.setup();
}
/**
* Gets the effective icon value, supporting both the new `icon` property
* and the legacy `iconFA` property for backward compatibility.
* Prefers `icon` if both are set.
*/
private getEffectiveIcon(): IconWithPrefix | TIconKey | null {
// Prefer the new API
if (this.icon) {
return this.icon;
}
// Fall back to the old API
if (this.iconFA) {
// If iconFA is already in the proper format (lucide:name), use it directly
if (this.iconFA.startsWith('lucide:')) {
return this.iconFA;
}
// For FontAwesome icons with no prefix, add the prefix
return `fa:${this.iconFA}` as IconWithPrefix;
}
return null;
}
/**
* Parses an icon string into its type and name parts
* @param iconStr The icon string in format "type:name"
* @returns Object with type and name properties
*/
private parseIconString(iconStr: string): { type: 'fa' | 'lucide', name: string } {
if (iconStr.startsWith('fa:')) {
return {
type: 'fa',
name: iconStr.substring(3) // Remove 'fa:' prefix
};
} else if (iconStr.startsWith('lucide:')) {
return {
type: 'lucide',
name: iconStr.substring(7) // Remove 'lucide:' prefix
};
} else {
// For backward compatibility, assume FontAwesome if no prefix
return {
type: 'fa',
name: iconStr
};
}
}
private renderLucideIcon(iconName: string): string {
// Create a cache key based on all visual properties
const cacheKey = `lucide:${iconName}:${this.iconSize}:${this.color}:${this.strokeWidth}`;
// Check if we already have this icon in the cache
if (iconCache.has(cacheKey)) {
return iconCache.get(cacheKey) || '';
}
try {
// Get the Pascal case icon name (Menu instead of menu)
const pascalCaseName = iconName.charAt(0).toUpperCase() + iconName.slice(1);
// Check if the icon exists in lucideIcons
if (!lucideIcons[pascalCaseName]) {
console.warn(`Lucide icon '${pascalCaseName}' not found in lucideIcons object`);
return '';
}
// Use the exact pattern from Lucide documentation
const svgElement = createElement(lucideIcons[pascalCaseName], {
color: this.color,
size: this.iconSize,
strokeWidth: this.strokeWidth
});
if (!svgElement) {
console.warn(`createElement returned empty result for ${pascalCaseName}`);
return '';
}
// Get the HTML
const result = svgElement.outerHTML;
// Cache the result for future use
iconCache.set(cacheKey, result);
limitCacheSize();
return result;
} catch (error) {
console.error(`Error rendering Lucide icon ${iconName}:`, error);
// Create a fallback SVG with the icon name
return `<svg xmlns="http://www.w3.org/2000/svg" width="${this.iconSize}" height="${this.iconSize}" viewBox="0 0 24 24" fill="none" stroke="${this.color}" stroke-width="${this.strokeWidth}" stroke-linecap="round" stroke-linejoin="round">
<text x="50%" y="50%" font-size="6" text-anchor="middle" dominant-baseline="middle" fill="${this.color}">${iconName}</text>
</svg>`;
}
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
white-space: nowrap;
display: flex;
display: inline-flex;
align-items: center;
justify-content: center;
line-height: 1;
vertical-align: middle;
}
* {
transition: inherit !important;
/* Improve rendering performance */
#iconContainer svg {
display: block;
height: 100%;
width: 100%;
will-change: transform; /* Helps with animations */
contain: strict; /* Performance optimization */
}
`,
];
@ -175,8 +350,8 @@ export class DeesIcon extends DeesElement {
return html`
${domtools.elementBasic.styles}
<style>
#iconContainer svg {
display: block;
#iconContainer {
width: ${this.iconSize}px;
height: ${this.iconSize}px;
}
</style>
@ -184,14 +359,95 @@ export class DeesIcon extends DeesElement {
`;
}
public async updated() {
public updated() {
// If size is not specified, use font size as a base
if (!this.iconSize) {
this.iconSize = parseInt(globalThis.getComputedStyle(this).fontSize.replace(/\D/g,''));
}
if (this.iconFA) {
this.shadowRoot.querySelector('#iconContainer').innerHTML = this.iconFA
? icon(faIcons[this.iconFA]).html[0]
: 'icon not found';
// Get the effective icon (either from icon or iconFA property)
const effectiveIcon = this.getEffectiveIcon();
// Check if we actually need to update the icon
// This prevents unnecessary DOM operations when properties haven't changed
if (this.lastIcon === effectiveIcon &&
this.lastIconSize === this.iconSize &&
this.lastColor === this.color &&
this.lastStrokeWidth === this.strokeWidth) {
return; // No visual changes - skip update
}
// Update our "last properties" for future change detection
this.lastIcon = effectiveIcon;
this.lastIconSize = this.iconSize;
this.lastColor = this.color;
this.lastStrokeWidth = this.strokeWidth;
const container = this.shadowRoot?.querySelector('#iconContainer');
if (!container || !effectiveIcon) return;
try {
// Parse the icon string to get type and name
const { type, name } = this.parseIconString(effectiveIcon);
if (type === 'lucide') {
// For Lucide, use direct DOM manipulation as shown in the docs
// This approach avoids HTML string issues
container.innerHTML = ''; // Clear container
try {
// Convert to PascalCase
const pascalCaseName = name.charAt(0).toUpperCase() + name.slice(1);
if (lucideIcons[pascalCaseName]) {
// Use the documented pattern from Lucide docs
const svgElement = createElement(lucideIcons[pascalCaseName], {
color: this.color,
size: this.iconSize,
strokeWidth: this.strokeWidth
});
if (svgElement) {
// Directly append the element
container.appendChild(svgElement);
return; // Exit early since we've added the element
}
}
// If we reach here, something went wrong
throw new Error(`Could not create element for ${pascalCaseName}`);
} catch (error) {
console.error(`Error rendering Lucide icon:`, error);
// Fall back to the string-based approach
const iconHtml = this.renderLucideIcon(name);
if (iconHtml) {
container.innerHTML = iconHtml;
}
}
} else {
// Use FontAwesome rendering via HTML string
const faIcon = icons.fa[name as FAIconKey];
if (faIcon) {
const iconHtml = icon(faIcon).html[0];
container.innerHTML = iconHtml;
} else {
console.warn(`FontAwesome icon not found: ${name}`);
}
}
} catch (error) {
console.error(`Error updating icon ${effectiveIcon}:`, error);
}
}
}
// Clean up resources when element is removed
async disconnectedCallback() {
super.disconnectedCallback();
// Clear our references
this.lastIcon = null;
this.lastIconSize = null;
this.lastColor = null;
this.lastStrokeWidth = null;
}
}

View File

@ -89,7 +89,7 @@ export class DeesInputDropdown extends DeesElement {
.label {
font-size: 14px;
margin-bottom: 4px;
margin-bottom: 8px;
}
.selectedBox {
@ -102,9 +102,11 @@ export class DeesInputDropdown extends DeesElement {
background: ${cssManager.bdTheme('#fafafa', '#222222')};
box-shadow: ${cssManager.bdTheme('0px 1px 4px rgba(0,0,0,0.3)', 'none')};
border-radius: 3px;
border-top: ${cssManager.bdTheme('1px solid #CCC', '1px solid #444')};
border-bottom: ${cssManager.bdTheme('1px solid #CCC', '1px solid #333')};
border-top: ${cssManager.bdTheme('1px solid #CCC', '1px solid #ffffff10')};
border-bottom: ${cssManager.bdTheme('1px solid #CCC', '1px solid #222')};
transition: all 0.2s ease;
font-size: 16px;
color: ${cssManager.bdTheme('#222', '#ccc')};
}
.selectedBox:hover {
@ -132,6 +134,8 @@ export class DeesInputDropdown extends DeesElement {
padding: 4px 8px;
position: absolute;
user-select: none;
margin: 3px 0px 0px 0px;
border-top: ${cssManager.bdTheme('1px solid #CCC', '1px solid #ffffff10')};
}
.selectionBox.top {
transform: translate(0px, 4px);

View File

@ -142,7 +142,7 @@ export class DeesInputFileupload extends DeesElement {
text-align: left;
border-radius: 3px;
color: ${cssManager.bdTheme('#666', '#ccc')};
font-family: 'Roboto', 'Inter', sans-serif;
font-family: 'Geist Sans', sans-serif;
cursor: default;
transition: all 0.2s;
border-top: 1px solid #ffffff10;

View File

@ -12,6 +12,12 @@ import {
const { demoFunc } = await import('./dees-input-multitoggle.demo.js');
declare global {
interface HTMLElementTagNameMap {
'dees-input-multitoggle': DeesInputMultitoggle;
}
}
@customElement('dees-input-multitoggle')
export class DeesInputMultitoggle extends DeesElement {
public static demo = demoFunc;
@ -21,6 +27,11 @@ export class DeesInputMultitoggle extends DeesElement {
})
public label: string;
@property({
type: String,
})
public description: string;
@property()
type: 'boolean' | 'multi' | 'single' = 'multi';
@ -61,7 +72,7 @@ export class DeesInputMultitoggle extends DeesElement {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
background: #333;
background: ${cssManager.bdTheme('#fff', '#222')};
width: min-content;
border-radius: 20px;
height: 32px;
@ -104,7 +115,7 @@ export class DeesInputMultitoggle extends DeesElement {
public render(): TemplateResult {
return html`
${this.label ? html`<div class="label">${this.label}</div>` : html``}
<dees-label .label=${this.label} .description=${this.description}></dees-label>
<div class="mainbox">
<div class="selections">
<div class="indicator"></div>

View File

@ -1,3 +1,5 @@
import * as colors from './00colors.js';
import {
customElement,
DeesElement,
@ -31,6 +33,11 @@ export class DeesInputText extends DeesElement {
})
public label: string;
@property({
type: String,
})
public description: string;
@property({
type: String,
reflect: true,
@ -98,11 +105,6 @@ export class DeesInputText extends DeesElement {
color: ${cssManager.bdTheme('#333', '#ccc')};
}
.label {
font-size: 14px;
margin-bottom: 8px;
}
input {
margin-top: 0px;
background: ${cssManager.bdTheme('#fafafa', '#222')};
@ -132,7 +134,7 @@ export class DeesInputText extends DeesElement {
input:focus {
outline: none;
border-bottom: 1px solid #e4002b;
border-bottom: 1px solid ${cssManager.bdTheme( colors.bright.blueActive, colors.dark.blueActive)};
cursor: text;
}
@ -174,7 +176,7 @@ export class DeesInputText extends DeesElement {
return html`
<style>
input {
font-family: ${this.isPasswordBool ? 'monospace' : 'Roboto'};
font-family: ${this.isPasswordBool ? 'monospace' : 'Geist Sans'};
letter-spacing: ${this.isPasswordBool ? '1px' : 'normal'};
color: ${this.goBright ? '#333' : '#ccc'};
}
@ -192,7 +194,7 @@ export class DeesInputText extends DeesElement {
`}
</style>
<div class="maincontainer">
${this.label ? html`<div class="label">${this.label}</div>` : html``}
<dees-label .label=${this.label} .description=${this.description}></dees-label>
<input
type="${this.isPasswordBool && !this.showPasswordBool ? 'password' : 'text'}"
.value=${this.value}

View File

@ -0,0 +1,7 @@
import { html, cssManager } from '@design.estate/dees-element';
export const demoFunc = () => {
return html`
<dees-label .label=${'a label'}></dees-label>
`;
}

View File

@ -0,0 +1,70 @@
import * as plugins from './00plugins.js';
import * as colors from './00colors.js';
import {
customElement,
html,
css,
cssManager,
DeesElement,
property,
unsafeCSS,
query,
} from '@design.estate/dees-element';
import { demoFunc } from './dees-label.demo.js';
@customElement('dees-label')
export class DeesLabel extends DeesElement {
public static demo = demoFunc;
// INSTANCE
@property({
type: String,
reflect: true,
})
public label = '';
@property({
type: String,
reflect: true,
})
public description: string;
public static styles = [
cssManager.defaultStyles,
css`
.label {
color: ${cssManager.bdTheme('#333', '#ccc')};
font-size: 14px;
margin-bottom: 8px;
cursor: default;
user-select: none;
}
dees-icon {
display: inline-block;
font-size: 14px;
transform: translateY(1.5px);
}
`,
];
public render() {
return html`
${this.label
? html`
<div class="label">
${this.label}
${this.description
? html`
<dees-icon .iconFA=${'circleInfo'}></dees-icon>
<dees-speechbubble .text=${this.description}></dees-speechbubble>
`
: html``}
</div>
`
: html``}
`;
}
}

View File

@ -115,7 +115,7 @@ export class DeesMobilenavigation extends DeesElement {
text-align: left;
font-size: 24px;
padding: 8px 0px;
font-family: 'Roboto', 'Inter', sans-serif;
font-family: 'Geist Sans', sans-serif;
font-weight: 300;
border-bottom: 1px dashed #444;
margin-top: 16px;

View File

@ -71,7 +71,7 @@ export class DeesModal extends DeesElement {
cssManager.defaultStyles,
css`
:host {
font-family: 'Roboto', 'Inter', sans-serif;
font-family: 'Geist Sans', sans-serif;
color: ${cssManager.bdTheme('#333', '#fff')};
will-change: transform;
}
@ -113,7 +113,7 @@ export class DeesModal extends DeesElement {
.modal .heading {
height: 32px;
font-family: 'Roboto', 'Inter', sans-serif;
font-family: 'Geist Sans', sans-serif;
line-height: 32px;
text-align: center;
font-weight: 600;

View File

@ -0,0 +1,28 @@
import { html } from '@design.estate/dees-element';
/**
* Demo for dees-pagination component
*/
export const demoFunc = () => html`
<div style="display: flex; align-items: center; gap: 16px;">
<!-- Small set of pages -->
<div style="display: flex; flex-direction: column; gap: 4px;">
<span>5 pages, starting at 1:</span>
<dees-pagination
.total=${5}
.page=${1}
@page-change=${(e: CustomEvent) => console.log('Page changed to', e.detail.page)}
></dees-pagination>
</div>
<!-- Larger set of pages -->
<div style="display: flex; flex-direction: column; gap: 4px;">
<span>15 pages, starting at 8:</span>
<dees-pagination
.total=${15}
.page=${8}
@page-change=${(e: CustomEvent) => console.log('Page changed to', e.detail.page)}
></dees-pagination>
</div>
</div>
`;

View File

@ -0,0 +1,133 @@
import { customElement, html, DeesElement, property, css, cssManager, type TemplateResult } from '@design.estate/dees-element';
import { demoFunc } from './dees-pagination.demo.js';
declare global {
interface HTMLElementTagNameMap {
'dees-pagination': DeesPagination;
}
}
/**
* A simple pagination component.
* @fires page-change - Emitted when the page is changed. detail: { page: number }
*/
@customElement('dees-pagination')
export class DeesPagination extends DeesElement {
public static demo = demoFunc;
/** Current page (1-based) */
@property({ type: Number, reflect: true })
public page = 1;
/** Total number of pages */
@property({ type: Number, reflect: true })
public total = 1;
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: inline-flex;
align-items: center;
}
button {
background: none;
border: none;
margin: 0 2px;
padding: 6px 10px;
font-size: 14px;
cursor: pointer;
color: ${cssManager.bdTheme('#333', '#ccc')};
border-radius: 3px;
transition: background 0.2s;
}
button:hover:not(:disabled) {
background: ${cssManager.bdTheme('#eee', '#444')};
}
button:disabled {
cursor: default;
color: ${cssManager.bdTheme('#aaa', '#666')};
}
button.current {
background: #0050b9;
color: #fff;
cursor: default;
}
span.ellipsis {
margin: 0 4px;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
`,
];
private get pages(): (number | string)[] {
const pages: (number | string)[] = [];
const total = this.total;
const current = this.page;
if (total <= 7) {
for (let i = 1; i <= total; i++) {
pages.push(i);
}
} else {
pages.push(1);
if (current > 4) {
pages.push('...');
}
const start = Math.max(2, current - 2);
const end = Math.min(total - 1, current + 2);
for (let i = start; i <= end; i++) {
pages.push(i);
}
if (current < total - 3) {
pages.push('...');
}
pages.push(total);
}
return pages;
}
public render(): TemplateResult {
return html`
<button
@click=${() => this.changePage(this.page - 1)}
?disabled=${this.page <= 1}
aria-label="Previous page"
>
</button>
${this.pages.map((p) =>
p === '...'
? html`<span class="ellipsis">…</span>`
: html`
<button
class="${p === this.page ? 'current' : ''}"
@click=${() => this.changePage(p as number)}
?disabled=${p === this.page}
aria-label="Page ${p}"
>
${p}
</button>
`
)}
<button
@click=${() => this.changePage(this.page + 1)}
?disabled=${this.page >= this.total}
aria-label="Next page"
>
</button>
`;
}
private changePage(newPage: number) {
if (newPage < 1 || newPage > this.total || newPage === this.page) {
return;
}
this.page = newPage;
this.dispatchEvent(
new CustomEvent('page-change', {
detail: { page: this.page },
bubbles: true,
})
);
}
}

View File

@ -32,7 +32,7 @@ export class DeesPdf extends DeesElement {
return html`
<style>
:host {
font-family: 'Roboto', 'Inter', sans-serif;
font-family: 'Geist Sans', sans-serif;
display: block;
box-sizing: border-box;
max-width: 800px;

View File

@ -0,0 +1,46 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => {
const onChanged = (e: CustomEvent) => {
// find the demo wrapper and update the 'changed' log inside it
const wrapper = (e.target as HTMLElement).closest('.demoWrapper');
const el = wrapper?.querySelector('#changed');
if (el) el.textContent = `search-changed: ${e.detail.value}`;
};
const onSubmit = (e: CustomEvent) => {
// find the demo wrapper and update the 'submitted' log inside it
const wrapper = (e.target as HTMLElement).closest('.demoWrapper');
const el = wrapper?.querySelector('#submitted');
if (el) el.textContent = `search-submit: ${e.detail.value}`;
};
return html`
<style>
.demoWrapper {
display: block;
flex-direction: column;
align-items: center;
background: #888888;
}
.logs {
padding: 16px;
width: 600px;
color: #fff;
font-family: monospace;
}
.logs div {
margin: 4px 0;
}
</style>
<div class="demoWrapper">
<dees-searchbar
@search-changed=${onChanged}
@search-submit=${onSubmit}
></dees-searchbar>
<div class="logs">
<div id="changed">search-changed:</div>
<div id="submitted">search-submit:</div>
</div>
</div>
`;
};

View File

@ -0,0 +1,160 @@
import {
customElement,
DeesElement,
property,
html,
cssManager,
unsafeCSS,
css,
type TemplateResult,
domtools,
query,
} from '@design.estate/dees-element';
import * as colors from './00colors.js';
import { demoFunc } from './dees-searchbar.demo.js';
declare global {
interface HTMLElementTagNameMap {
'dees-searchbar': DeesSearchbar;
}
}
@customElement('dees-searchbar')
export class DeesSearchbar extends DeesElement {
// DEMO
public static demo = demoFunc;
// STATIC
public static styles = [
cssManager.defaultStyles,
css`
:host {
padding: 40px;
font-family: Dees Sans;
display: block;
background: ${cssManager.bdTheme('#eeeeeb', '#000000')};
}
.searchboxContainer {
position: relative;
margin: auto;
max-width: 800px;
background: ${cssManager.bdTheme('#00000015', '#ffffff15')};
--boxHeight: 60px;
height: var(--boxHeight);
border-radius: var(--boxHeight);
display: grid;
grid-template-columns: 1fr 140px;
justify-content: center;
align-items: center;
border-top: 1px solid ${cssManager.bdTheme('#00000015', '#ffffff20')};
}
input {
height: 100%;
width: 100%;
border: none;
background: none;
color: ${cssManager.bdTheme('#000000', '#eeeeeb')};
padding-left: 25px;
margin-right: -8px;
outline: none;
font-size: 16px;
}
.searchButton {
--buttonPadding: 8px;
background: ${cssManager.bdTheme('#eeeeeb', '#000000')};
color: ${cssManager.bdTheme('#000000', '#eeeeeb')};
line-height: calc(var(--boxHeight) - (var(--buttonPadding) * 2));
border-radius: var(--boxHeight);
transform: scale(1) ;
transform-origin: 50% 50%;
text-align: center;
transition: transform 0.1s, background 0.1s;
margin-right: var(--buttonPadding);
user-select: none;
}
.searchButton:hover {
color: #fff;
background: ${cssManager.bdTheme(colors.bright.blue, colors.dark.blue)};
}
.searchButton:active {
color: #fff;
background: ${cssManager.bdTheme(colors.bright.blueActive, colors.dark.blueActive)};
transform: scale(0.98);
}
.filters {
margin: auto;
max-width: 800px;
}
`,
];
// INSTANCE
@property()
public filters = [];
@query('input')
public searchInput!: HTMLInputElement;
@query('.searchButton')
public searchButton!: HTMLElement;
constructor() {
super();
}
public render(): TemplateResult {
return html`
<div class="searchboxContainer">
<input type="text" placeholder="Your Skills (e.g. TypeScript, Rust, Projectmanagement)" />
<div class="searchButton">Search -></div>
</div>
${this.filters.length > 0 ? html`
<div class="filters">
<dees-heading level="hr-small">Filters</dees-heading>
<dees-input-dropdown .label=${'location'}></dees-input-dropdown>
</div>
` : html``}
`;
}
/**
* Lifecycle: after first render, wire up events for input and submit actions
*/
public firstUpdated(): void {
// dispatch change on each input
this.searchInput.addEventListener('input', () => {
this.dispatchEvent(new CustomEvent('search-changed', {
bubbles: true,
composed: true,
detail: { value: this.searchInput.value }
}));
});
// submit on Enter key
this.searchInput.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.key === 'Enter') {
this._dispatchSubmit();
}
});
// submit on button click
this.searchButton.addEventListener('click', () => this._dispatchSubmit());
}
/**
* Dispatch a submit event with the current search value
*/
private _dispatchSubmit(): void {
this.dispatchEvent(new CustomEvent('search-submit', {
bubbles: true,
composed: true,
detail: { value: this.searchInput.value }
}));
}
}

View File

@ -1,5 +1,21 @@
import { html } from '@design.estate/dees-element';
import type { IView } from './dees-simple-appdash.js';
export const demoFunc = () => html`
<dees-simple-appdash>Hello there</dees-simple-appdash>
<dees-simple-appdash
.viewTabs=${[
{
name: 'View 1',
element: null,
},
{
name: 'View 2',
element: null,
},
{
name: 'View 3',
element: null,
}
] as IView[]}
>Hello there</dees-simple-appdash>
`;

View File

@ -1,4 +1,6 @@
import { demoFunc } from './dees-simple-appdash.demo.js';
import * as colors from './00colors.js';
import {
customElement,
html,
@ -10,7 +12,9 @@ import {
unsafeCSS,
type CSSResult,
state,
domtools,
} from '@design.estate/dees-element';
import { DeesTerminal } from './dees-terminal.js';
declare global {
interface HTMLElementTagNameMap {
@ -18,6 +22,11 @@ declare global {
}
}
export interface IView {
name: string;
element: DeesElement['constructor']['prototype'];
}
@customElement('dees-simple-appdash')
export class DeesSimpleAppDash extends DeesElement {
// STATIC
@ -27,42 +36,159 @@ export class DeesSimpleAppDash extends DeesElement {
@property()
public name = 'Dees Simple Login';
@property()
public viewTabs: IView[] = [];
@property()
public terminalSetupCommand: string = `pnpm install @serve.zone/cli && clear && servezone info`;
public static styles = [
cssManager.defaultStyles,
css`
:host {
color: ${cssManager.bdTheme('#333', '#fff')};
color: ${cssManager.bdTheme('#333', '#ccc')};
user-select: none;
display: block;
overflow: hidden;
position: relative;
height: 100%;
width: 100%;
}
.maincontainer {
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
overflow: hidden;
}
.appbar {
position: absolute;
top: 0;
height: 40px;
width: 100%;
top: 0px;
left: 0px;
height: calc(100% - 24px);
width: 200px;
background: ${cssManager.bdTheme('#eeeeeb', '#000')};
border-bottom: 1px solid ${cssManager.bdTheme('#ccc', '#333')};
border-right: 1px solid ${cssManager.bdTheme('#ccc', '#ffffff20')};
font-size: 14px;
line-height: 40px;
font-family: 'Roboto', 'Inter', sans-serif;
line-height: 32px;
font-family: 'Geist Sans', sans-serif;
padding: 0px 16px;
z-index: 2;
box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.8);
display: grid;
grid-template-rows: min-content auto min-content;
}
.appbar .viewTabs {
margin-left: -8px;
margin-right: -8px;
display: flex;
flex-direction: column;
align-items: top;
}
.viewTab {
padding: 0px 8px;
}
.viewTab:hover {
background: ${cssManager.bdTheme('#ccc', '#ffffff10')};
color: ${cssManager.bdTheme('#000', '#fff')};
}
.viewTab:active {
background: ${cssManager.bdTheme('#aaa', '#ffffff20')};
color: ${cssManager.bdTheme('#000', '#fff')};
}
.appName {
white-space: nowrap;
color: ${cssManager.bdTheme('#666', '#999')};
}
.appActions {
display: flex;
}
.appActions .action {
}
.appActions .action:hover {
color: ${cssManager.bdTheme('#000', '#fff')};
}
.appcontent {
z-index: 1;
position: absolute;
top: 40px;
bottom: 0;
width: 100%;
top: 0px;
right: 0px;
height: calc(100vh - 24px);
bottom: 24px;
width: calc(100vw - 200px);
overflow: auto;
background: ${cssManager.bdTheme('#eeeeeb', '#000')};
overscroll-behavior: contain;
}
.controlbar {
color: #fff;
position: absolute;
bottom: 0px;
left: 0px;
width: 100%;
border-top: 1px solid #44444480;
height: 24px;
background: ${cssManager.bdTheme(colors.bright.blueMuted, colors.dark.blueMuted)};
z-index: 2;
display: flex;
justify-content: flex-end;
align-items: center;
flex-direction: row;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.8);
}
.control {
width: min-content;
margin-right: 16px;
font-size: 12px;
white-space: nowrap;
}
`,
];
public render(): TemplateResult {
return html`
<div class="appbar">
configvault v1.2.3
</div>
<div class="appcontent">
<slot></slot>
<div class="maincontainer">
<div class="appbar">
<div class="appName">${this.name}</div>
<div class="viewTabs">
${this.viewTabs.map(
(view) => html`
<div class="viewTab" @click=${() => {
this.loadView(view);
}}>${view.name}</div>
`
)}
</div>
<div class="appActions">
<div class="action" @click=${() => {
this.dispatchEvent(new CustomEvent('logout'));
}}>Logout</div>
</div>
</div>
<div class="appcontent">
</div>
<div class="controlbar">
<div class="control">
<dees-icon .iconFA=${'networkWired'}></dees-icon>
</div>
<div class="control" @click=${this.launchTerminal}>
<dees-icon .iconFA=${'terminal'}></dees-icon>
</div>
</div>
</div>
`;
}
@ -70,5 +196,39 @@ export class DeesSimpleAppDash extends DeesElement {
public async firstUpdated(_changedProperties): Promise<void> {
const domtools = await this.domtoolsPromise;
super.firstUpdated(_changedProperties);
await this.loadView(this.viewTabs[0]);
}
public currentTerminal: DeesTerminal;
public async launchTerminal() {
const maincontainer = this.shadowRoot.querySelector('.maincontainer');
const terminal = new DeesTerminal();
terminal.setupCommand = this.terminalSetupCommand;
this.currentTerminal = terminal;
maincontainer.appendChild(terminal);
terminal.style.position = 'absolute';
terminal.style.zIndex = '1';
terminal.style.top = '0px';
terminal.style.left = '200px';
terminal.style.right = '0px';
terminal.style.bottom = '24px';
terminal.style.opacity = '0';
terminal.style.transform = 'translateY(20px)';
terminal.style.transition = 'all 0.2s';
await domtools.plugins.smartdelay.delayFor(0);
terminal.style.opacity = '1';
terminal.style.transform = 'translateY(0px)';
return terminal;
}
private currentView: DeesElement;
public async loadView(viewArg: IView) {
const appcontent = this.shadowRoot.querySelector('.appcontent');
const view = new viewArg.element();
if (this.currentView) {
this.currentView.remove();
}
appcontent.appendChild(view);
this.currentView = view;
}
}

View File

@ -42,7 +42,18 @@ export class DeesSimpleLogin extends DeesElement {
align-items: center; /* aligns vertically */
width: 100%;
height: 100%;
top: 0px;
left: 0px;
}
.slotContainer {
position: absolute;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
}
.login {
min-width: 320px;
min-height: 100px;
@ -92,7 +103,6 @@ export class DeesSimpleLogin extends DeesElement {
const submit = this.shadowRoot.querySelector('dees-form-submit');
form.addEventListener('formData', (event: CustomEvent) => {
this.dispatchEvent(new CustomEvent('login', { detail: event.detail }));
// this.switchToSlottedContent();
});
}

View File

@ -0,0 +1,23 @@
import { html, cssManager } from '@design.estate/dees-element';
export const demoFunc = () => {
return html`
<style>
.ref1 {
margin: 20px;
width: 10px;
height: 10px;
background-color: red;
}
</style>
<div class="ref1"></div>
<dees-speechbubble .text=${`
**This is a longer markdown text that can be used the write**
a longer description about whats going on the app
**This is a subheader**
and another text
`}></dees-speechbubble>
`;
};

View File

@ -1,3 +1,7 @@
import * as colors from './00colors.js';
import * as plugins from './00plugins.js';
import { demoFunc } from './dees-speechbubble.demo.js';
import {
customElement,
html,
@ -8,9 +12,11 @@ import {
css,
type CSSResult,
unsafeCSS,
domtools,
directives,
unsafeHTML,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { DeesWindowLayer } from './dees-windowlayer.js';
declare global {
interface HTMLElementTagNameMap {
@ -20,26 +26,53 @@ declare global {
@customElement('dees-speechbubble')
export class DeesSpeechbubble extends DeesElement {
public static demo = () => html` <dees-speechbubble></dees-speechbubble> `;
public static demo = demoFunc;
@property()
// STATIC
public static async createAndShow(refElement: HTMLElement, textArg: string) {
const windowLayer = await DeesWindowLayer.createAndShow({
blur: false,
});
const speechbubble = document.createElement('dees-speechbubble');
speechbubble.windowLayer = windowLayer;
speechbubble.reffedElement = refElement;
speechbubble.text = textArg;
speechbubble.manifested = true;
windowLayer.appendChild(speechbubble);
windowLayer.style.pointerEvents = 'none';
(windowLayer.shadowRoot.querySelector('.windowOverlay') as HTMLElement).style.pointerEvents = 'none';
return speechbubble;
}
// INSTANCE
@property({
type: Object,
})
reffedElement: HTMLElement;
@property({
type: String,
reflect: true,
})
public text: string;
@property({
type: Boolean,
})
public disabled = false;
public wave: boolean = false;
@property({
type: Boolean,
})
public isHidden = false;
public manifested = false;
@property({
type: String,
})
public status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
public windowLayer: DeesWindowLayer;
constructor() {
super();
}
@ -48,27 +81,22 @@ export class DeesSpeechbubble extends DeesElement {
cssManager.defaultStyles,
css`
:host {
position: relative;
display: block;
box-sizing: border-box;
color: ${cssManager.bdTheme('#333', '#fff')};
user-select: none;
}
:host([hidden]) {
display: none;
}
.maincontainer {
position: relative;
will-change: transform;
transition: transform 0.2s;
transform: translateX(0px);
position: relative;
transition: all 0.2s;
margin-left: 0px;
}
.maincontainer:hover {
transform: translateX(3px);
filter: drop-shadow(0px 0px 2px rgba(0, 0, 0, 0.2));
pointer-events: none;
opacity: 0;
transition: all 0.2s;
}
.arrow {
@ -77,17 +105,17 @@ export class DeesSpeechbubble extends DeesElement {
background: ${cssManager.bdTheme('#fff', '#333')};
height: 15px;
width: 15px;
left: 4px;
top: 5px;
border-radius: 2px;
left: 2px;
top: 12px;
border-radius: 3px;
}
.speechbubble {
background: ${cssManager.bdTheme('#fff', '#333')};
padding: 0px 10px;
padding: 0px 16px;
border-radius: 3px;
position: absolute;
line-height: 25px;
min-width: 240px;
font-size: 12px;
top: 0px;
left: 8px;
@ -132,25 +160,70 @@ export class DeesSpeechbubble extends DeesElement {
public render(): TemplateResult {
return html`
<div class="maincontainer" @click=${this.handleClick}>
<div class="arrow"></div>
<div class="speechbubble"><span class="wave">👋</span> We build with launch.sh, and you can too.</div>
</div>
${this.manifested
? html`
<div class="maincontainer" @click=${this.handleClick}>
<div class="arrow"></div>
<div class="speechbubble">
${this.wave ? html`<span class="wave">👋</span>` : html``}
${directives.resolve(this.getHtml())}
</div>
</div>
`
: html``}
`;
}
public async handleClick() {
if (this.disabled) {
return;
}
globalThis.location.href = "https://launch.sh"
console.log('speechbubble got clicked.');
}
public async firstUpdated() {
if (!this.textContent) {
this.textContent = 'Button';
this.performUpdate();
// lets make sure we have a ref
if (!this.reffedElement) {
this.reffedElement = this.previousElementSibling as HTMLElement;
}
if (this.manifested) {
await this.updatePosition();
(this.shadowRoot.querySelector('.maincontainer') as HTMLElement).style.opacity = '1';
} else {
// lets make sure we instrument it
let speechbubble: DeesSpeechbubble;
this.reffedElement.addEventListener('mouseenter', async () => {
speechbubble = await DeesSpeechbubble.createAndShow(this.reffedElement, this.text);
});
this.reffedElement.addEventListener('mouseleave', () => {
speechbubble.destroy();
});
}
}
public async updatePosition() {
const refElement = this.reffedElement;
const boundingClientRect = refElement.getBoundingClientRect();
this.style.position = 'fixed';
this.style.top = `${boundingClientRect.top - 13}px`;
this.style.left = `${boundingClientRect.left + refElement.clientWidth + 4}px`;
if (boundingClientRect.right > 250) {
this.style.width = `250px`;
}
}
public async getHtml(): Promise<any> {
if (!this.text) {
return '';
}
const normalized = domtools.plugins.smartstring.normalize.standard(this.text);
const result = await domtools.plugins.smartmarkdown.SmartMarkdown.easyMarkdownToHtml(
normalized
);
return unsafeHTML(result);
}
public async show() {}
public async destroy() {
(this.shadowRoot.querySelector('.maincontainer') as HTMLElement).style.opacity = '0';
this.windowLayer.destroy();
}
}

View File

@ -0,0 +1,389 @@
import { html, cssManager } from '@design.estate/dees-element';
import type { IStatsTile } from './dees-statsgrid.js';
export const demoFunc = () => {
// Demo data with different tile types
const demoTiles: IStatsTile[] = [
{
id: 'revenue',
title: 'Total Revenue',
value: 125420,
unit: '$',
type: 'number',
icon: 'faDollarSign',
description: '+12.5% from last month',
color: '#22c55e',
actions: [
{
name: 'View Details',
iconName: 'faChartLine',
action: async () => {
console.log('Viewing revenue details for tile:', 'revenue');
console.log('Current value:', 125420);
alert(`Revenue Details: $125,420 (+12.5%)`);
}
},
{
name: 'Export Data',
iconName: 'faFileExport',
action: async () => {
console.log('Exporting revenue data');
alert('Revenue data exported to CSV');
}
}
]
},
{
id: 'users',
title: 'Active Users',
value: 3847,
type: 'number',
icon: 'faUsers',
description: '324 new this week',
actions: [
{
name: 'View User List',
iconName: 'faList',
action: async () => {
console.log('Viewing user list');
}
}
]
},
{
id: 'cpu',
title: 'CPU Usage',
value: 73,
type: 'gauge',
icon: 'faMicrochip',
gaugeOptions: {
min: 0,
max: 100,
thresholds: [
{ value: 0, color: '#22c55e' },
{ value: 60, color: '#f59e0b' },
{ value: 80, color: '#ef4444' }
]
}
},
{
id: 'storage',
title: 'Storage Used',
value: 65,
type: 'percentage',
icon: 'faHardDrive',
description: '650 GB of 1 TB',
color: '#3b82f6'
},
{
id: 'memory',
title: 'Memory Usage',
value: 45,
type: 'gauge',
icon: 'faMemory',
gaugeOptions: {
min: 0,
max: 100,
thresholds: [
{ value: 0, color: '#22c55e' },
{ value: 70, color: '#f59e0b' },
{ value: 90, color: '#ef4444' }
]
}
},
{
id: 'requests',
title: 'API Requests',
value: '1.2k',
unit: '/min',
type: 'trend',
icon: 'faServer',
trendData: [45, 52, 38, 65, 72, 68, 75, 82, 79, 85, 88, 92]
},
{
id: 'uptime',
title: 'System Uptime',
value: '99.95%',
type: 'text',
icon: 'faCheckCircle',
color: '#22c55e',
description: 'Last 30 days'
},
{
id: 'latency',
title: 'Response Time',
value: 142,
unit: 'ms',
type: 'trend',
icon: 'faClock',
trendData: [150, 145, 148, 142, 138, 140, 135, 145, 142],
description: 'P95 latency'
},
{
id: 'errors',
title: 'Error Rate',
value: 0.03,
unit: '%',
type: 'number',
icon: 'faExclamationTriangle',
color: '#ef4444',
actions: [
{
name: 'View Error Logs',
iconName: 'faFileAlt',
action: async () => {
console.log('Viewing error logs');
}
}
]
}
];
// Grid actions for the demo
const gridActions = [
{
name: 'Refresh',
iconName: 'faSync',
action: async () => {
console.log('Refreshing stats...');
// Simulate refresh animation
const grid = document.querySelector('dees-statsgrid');
if (grid) {
grid.style.opacity = '0.5';
setTimeout(() => {
grid.style.opacity = '1';
}, 500);
}
}
},
{
name: 'Export Report',
iconName: 'faFileExport',
action: async () => {
console.log('Exporting stats report...');
}
},
{
name: 'Settings',
iconName: 'faCog',
action: async () => {
console.log('Opening settings...');
}
}
];
return html`
<style>
.demo-container {
padding: 32px;
background: ${cssManager.bdTheme('#f8f9fa', '#0a0a0a')};
min-height: 100vh;
}
.demo-section {
margin-bottom: 48px;
}
.demo-title {
font-size: 24px;
font-weight: 600;
margin-bottom: 16px;
color: ${cssManager.bdTheme('#333', '#fff')};
}
.demo-description {
font-size: 14px;
color: ${cssManager.bdTheme('#666', '#aaa')};
margin-bottom: 24px;
}
.theme-toggle {
position: fixed;
top: 16px;
right: 16px;
padding: 8px 16px;
background: ${cssManager.bdTheme('#fff', '#1a1a1a')};
border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#2a2a2a')};
border-radius: 8px;
cursor: pointer;
z-index: 100;
}
</style>
<div class="demo-container">
<button class="theme-toggle" @click=${() => {
document.body.classList.toggle('bright');
}}>Toggle Theme</button>
<div class="demo-section">
<h2 class="demo-title">Full Featured Stats Grid</h2>
<p class="demo-description">
A comprehensive dashboard with various tile types, actions, and real-time updates.
</p>
<dees-statsgrid
.tiles=${demoTiles}
.gridActions=${gridActions}
.minTileWidth=${250}
.gap=${16}
></dees-statsgrid>
</div>
<div class="demo-section">
<h2 class="demo-title">Compact Grid (Smaller Tiles)</h2>
<p class="demo-description">
Same data displayed with smaller minimum tile width for more compact layouts.
</p>
<dees-statsgrid
.tiles=${demoTiles.slice(0, 6)}
.minTileWidth=${180}
.gap=${12}
></dees-statsgrid>
</div>
<div class="demo-section">
<h2 class="demo-title">Simple Metrics (No Actions)</h2>
<p class="demo-description">
Clean display without interactive elements for pure visualization.
</p>
<dees-statsgrid
.tiles=${[
{
id: 'metric1',
title: 'Total Sales',
value: 48293,
type: 'number',
icon: 'faShoppingCart'
},
{
id: 'metric2',
title: 'Conversion Rate',
value: 3.4,
unit: '%',
type: 'number',
icon: 'faChartLine'
},
{
id: 'metric3',
title: 'Avg Order Value',
value: 127.50,
unit: '$',
type: 'number',
icon: 'faReceipt'
},
{
id: 'metric4',
title: 'Customer Satisfaction',
value: 92,
type: 'percentage',
icon: 'faSmile',
color: '#22c55e'
}
]}
.minTileWidth=${220}
.gap=${16}
></dees-statsgrid>
</div>
<div class="demo-section">
<h2 class="demo-title">Performance Monitoring</h2>
<p class="demo-description">
Real-time performance metrics with gauge visualizations and thresholds.
</p>
<dees-statsgrid
.tiles=${[
{
id: 'perf1',
title: 'Database Load',
value: 42,
type: 'gauge',
icon: 'faDatabase',
gaugeOptions: {
min: 0,
max: 100,
thresholds: [
{ value: 0, color: '#10b981' },
{ value: 50, color: '#f59e0b' },
{ value: 75, color: '#ef4444' }
]
}
},
{
id: 'perf2',
title: 'Network I/O',
value: 856,
unit: 'MB/s',
type: 'trend',
icon: 'faNetworkWired',
trendData: [720, 780, 823, 845, 812, 876, 856]
},
{
id: 'perf3',
title: 'Cache Hit Rate',
value: 94.2,
type: 'percentage',
icon: 'faBolt',
color: '#3b82f6'
},
{
id: 'perf4',
title: 'Active Connections',
value: 1428,
type: 'number',
icon: 'faLink',
description: 'Peak: 2,100'
}
]}
.gridActions=${[
{
name: 'Auto Refresh',
iconName: 'faPlay',
action: async () => {
console.log('Starting auto refresh...');
}
}
]}
.minTileWidth=${280}
.gap=${20}
></dees-statsgrid>
</div>
<script>
// Simulate real-time updates
setInterval(() => {
const grids = document.querySelectorAll('dees-statsgrid');
grids.forEach(grid => {
if (grid.tiles && grid.tiles.length > 0) {
// Update some random values
const updatedTiles = [...grid.tiles];
// Update trends with new data point
updatedTiles.forEach(tile => {
if (tile.type === 'trend' && tile.trendData) {
tile.trendData = [...tile.trendData.slice(1),
tile.trendData[tile.trendData.length - 1] + Math.random() * 10 - 5
];
}
// Randomly update some numeric values
if (tile.type === 'number' && Math.random() > 0.7) {
const currentValue = typeof tile.value === 'number' ? tile.value : parseFloat(tile.value);
tile.value = Math.round(currentValue + (Math.random() * 10 - 5));
}
// Update gauge values
if (tile.type === 'gauge' && Math.random() > 0.5) {
const currentValue = typeof tile.value === 'number' ? tile.value : parseFloat(tile.value);
const newValue = currentValue + (Math.random() * 10 - 5);
tile.value = Math.max(tile.gaugeOptions?.min || 0,
Math.min(tile.gaugeOptions?.max || 100, Math.round(newValue)));
}
});
grid.tiles = updatedTiles;
}
});
}, 3000);
</script>
</div>
`;
};

View File

@ -0,0 +1,518 @@
import { demoFunc } from './dees-statsgrid.demo.js';
import * as plugins from './00plugins.js';
import {
customElement,
html,
DeesElement,
property,
state,
css,
unsafeCSS,
cssManager,
} from '@design.estate/dees-element';
import type { TemplateResult } from '@design.estate/dees-element';
import './dees-icon.js';
import './dees-contextmenu.js';
import './dees-button.js';
declare global {
interface HTMLElementTagNameMap {
'dees-statsgrid': DeesStatsGrid;
}
}
export interface IStatsTile {
id: string;
title: string;
value: number | string;
unit?: string;
type: 'number' | 'gauge' | 'percentage' | 'trend' | 'text';
// For gauge type
gaugeOptions?: {
min: number;
max: number;
thresholds?: Array<{value: number; color: string}>;
};
// For trend type
trendData?: number[];
// Visual customization
color?: string;
icon?: string;
description?: string;
// Tile-specific actions
actions?: plugins.tsclass.website.IMenuItem[];
}
@customElement('dees-statsgrid')
export class DeesStatsGrid extends DeesElement {
public static demo = demoFunc;
@property({ type: Array })
public tiles: IStatsTile[] = [];
@property({ type: Number })
public minTileWidth: number = 250;
@property({ type: Number })
public gap: number = 16;
@property({ type: Array })
public gridActions: plugins.tsclass.website.IMenuItem[] = [];
@state()
private contextMenuVisible = false;
@state()
private contextMenuPosition = { x: 0, y: 0 };
@state()
private contextMenuActions: plugins.tsclass.website.IMenuItem[] = [];
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
width: 100%;
}
.grid-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: ${unsafeCSS(16)}px;
min-height: 32px;
}
.grid-title {
font-size: 18px;
font-weight: 600;
color: ${cssManager.bdTheme('#333', '#fff')};
}
.grid-actions {
display: flex;
gap: 8px;
}
.grid-actions dees-button {
font-size: 14px;
min-width: auto;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(${unsafeCSS(250)}px, 1fr));
gap: ${unsafeCSS(16)}px;
width: 100%;
}
.stats-tile {
background: ${cssManager.bdTheme('#fff', '#1a1a1a')};
border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#2a2a2a')};
border-radius: 12px;
padding: 20px;
transition: all 0.3s ease;
cursor: pointer;
position: relative;
overflow: hidden;
}
.stats-tile:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px ${cssManager.bdTheme('rgba(0,0,0,0.1)', 'rgba(0,0,0,0.3)')};
border-color: ${cssManager.bdTheme('#d0d0d0', '#3a3a3a')};
}
.stats-tile.clickable {
cursor: pointer;
}
.tile-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
width: 100%;
}
.tile-title {
font-size: 14px;
font-weight: 500;
color: ${cssManager.bdTheme('#666', '#aaa')};
margin: 0;
}
.tile-icon {
opacity: 0.6;
}
.tile-content {
height: 90px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
}
.tile-value {
font-size: 32px;
font-weight: 600;
color: ${cssManager.bdTheme('#333', '#fff')};
line-height: 1.2;
display: flex;
align-items: baseline;
justify-content: center;
gap: 6px;
width: 100%;
}
.tile-unit {
font-size: 18px;
font-weight: 400;
color: ${cssManager.bdTheme('#666', '#aaa')};
}
.tile-description {
font-size: 12px;
color: ${cssManager.bdTheme('#888', '#777')};
margin-top: 8px;
}
.gauge-container {
width: 100%;
height: 80px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.gauge-svg {
width: 100%;
height: 100%;
}
.gauge-background {
fill: none;
stroke: ${cssManager.bdTheme('#e0e0e0', '#2a2a2a')};
stroke-width: 6;
}
.gauge-fill {
fill: none;
stroke-width: 6;
stroke-linecap: round;
transition: stroke-dashoffset 0.5s ease;
}
.gauge-text {
fill: ${cssManager.bdTheme('#333', '#fff')};
font-size: 18px;
font-weight: 600;
text-anchor: middle;
}
.percentage-container {
width: 100%;
height: 24px;
background: ${cssManager.bdTheme('#f0f0f0', '#2a2a2a')};
border-radius: 12px;
overflow: hidden;
position: relative;
}
.percentage-fill {
height: 100%;
background: ${cssManager.bdTheme('#0084ff', '#0066cc')};
transition: width 0.5s ease;
border-radius: 12px;
}
.percentage-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 12px;
font-weight: 600;
color: ${cssManager.bdTheme('#333', '#fff')};
}
.trend-container {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 4px;
}
.trend-svg {
width: 100%;
height: 40px;
flex-shrink: 0;
}
.trend-line {
fill: none;
stroke: ${cssManager.bdTheme('#0084ff', '#0066cc')};
stroke-width: 2;
}
.trend-area {
fill: ${cssManager.bdTheme('rgba(0, 132, 255, 0.1)', 'rgba(0, 102, 204, 0.2)')};
}
.text-value {
font-size: 32px;
font-weight: 600;
color: ${cssManager.bdTheme('#333', '#fff')};
}
.trend-value {
font-size: 32px;
font-weight: 600;
color: ${cssManager.bdTheme('#333', '#fff')};
display: flex;
align-items: baseline;
gap: 6px;
}
.trend-value .tile-unit {
font-size: 18px;
}
dees-contextmenu {
position: fixed;
z-index: 1000;
}
`,
];
constructor() {
super();
}
public render(): TemplateResult {
return html`
${this.gridActions.length > 0 ? html`
<div class="grid-header">
<div class="grid-title">Statistics</div>
<div class="grid-actions">
${this.gridActions.map(action => html`
<dees-button @clicked=${() => this.handleGridAction(action)}>
${action.iconName ? html`<dees-icon .iconFA=${action.iconName} size="small"></dees-icon>` : ''}
${action.name}
</dees-button>
`)}
</div>
</div>
` : ''}
<div class="stats-grid" style="grid-template-columns: repeat(auto-fit, minmax(${this.minTileWidth}px, 1fr)); gap: ${this.gap}px;">
${this.tiles.map(tile => this.renderTile(tile))}
</div>
${this.contextMenuVisible ? html`
<dees-contextmenu
.x=${this.contextMenuPosition.x}
.y=${this.contextMenuPosition.y}
.menuItems=${this.contextMenuActions}
@clicked=${() => this.contextMenuVisible = false}
></dees-contextmenu>
` : ''}
`;
}
private renderTile(tile: IStatsTile): TemplateResult {
const hasActions = tile.actions && tile.actions.length > 0;
const clickable = hasActions && tile.actions.length === 1;
return html`
<div
class="stats-tile ${clickable ? 'clickable' : ''}"
@click=${clickable ? () => this.handleTileAction(tile.actions![0], tile) : undefined}
@contextmenu=${hasActions ? (e: MouseEvent) => this.showContextMenu(e, tile) : undefined}
>
<div class="tile-header">
<h3 class="tile-title">${tile.title}</h3>
${tile.icon ? html`
<dees-icon class="tile-icon" .iconFA=${tile.icon} size="small"></dees-icon>
` : ''}
</div>
<div class="tile-content">
${this.renderTileContent(tile)}
</div>
${tile.description ? html`
<div class="tile-description">${tile.description}</div>
` : ''}
</div>
`;
}
private renderTileContent(tile: IStatsTile): TemplateResult {
switch (tile.type) {
case 'number':
return html`
<div class="tile-value" style="${tile.color ? `color: ${tile.color}` : ''}">
<span>${tile.value}</span>
${tile.unit ? html`<span class="tile-unit">${tile.unit}</span>` : ''}
</div>
`;
case 'gauge':
return this.renderGauge(tile);
case 'percentage':
return this.renderPercentage(tile);
case 'trend':
return this.renderTrend(tile);
case 'text':
return html`
<div class="text-value" style="${tile.color ? `color: ${tile.color}` : ''}">
${tile.value}
</div>
`;
default:
return html`<div class="tile-value">${tile.value}</div>`;
}
}
private renderGauge(tile: IStatsTile): TemplateResult {
const value = typeof tile.value === 'number' ? tile.value : parseFloat(tile.value);
const options = tile.gaugeOptions || { min: 0, max: 100 };
const percentage = ((value - options.min) / (options.max - options.min)) * 100;
const strokeDasharray = 188.5; // Circumference of circle with r=30
const strokeDashoffset = strokeDasharray - (strokeDasharray * percentage) / 100;
let strokeColor = tile.color || cssManager.bdTheme('#0084ff', '#0066cc');
if (options.thresholds) {
for (const threshold of options.thresholds.reverse()) {
if (value >= threshold.value) {
strokeColor = threshold.color;
break;
}
}
}
return html`
<div class="gauge-container">
<svg class="gauge-svg" viewBox="0 0 80 80">
<circle
class="gauge-background"
cx="40"
cy="40"
r="30"
transform="rotate(-90 40 40)"
/>
<circle
class="gauge-fill"
cx="40"
cy="40"
r="30"
transform="rotate(-90 40 40)"
stroke="${strokeColor}"
stroke-dasharray="${strokeDasharray}"
stroke-dashoffset="${strokeDashoffset}"
/>
<text class="gauge-text" x="40" y="40" dy="0.35em">
${value}${tile.unit || ''}
</text>
</svg>
</div>
`;
}
private renderPercentage(tile: IStatsTile): TemplateResult {
const value = typeof tile.value === 'number' ? tile.value : parseFloat(tile.value);
const percentage = Math.min(100, Math.max(0, value));
return html`
<div class="percentage-container">
<div
class="percentage-fill"
style="width: ${percentage}%; ${tile.color ? `background: ${tile.color}` : ''}"
></div>
<div class="percentage-text">${percentage}%</div>
</div>
`;
}
private renderTrend(tile: IStatsTile): TemplateResult {
if (!tile.trendData || tile.trendData.length < 2) {
return html`<div class="tile-value">${tile.value}</div>`;
}
const data = tile.trendData;
const max = Math.max(...data);
const min = Math.min(...data);
const range = max - min || 1;
const width = 200;
const height = 40;
const points = data.map((value, index) => {
const x = (index / (data.length - 1)) * width;
const y = height - ((value - min) / range) * height;
return `${x},${y}`;
}).join(' ');
const areaPoints = `0,${height} ${points} ${width},${height}`;
return html`
<div class="trend-container">
<svg class="trend-svg" viewBox="0 0 ${width} ${height}" preserveAspectRatio="none">
<polygon class="trend-area" points="${areaPoints}" />
<polyline class="trend-line" points="${points}" />
</svg>
<div class="trend-value">
<span>${tile.value}</span>
${tile.unit ? html`<span class="tile-unit">${tile.unit}</span>` : ''}
</div>
</div>
`;
}
private async handleGridAction(action: plugins.tsclass.website.IMenuItem) {
if (action.action) {
await action.action();
}
}
private async handleTileAction(action: plugins.tsclass.website.IMenuItem, _tile: IStatsTile) {
if (action.action) {
await action.action();
}
// Note: tile data is available through closure when defining actions
}
private showContextMenu(event: MouseEvent, tile: IStatsTile) {
if (!tile.actions || tile.actions.length === 0) return;
event.preventDefault();
this.contextMenuPosition = { x: event.clientX, y: event.clientY };
this.contextMenuActions = tile.actions;
this.contextMenuVisible = true;
// Close context menu on click outside
const closeHandler = () => {
this.contextMenuVisible = false;
document.removeEventListener('click', closeHandler);
};
setTimeout(() => {
document.addEventListener('click', closeHandler);
}, 100);
}
}

View File

@ -102,7 +102,7 @@ export class DeesStepper extends DeesElement {
transition: all 0.7s ease-in-out;
max-width: 500px;
min-height: 300px;
border-radius: 8px;
border-radius: 16px;
background: ${cssManager.bdTheme('#ffffff', '#181818')};
border-top: 1px solid ${cssManager.bdTheme('#ffffff', '#181818')};
color: ${cssManager.bdTheme('#333', '#fff')};
@ -174,9 +174,10 @@ export class DeesStepper extends DeesElement {
.step .title {
text-align: center;
padding-top: 50px;
font-family: 'Roboto', 'Inter', sans-serif;
font-size: 25px;
font-weight: 300;
font-family: 'Geist Sans', sans-serif;
font-size: 22px;
font-weight: 500;
}
.step .content {

View File

@ -12,7 +12,7 @@ import {
unsafeCSS,
type CSSResult,
state,
resolveExec,
directives,
} from '@design.estate/dees-element';
import { DeesContextmenu } from './dees-contextmenu.js';
@ -117,12 +117,21 @@ export class DeesTable<T> extends DeesElement {
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject<DeesTable<T>>();
// end dees-form compatibility -----------------------------------------
/**
* What does a row of data represent?
*/
@property({
type: String,
reflect: true,
})
public dataName: string;
@property({
type: Boolean,
})
searchable: boolean = true;
@property({
type: Array,
})
@ -162,16 +171,16 @@ export class DeesTable<T> extends DeesElement {
css`
.mainbox {
color: ${cssManager.bdTheme('#333', '#fff')};
font-family: 'Roboto', 'Inter', sans-serif;
font-family: 'Geist Sans', sans-serif;
font-weight: 400;
font-size: 14px;
padding: 16px;
display: block;
width: 100%;
min-height: 50px;
background: ${cssManager.bdTheme('#ffffff', '#333333')};
background: ${cssManager.bdTheme('#ffffff', '#222222')};
border-radius: 3px;
border-top: 1px solid ${cssManager.bdTheme('#fff', '#444')};
border-top: 1px solid ${cssManager.bdTheme('#fff', '#ffffff10')};
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3);
overflow-x: auto;
cursor: default;
@ -181,7 +190,7 @@ export class DeesTable<T> extends DeesElement {
display: flex;
justify-content: flex-end;
align-items: center;
font-family: 'Roboto', 'Inter', sans-serif;
font-family: 'Geist Sans', sans-serif;
}
.headingContainer {
@ -199,15 +208,20 @@ export class DeesTable<T> extends DeesElement {
.headingSeparation {
margin-top: 7px;
border-bottom: 1px solid ${cssManager.bdTheme('#bcbcbc', '#bcbcbc')};
border-bottom: 1px solid ${cssManager.bdTheme('#bcbcbc', '#444444')};
}
.headerActions {
user-select: none;
display: flex;
flex-direction: row;
margin-left: auto;
}
.headerAction {
display: flex;
flex-direction: row;
color: ${cssManager.bdTheme('#333', '#ccc')};
margin-left: 16px;
}
.headerAction:hover {
@ -218,6 +232,24 @@ export class DeesTable<T> extends DeesElement {
margin-right: 8px;
}
.searchGrid {
background: ${cssManager.bdTheme('#fff', '#111111')};
display: grid;
grid-gap: 16px;
grid-template-columns: 1fr 200px;
margin-top: 16px;
padding: 0px 16px;
border-top: 1px solid ${cssManager.bdTheme('#fff', '#ffffff20')};
border-radius: 8px;
}
.searchGrid.hidden {
height: 0px;
opacity: 0;
overflow: hidden;
margin-top: 0px;
}
table,
.noDataSet {
margin-top: 16px;
@ -237,7 +269,6 @@ export class DeesTable<T> extends DeesElement {
text-align: left;
}
tr:hover {
}
tr:hover td {
background: ${cssManager.bdTheme('#22222210', '#ffffff10')};
@ -258,7 +289,7 @@ export class DeesTable<T> extends DeesElement {
th {
text-transform: none;
font-family: 'Roboto', 'Inter', sans-serif;
font-family: 'Geist Sans', sans-serif;
font-weight: 500;
}
th,
@ -306,19 +337,20 @@ export class DeesTable<T> extends DeesElement {
font-weight: inherit;
padding: 0px 6px;
}
.action {
margin: -6px 0px;
padding: 10px;
line-height: 36px;
height: 36px;
display: inline-block;
border-radius: 8px;
.actionsContainer {
display: flex;
flex-direction: row;
height: 24px;
transform: translateY(-4px);
margin-left: -6px;
}
.action:first-child {
margin-left: -8px;
width: min-content;
.action {
position: relative;
padding: 8px 10px;
line-height: 24px;
height: 32px;
size: 16px;
border-radius: 8px;
}
.action:hover {
@ -334,7 +366,7 @@ export class DeesTable<T> extends DeesElement {
}
.footer {
font-family: 'Roboto', 'Inter', sans-serif;
font-family: 'Geist Sans', sans-serif;
font-size: 14px;
color: ${cssManager.bdTheme('#111', '#ffffff90')};
background: ${cssManager.bdTheme('#eeeeeb', '#00000050')};
@ -369,7 +401,6 @@ export class DeesTable<T> extends DeesElement {
}
.footerActions .footerAction:hover dees-icon {
}
`,
];
@ -384,7 +415,7 @@ export class DeesTable<T> extends DeesElement {
<div class="heading heading2">${this.heading2}</div>
</div>
<div class="headerActions">
${resolveExec(async () => {
${directives.resolveExec(async () => {
const resultArray: TemplateResult[] = [];
for (const action of this.dataActions) {
if (!action.type.includes('header')) continue;
@ -410,8 +441,32 @@ export class DeesTable<T> extends DeesElement {
</div>
</div>
<div class="headingSeparation"></div>
<dees-input-text></dees-input-text>
<div class="searchGrid hidden">
<dees-input-text
.label=${'lucene syntax search'}
.description=${`
You can use the lucene syntax to search for data, e.g.:
\`\`\`
name: "john" AND age: 18
\`\`\`
`}
></dees-input-text>
<dees-input-multitoggle
.label=${'search mode'}
.options=${['table', 'data', 'server']}
.selectedOption=${'table'}
.description=${`
There are three basic modes:
* table: only searches data already in the table
* data: searches original data, ignoring table transforms
* server: searches data on the server
`}
></dees-input-multitoggle>
</div>
<!-- the actual table -->
<style></style>
@ -538,22 +593,28 @@ export class DeesTable<T> extends DeesElement {
return html`
<td>
<div class="innerCellContainer">
${this.getActionsForType('inRow').map(
(actionArg) => html`<div
class="action"
@click=${() =>
actionArg.actionFunc({
item: itemArg,
table: this,
})}
>
${actionArg.iconName
? html`
<dees-icon .iconFA=${actionArg.iconName}></dees-icon>
`
: actionArg.name}
</div>`
)}
<div class="actionsContainer">
${this.getActionsForType('inRow').map(
(actionArg) => html`
<div
class="action"
@click=${() =>
actionArg.actionFunc({
item: itemArg,
table: this,
})}
>
${actionArg.iconName
? html`
<dees-icon
.iconFA=${actionArg.iconName}
></dees-icon>
`
: actionArg.name}
</div>
`
)}
</div>
</div>
</td>
`;
@ -573,7 +634,7 @@ export class DeesTable<T> extends DeesElement {
selected
</div>
<div class="footerActions">
${resolveExec(async () => {
${directives.resolveExec(async () => {
const resultArray: TemplateResult[] = [];
for (const action of this.dataActions) {
if (!action.type.includes('footer')) continue;
@ -602,11 +663,30 @@ export class DeesTable<T> extends DeesElement {
`;
}
public async firstUpdated() {}
public async firstUpdated() {
}
public async updated(changedProperties: Map<string | number | symbol, unknown>): Promise<void> {
super.updated(changedProperties);
this.determineColumnWidths();
if (this.searchable) {
const existing = this.dataActions.find((actionArg) => actionArg.type.includes('header') && actionArg.name === 'Search');
if (!existing) {
this.dataActions.unshift({
name: 'Search',
iconName: 'magnifyingGlass',
type: ['header'],
actionFunc: async () => {
console.log('open search');
const searchGrid = this.shadowRoot.querySelector('.searchGrid');
searchGrid.classList.toggle('hidden');
}
});
console.log(this.dataActions);
this.requestUpdate();
};
}
}
public async determineColumnWidths() {
@ -627,7 +707,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 * 35;
this.dataActions.filter((actionArg) => actionArg.type.includes('inRow')).length * 36;
cell.style.width = `${Math.max(neededWidth, 68)}px`;
} else {
cell.style.width = width;

View File

@ -0,0 +1,355 @@
import {
DeesElement,
property,
html,
customElement,
type TemplateResult,
css,
cssManager,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import * as webcontainer from '@webcontainer/api';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
declare global {
interface HTMLElementTagNameMap {
'dees-terminal': DeesTerminal;
}
}
@customElement('dees-terminal')
export class DeesTerminal extends DeesElement {
public static demo = () => html` <dees-terminal
.environment=${{
NODE_ENV: 'development',
PORT: '3000',
}}
></dees-terminal> `;
// INSTANCE
private resizeObserver: ResizeObserver;
@property()
public setupCommand = `pnpm install @serve.zone/cli && servezone cli\n`;
@property()
environment: {[key: string]: string} = {};
// exposing webcontainer
private webcontainerDeferred = new domtools.plugins.smartpromise.Deferred<webcontainer.WebContainer>();
public webcontainerPromise = this.webcontainerDeferred.promise;
constructor() {
super();
this.resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
// Handle the resize event
console.log(`Terminal Resized`);
this.handleResize();
}
});
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
padding: 20px;
background: #000;
position: absolute;
height: 100%;
width: 100%;
}
* {
box-sizing: border-box;
}
#container {
position: absolute;
height: calc(100% - 40px);
width: calc(100% - 40px);
}
/**
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
* https://github.com/chjj/term.js
* @license MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Originally forked from (with the author's permission):
* Fabrice Bellard's javascript vt100 for jslinux:
* http://bellard.org/jslinux/
* Copyright (c) 2011 Fabrice Bellard
* The original design remains. The terminal itself
* has been extended to include xterm CSI codes, among
* other features.
*/
/**
* Default styles for xterm.js
*/
.xterm {
font-feature-settings: 'liga' 0;
position: relative;
user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
}
.xterm.focus,
.xterm:focus {
outline: none;
}
.xterm .xterm-helpers {
position: absolute;
top: 0;
/**
* The z-index of the helpers must be higher than the canvases in order for
* IMEs to appear on top.
*/
z-index: 5;
}
.xterm .xterm-helper-textarea {
padding: 0;
border: 0;
margin: 0;
/* Move textarea out of the screen to the far left, so that the cursor is not visible */
position: absolute;
opacity: 0;
left: -9999em;
top: 0;
width: 0;
height: 0;
z-index: -5;
/** Prevent wrapping so the IME appears against the textarea at the correct position */
white-space: nowrap;
overflow: hidden;
resize: none;
}
.xterm .composition-view {
/* TODO: Composition position got messed up somewhere */
background: #000;
color: #fff;
display: none;
position: absolute;
white-space: nowrap;
z-index: 1;
}
.xterm .composition-view.active {
display: block;
}
.xterm .xterm-viewport {
/* On OS X this is required in order for the scroll bar to appear fully opaque */
background-color: #000;
overflow-y: scroll;
cursor: default;
position: absolute;
right: 0;
left: 0;
top: 0;
bottom: 0;
}
.xterm .xterm-screen {
position: relative;
}
.xterm .xterm-screen canvas {
position: absolute;
left: 0;
top: 0;
}
.xterm .xterm-scroll-area {
visibility: hidden;
}
.xterm-char-measure-element {
display: inline-block;
visibility: hidden;
position: absolute;
top: 0;
left: -9999em;
line-height: normal;
}
.xterm {
cursor: text;
}
.xterm.enable-mouse-events {
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
cursor: default;
}
.xterm.xterm-cursor-pointer {
cursor: pointer;
}
.xterm.column-select.focus {
/* Column selection mode */
cursor: crosshair;
}
.xterm .xterm-accessibility,
.xterm .xterm-message {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 10;
color: transparent;
}
.xterm .live-region {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
overflow: hidden;
}
.xterm-dim {
opacity: 0.5;
}
.xterm-underline {
text-decoration: underline;
}
`,
];
public render(): TemplateResult {
return html`
<div class="mainbox">
<div id="container"></div>
</div>
`;
}
private fitAddon: FitAddon;
public async firstUpdated(
_changedProperties: Map<string | number | symbol, unknown>
): Promise<void> {
const domtools = await this.domtoolsPromise;
super.firstUpdated(_changedProperties);
const container = this.shadowRoot.getElementById('container');
const term = new Terminal({
convertEol: true,
cursorBlink: true,
});
this.fitAddon = new FitAddon();
term.loadAddon(this.fitAddon);
// Open the terminal in #terminal-container
term.open(container);
// Make the terminal's size and geometry fit the size of #terminal-container
this.fitAddon.fit();
term.write(`dees-terminal custom terminal. \r\n$ `);
// lets start the webcontainer
// Call only once
const webcontainerInstance = await webcontainer.WebContainer.boot();
const shellProcess = await webcontainerInstance.spawn('jsh');
shellProcess.output.pipeTo(
new WritableStream({
write(data) {
term.write(data);
},
})
);
const input = shellProcess.input.getWriter();
term.onData((data) => {
input.write(data);
});
await this.waitForPrompt(term, '~/');
// lets set the environment variables
await this.setEnvironmentVariables(this.environment, webcontainerInstance);
input.write(`source source.env\n`);
await this.waitForPrompt(term, '~/');
// lets run the setup command
input.write(this.setupCommand);
await this.waitForPrompt(term, '~/');
input.write(`clear && echo 'welcome'\n`);
this.webcontainerDeferred.resolve(webcontainerInstance);
}
async connectedCallback(): Promise<void> {
await super.connectedCallback();
this.resizeObserver.observe(this);
}
async disconnectedCallback(): Promise<void> {
this.resizeObserver.unobserve(this);
await super.disconnectedCallback();
}
handleResize() {
this.fitAddon.fit();
}
public async waitForPrompt(term: Terminal, prompt: string): Promise<void> {
return new Promise<void>((resolve) => {
const checkPrompt = () => {
const lines = term.buffer.active;
for (let i = 0; i < lines.length; i++) {
const line = lines.getLine(i);
if (line && line.translateToString().includes(prompt)) {
setTimeout(() => {
resolve();
}, 100);
return;
}
}
setTimeout(checkPrompt, 100); // check every 100 ms
};
checkPrompt();
});
}
public async setEnvironmentVariables(envArg: {[key: string]: string}, webcontainerInstanceArg?: webcontainer.WebContainer) {
const webcontainerInstance = webcontainerInstanceArg ||await this.webcontainerPromise;
let envFile = ``
for (const key in envArg) {
envFile += `export ${key}="${envArg[key]}"\n`;
}
await webcontainerInstance.mount({'source.env': {
file: {
contents: envFile,
}
}});
}
}

View File

@ -1,11 +1,24 @@
export * from './dees-appui-activitylog.js';
export * from './dees-appui-appbar.js';
export * from './dees-appui-base.js';
export * from './dees-appui-maincontent.js';
export * from './dees-appui-mainmenu.js';
export * from './dees-appui-mainselector.js';
export * from './dees-badge.js';
export * from './dees-button-exit.js';
export * from './dees-button.js';
export * from './dees-chart-area.js';
export * from './dees-chart-log.js';
export * from './dees-chips.js';
export * from './dees-contextmenu.js';
export * from './dees-dataview-codebox.js';
export * from './dees-dataview-statusobject.js';
export * from './dees-editor.js';
export * from './dees-editor-markdown.js';
export * from './dees-editor-markdownoutlet.js';
export * from './dees-form-submit.js';
export * from './dees-form.js';
export * from './dees-heading.js';
export * from './dees-hint.js';
export * from './dees-icon.js';
export * from './dees-input-checkbox.js';
@ -18,17 +31,22 @@ export * from './dees-progressbar.js';
export * from './dees-input-quantityselector.js';
export * from './dees-input-radio.js';
export * from './dees-input-text.js';
export * from './dees-label.js';
export * from './dees-mobilenavigation.js';
export * from './dees-modal.js';
export * from './dees-input-multitoggle.js';
export * from './dees-pdf.js';
export * from './dees-searchbar.js';
export * from './dees-simple-appdash.js';
export * from './dees-simple-login.js';
export * from './dees-speechbubble.js';
export * from './dees-spinner.js';
export * from './dees-statsgrid.js';
export * from './dees-stepper.js';
export * from './dees-table.js';
export * from './dees-terminal.js';
export * from './dees-toast.js';
export * from './dees-updater.js';
export * from './dees-windowcontrols.js';
export * from './dees-windowlayer.js';
export * from './dees-pagination.js';

View File

@ -0,0 +1,2 @@
export * from './tab.js';
export * from './selectionoption.js';

View File

@ -0,0 +1,4 @@
export interface ISelectionOption {
key: string;
action: () => void;
}

View File

@ -0,0 +1,5 @@
export interface ITab {
key: string;
iconName?: string;
action: () => void;
}

View File

@ -1,2 +1,4 @@
export * from './elements/index.js';
import * as colors from './elements/00colors.js';
export { colors };
export { commitinfo } from './00_commitinfo_data.js';