Compare commits

...

606 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
8fa01fbaad 1.0.246 2024-01-18 02:08:20 +01:00
9cdae8ccdb fix(core): update 2024-01-18 02:08:19 +01:00
7e1c5f3dbb 1.0.245 2024-01-15 19:42:34 +01:00
1f3f17247c fix(core): update 2024-01-15 19:42:33 +01:00
f07f2bb95e 1.0.244 2024-01-15 19:42:16 +01:00
0b2f6715e0 fix(core): update 2024-01-15 19:42:15 +01:00
e473364d40 1.0.243 2024-01-15 12:57:50 +01:00
554b72b075 fix(core): update 2024-01-15 12:57:49 +01:00
2c34ec8b39 1.0.242 2024-01-11 21:14:31 +01:00
e6b8e2de19 fix(core): update 2024-01-11 21:14:30 +01:00
a99d270ef1 1.0.241 2024-01-10 05:11:56 +01:00
93ee42135c fix(core): update 2024-01-10 05:11:55 +01:00
f3ca4a114a 1.0.240 2024-01-09 13:57:54 +01:00
7515b824eb fix(core): update 2024-01-09 13:57:53 +01:00
18b98b831a 1.0.239 2023-12-26 21:21:19 +01:00
d99fc8bde9 fix(core): update 2023-12-26 21:21:18 +01:00
f8d5f86814 1.0.238 2023-12-20 19:11:16 +01:00
9c1de08b4b fix(core): update 2023-12-20 19:11:16 +01:00
7f26337e1b 1.0.237 2023-12-20 19:09:55 +01:00
49f3fc8feb fix(core): update 2023-12-20 19:09:55 +01:00
5613ad7fa6 1.0.236 2023-12-08 19:16:23 +01:00
5af43900c4 fix(core): update 2023-12-08 19:16:22 +01:00
c5598f95ef 1.0.235 2023-12-08 18:26:40 +01:00
13d0cf729c fix(core): update 2023-12-08 18:26:40 +01:00
032e928dde 1.0.234 2023-12-08 18:22:19 +01:00
14ab3682f2 fix(core): update 2023-12-08 18:22:18 +01:00
c554e730a6 1.0.233 2023-12-08 18:15:40 +01:00
4d5a490e80 fix(core): update 2023-12-08 18:15:40 +01:00
7708c89fe1 1.0.232 2023-11-29 20:39:18 +01:00
0a0be3e357 fix(core): update 2023-11-29 20:39:17 +01:00
7b7c469fab 1.0.231 2023-11-29 17:20:33 +01:00
d852186888 fix(core): update 2023-11-29 17:20:32 +01:00
6cfda1ebf3 1.0.230 2023-10-31 13:44:19 +01:00
8e0062fdd5 fix(core): update 2023-10-31 13:44:18 +01:00
844cc30551 1.0.229 2023-10-24 14:18:04 +02:00
64074e37fc fix(core): update 2023-10-24 14:18:03 +02:00
cd9b028e9b 1.0.228 2023-10-23 21:23:19 +02:00
81da871e38 fix(core): update 2023-10-23 21:23:18 +02:00
90e78a2e31 1.0.227 2023-10-23 17:26:04 +02:00
5cec1fea73 fix(core): update 2023-10-23 17:26:03 +02:00
52130d67e2 1.0.226 2023-10-23 16:13:02 +02:00
a73ad40eb1 fix(core): update 2023-10-23 16:13:02 +02:00
6589818b0b 1.0.225 2023-10-20 11:17:43 +02:00
555fa32ac2 fix(core): update 2023-10-20 11:17:42 +02:00
50a84f2422 1.0.224 2023-10-20 10:47:54 +02:00
c724a3e85b fix(core): update 2023-10-20 10:47:53 +02:00
b47530e254 1.0.223 2023-10-18 15:18:50 +02:00
b80fdf113e fix(core): update 2023-10-18 15:18:49 +02:00
963edbffa3 1.0.222 2023-10-17 20:07:46 +02:00
f3687f724f fix(core): update 2023-10-17 20:07:45 +02:00
a736ee9800 1.0.221 2023-10-12 15:43:11 +02:00
bea2047092 fix(core): update 2023-10-12 15:43:10 +02:00
7d98ac8f38 1.0.220 2023-10-11 12:24:13 +02:00
32244bb450 fix(core): update 2023-10-11 12:24:12 +02:00
b154631c77 1.0.219 2023-10-07 20:01:50 +02:00
466a24ac10 fix(core): update 2023-10-07 20:01:49 +02:00
7db91a2fe6 1.0.218 2023-10-07 19:33:05 +02:00
46652dec6f fix(core): update 2023-10-07 19:33:04 +02:00
50e591b80c 1.0.217 2023-10-05 22:42:34 +02:00
6710a163a9 fix(core): update 2023-10-05 22:42:33 +02:00
a39f43f79a 1.0.216 2023-10-05 14:37:00 +02:00
964520a2f9 fix(core): update 2023-10-05 14:36:59 +02:00
6e680085a4 1.0.215 2023-09-22 20:02:49 +02:00
286a843b67 fix(core): update 2023-09-22 20:02:48 +02:00
df7c5ebafc 1.0.214 2023-09-22 19:42:23 +02:00
9927323a9d fix(core): update 2023-09-22 19:42:23 +02:00
66f3e66c8b 1.0.213 2023-09-22 19:04:03 +02:00
c68b0c5090 fix(core): update 2023-09-22 19:04:02 +02:00
53ac03507d 1.0.212 2023-09-22 13:15:34 +02:00
0031b51bcf fix(core): update 2023-09-22 13:15:34 +02:00
67a8c6e591 1.0.211 2023-09-20 18:57:54 +02:00
806390a068 fix(core): update 2023-09-20 18:57:54 +02:00
508b18bc3b 1.0.210 2023-09-18 15:12:58 +02:00
c2499ea507 fix(core): update 2023-09-18 15:12:57 +02:00
c0c91aec94 1.0.209 2023-09-17 21:38:02 +02:00
a27008a295 fix(core): update 2023-09-17 21:38:02 +02:00
7e11627618 1.0.208 2023-09-17 10:29:23 +02:00
968ce63a53 fix(core): update 2023-09-17 10:29:22 +02:00
7c0eb3290f 1.0.207 2023-09-16 14:31:03 +02:00
62403625ba fix(core): update 2023-09-16 14:31:03 +02:00
9a34b03540 1.0.206 2023-09-15 20:51:04 +02:00
8bb63df7e3 fix(core): update 2023-09-15 20:51:03 +02:00
65cd73845a 1.0.205 2023-09-15 20:11:52 +02:00
8534bc254b fix(core): update 2023-09-15 20:11:51 +02:00
2a2fd324a0 1.0.204 2023-09-15 19:07:35 +02:00
58e3de2559 fix(core): update 2023-09-15 19:07:34 +02:00
c30736870d 1.0.203 2023-09-15 19:03:19 +02:00
cfd48de885 fix(core): update 2023-09-15 19:03:18 +02:00
462df2b648 1.0.202 2023-09-15 17:27:36 +02:00
f64da93cf9 fix(core): update 2023-09-15 17:27:35 +02:00
a55db621ef 1.0.201 2023-09-14 19:46:10 +02:00
c033bdfc3b fix(core): update 2023-09-14 19:46:09 +02:00
2610e56ec1 1.0.200 2023-09-14 19:43:27 +02:00
08aa9e3fe4 fix(core): update 2023-09-14 19:43:26 +02:00
411ae7ee07 1.0.199 2023-09-13 21:14:06 +02:00
41700c1eb1 fix(core): update 2023-09-13 21:14:05 +02:00
8d7bac9793 1.0.198 2023-09-13 21:13:48 +02:00
0229eefa4d fix(core): update 2023-09-13 21:13:47 +02:00
61f646743a 1.0.197 2023-09-13 19:15:54 +02:00
e3babde7e8 fix(core): update 2023-09-13 19:15:53 +02:00
c389e43e93 1.0.196 2023-09-13 18:12:02 +02:00
1511db4eea fix(core): update 2023-09-13 18:12:01 +02:00
d713756034 1.0.195 2023-09-13 16:46:01 +02:00
17d224332d fix(core): update 2023-09-13 16:46:00 +02:00
32dd5e769b 1.0.194 2023-09-13 16:25:55 +02:00
12ace00a90 fix(core): update 2023-09-13 16:25:54 +02:00
bbc09330c9 1.0.193 2023-09-13 13:41:57 +02:00
82ead7bd1a fix(core): update 2023-09-13 13:41:56 +02:00
f8f9b150b8 1.0.192 2023-09-13 01:37:03 +02:00
494e8b7e26 fix(core): update 2023-09-13 01:37:02 +02:00
c76d364071 1.0.191 2023-09-12 13:44:07 +02:00
760e0085f7 fix(core): update 2023-09-12 13:44:07 +02:00
6890ca1f1f 1.0.190 2023-09-12 13:42:56 +02:00
9ca000cf06 fix(core): update 2023-09-12 13:42:55 +02:00
1c0f5129a9 1.0.189 2023-09-09 13:34:46 +02:00
69e17949f4 fix(core): update 2023-09-09 13:34:46 +02:00
bcfd3495dd 1.0.188 2023-09-08 11:44:04 +02:00
03a46a72c5 fix(core): update 2023-09-08 11:44:03 +02:00
2465ce5f9b 1.0.187 2023-09-07 18:34:39 +02:00
00ac83f205 fix(core): update 2023-09-07 18:34:38 +02:00
67065b1ffb 1.0.186 2023-09-07 02:57:31 +02:00
783c10479f fix(core): update 2023-09-07 02:57:30 +02:00
d4eae1cd9e 1.0.185 2023-09-04 19:29:39 +02:00
4e674f67c5 fix(core): update 2023-09-04 19:29:39 +02:00
889a543780 1.0.184 2023-09-04 19:28:50 +02:00
fc5f3a9576 fix(core): update 2023-09-04 19:28:50 +02:00
66644364b5 1.0.183 2023-09-02 14:05:34 +02:00
51febdae06 fix(core): update 2023-09-02 14:05:33 +02:00
745ff299dc 1.0.182 2023-09-01 16:44:14 +02:00
3606d60ba6 fix(core): update 2023-09-01 16:44:13 +02:00
8970a79141 1.0.181 2023-09-01 14:21:16 +02:00
566a7ce148 fix(core): update 2023-09-01 14:21:15 +02:00
362bef15e3 1.0.180 2023-08-30 11:27:47 +02:00
446c494863 fix(core): update 2023-08-30 11:27:46 +02:00
dbe2f2f217 1.0.179 2023-08-28 09:59:13 +02:00
02ca92a431 fix(core): update 2023-08-28 09:59:12 +02:00
a9015e787c 1.0.178 2023-08-28 09:49:52 +02:00
9cd28fa819 fix(core): update 2023-08-28 09:49:51 +02:00
7b3793e943 1.0.177 2023-08-26 12:15:49 +02:00
a22c04d98d fix(core): update 2023-08-26 12:15:49 +02:00
5367512292 1.0.176 2023-08-20 17:11:41 +02:00
9c387f8ace fix(core): update 2023-08-20 17:11:41 +02:00
c3ec288d09 1.0.175 2023-08-19 18:56:33 +02:00
9d952afebb fix(core): update 2023-08-19 18:56:32 +02:00
621262fde7 1.0.174 2023-08-19 11:47:45 +02:00
e3e79d9a11 fix(core): update 2023-08-19 11:47:45 +02:00
d08a92814e 1.0.173 2023-08-08 01:10:03 +02:00
a6f277250e fix(core): update 2023-08-08 01:10:02 +02:00
09cb82d587 1.0.172 2023-08-07 20:02:19 +02:00
316e2a0b27 fix(core): update 2023-08-07 20:02:18 +02:00
e0d2679801 1.0.171 2023-08-07 19:13:30 +02:00
107800a057 fix(core): update 2023-08-07 19:13:29 +02:00
4ee8512ee4 1.0.170 2023-04-12 02:47:46 +02:00
f75a3714ae fix(core): update 2023-04-12 02:47:45 +02:00
d6047f2e78 1.0.169 2023-04-12 00:40:40 +02:00
03769c8412 fix(core): update 2023-04-12 00:40:40 +02:00
1a782eb4ee 1.0.168 2023-04-10 00:26:55 +02:00
a27adf25cb fix(core): update 2023-04-10 00:26:55 +02:00
96e237b9af 1.0.167 2023-04-10 00:18:17 +02:00
710e06b2f1 fix(core): update 2023-04-10 00:18:16 +02:00
a3bc25c4ac 1.0.166 2023-04-10 00:17:38 +02:00
ee5b712a37 fix(core): update 2023-04-10 00:17:38 +02:00
879ae5ad91 1.0.165 2023-04-07 23:55:56 +02:00
2bbda416b2 fix(core): update 2023-04-07 23:55:55 +02:00
de88957857 1.0.164 2023-04-07 23:46:09 +02:00
aa0c63d569 fix(core): update 2023-04-07 23:46:08 +02:00
d843f27fd3 1.0.163 2023-04-06 17:41:46 +02:00
a26755b7d3 fix(core): update 2023-04-06 17:41:46 +02:00
345a562a21 1.0.162 2023-04-06 17:32:50 +02:00
3fae99554d fix(core): update 2023-04-06 17:32:49 +02:00
ed30ed3f8f 1.0.161 2023-04-06 17:29:08 +02:00
beef47959b fix(core): update 2023-04-06 17:29:07 +02:00
aec2cf18fd 1.0.160 2023-04-05 18:57:13 +02:00
0f45633666 fix(core): update 2023-04-05 18:57:12 +02:00
8588888ce7 1.0.159 2023-04-05 17:17:02 +02:00
2a62a635cd fix(core): update 2023-04-05 17:17:02 +02:00
0eb2557956 1.0.158 2023-04-05 17:00:44 +02:00
9960bf7018 fix(core): update 2023-04-05 17:00:44 +02:00
f26f0d0450 1.0.157 2023-04-05 16:51:05 +02:00
53fadfcbd5 fix(core): update 2023-04-05 16:51:05 +02:00
497d3a3ac5 1.0.156 2023-04-05 14:46:21 +02:00
37524765ae fix(core): update 2023-04-05 14:46:20 +02:00
ab0219d3e4 1.0.155 2023-03-27 23:21:27 +02:00
7cefd9cba5 fix(core): update 2023-03-27 23:21:27 +02:00
4cf5ca2d7f 1.0.154 2023-03-27 01:22:16 +02:00
a9791220fb fix(core): update 2023-03-27 01:22:15 +02:00
4aed14c7a2 1.0.153 2023-03-25 20:56:13 +01:00
49d1cba3fd fix(core): update 2023-03-25 20:56:12 +01:00
9bacca3070 1.0.152 2023-03-25 17:32:55 +01:00
42e7ae6d6b fix(core): update 2023-03-25 17:32:55 +01:00
a8b244520c 1.0.151 2023-03-25 17:30:42 +01:00
9879a2cb6a fix(core): update 2023-03-25 17:30:41 +01:00
e2a54c6f18 1.0.150 2023-03-09 17:08:20 +01:00
dfd13e641a fix(core): update 2023-03-09 17:08:19 +01:00
2284159b72 1.0.149 2023-01-17 17:12:48 +01:00
1de86c6bfa fix(core): update 2023-01-17 17:12:47 +01:00
62087a686a 1.0.148 2023-01-17 16:52:14 +01:00
572deb990e fix(core): update 2023-01-17 16:52:13 +01:00
719c63a092 1.0.147 2023-01-16 11:57:25 +01:00
9d9700214f fix(core): update 2023-01-16 11:57:24 +01:00
bc82e110ef 1.0.146 2023-01-16 11:51:22 +01:00
fe15ebe82d fix(core): update 2023-01-16 11:51:21 +01:00
1b8577d300 1.0.145 2023-01-16 01:19:34 +01:00
ef4cfb81d8 fix(core): update 2023-01-16 01:19:33 +01:00
abea5942b7 1.0.144 2023-01-13 12:03:20 +01:00
a6274e9a2d fix(core): update 2023-01-13 12:03:19 +01:00
8b3752f586 1.0.143 2023-01-13 11:57:47 +01:00
c8e1d24224 fix(core): update 2023-01-13 11:57:47 +01:00
dbd7748ac0 1.0.142 2023-01-13 11:48:00 +01:00
68984d5702 fix(core): update 2023-01-13 11:48:00 +01:00
5237439f88 1.0.141 2023-01-13 02:15:31 +01:00
c3df73616f fix(core): update 2023-01-13 02:15:30 +01:00
a03b095d14 1.0.140 2023-01-13 01:20:29 +01:00
9a18658e09 fix(core): update 2023-01-13 01:20:28 +01:00
2910757e8c 1.0.139 2023-01-13 01:17:08 +01:00
a00bacd4ff fix(core): update 2023-01-13 01:17:08 +01:00
01963447dd 1.0.138 2023-01-13 00:50:34 +01:00
5d1d1ba4ba fix(core): update 2023-01-13 00:50:34 +01:00
7885422033 1.0.137 2023-01-13 00:46:18 +01:00
fb48425c13 fix(core): update 2023-01-13 00:46:17 +01:00
3792c5edf5 1.0.136 2023-01-13 00:30:56 +01:00
2abd91ab4a fix(core): update 2023-01-13 00:30:56 +01:00
310910e8ee 1.0.135 2023-01-12 19:19:32 +01:00
3301ad1556 fix(core): update 2023-01-12 19:19:31 +01:00
0f8a7a5a7b 1.0.134 2023-01-12 18:15:00 +01:00
f1a0c5741c fix(core): update 2023-01-12 18:14:59 +01:00
cbf60db9fa 1.0.133 2023-01-12 00:07:39 +01:00
34d5bda579 fix(core): update 2023-01-12 00:07:39 +01:00
916ba68c94 1.0.132 2023-01-11 20:55:07 +01:00
46fc396772 fix(core): update 2023-01-11 20:55:06 +01:00
0e77baf600 1.0.131 2023-01-11 20:52:38 +01:00
89fd8b5080 fix(core): update 2023-01-11 20:52:37 +01:00
884f9725b5 1.0.130 2023-01-11 18:32:05 +01:00
48e093b1ba fix(core): update 2023-01-11 18:32:05 +01:00
2b3875d738 1.0.129 2023-01-11 18:15:32 +01:00
279d1c2f3f fix(core): update 2023-01-11 18:15:31 +01:00
29f2839d5b 1.0.128 2023-01-11 18:09:57 +01:00
96683b4380 fix(core): update 2023-01-11 18:09:57 +01:00
36fbf7f29e 1.0.127 2023-01-11 17:50:38 +01:00
67d4c216ed fix(core): update 2023-01-11 17:50:37 +01:00
9d51cdd480 1.0.126 2023-01-11 17:43:59 +01:00
d6679ca41f fix(core): update 2023-01-11 17:43:58 +01:00
baadd1e06b 1.0.125 2023-01-11 17:19:22 +01:00
600ab56026 fix(core): update 2023-01-11 17:19:22 +01:00
ae40a9f541 1.0.124 2023-01-09 23:13:44 +01:00
48f7768b96 fix(core): update 2023-01-09 23:13:44 +01:00
de3f9fcb34 1.0.123 2023-01-09 14:19:59 +01:00
313009d3ff fix(core): update 2023-01-09 14:19:58 +01:00
83ede1edea 1.0.122 2023-01-09 13:55:15 +01:00
ec50b4b3a0 fix(core): update 2023-01-09 13:55:14 +01:00
4fbb1caaeb 1.0.121 2023-01-07 16:48:40 +01:00
41a2529275 fix(core): update 2023-01-07 16:48:39 +01:00
41ff71e6b0 1.0.120 2023-01-07 16:47:02 +01:00
2d07765c16 fix(core): update 2023-01-07 16:47:01 +01:00
15db9fb6d9 1.0.119 2023-01-07 16:42:13 +01:00
73d92624de fix(core): update 2023-01-07 16:42:12 +01:00
0d77685338 1.0.118 2023-01-07 16:32:44 +01:00
05bbfccd22 fix(core): update 2023-01-07 16:32:43 +01:00
5cca89de37 1.0.117 2023-01-07 16:08:11 +01:00
ab5b7a5b89 fix(core): update 2023-01-07 16:08:10 +01:00
253d5623c4 1.0.116 2023-01-07 15:43:38 +01:00
1048192bfa fix(core): update 2023-01-07 15:43:37 +01:00
289e6d37fa 1.0.115 2023-01-07 15:24:58 +01:00
7308c1454f fix(core): update 2023-01-07 15:24:57 +01:00
cfc880d7c2 1.0.114 2023-01-07 15:06:41 +01:00
c32cff8126 fix(core): update 2023-01-07 15:06:41 +01:00
4526c0c365 1.0.113 2023-01-07 15:05:49 +01:00
45e7f39af0 fix(core): update 2023-01-07 15:05:48 +01:00
26b72fbccf 1.0.112 2023-01-07 14:23:17 +01:00
8acf3f4196 fix(core): update 2023-01-07 14:23:17 +01:00
75c8b409cc 1.0.111 2023-01-07 14:12:19 +01:00
6509ddb81c fix(core): update 2023-01-07 14:12:18 +01:00
7cc0173e3c 1.0.110 2023-01-07 14:09:46 +01:00
f797d71e52 fix(core): update 2023-01-07 14:09:45 +01:00
54db88e4b4 1.0.109 2023-01-07 13:41:24 +01:00
93c73c36c8 fix(core): update 2023-01-07 13:41:24 +01:00
9c9bd4a180 1.0.108 2023-01-07 13:27:25 +01:00
0a54de3e59 fix(core): update 2023-01-07 13:27:24 +01:00
d6b4b59e6d 1.0.107 2023-01-03 17:37:11 +01:00
c4236d28c6 fix(core): update 2023-01-03 17:37:10 +01:00
5f1b29f742 1.0.106 2022-12-11 17:24:13 +01:00
e7978a22e4 fix(core): update 2022-12-11 17:24:12 +01:00
15de86774b 1.0.105 2022-12-07 14:54:41 +01:00
8f57647c85 fix(core): update 2022-12-07 14:54:40 +01:00
59c5bcabd5 1.0.104 2022-12-07 02:28:31 +01:00
cae421e140 fix(core): update 2022-12-07 02:28:31 +01:00
05b2b09395 1.0.103 2022-12-06 14:07:12 +01:00
4dbd4f84c1 fix(core): update 2022-12-06 14:07:12 +01:00
bb0ca2f19d 1.0.102 2022-12-06 13:32:01 +01:00
55175f9ac7 fix(core): update 2022-12-06 13:32:00 +01:00
0be0f9fa34 1.0.101 2022-12-06 13:11:06 +01:00
7baab5d7ea fix(core): update 2022-12-06 13:11:06 +01:00
c3bdec176b 1.0.100 2022-08-18 02:15:41 +02:00
2a84e2c270 fix(core): update 2022-08-18 02:15:41 +02:00
c86f7d466c 1.0.99 2022-08-18 02:11:36 +02:00
9a6db7d882 fix(core): update 2022-08-18 02:11:35 +02:00
b69b1179a1 1.0.98 2022-08-17 19:56:22 +02:00
0df9fe750c fix(core): update 2022-08-17 19:56:22 +02:00
0d05bfa5ff 1.0.97 2022-08-17 19:28:12 +02:00
4dc9e04c2e fix(core): update 2022-08-17 19:28:11 +02:00
5341a0d6b6 1.0.96 2022-08-17 19:27:14 +02:00
615bea9fc4 fix(core): update 2022-08-17 19:27:14 +02:00
e518bc6976 1.0.95 2022-08-17 18:49:34 +02:00
076f8c7a91 fix(core): update 2022-08-17 18:49:33 +02:00
b016b959cf 1.0.94 2022-07-14 23:29:25 +02:00
eca68e28a2 fix(core): update 2022-07-14 23:29:25 +02:00
68a164aac2 1.0.93 2022-06-26 15:04:05 +02:00
9b5448e096 fix(core): update 2022-06-26 15:04:04 +02:00
232d063485 1.0.92 2022-06-10 15:50:57 +02:00
6a357cb227 fix(core): update 2022-06-10 15:50:56 +02:00
7b8218ab77 1.0.91 2022-05-30 23:15:33 +02:00
5b6436b023 fix(core): update 2022-05-30 23:15:32 +02:00
57edbb872b 1.0.90 2022-05-26 21:23:55 +02:00
76f82fe15e fix(core): update 2022-05-26 21:23:55 +02:00
0b03718f4a 1.0.89 2022-05-26 20:03:19 +02:00
2754de26d8 fix(core): update 2022-05-26 20:03:18 +02:00
abc91600e2 1.0.88 2022-05-26 18:07:02 +02:00
6a8a4aa4d2 fix(core): update 2022-05-26 18:07:02 +02:00
6a6f3b1907 1.0.87 2022-05-24 09:13:15 +02:00
0dc8e21e07 fix(core): update 2022-05-24 09:13:15 +02:00
68ccc02699 1.0.86 2022-05-20 21:04:59 +02:00
ed1c18b8ac fix(core): update 2022-05-20 21:04:59 +02:00
e8cc4c7c90 1.0.85 2022-05-20 19:51:21 +02:00
187ebed3ab fix(core): update 2022-05-20 19:51:20 +02:00
b909617271 1.0.84 2022-05-20 19:48:48 +02:00
9c2fe9f739 fix(core): update 2022-05-20 19:48:48 +02:00
ed49de64bc 1.0.83 2022-05-20 19:43:17 +02:00
162003ba7a fix(core): update 2022-05-20 19:43:16 +02:00
5f793f0acc 1.0.82 2022-05-20 18:54:49 +02:00
d23b298bd5 fix(core): update 2022-05-20 18:54:49 +02:00
f9e2f00e9b 1.0.81 2022-05-20 18:44:33 +02:00
6c3645f2f8 fix(core): update 2022-05-20 18:44:33 +02:00
e3a6798469 1.0.80 2022-05-03 15:17:28 +02:00
60de4ef674 fix(core): update 2022-05-03 15:17:28 +02:00
02e029edbc 1.0.79 2022-04-29 15:54:39 +02:00
ff5a65bdce fix(core): update 2022-04-29 15:54:38 +02:00
daacbbd042 1.0.78 2022-04-29 10:17:21 +02:00
42b44be143 fix(core): update 2022-04-29 10:17:21 +02:00
7e4c186e66 1.0.77 2022-04-29 09:47:52 +02:00
f6823639cc fix(core): update 2022-04-29 09:47:52 +02:00
f292fd9692 1.0.76 2022-03-18 19:46:30 +01:00
fa2ab7730a fix(core): update 2022-03-18 19:46:30 +01:00
3a2ba14287 1.0.75 2022-03-18 19:40:29 +01:00
c1b5778d01 fix(core): update 2022-03-18 19:40:28 +01:00
24a5f48ce1 1.0.74 2022-01-24 01:39:48 +01:00
d43b80298b fix(core): update 2022-01-24 01:39:47 +01:00
27d338420c 1.0.73 2021-12-10 18:18:57 +01:00
81f2e662fb fix(core): update 2021-12-10 18:18:57 +01:00
d8905b687f 1.0.72 2021-11-27 17:38:53 +01:00
75f6da194b fix(core): update 2021-11-27 17:38:53 +01:00
e653450ec5 1.0.71 2021-11-26 20:06:10 +01:00
9de9f0c202 fix(core): update 2021-11-26 20:06:09 +01:00
75929c089d 1.0.70 2021-10-08 16:07:41 +02:00
6a97e8d020 fix(core): update 2021-10-08 16:07:40 +02:00
6aeb970bbe 1.0.69 2021-10-08 14:19:56 +02:00
ea7fa0816d fix(core): update 2021-10-08 14:19:55 +02:00
1b1de04f86 1.0.68 2021-10-07 18:47:36 +02:00
7941628d1d fix(core): update 2021-10-07 18:47:36 +02:00
369437ceba 1.0.67 2021-10-07 18:01:05 +02:00
72f7782898 fix(core): update 2021-10-07 18:01:05 +02:00
820d6ae40f 1.0.66 2021-09-15 13:10:28 +02:00
5dd3da9f31 fix(core): update 2021-09-15 13:10:28 +02:00
3ce0683a05 1.0.65 2021-09-15 00:59:51 +02:00
00d41f1b6e fix(core): update 2021-09-15 00:59:50 +02:00
caba7ada28 1.0.64 2021-09-15 00:48:29 +02:00
6cff32cc36 fix(core): update 2021-09-15 00:48:29 +02:00
e6e8ac9782 1.0.63 2021-09-15 00:45:15 +02:00
7a13fdcd95 fix(core): update 2021-09-15 00:45:14 +02:00
c450fb32ea 1.0.62 2021-09-14 17:31:16 +02:00
6554bfd721 fix(core): update 2021-09-14 17:31:16 +02:00
9ce4ca14b8 1.0.61 2021-09-10 15:56:37 +02:00
76bcda760c fix(core): update 2021-09-10 15:56:37 +02:00
6949aed381 1.0.60 2021-09-10 15:51:30 +02:00
1f3502685f fix(core): update 2021-09-10 15:51:30 +02:00
8d1451fffa 1.0.59 2021-09-10 15:42:16 +02:00
8b2fedf1d6 fix(core): update 2021-09-10 15:42:16 +02:00
30ffb2650a 1.0.58 2021-09-10 15:23:54 +02:00
55b65c7e4c fix(core): update 2021-09-10 15:23:54 +02:00
5c81dd540a 1.0.57 2021-09-10 15:02:48 +02:00
cb5bc809ea fix(core): update 2021-09-10 15:02:48 +02:00
ab1956c452 1.0.56 2021-09-09 00:35:11 +02:00
3b99796073 fix(core): update 2021-09-09 00:35:10 +02:00
20755775ea 1.0.55 2021-09-09 00:06:06 +02:00
4e1b797377 fix(core): update 2021-09-09 00:06:05 +02:00
4a3aa2d6d9 1.0.54 2021-09-09 00:02:36 +02:00
4e49045444 fix(core): update 2021-09-09 00:02:35 +02:00
9e54c973d5 1.0.53 2021-09-01 22:44:44 +02:00
122c535dec fix(core): update 2021-09-01 22:44:43 +02:00
aabda883cd 1.0.52 2021-09-01 22:43:32 +02:00
5890977009 fix(core): update 2021-09-01 22:43:31 +02:00
f4aebcf4e6 1.0.51 2021-09-01 21:48:22 +02:00
66bf117bfc fix(core): update 2021-09-01 21:48:22 +02:00
f7ee4d77fd 1.0.50 2021-08-29 17:10:25 +02:00
ab19e97c31 fix(core): update 2021-08-29 17:10:25 +02:00
631502b480 1.0.49 2021-08-27 14:51:29 +02:00
ea519cf543 1.0.48 2021-08-27 14:48:59 +02:00
91c97cad1d fix(core): update 2021-08-27 14:48:58 +02:00
de958303e3 1.0.47 2021-08-27 14:27:19 +02:00
95192ea3ce fix(core): update 2021-08-27 14:27:18 +02:00
0b1e50bbc6 1.0.46 2021-08-27 14:25:45 +02:00
888f07db79 fix(core): update 2021-08-27 14:25:44 +02:00
e14d83b3a7 1.0.45 2021-08-27 14:17:10 +02:00
f4055fb8c1 fix(core): update 2021-08-27 14:17:09 +02:00
d0d4638264 1.0.44 2021-08-27 14:02:38 +02:00
7d63f16fc1 fix(core): update 2021-08-27 14:02:37 +02:00
00cb552f44 1.0.43 2021-08-27 13:38:09 +02:00
2f52f14cf9 fix(core): update 2021-08-27 13:38:08 +02:00
54ce305cf1 1.0.42 2021-08-26 21:30:35 +02:00
9d5f0b5ff8 fix(core): update 2021-08-26 21:30:35 +02:00
c1594736ec 1.0.41 2021-08-25 17:34:16 +02:00
4eb1abaa39 fix(core): update 2021-08-25 17:34:15 +02:00
b8c231fc61 1.0.40 2021-08-25 16:10:56 +02:00
260c4a269a fix(core): update 2021-08-25 16:10:56 +02:00
c1e7629f1f 1.0.39 2021-08-25 16:09:53 +02:00
b84e2c4774 fix(core): update 2021-08-25 16:09:52 +02:00
e9374900a0 1.0.38 2021-08-25 13:51:55 +02:00
486b8cb6a6 fix(core): update 2021-08-25 13:51:55 +02:00
0adb319616 1.0.37 2021-08-24 12:41:01 +02:00
a6e7adc983 fix(core): update 2021-08-24 12:41:01 +02:00
59dfbb34bb 1.0.36 2021-08-20 18:55:39 +02:00
cf34cea94d fix(core): update 2021-08-20 18:55:39 +02:00
7f5e72c27f 1.0.35 2021-08-20 00:25:15 +02:00
4311c0fff6 fix(core): update 2021-08-20 00:25:14 +02:00
e028f37493 1.0.34 2021-05-05 20:56:31 +00:00
c6967156d6 fix(core): update 2021-05-05 20:56:31 +00:00
efaf2a78df 1.0.33 2021-05-05 20:55:49 +00:00
e6adbf9b6d fix(core): update 2021-05-05 20:55:49 +00:00
e88605a4aa 1.0.32 2021-03-06 15:59:53 +00:00
6a161982b7 fix(core): update 2021-03-06 15:59:52 +00:00
22d7883c04 1.0.31 2021-03-06 15:48:23 +00:00
2a613c96a0 fix(core): update 2021-03-06 15:48:22 +00:00
bdd766e4bc 1.0.30 2021-03-06 15:48:02 +00:00
b1ce8f093f fix(core): update 2021-03-06 15:48:02 +00:00
056090856e 1.0.29 2021-02-13 21:52:37 +00:00
fef0705acc fix(core): update 2021-02-13 21:52:36 +00:00
6eced2321f 1.0.28 2020-12-09 23:05:13 +00:00
05a9042de8 fix(core): update 2020-12-09 23:05:13 +00:00
d369805ee8 1.0.27 2020-12-09 14:06:24 +00:00
3a2f3500fd fix(core): update 2020-12-09 14:06:24 +00:00
2f9711a094 1.0.26 2020-12-03 11:12:11 +00:00
cf80efd1ec fix(core): update 2020-12-03 11:12:11 +00:00
cf5cd37ebc 1.0.25 2020-12-02 17:30:42 +00:00
88554396a4 fix(core): update 2020-12-02 17:30:41 +00:00
2f13311098 1.0.24 2020-12-02 17:11:05 +00:00
5407b158d0 fix(core): update 2020-12-02 17:11:04 +00:00
b171eaf3fc 1.0.23 2020-12-01 22:49:45 +00:00
5585d7e304 fix(core): update 2020-12-01 22:49:44 +00:00
02119a1ce4 1.0.22 2020-12-01 22:24:55 +00:00
222f06bda7 fix(core): update 2020-12-01 22:24:54 +00:00
8ee99136cc 1.0.21 2020-09-17 10:35:33 +00:00
faf35c3144 fix(core): update 2020-09-17 10:35:33 +00:00
115 changed files with 22696 additions and 11010 deletions

View File

@ -0,0 +1,66 @@
name: Default (not tags)
on:
push:
tags-ignore:
- '**'
env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
jobs:
security:
runs-on: ubuntu-latest
continue-on-error: true
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Install pnpm and npmci
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
- name: Run npm prepare
run: npmci npm prepare
- name: Audit production dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --prod
continue-on-error: true
- name: Audit development dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --dev
continue-on-error: true
test:
if: ${{ always() }}
needs: security
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build

View File

@ -0,0 +1,124 @@
name: Default (tags)
on:
push:
tags:
- '*'
env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
jobs:
security:
runs-on: ubuntu-latest
continue-on-error: true
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Audit production dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --prod
continue-on-error: true
- name: Audit development dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --dev
continue-on-error: true
test:
if: ${{ always() }}
needs: security
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build
release:
needs: test
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Release
run: |
npmci node install stable
npmci npm publish
metadata:
needs: test
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
continue-on-error: true
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Code quality
run: |
npmci command npm install -g typescript
npmci npm install
- name: Trigger
run: npmci trigger
- name: Build docs and upload artifacts
run: |
npmci node install stable
npmci npm install
pnpm install -g @git.zone/tsdoc
npmci command tsdoc
continue-on-error: true

View File

@ -1,137 +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
# ====================
# security stage
# ====================
mirror:
stage: security
script:
- npmci git mirror
only:
- tags
tags:
- lossless
- docker
- notpriv
auditProductionDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci npm prepare
- npmci command npm install --production --ignore-scripts
- npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high --only=prod --production
tags:
- docker
auditDevDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci npm prepare
- npmci command npm install --ignore-scripts
- npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high --only=dev
tags:
- docker
allow_failure: true
# ====================
# test stage
# ====================
testStable:
stage: test
script:
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
testBuild:
stage: test
script:
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci command npm run 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 tslint typescript
- npmci npm prepare
- npmci npm install
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
tags:
- lossless
- docker
- priv
trigger:
stage: metadata
script:
- npmci trigger
only:
- tags
tags:
- lossless
- docker
- notpriv
pages:
stage: metadata
script:
- npmci node install lts
- npmci command npm install -g @gitzone/tsdoc
- npmci npm prepare
- npmci npm install
- npmci command tsdoc
tags:
- lossless
- docker
- notpriv
only:
- tags
artifacts:
expire_in: 1 week
paths:
- public
allow_failure: true

24
.vscode/launch.json vendored
View File

@ -2,28 +2,10 @@
"version": "0.2.0",
"configurations": [
{
"name": "current file",
"type": "node",
"command": "npm test",
"name": "Run npm test",
"request": "launch",
"args": [
"${relativeFile}"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
},
{
"name": "test.ts",
"type": "node",
"request": "launch",
"args": [
"test/test.ts"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
"type": "node-terminal"
}
]
}

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

@ -1,5 +1,5 @@
<!--gitzone element-->
<!-- made by Lossless GmbH -->
<!-- made by Task Venture Capital GmbH -->
<!-- checkout https://maintainedby.lossless.com for awesome OpenSource projects -->
<html lang="en">
<head>
@ -10,6 +10,10 @@
/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!--Lets load standard fonts-->
<link rel="preconnect" href="https://assetbroker.lossless.one/" crossorigin>
<link rel="stylesheet" href="https://assetbroker.lossless.one/fonts/fonts.css">
<style>
body {
margin: 0px;
@ -17,7 +21,7 @@
}
</style>
<script src="./index.ts"></script>
<script type="module" src="/bundle.js"></script>
</head>
<body>
</body>

View File

@ -1,10 +1,10 @@
// dees tools
import * as deesWccTools from '@designestate/dees-wcctools';
import * as deesDomTools from '@designestate/dees-domtools';
import * as deesWccTools from '@design.estate/dees-wcctools';
import * as deesDomTools from '@design.estate/dees-domtools';
// elements and pages
import * as elements from '../ts_web/elements';
import * as pages from '../ts_web/pages';
import * as elements from '../ts_web/elements/index.js';
import * as pages from '../ts_web/pages/index.js';
deesWccTools.setupWccTools(elements as any, pages);
deesDomTools.elementBasic.setup();

View File

@ -8,7 +8,10 @@ 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.
copies or substantial portions of the Software. You agree to being mentioned
as reference by Lossless GmbH. This includes the use of your entity logos
or profile picture by Lossless GmbH on websites and readme's, also on third party
pages like gitlab.com or github.com.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

View File

@ -2,17 +2,46 @@
"gitzone": {
"projectType": "wcc",
"module": {
"githost": "gitlab.com",
"gitscope": "designestate",
"githost": "code.foss.global",
"gitscope": "design.estate",
"gitrepo": "dees-catalog",
"shortDescription": "a webcomponents catalog for handling daily stuff on the web",
"npmPackagename": "@designestate/dees-catalog",
"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"
}
}

10455
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +1,49 @@
{
"name": "@designestate/dees-catalog",
"version": "1.0.20",
"name": "@design.estate/dees-catalog",
"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": "npm run build",
"build": "tsbuild element && tsbundle element --production",
"watch": "tswatch element"
"test": "tstest test/ --web",
"build": "tsbuild tsfolders --allowimplicitany && tsbundle element --production",
"watch": "tswatch element",
"buildDocs": "tsdoc"
},
"author": "Lossless GmbH",
"license": "MIT",
"dependencies": {
"@designestate/dees-domtools": "^1.0.47",
"@designestate/dees-wcctools": "^1.0.37",
"lit-element": "^2.4.0",
"typescript": "^4.0.2"
"@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.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": {
"@gitzone/tsbuild": "^2.1.25",
"@gitzone/tsbundle": "^1.0.78",
"@gitzone/tswatch": "^1.0.50",
"@pushrocks/projectinfo": "^4.0.5",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.17.0"
"@git.zone/tsbuild": "^2.1.84",
"@git.zone/tsbundle": "^2.0.15",
"@git.zone/tstest": "^1.0.90",
"@git.zone/tswatch": "^2.0.37",
"@push.rocks/projectinfo": "^5.0.2",
"@push.rocks/tapbundle": "^5.5.6",
"@types/node": "^22.14.1"
},
"files": [
"ts/**/*",
@ -40,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"
}

10215
pnpm-lock.yaml generated Normal file

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.

1398
readme.md

File diff suppressed because it is too large Load Diff

12
test/test.browser.ts Normal file
View File

@ -0,0 +1,12 @@
import { tap, expect, webhelpers } from '@push.rocks/tapbundle';
import * as deesCatalog from '../ts_web';
tap.test('should create a working button', async () => {
const button: deesCatalog.DeesButton = await webhelpers.fixture(
webhelpers.html`<dees-button></dees-button>`
);
expect(button).toBeInstanceOf(deesCatalog.DeesButton);
});
tap.start();

View File

@ -0,0 +1,8 @@
/**
* autocreated commitinfo by @push.rocks/commitinfo
*/
export const commitinfo = {
name: '@design.estate/dees-catalog',
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

@ -0,0 +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,13 @@
// @push.rocks scope
import * as smartpromise from '@push.rocks/smartpromise';
export {
smartpromise,
}
// @tsclass scope
import * as tsclass from '@tsclass/tsclass';
export {
tsclass
}

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

@ -0,0 +1,48 @@
import {
cssManager,
customElement,
DeesElement,
html,
type TemplateResult,
css,
type CSSResult,
state,
property
} from '@design.estate/dees-element';
@customElement('dees-button-exit')
export class DeesButtonExit extends DeesElement {
// DEMO
public static demo = () => html`
<dees-button-exit></dees-button-exit>
`;
// INSTANCE
@property({
type: Number
})
public size: number = 24;
public styles = [
cssManager.defaultStyles,
css`
`
]
public render (): TemplateResult {
return html`
<style>
.maincontainer {
position: relative;
width: ${this.size}px;
height: ${this.size}px;
}
</style>
<div class="maincontainer">
<div class="firstLine"></div>
<div class="secondLine"></div>
</div>
`;
}
}

View File

@ -0,0 +1,15 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html`
<dees-button>This is a slotted Text</dees-button>
<p>
<dees-button text="Highlighted: This text shows" type="highlighted">Highlighted</dees-button>
</p>
<p><dees-button type="discreet">This is discreete button</dees-button></p>
<p><dees-button disabled>This is a disabled button</dees-button></p>
<p><dees-button type="big">This is a slotted Text</dees-button></p>
<p><dees-button status="normal">Normal Status</dees-button></p>
<p><dees-button disabled status="pending">Pending Status</dees-button></p>
<p><dees-button disabled status="success">Success Status</dees-button></p>
<p><dees-button disabled status="error">Error Status</dees-button></p>
`;

View File

@ -1,125 +1,207 @@
import { customElement, html, LitElement, property, TemplateResult } from 'lit-element';
import { demoFunc } from './dees-button.demo.js';
import {
customElement,
html,
DeesElement,
property,
type TemplateResult,
cssManager,
css,
type CSSResult,
unsafeCSS,
} from '@design.estate/dees-element';
import * as domtools from '@designestate/dees-domtools';
import * as domtools from '@design.estate/dees-domtools';
declare global {
interface HTMLElementTagNameMap {
'dees-button': DeesButton;
}
}
@customElement('dees-button')
export class DeesButton extends LitElement {
@property()
text: string;
export class DeesButton extends DeesElement {
public static demo = demoFunc;
@property({
reflect: true,
hasChanged() {
return true;
}
})
public text: string;
@property()
eventDetailData: string;
public eventDetailData: string;
@property()
disabled = false;
@property({
type: Boolean,
reflect: true,
})
public disabled = false;
@property()
isQuote = false;
@property({
type: Boolean
})
public isHidden = false;
@property()
isHidden = false;
@property()
@property({
type: String
})
public type: 'normal' | 'highlighted' | 'discreet' | 'big' = 'normal';
@property({
type: String
})
public status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
constructor() {
super();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
box-sizing: border-box;
font-family: 'Geist Sans', 'monospace';
}
:host([hidden]) {
display: none;
}
.button {
transition: all 0.1s , color 0s;
position: relative;
font-size: 14px;
font-weight: 400;
display: flex;
justify-content: center;
align-items: center;
background: ${cssManager.bdTheme('#fff', '#333')};
box-shadow: ${cssManager.bdTheme('0px 1px 3px rgba(0,0,0,0.3)', 'none')};
border: 1px solid ${cssManager.bdTheme('#eee', '#333')};
border-top: ${cssManager.bdTheme('1px solid #eee', '1px solid #444')};
border-radius: 4px;
height: 40px;
padding: 0px 8px;
min-width: 100px;
user-select: none;
color: ${cssManager.bdTheme('#333', ' #ccc')};
max-width: 500px;
}
.button:hover {
background: #0050b9;
color: #ffffff;
border: 1px solid #0050b9;
border-top: 1px solid #0050b9;
}
.button:active {
background: #0069f2;
border-top: 1px solid #0069f2;
}
.button.highlighted {
background: #e4002b;
border: none;
color: #fff;
}
.button.highlighted:hover {
background: #b50021;
border: none;
color: #fff;
}
.button.discreet {
background: none;
border: 1px solid #9b9b9e;
color: ${cssManager.bdTheme('#000', '#fff')};
}
.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;
}
.button.big {
width: 300px;
line-height: 48px;
font-size: 16px;
padding: 0px 48px;
margin-top: 32px;
}
.button.pending {
border: 1px dashed ${cssManager.bdTheme('#0069f2', '#0069f270')};
background: ${cssManager.bdTheme('#0069f2', '#0069f270')};
color: #fff;
}
.button.success {
border: 1px dashed ${cssManager.bdTheme('#689F38', '#8BC34A70')};
background: ${cssManager.bdTheme('#689F38', '#8BC34A70')};
color: #fff;
}
.button.error {
border: 1px dashed ${cssManager.bdTheme('#B71C1C', '#E64A1970')};
background: ${cssManager.bdTheme('#B71C1C', '#E64A1970')};
color: #fff;
}
dees-spinner {
position: absolute;
left: 10px;
}
`,
];
public render(): TemplateResult {
return html`
${domtools.elementBasic.styles}
<style>
:host {
display: block;
box-sizing: border-box;
}
:host([hidden]) {
display: none;
}
.button {
transition: all 0.2s ease;
font-size: 14px;
display: block;
text-align: center;
background: #333;
border-top: 1px solid #444;
border-radius: 2px;
line-height: 40px;
padding: 0px 10px;
min-width: 100px;
color: ${this.isQuote ? '#ffffff' : '#333333' };
user-select: none;
color: #ccc;
}
.button:hover {
cursor: pointer;
background: #039BE5;
border-top: 1px solid #039BE5;
color: #ffffff;
}
.button:active {
background: #0277BD;
border-top: 1px solid #0277BD;
}
.button.disabled {
background: #fff;
border: 1px solid #eeeff3;
color: #9b9b9e;
cursor: default;
}
.button.highlighted {
background: #e4002b;
color: #fff;
}
.button.discreet {
background: none;
border: 1px solid #9b9b9e;
color: #000;
}
.button.discreet:hover {
background: rgba(0,0,0,0.1)
}
.hidden {
display: none;
}
.big {
display: inline-block;
line-height: 48px;
font-size: 16px;
padding: 0px 48px;
margin-top: 36px;
}
</style>
<div class="button ${this.isHidden ? 'hidden' : 'block'} ${this.type} ${this.disabled ? 'disabled' : null}" @click="${this.dispatchClick}">
${this.text ? this.text : this.textContent}
<div
class="button ${this.isHidden ? 'hidden' : 'block'} ${this.type} ${this.status} ${this.disabled
? 'disabled'
: null}"
@click="${this.dispatchClick}"
>
${this.status === 'normal' ? html``: html`
<dees-spinner .bnw=${true} status="${this.status}"></dees-spinner>
`}
<div class="textbox">${this.text ? this.text : this.textContent}</div>
</div>
`;
}
public async dispatchClick() {
if (this.disabled) {
return;
}
this.dispatchEvent(new CustomEvent('clicked', {
detail: {
data: this.eventDetailData
},
bubbles: true
}));
this.dispatchEvent(
new CustomEvent('clicked', {
detail: {
data: this.eventDetailData,
},
bubbles: true,
})
);
}
public async firstUpdated () {
public async firstUpdated() {
if (!this.textContent) {
this.textContent = 'Button';
this.performUpdate();

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

@ -0,0 +1,41 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html`
<style>
.demoContainer {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
background: #222;
}
</style>
<div class="demoContainer">
<dees-chips
selectionMode="none"
.selectableChips=${[
{ key: 'account1', value: 'Payment Account 1' },
{ key: 'account2', value: 'PaymentAccount2' },
{ key: 'account3', value: 'Payment Account 3' },
]}
></dees-chips>
<dees-chips
selectionMode="single"
chipsAreRemovable
.selectableChips=${[
{ key: 'account1', value: 'Payment Account 1' },
{ key: 'account2', value: 'PaymentAccount2' },
{ key: 'account3', value: 'Payment Account 3' },
]}
></dees-chips>
<dees-chips
selectionMode="multiple"
.selectableChips=${[
{ key: 'account1', value: 'Payment Account 1' },
{ key: 'account2', value: 'PaymentAccount2' },
{ key: 'account3', value: 'Payment Account 3' },
]}
></dees-chips>
</div>
`;

View File

@ -0,0 +1,195 @@
import {
customElement,
html,
DeesElement,
property,
type TemplateResult,
cssManager,
css,
type CSSResult,
unsafeCSS,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { demoFunc } from './dees-chips.demo.js';
declare global {
interface HTMLElementTagNameMap {
'dees-chips': DeesChips;
}
}
type Tag = { key: string; value: string };
@customElement('dees-chips')
export class DeesChips extends DeesElement {
public static demo = demoFunc;
@property()
public selectionMode: 'none' | 'single' | 'multiple' = 'single';
@property({
type: Boolean,
})
public chipsAreRemovable: boolean = false;
@property({
type: Array,
})
public selectableChips: Tag[] = [];
@property()
public selectedChip: Tag = null;
@property({
type: Array,
})
public selectedChips: Tag[] = [];
constructor() {
super();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
box-sizing: border-box;
}
.mainbox {
user-select: none;
}
.chip {
border-top: ${cssManager.bdTheme('1px solid #CCC', '1px solid #444')};
background: #333333;
display: inline-flex;
height: 20px;
line-height: 20px;
padding: 0px 8px;
font-size: 12px;
color: #fff;
border-radius: 40px;
margin-right: 4px;
margin-bottom: 4px;
position: relative;
overflow: hidden;
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3);
}
.chip:hover {
background: #666666;
}
.chip.selected {
background: #00a3ff;
}
.chipKey {
background: rgba(0, 0, 0, 0.3);
height: 100%;
display: inline-block;
margin-left: -8px;
padding-left: 8px;
padding-right: 8px;
margin-right: 8px;
}
dees-icon {
padding: 0px 6px 0px 4px;
margin-left: 4px;
margin-right: -8px;
background: rgba(0, 0, 0, 0.3);
}
dees-icon:hover {
background: #e4002b;
}
`,
];
public render(): TemplateResult {
return html`
<div class="mainbox">
${this.selectableChips.map(
(chip) => html`
<div
@click=${() => this.selectChip(chip)}
class="chip ${this.isSelected(chip) ? 'selected' : ''}"
>
${chip.key ? html`<div class="chipKey">${chip.key}</div>` : html``} ${chip.value}
${this.chipsAreRemovable
? html`
<dees-icon
@click=${(event: Event) => {
event.stopPropagation(); // prevent the selectChip event from being triggered
this.removeChip(chip);
}}
.iconFA=${'xmark'}
></dees-icon>
`
: html``}
</div>
`
)}
</div>
`;
}
public async firstUpdated() {
if (!this.textContent) {
this.textContent = 'Button';
this.performUpdate();
}
}
private isSelected(chip: Tag): boolean {
if (this.selectionMode === 'single') {
return this.selectedChip?.key === chip.key;
} else {
return this.selectedChips.some((selected) => selected.key === chip.key);
}
}
public async selectChip(chip: Tag) {
if (this.selectionMode === 'none') {
return;
}
if (this.selectionMode === 'single') {
if (this.isSelected(chip)) {
this.selectedChip = null;
this.selectedChips = [];
} else {
this.selectedChip = chip;
this.selectedChips = [chip];
}
} else if (this.selectionMode === 'multiple') {
if (this.isSelected(chip)) {
this.selectedChips = this.selectedChips.filter((selected) => selected.key !== chip.key);
} else {
this.selectedChips = [...this.selectedChips, chip];
}
this.requestUpdate();
}
console.log(this.selectedChips);
}
public removeChip(chipToRemove: Tag): void {
// Remove the chip from selectableChips
this.selectableChips = this.selectableChips.filter((chip) => chip.key !== chipToRemove.key);
// Remove the chip from selectedChips if present
this.selectedChips = this.selectedChips.filter((chip) => chip.key !== chipToRemove.key);
// If the removed chip was the selectedChip, set selectedChip to null
if (this.selectedChip && this.selectedChip.key === chipToRemove.key) {
this.selectedChip = null;
}
// Trigger an update to re-render the component
this.requestUpdate();
}
}

View File

@ -0,0 +1,57 @@
import { html } from '@design.estate/dees-element';
import * as plugins from './00plugins.js';
import { DeesContextmenu } from './dees-contextmenu.js';
export const demoFunc = () => html`
<style>
.withMargin {
display: block;
margin: 20px;
}
</style>
<dees-button @contextmenu=${(eventArg) => {
DeesContextmenu.openContextMenuWithOptions(eventArg, [
{
name: 'copy',
iconName: 'copySolid',
action: async () => {
return null;
},
},
{
name: 'edit',
iconName: 'penToSquare',
action: async () => {
return null;
},
},{
name: 'paste',
iconName: 'pasteSolid',
action: async () => {
return null;
},
},
]);
}}>Right-Click for contextmenu</dees-button>
<dees-contextmenu class="withMargin"></dees-contextmenu>
<dees-contextmenu
class="withMargin"
.menuItems=${[
{
name: 'copy',
iconName: 'copySolid',
action: async () => {},
},
{
name: 'edit',
iconName: 'penToSquare',
action: async () => {},
},{
name: 'paste',
iconName: 'pasteSolid',
action: async () => {},
},
] as plugins.tsclass.website.IMenuItem[]}
></dees-contextmenu>
`;

View File

@ -0,0 +1,189 @@
import * as colors from './00colors.js';
import * as plugins from './00plugins.js';
import { demoFunc } from './dees-contextmenu.demo.js';
import {
customElement,
html,
DeesElement,
property,
type TemplateResult,
cssManager,
css,
type CSSResult,
unsafeCSS,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { DeesWindowLayer } from './dees-windowlayer.js';
declare global {
interface HTMLElementTagNameMap {
'dees-contextmenu': DeesContextmenu;
}
}
@customElement('dees-contextmenu')
export class DeesContextmenu extends DeesElement {
// DEMO
public static demo = demoFunc
// STATIC
// This will store all the accumulated menu items
public static contextMenuDeactivated = false;
public static accumulatedMenuItems: plugins.tsclass.website.IMenuItem[] = [];
// Add a global event listener for the right-click context menu
public static initializeGlobalListener() {
document.addEventListener('contextmenu', (event: MouseEvent) => {
if (this.contextMenuDeactivated) {
return;
}
event.preventDefault();
// Get the target element of the right-click
let target: EventTarget | null = event.target;
// Clear previously accumulated items
DeesContextmenu.accumulatedMenuItems = [];
// Traverse up the DOM tree to accumulate menu items
while (target) {
if ((target as any).getContextMenuItems) {
DeesContextmenu.accumulatedMenuItems.push(...(target as any).getContextMenuItems());
}
target = (target as Node).parentNode;
}
// Open the context menu with the accumulated items
DeesContextmenu.openContextMenuWithOptions(event, DeesContextmenu.accumulatedMenuItems);
});
}
// allows opening of a contextmenu with options
public static async openContextMenuWithOptions(eventArg: MouseEvent, menuItemsArg: plugins.tsclass.website.IMenuItem[]) {
if (this.contextMenuDeactivated) {
return;
}
eventArg.preventDefault();
eventArg.stopPropagation();
const contextMenu = new DeesContextmenu();
contextMenu.style.position = 'fixed';
contextMenu.style.zIndex = '2000';
contextMenu.style.top = `${eventArg.clientY.toString()}px`;
contextMenu.style.left = `${eventArg.clientX.toString()}px`;
contextMenu.style.opacity = '0';
contextMenu.style.transform = 'scale(0.95,0.95)';
contextMenu.style.transformOrigin = 'top left';
contextMenu.menuItems = menuItemsArg;
contextMenu.windowLayer = await DeesWindowLayer.createAndShow();
contextMenu.windowLayer.addEventListener('click', async () => {
await contextMenu.destroy();
})
document.body.append(contextMenu);
await domtools.plugins.smartdelay.delayFor(0);
contextMenu.style.opacity = '1';
contextMenu.style.transform = 'scale(1,1)';
}
// INSTANCE
@property({
type: Array,
})
public menuItems: plugins.tsclass.website.IMenuItem[] = [];
windowLayer: DeesWindowLayer;
constructor() {
super();
}
/**
* STATIC STYLES
*/
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
transition: all 0.1s;
}
.mainbox {
color: ${cssManager.bdTheme('#222', '#ccc')};
font-size: 14px;
width: 200px;
border: 1px solid ${cssManager.bdTheme('#fff', '#ffffff10')};
min-height: 34px;
border-radius: 3px;
background: ${cssManager.bdTheme('#fff', '#222')};
box-shadow: 0px 1px 4px ${cssManager.bdTheme('#00000020', '#000000')};
user-select: none;
padding: 4px;
}
.mainbox .menuitem {
padding: 4px 8px;
border-radius: 3px;
}
.mainbox .menuitem dees-icon {
display: inline-block;
margin-right: 8px;
width: 14px;
transform: translateY(2px);
}
.mainbox .menuitem:hover {
background: ${cssManager.bdTheme(colors.bright.blue, colors.dark.blue)};
}
.mainbox .menuitem:active {
background: #ffffff05;
}
`,
];
public render(): TemplateResult {
return html`
<div class="mainbox">
${this.menuItems.map((menuItemArg) => {
return html`
<div class="menuitem" @click=${() => this.handleClick(menuItemArg)}>
<dees-icon .iconFA=${(menuItemArg.iconName as any) || 'minus'}></dees-icon
>${menuItemArg.name}
</div>
`;
})}
${this.menuItems.length === 0 ? html`
<div class="menuitem" @click=${() => {
DeesContextmenu.contextMenuDeactivated = true;
this.destroy();
}}>
<dees-icon .iconFA=${'xmark'}></dees-icon
>allow native context
</div>
` : html``}
</div>
`;
}
public async firstUpdated() {
}
public async handleClick(menuItem: plugins.tsclass.website.IMenuItem) {
menuItem.action();
await this.destroy();
}
public async destroy() {
if (this.windowLayer) {
this.windowLayer.destroy();
}
this.style.opacity = '0';
this.style.transform = 'scale(0.95,0,95)';
await domtools.plugins.smartdelay.delayFor(100);
this.parentElement.removeChild(this);
}
}
DeesContextmenu.initializeGlobalListener();

View File

@ -0,0 +1,18 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html` <style>
.demoWrapper {
box-sizing: border-box;
position: absolute;
width: 100%;
height: 100%;
padding: 20px;
background: none;
}
</style>
<div class="demoWrapper">
<dees-dataview-codebox proglang="typescript">
import * as text from './hello'; const hiThere = 'nice'; const myFunction = async () => {
console.log('nice one'); }
</dees-dataview-codebox>
</div>`

View File

@ -0,0 +1,237 @@
import { demoFunc } from './dees-dataview-codebox.demo.js';
import {
DeesElement,
html,
customElement,
type TemplateResult,
property,
state,
cssManager,
} from '@design.estate/dees-element';
import hlight from 'highlight.js';
import * as smartstring from '@push.rocks/smartstring';
import * as domtools from '@design.estate/dees-domtools';
import { DeesContextmenu } from './dees-contextmenu.js';
declare global {
interface HTMLElementTagNameMap {
'dees-dataview-codebox': DeesDataviewCodebox;
}
}
@customElement('dees-dataview-codebox')
export class DeesDataviewCodebox extends DeesElement {
public static demo = demoFunc;
@property()
public progLang: string = 'typescript';
@property({
type: String,
reflect: true,
})
public codeToDisplay: string = '';
constructor() {
super();
}
render(): TemplateResult {
return html`
${domtools.elementBasic.styles}
<style>
:host {
position: relative;
display: block;
text-align: left;
font-size: 16px;
font-family: 'Geist Sans', sans-serif;
}
.mainbox {
position: relative;
color: ${this.goBright ? '#333333' : '#ffffff'};
border-top: 1px solid ${this.goBright ? '#ffffff' : '#333333'};
box-shadow: 0px 0px 5px ${this.goBright ? 'rgba(0,0,0,0.1)' : 'rgba(0,0,0,0.5)'};
background: ${this.goBright ? '#ffffff' : '#191919'};
border-radius: 16px;
overflow: hidden;
}
.appbar {
position: relative;
color: ${cssManager.bdTheme('#333', '#ccc')};
background: ${cssManager.bdTheme('#ffffff', '#161616')};
border-bottom: 1px solid ${cssManager.bdTheme('#eeeeeb', '#222222')};
height: 24px;
display: flex;
font-size: 12px;
line-height: 24px;
justify-content: center;
align-items: center;
}
.appbar .fileName {
line-height: inherit;
position: relative;
flex: 1;
text-align: center;
}
.bottomBar {
color: ${cssManager.bdTheme('#333', '#ccc')};
background: ${cssManager.bdTheme('#ffffff', '#161616')};
border-top: 1px solid ${cssManager.bdTheme('#eeeeeb', '#222222')};
height: 24px;
font-size: 12px;
line-height: 24px;
text-align: right;
padding-right: 100px;
}
.languageLabel {
color: ${cssManager.bdTheme('#333', '#ccc')};
font-size: 12px;
line-height: 24px;
z-index: 10;
background: #6596ff20;
display: inline-block;
position: absolute;
bottom: 0px;
right: 0px;
padding: 0px 16px 0px 8px;
}
.hljs-keyword {
color: #ff65ec;
}
.codegrid {
display: grid;
grid-template-columns: 50px auto;
overflow: hidden;
}
.lineNumbers {
color: ${this.goBright ? '#acacac' : '#666666'};
padding: 30px 16px 0px 0px;
text-align: right;
border-right: 1px solid ${this.goBright ? '#eaeaea' : '#222222'};
}
.lineCounter:last-child {
opacity: 50%;
}
pre {
overflow-x: auto;
margin: 0px;
padding: 30px 40px;
}
code {
font-weight: ${this.goBright ? '400' : '300'};
padding: 0px;
margin: 0px;
}
code,
code *,
.lineNumbers {
line-height: 1.4em;
font-weight: 200;
font-family: 'Intel One Mono', 'Geist Mono', 'monospace';
}
.hljs-string {
color: #ffa465;
}
.hljs-built_in {
color: #65ff6a;
}
.hljs-function {
color: ${this.goBright ? '#2765DF' : '#6596ff'};
}
.hljs-params {
color: ${this.goBright ? '#3DB420' : '#65d5ff'};
}
.hljs-comment {
color: ${this.goBright ? '#EF9300' : '#ffd765'};
}
</style>
<div
class="mainbox"
@contextmenu="${(eventArg) => {
DeesContextmenu.openContextMenuWithOptions(eventArg, [
{
name: 'About',
iconName: 'circleInfo',
action: async () => {
return null;
},
},
]);
}}"
>
<div class="appbar">
<dees-windowcontrols type="mac" position="left"></dees-windowcontrols>
<div class="fileName">index.ts</div>
<dees-windowcontrols type="mac" position="right"></dees-windowcontrols>
</div>
<div class="codegrid">
<div class="lineNumbers">
${(() => {
let lineCounter = 0;
return this.codeToDisplay.split('\n').map((lineArg) => {
lineCounter++;
return html`<div class="lineCounter">${lineCounter}</div>`;
});
})()}
</div>
<pre><code></code></pre>
</div>
<div class="bottomBar">
Spaces: 2
<div class="languageLabel">${this.progLang}</div>
</div>
</div>
`;
}
@state()
private codeToDisplayStore = '';
public async updated(_changedProperties) {
super.updated(_changedProperties);
console.log('highlighting now');
console.log(this.childNodes);
const slottedCodeNodes: Text[] = [];
this.childNodes.forEach((childNode) => {
if (childNode.nodeName === '#text') {
slottedCodeNodes.push(childNode as Text);
}
});
if (this.codeToDisplay && this.codeToDisplay !== this.codeToDisplayStore) {
this.codeToDisplayStore = smartstring.indent.normalize(this.codeToDisplay).trimStart();
}
if (slottedCodeNodes[0] && slottedCodeNodes[0].wholeText && !this.codeToDisplay) {
this.codeToDisplayStore = smartstring.indent
.normalize(slottedCodeNodes[0].wholeText)
.trimStart();
this.codeToDisplay = this.codeToDisplayStore;
}
await domtools.plugins.smartdelay.delayFor(0);
const localCodeNode = this.shadowRoot.querySelector('code');
const html = hlight.highlight(this.codeToDisplayStore, {
language: this.progLang,
ignoreIllegals: true,
});
localCodeNode.innerHTML = html.value;
}
}

View File

@ -0,0 +1,49 @@
import { html, cssManager } from '@design.estate/dees-element';
import * as tsclass from '@tsclass/tsclass';
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

@ -0,0 +1,165 @@
import * as colors from './00colors.js';
import * as plugins from './00plugins.js';
import { demoFunc } from './dees-dataview-statusobject.demo.js';
import {
DeesElement,
html,
customElement,
type TemplateResult,
property,
state,
cssManager,
css,
type CSSResult,
} from '@design.estate/dees-element';
import * as tsclass from '@tsclass/tsclass';
declare global {
interface HTMLElementTagNameMap {
'dees-dataview-statusobject': DeesDataviewStatusobject;
}
}
@customElement('dees-dataview-statusobject')
export class DeesDataviewStatusobject extends DeesElement {
public static demo = demoFunc;
@property({ type: Object }) statusObject: tsclass.code.IStatusObject;
public static styles = [
cssManager.defaultStyles,
css`
.mainbox {
border-radius: 8px;
background: ${cssManager.bdTheme('#fff', '#1b1b1b')};
box-shadow: 0px 1px 3px #00000030;
min-height: 48px;
color: ${cssManager.bdTheme('#000', '#fff')};
border-top: ${cssManager.bdTheme('none', '1px solid #ffffff10')};
cursor: default;
}
.heading {
display: grid;
align-items: center;
grid-template-columns: 40px auto 120px;
}
h1 {
display: block;
margin: 0px;
padding: 0px;
height: 48px;
text-transform: uppercase;
font-size: 12px;
line-height: 48px;
}
.statusdot {
height: 8px;
width: 8px;
border-radius: 6px;
background: grey;
margin: auto;
}
.copyMain {
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#444')};
text-align: center;
padding: 4px;
border-radius: 3px;
margin-right: 16px;
color: ${cssManager.bdTheme('#333', '#ffffff80')};
user-select: none;
}
.copyMain:hover {
background: ${cssManager.bdTheme(colors.bright.blue, colors.dark.blue)};
border: 1px solid ${cssManager.bdTheme(colors.bright.blue, colors.dark.blue)};
color: #fff;
}
.copyMain:active {
background: ${cssManager.bdTheme(colors.bright.blueActive, colors.dark.blueActive)};
border: 1px solid ${cssManager.bdTheme(colors.bright.blueActive, colors.dark.blueActive)};
color: #fff;
}
.statusdot.ok {
background: green;
}
.statusdot.not_ok{
background: red;
}
.statusdot.partly_ok {
background: orange;
}
.detail {
min-height: 60px;
align-items: center;
display: grid;
grid-template-columns: 40px auto;
border-top: 1px dotted ${cssManager.bdTheme('#e0e0e0', '#282828')};
transition: all 0.2s;
}
.detail:hover {
background: #ffffff05;
}
.detail:active {
background: #ffffff10;
}
.detail .detailsText {
padding-top: 8px;
padding-bottom: 8px;
padding-right: 8px;
word-break: break-all;
}
.detail .detailsText .label {
font-size: 12px;
color: #ffffff80
}
.detail .detailsText .value {
font-size: 14px;
font-family: 'Intel One Mono', 'Geist Mono';
}
`,
];
render(): TemplateResult {
return html`
<div class="mainbox">
<div class="heading">
<div class="statusdot ${this.statusObject?.combinedStatus}"></div>
<h1>${this.statusObject?.name || 'no status object assigned'}</h1>
<div class="copyMain">Copy as JSON</div>
</div>
${this.statusObject?.details?.map((detailArg) => {
return html`
<div class="detail">
<div class="statusdot ${detailArg.status}"></div>
<div class="detailsText">
<div class="label">${detailArg.name}</div>
<div class="value">${detailArg.value}</div>
</div>
</div>
`;
})}
</div>
`;
}
async firstUpdated() {}
}

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,19 +1,71 @@
import { customElement, html, LitElement } from 'lit-element';
import {
customElement,
html,
DeesElement,
css,
cssManager,
property,
type CSSResult,
} from '@design.estate/dees-element';
import { DeesForm } from './dees-form.js';
import {DeesForm} from './dees-form';
declare global {
interface HTMLElementTagNameMap {
'dees-form-submit': DeesFormSubmit;
}
}
@customElement('dees-form-submit')
export class DeesFormSubmit extends LitElement {
export class DeesFormSubmit extends DeesElement {
public static demo = () => html`<dees-form-submit>This is a sloted text</dees-form-submit>`;
@property({
type: Boolean,
reflect: true,
})
public disabled = false;
@property({
type: String,
})
public text: string;
@property({
type: String,
})
public status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
constructor() {
super();
}
public static styles = [cssManager.defaultStyles, css``];
public render() {
return html`
<dees-button @click="${this.submit}">${this.textContent}</dees-button>
<dees-button
status=${this.status}
@click=${this.submit}
.disabled=${this.disabled}
.text=${this.text ? this.text : this.textContent}
>
</dees-button>
`;
}
public async submit() {
if (this.disabled) {
return;
}
const parentElement: DeesForm = this.parentElement as DeesForm;
parentElement.gatherAndDispatch();
}
public async focus() {
const domtools = await this.domtoolsPromise;
if (!this.disabled) {
domtools.convenience.smartdelay.delayFor(0);
this.submit();
}
}
}

View File

@ -0,0 +1,69 @@
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: 24px auto;
padding: 16px;
background: ${cssManager.bdTheme('#eeeeeb', '#111')};
box-shadow: 0px 1px 3px #00000030;
}
</style>
<div class="demoContainer">
<dees-form
style="display: block; margin:auto; max-width: 500px; padding: 20px"
@formData=${async (eventArg) => {
const form: DeesForm = eventArg.currentTarget;
form.setStatus('pending', 'authenticating...');
await domtools.plugins.smartdelay.delayFor(1000);
form.setStatus('success', 'authenticated!');
}}
>
<dees-input-dropdown
.label=${'title'}
.options=${[
{ option: 'option 1', key: 'option1' },
{ option: 'option 2', key: 'option2' },
{ option: 'option 3', key: 'option3' },
]}
></dees-input-dropdown>
<dees-input-multiselect
.label=${'title'}
.options=${[
{ option: 'option 1', key: 'option1' },
{ option: 'option 2', key: 'option2' },
{ option: 'option 3', key: 'option3' },
]}></dees-input-multiselect>
<dees-input-typelist
.label=${'a type list'}
></dees-input-typelist>
<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}"
key="hello3"
label="a password"
isPasswordBool
></dees-input-text>
<dees-input-checkbox
.required="${true}"
key="hello3"
label="another text"
></dees-input-checkbox>
<dees-input-iban></dees-input-iban>
<dees-input-multitoggle
.label=${'multi select'}
.options=${['option 1', 'option 2', 'option 3']}
.selectedOption=${'option 1'}
></dees-input-multitoggle>
<dees-input-fileupload
.label=${'attachments'}
></dees-input-fileupload>
<dees-form-submit>Submit</dees-form-submit>
</dees-form>
</div>
`;

View File

@ -1,20 +1,52 @@
import { customElement, html, TemplateResult, LitElement } from 'lit-element';
import {
customElement,
html,
type TemplateResult,
DeesElement,
type CSSResult,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { DeesInputCheckbox } from './dees-input-checkbox';
import { DeesInputText } from './dees-input-text';
import { DeesInputQuantitySelector } from './dees-input-quantityselector';
import { DeesInputRadio } from './dees-input-radio';
import { DeesInputCheckbox } from './dees-input-checkbox.js';
import { DeesInputText } from './dees-input-text.js';
import { DeesInputQuantitySelector } from './dees-input-quantityselector.js';
import { DeesInputRadio } from './dees-input-radio.js';
import { DeesFormSubmit } from './dees-form-submit.js';
import { DeesTable } from './dees-table.js';
import { demoFunc } from './dees-form.demo.js';
import { DeesInputIban } from './dees-input-iban.js';
// Unified set for form input types
const FORM_INPUT_TYPES = [
DeesInputCheckbox,
DeesInputIban,
DeesInputText,
DeesInputQuantitySelector,
DeesInputRadio,
DeesTable,
];
export type TFormInputElement =
| DeesInputCheckbox
| DeesInputIban
| DeesInputText
| DeesInputQuantitySelector
| DeesInputRadio
| DeesTable<any>;
declare global {
interface HTMLElementTagNameMap {
'dees-form': DeesForm;
}
}
@customElement('dees-form')
export class DeesForm extends LitElement {
public static demo = () => html`
<dees-form style="display: block; margin:auto; width: 500px; padding: 20px; background: #111;">
<dees-input-text key="hello1"></dees-input-text>
<dees-input-text key="hello2"></dees-input-text>
<dees-form-submit>Submit</dees-form-submit>
</dees-form>`;
export class DeesForm extends DeesElement {
public static demo = demoFunc;
public name: string = 'myform';
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
public readyDeferred = domtools.plugins.smartpromise.defer();
public render(): TemplateResult {
return html`
@ -27,24 +59,147 @@ export class DeesForm extends LitElement {
`;
}
public async gatherAndDispatch() {
public async firstUpdated() {
const formChildren = this.getFormElements();
this.updateRequiredStatus();
const children: Array<DeesInputCheckbox | DeesInputText | DeesInputQuantitySelector | DeesInputRadio> = this.children as any;
const valueObject: { [key: string]: string | number | boolean} = {};
for (const child of children) {
if (child instanceof DeesInputCheckbox || child instanceof DeesInputText || child instanceof DeesInputQuantitySelector) {
valueObject[child.key] = child.value;
for (const child of formChildren) {
child.changeSubject.subscribe(async () => {
const valueObject = await this.collectFormData();
this.changeSubject.next(valueObject);
console.log(valueObject);
this.updateRequiredStatus();
});
}
await this.addBehaviours();
this.readyDeferred.resolve();
}
public getFormElements(): Array<TFormInputElement> {
return Array.from(this.children).filter((child) =>
FORM_INPUT_TYPES.includes(child.constructor as any)
) as unknown as TFormInputElement[];
}
public getSubmitButton(): DeesFormSubmit | undefined {
return Array.from(this.children).find(
(child) => child instanceof DeesFormSubmit
) as DeesFormSubmit;
}
public async updateRequiredStatus() {
console.log('checking the required status.');
let requiredOK = true;
for (const childArg of this.getFormElements()) {
if (childArg.required && !childArg.value) {
requiredOK = false;
}
}
if (this.getSubmitButton()) {
this.getSubmitButton().disabled = !requiredOK;
}
}
/**
* collects the form data
* @returns
*/
public async collectFormData() {
const children = this.getFormElements();
const valueObject: { [key: string]: string | number | boolean | any[] } = {};
for (const child of children) {
if (!child.key) {
console.log(`form element with label "${child.label}" has no key. skipping.`);
}
valueObject[child.key] = child.value;
}
return valueObject;
}
console.log(valueObject);
public async gatherAndDispatch() {
const valueObject = await this.collectFormData();
const formDataEvent = new CustomEvent('formData', {
detail: {
data: valueObject
data: valueObject,
},
bubbles: true
bubbles: true,
});
this.dispatchEvent(formDataEvent);
console.log('dispatched data:');
console.log(valueObject);
}
public setStatus(
visualStateArg: 'normal' | 'pending' | 'error' | 'success',
textStateArg: string
) {
const inputChildren = this.getFormElements();
const submitButton = this.getSubmitButton();
switch (visualStateArg) {
case 'normal':
submitButton.disabled = false;
submitButton.status = 'normal';
for (const inputChild of inputChildren) {
inputChild.disabled = false;
}
break;
case 'pending':
submitButton.disabled = true;
submitButton.status = 'pending';
for (const inputChild of inputChildren) {
inputChild.disabled = true;
}
break;
case 'success':
submitButton.disabled = true;
submitButton.status = 'success';
for (const inputChild of inputChildren) {
inputChild.disabled = true;
}
break;
case 'error':
submitButton.disabled = true;
submitButton.status = 'error';
for (const inputChild of inputChildren) {
inputChild.disabled = true;
}
break;
}
submitButton.text = textStateArg;
}
/**
* resets the form
*/
reset() {
const inputChildren = this.getFormElements();
const submitButton = this.getSubmitButton();
for (const inputChild of inputChildren) {
inputChild.value = null;
}
this.setStatus('normal', 'Submit');
}
public async addBehaviours() {
// Use event delegation
this.addEventListener('keydown', (event: KeyboardEvent) => {
const target = event.target as DeesElement;
if (!FORM_INPUT_TYPES.includes(target.constructor as any)) return;
if (event.key === 'Enter') {
const children = this.getFormElements();
const currentIndex = children.indexOf(target as any);
if (currentIndex < children.length - 1) {
children[currentIndex + 1].focus();
} else {
target.blur();
this.getSubmitButton()?.focus();
}
}
});
}
}

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

@ -0,0 +1,5 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html`
<dees-hint></dees-hint>
`;

View File

@ -0,0 +1,38 @@
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-hint.demo.js';
declare global {
interface HTMLElementTagNameMap {
'dees-hint': DeesHint;
}
}
@customElement('dees-hint')
export class DeesHint extends DeesElement {
public static demo = demoFunc;
@property({ type: String })
public type: 'info' | 'warn' | 'error' | 'critical' = 'info';
constructor() {
super();
domtools.elementBasic.setup();
}
public static styles = [cssManager.defaultStyles, css``];
public render(): TemplateResult {
return html` <div class="mainbox"></div> `;
}
}

View File

@ -0,0 +1,292 @@
import { html } from '@design.estate/dees-element';
import { icons, type IconWithPrefix } from './dees-icon.js';
import * as lucideIcons from 'lucide';
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`);
}
}
// 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: 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: all 0.2s ease;
color: #ffffff;
}
.iconContainer {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 16px 0px 16px;
border: 1px solid #333333;
margin-right: 10px;
margin-bottom: 10px;
border-radius: 4px;
transition: background-color 0.2s;
cursor: pointer;
position: relative;
}
.iconContainer:hover {
background-color: #222;
}
.iconName {
font-size: 12px;
text-align: center;
color: #ccc;
background: #333333;
padding: 6px 10px;
margin-left: -16px;
margin-right: -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">
<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

@ -0,0 +1,453 @@
import {
DeesElement,
html,
property,
customElement,
cssManager,
css,
type CSSResult,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { icon, type IconDefinition } from '@fortawesome/fontawesome-svg-core';
import {
faFacebook,
faGoogle,
faLinkedin,
faMedium,
faSlackHash,
faTwitter,
faInstagram,
faTiktok,
} from '@fortawesome/free-brands-svg-icons';
import {
faCopy as faCopyRegular,
faCircleCheck as faCircleCheckRegular,
faCircleXmark as faCircleXmarkRegular,
faMessage as faMessageRegular,
faPaste as faPasteRegular,
faSun as faSunRegular,
faTrashCan as faTrashCanRegular,
} from '@fortawesome/free-regular-svg-icons';
import {
faArrowRight as faArrowRightSolid,
faArrowUpRightFromSquare as faArrowUpRightFromSquareSolid,
faBell as faBellSolid,
faBug as faBugSolid,
faBuilding as faBuildingSolid,
faCaretLeft as faCaretLeftSolid,
faCaretRight as faCaretRightSolid,
faCheck as faCheckSolid,
faCircleInfo as faCircleInfoSolid,
faCircleCheck as faCircleCheckSolid,
faCircleXmark as faCircleXmarkSolid,
faClockRotateLeft as faClockRotateLeftSolid,
faCopy as faCopySolid,
faDesktop as faDesktopSolid,
faEye as faEyeSolid,
faEyeSlash as faEyeSlashSolid,
faFileInvoice as faFileInvoiceSolid,
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,
faPlus as faPlusSolid,
faReceipt as faReceiptSolid,
faRss as faRssSolid,
faUsers as faUsersSolid,
faShare as faShareSolid,
faSun as faSunSolid,
faTerminal as faTerminalSolid,
faTrash as faTrashSolid,
faTrashCan as faTrashCanSolid,
faWallet as faWalletSolid,
faXmark as faXmarkSolid,
} from '@fortawesome/free-solid-svg-icons';
import { demoFunc } from './dees-icon.demo.js';
// 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,
bell: faBellSolid,
bug: faBugSolid,
building: faBuildingSolid,
caretLeft: faCaretLeftSolid,
caretRight: faCaretRightSolid,
check: faCheckSolid,
circleInfo: faCircleInfoSolid,
circleCheck: faCircleCheckRegular,
circleCheckSolid: faCircleCheckSolid,
circleXmark: faCircleXmarkRegular,
circleXmarkSolid: faCircleXmarkSolid,
clockRotateLeft: faClockRotateLeftSolid,
copy: faCopyRegular,
copySolid: faCopySolid,
desktop: faDesktopSolid,
eye: faEyeSolid,
eyeSlash: faEyeSlashSolid,
fileInvoice: faFileInvoiceSolid,
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,
penToSquare: faPenToSquareSolid,
plus: faPlusSolid,
receipt: faReceiptSolid,
rss: faRssSolid,
share: faShareSolid,
sun: faSunRegular,
sunSolid: faSunSolid,
terminal: faTerminalSolid,
trash: faTrashSolid,
trashSolid: faTrashSolid,
trashCan: faTrashCanRegular,
trashCanSolid: faTrashCanSolid,
users: faUsersSolid,
wallet: faWalletSolid,
xmark: faXmarkSolid,
// brands
facebook: faFacebook,
google: faGoogle,
instagram: faInstagram,
linkedin: faLinkedin,
medium: faMedium,
slack: faSlackHash,
tiktok: faTiktok,
twitter: faTwitter,
};
// 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 {
'dees-icon': DeesIcon;
}
}
@customElement('dees-icon')
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,
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?: TIconKey;
/**
* 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: inline-flex;
align-items: center;
justify-content: center;
line-height: 1;
vertical-align: middle;
}
/* Improve rendering performance */
#iconContainer svg {
display: block;
height: 100%;
width: 100%;
will-change: transform; /* Helps with animations */
contain: strict; /* Performance optimization */
}
`,
];
public render() {
return html`
${domtools.elementBasic.styles}
<style>
#iconContainer {
width: ${this.iconSize}px;
height: ${this.iconSize}px;
}
</style>
<div id="iconContainer"></div>
`;
}
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,''));
}
// 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

@ -1,19 +1,55 @@
import { customElement, LitElement, TemplateResult, property, html } from 'lit-element';
import * as domtools from '@designestate/dees-domtools';
import {
customElement,
DeesElement,
type TemplateResult,
property,
html,
css,
cssManager,
type CSSResult,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
declare global {
interface HTMLElementTagNameMap {
'dees-input-checkbox': DeesInputCheckbox;
}
}
@customElement('dees-input-checkbox')
export class DeesInputCheckbox extends LitElement {
export class DeesInputCheckbox extends DeesElement {
// STATIC
public static demo = () => html`<dees-input-checkbox></dees-input-checkbox>`;
@property()
// INSTANCE
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
@property({
type: String,
reflect: true,
})
public key: string;
@property()
@property({
type: String,
})
public label: string = 'Label';
@property()
@property({
type: Boolean,
})
public value: boolean = false;
@property({
type: Boolean,
})
public required: boolean = false;
@property({
type: Boolean
})
public disabled: boolean = false;
public render(): TemplateResult {
return html`
${domtools.elementBasic.styles}
@ -26,18 +62,21 @@ export class DeesInputCheckbox extends LitElement {
display: block;
position: relative;
margin: 20px 0px;
cursor: pointer;
cursor: default;
}
:host(:hover) {
filter: ${cssManager.bdTheme('brightness(0.95)', 'brightness(1.1)')};
}
.maincontainer {
display: grid;
grid-template-columns: 25px auto;
padding: 5px 0px;
color: #ccc;
color: ${this.goBright ? '#333' : '#ccc'};
}
.maincontainer:hover {
color: #fff;
${this.goBright ? '#000' : '#ccc'};
}
.label {
@ -55,20 +94,25 @@ export class DeesInputCheckbox extends LitElement {
.checkbox {
transition: all 0.1s;
box-sizing: border-box;
border: 1px solid #999;
border: 1px solid ${this.goBright ? '#CCC' : '#999'};
border-radius: 2px;
height: 24px;
width: 24px;
display: inline-block;
background: #222;
background: ${this.goBright ? '#fafafa' : '#222'};
}
.checkbox.selected {
background: #039BE5;
border: 1px solid #039BE5;
background: #0050b9;
border: 1px solid #0050b9;
}
.checkmark {
.checkbox.disabled {
background: none;
border: 1px dashed ${cssManager.bdTheme('#666666', '#666666')};
}
.checkbox .checkmark {
display: inline-block;
width: 22px;
height: 22px;
@ -77,16 +121,16 @@ export class DeesInputCheckbox extends LitElement {
transform: rotate(45deg);
}
.checkmark_stem {
.checkbox .checkmark_stem {
position: absolute;
width: 3px;
height: 9px;
background-color: #ccc;
background-color: #fff;
left: 11px;
top: 6px;
}
.checkmark_kick {
.checkbox .checkmark_kick {
position: absolute;
width: 3px;
height: 3px;
@ -95,12 +139,16 @@ export class DeesInputCheckbox extends LitElement {
top: 12px;
}
.checkbox.disabled .checkmark_stem, .checkbox.disabled .checkmark_kick {
background-color: ${cssManager.bdTheme('#333', '#fff')};
}
img {
padding: 4px;
}
</style>
<div class="maincontainer" @click="${this.toggleSelected}">
<div class="checkbox ${this.value ? 'selected' : ''}">
<div class="checkbox ${this.value ? 'selected' : ''} ${this.disabled ? 'disabled' : ''}" tabindex="0">
${this.value
? html`
<span class="checkmark">
@ -116,6 +164,9 @@ export class DeesInputCheckbox extends LitElement {
}
public async toggleSelected() {
if (this.disabled) {
return;
}
this.value = !this.value;
this.dispatchEvent(
new CustomEvent('newValue', {
@ -123,5 +174,13 @@ export class DeesInputCheckbox extends LitElement {
bubbles: true,
})
);
this.changeSubject.next(this);
}
public focus(): void {
const checkboxDiv = this.shadowRoot.querySelector('.checkbox');
if (checkboxDiv) {
(checkboxDiv as any).focus();
}
}
}

View File

@ -0,0 +1,27 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html`
<dees-input-dropdown
.options=${[
{option: 'option 1', key: 'option1'},
{option: 'option 2', key: 'option2'},
{option: 'option 3', key: 'option3'}
]}
></dees-input-dropdown>
<dees-input-dropdown
.enableSearch=${false}
.options=${[
{option: 'option 1', key: 'option1'},
{option: 'option 2', key: 'option2'},
{option: 'option 3', key: 'option3'}
]}
></dees-input-dropdown>
<div style="height: 300px"></div>
<dees-input-dropdown
.options=${[
{option: 'option 1', key: 'option1'},
{option: 'option 2', key: 'option2'},
{option: 'option 3', key: 'option3'}
]}
></dees-input-dropdown>
`

View File

@ -1,116 +1,375 @@
import { customElement, LitElement, TemplateResult, property, html } from 'lit-element';
import {
customElement,
DeesElement,
type TemplateResult,
property,
state,
html,
css,
cssManager,
type CSSResult,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { demoFunc } from './dees-input-dropdown.demo.js';
import { DeesWindowLayer } from './dees-windowlayer.js';
import * as domtools from '@designestate/dees-domtools';
declare global {
interface HTMLElementTagNameMap {
'dees-input-dropdown': DeesInputDropdown;
}
}
@customElement('dees-input-dropdown')
export class DeesInputDropdown extends LitElement {
@property()
export class DeesInputDropdown extends DeesElement {
public static demo = demoFunc;
// INSTANCE
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
@property({
type: String,
reflect: true,
})
public label: string = 'Label';
@property()
public key: string;
@property()
public options: {option: string, key: string, payload?: any}[] = [];
public options: { option: string; key: string; payload?: any }[] = [];
@property()
public selectedOption: {option: string, key: string, payload?: any} = {
key: null,
option: null,
payload: null
};
public selectedOption: { option: string; key: string; payload?: any } = null;
@property({
type: Boolean,
})
public required: boolean = false;
@property({
type: Boolean,
})
public enableSearch: boolean = true;
@property({
type: Boolean,
})
public disabled: boolean = false;
@state()
public opensToTop: boolean = false;
@state()
private filteredOptions: { option: string; key: string; payload?: any }[] = [];
@state()
private highlightedIndex: number = 0;
@state()
public isOpened = false;
public static styles = [
cssManager.defaultStyles,
css`
* {
box-sizing: border-box;
}
:host {
font-family: Roboto;
position: relative;
display: block;
color: ${cssManager.bdTheme('#222', '#fff')};
margin-bottom: 24px;
}
.maincontainer {
display: block;
}
.label {
font-size: 14px;
margin-bottom: 8px;
}
.selectedBox {
user-select: none;
position: relative;
max-width: 420px;
height: 40px;
line-height: 40px;
padding: 0px 8px;
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 #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 {
filter: ${cssManager.bdTheme('brightness(0.95)', 'brightness(1.1)')};
}
.accentBottom {
filter: none !important;
}
.accentTop {
filter: none !important;
}
.selectionBox {
will-change: transform;
pointer-events: none;
transition: all 0.2s ease;
opacity: 0;
background: ${cssManager.bdTheme('#ffffff', '#222222')};
max-width: 420px;
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
min-height: 40px;
border-radius: 8px;
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);
}
.selectionBox.bottom {
transform: translate(0px, -4px);
}
.selectionBox.show {
pointer-events: all;
transform: scale(1, 1) translate(0px, 0px);
opacity: 1;
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.8);
}
.option {
transition: all 0.1s;
line-height: 32px;
padding: 0px 4px;
border-radius: 3px;
margin: 4px 0px;
}
.option.highlighted {
border-left: 2px solid #0069f2;
padding-left: 6px;
background: #ffffff20;
}
.option:hover {
color: #fff;
padding-left: 8px;
background: #0069f2;
}
.search.top {
padding-top: 4px;
}
.search.bottom {
padding-bottom: 4px;
}
.search input {
display: block;
background: none;
border: none;
height: 24px;
color: inherit;
text-align: left;
font-size: 12px;
font-weight: 600;
width: 100%;
margin: auto;
}
.search.top input {
border-bottom: 1px dotted #333;
}
.search.bottom input {
border-top: 1px dotted #333;
}
.search input:focus {
outline: none;
}
`,
];
public render(): TemplateResult {
return html`
${domtools.elementBasic.styles}
<style>
* {
box-sizing: border-box;
}
:host {
position: relative;
display: block;
height: 40px;
}
.maincontainer {
display: block;
}
.label {
font-size: 14px;
margin-bottom: 15px;
}
.selectedBox {
cursor: pointer;
position: relative;
max-width: 420px;
height: 40px;
border: 1px solid #CCC;
padding: 12px;
z-index: 0px;
}
.selectionBox {
pointer-events: none;
cursor: pointer;
transition: all 0.2s ease;
opacity: 0;
position: relative;
background: #ffffff;
max-width: 420px;
box-shadow: 0px 0px 5px rgba(0,0,0,0.2);
height: 40px;
margin-top: -40px;
z-index: 100;
}
.selectionBox.show {
pointer-events: all;
opacity: 1;
min-height: 160px;
}
.option {
padding: 12px;
}
.option:hover {
background: #fafafa;
}
</style>
<div class="maincontainer">
<div class="selectedBox" @click="${event => {this.toggleSelectionBox();}}">
${this.selectedOption?.option}
</div>
<div class="maincontainer" @keydown="${this.isOpened ? this.handleKeyDown : undefined}">
${this.label ? html`<div class="label">${this.label}</div>` : html``}
<div class="selectionBox">
${this.options.map(option => {
${this.enableSearch && !this.opensToTop
? html`
<div class="search top">
<input type="text" placeholder="Search" @input="${this.handleSearch}" />
</div>
`
: null}
${this.filteredOptions.map((option, index) => {
const isHighlighted = this.highlightedIndex === index;
return html`
<div class="option" @click=${() => {this.updateSelection(option);}}>${option.option}</div>
`
<div
class="option ${isHighlighted ? 'highlighted' : ''}"
@click=${() => {
this.updateSelection(option);
}}
>
${option.option}
</div>
`;
})}
${this.enableSearch && this.opensToTop
? html`
<div class="search bottom">
<input type="text" placeholder="Search" @input="${this.handleSearch}" />
</div>
`
: null}
</div>
<div
class="selectedBox"
@click="${(event) => {
if (!this.isElevated) {
this.toggleSelectionBox();
} else {
this.updateSelection(this.selectedOption);
}
}}"
>
${this.selectedOption?.option || 'Select...'}
</div>
</div>
`;
}
firstUpdated() {
this.selectedOption = this.options[0] || null;
this.selectedOption = this.selectedOption || null;
this.filteredOptions = this.options; // Initialize filteredOptions
}
public async updateSelection(selectedOption) {
this.selectedOption = selectedOption;
this.dispatchEvent(new CustomEvent('selectedOption', {
detail: selectedOption,
bubbles: true
}));
this.toggleSelectionBox();
this.dispatchEvent(
new CustomEvent('selectedOption', {
detail: selectedOption,
bubbles: true,
})
);
if (this.isElevated) {
this.toggleSelectionBox();
}
this.changeSubject.next(this);
}
public toggleSelectionBox() {
this.shadowRoot.querySelector('.selectionBox').classList.toggle('show');
private isElevated: boolean = false;
private windowOverlay: DeesWindowLayer;
public async toggleSelectionBox() {
this.isOpened = !this.isOpened;
const domtoolsInstance = await this.domtoolsPromise;
const selectedBox: HTMLElement = this.shadowRoot.querySelector('.selectedBox');
const selectionBox: HTMLElement = this.shadowRoot.querySelector('.selectionBox');
if (!this.isElevated) {
this.windowOverlay = await DeesWindowLayer.createAndShow({
blur: false,
});
const elevatedDropdown = new DeesInputDropdown();
elevatedDropdown.isElevated = true;
elevatedDropdown.label = this.label;
elevatedDropdown.enableSearch = this.enableSearch;
elevatedDropdown.required = this.required;
elevatedDropdown.disabled = this.disabled;
elevatedDropdown.style.position = 'fixed';
elevatedDropdown.style.top = this.getBoundingClientRect().top + 'px';
elevatedDropdown.style.left = this.getBoundingClientRect().left + 'px';
elevatedDropdown.style.width = this.clientWidth + 'px';
elevatedDropdown.options = this.options;
elevatedDropdown.selectedOption = this.selectedOption;
elevatedDropdown.highlightedIndex = elevatedDropdown.selectedOption ? elevatedDropdown.options.indexOf(
elevatedDropdown.options.find((option) => option.key === this.selectedOption.key)
) : -1;
console.log(elevatedDropdown.options);
console.log(elevatedDropdown.selectedOption);
console.log(elevatedDropdown.highlightedIndex);
this.windowOverlay.appendChild(elevatedDropdown);
await domtoolsInstance.convenience.smartdelay.delayFor(0);
elevatedDropdown.toggleSelectionBox();
const destroyOverlay = async () => {
(elevatedDropdown.shadowRoot.querySelector('.selectionBox') as HTMLElement).style.opacity =
'0';
elevatedDropdown.removeEventListener('selectedOption', handleSelection);
this.windowOverlay.removeEventListener('clicked', destroyOverlay);
this.windowOverlay.destroy();
};
const handleSelection = async (event) => {
await this.updateSelection(elevatedDropdown.selectedOption);
destroyOverlay();
};
elevatedDropdown.addEventListener('selectedOption', handleSelection);
this.windowOverlay.addEventListener('clicked', destroyOverlay);
} else {
if (!selectionBox.classList.contains('show')) {
selectionBox.style.width = selectedBox.clientWidth + 'px';
const spaceData = selectedBox.getBoundingClientRect();
if (300 > window.innerHeight - spaceData.bottom) {
this.opensToTop = true;
selectedBox.classList.add('accentTop');
selectionBox.classList.add('top');
selectionBox.style.bottom = selectedBox.clientHeight + 2 + 'px';
} else {
selectedBox.classList.add('accentBottom');
selectionBox.classList.add('bottom');
this.opensToTop = false;
const labelOffset = this.label ? 24 : 0;
selectionBox.style.top = selectedBox.clientHeight + labelOffset + 'px';
}
await domtoolsInstance.convenience.smartdelay.delayFor(0);
const searchInput = selectionBox.querySelector('input');
searchInput?.focus();
selectionBox.classList.add('show');
} else {
selectedBox.style.pointerEvents = 'none';
selectionBox.classList.remove('show');
// selectedBox.style.opacity = '0';
}
}
}
private handleSearch(event: Event): void {
const searchTerm = (event.target as HTMLInputElement).value.toLowerCase();
this.filteredOptions = this.options.filter((option) =>
option.option.toLowerCase().includes(searchTerm)
);
this.highlightedIndex = 0; // Reset highlighted index
}
private handleKeyDown(event: KeyboardEvent): void {
if (!this.isOpened) {
console.log('discarded key event. Check why this function is called.');
return;
}
const key = event.key;
const maxIndex = this.filteredOptions.length - 1;
if (key === 'ArrowDown') {
this.highlightedIndex = this.highlightedIndex + 1 > maxIndex ? 0 : this.highlightedIndex + 1;
event.preventDefault();
} else if (key === 'ArrowUp') {
this.highlightedIndex = this.highlightedIndex - 1 < 0 ? maxIndex : this.highlightedIndex - 1;
event.preventDefault();
} else if (key === 'Enter') {
this.updateSelection(this.filteredOptions[this.highlightedIndex]);
event.preventDefault();
}
}
}

View File

@ -0,0 +1,266 @@
import * as colors from './00colors.js';
import * as plugins from './00plugins.js';
import { DeesContextmenu } from './dees-contextmenu.js';
import {
customElement,
DeesElement,
type TemplateResult,
property,
html,
css,
unsafeCSS,
cssManager,
type CSSResult,
domtools,
} from '@design.estate/dees-element';
declare global {
interface HTMLElementTagNameMap {
'dees-input-fileupload': DeesInputFileupload;
}
}
@customElement('dees-input-fileupload')
export class DeesInputFileupload extends DeesElement {
public static demo = () =>
html`<dees-input-fileupload .label=${'Attachments'}></dees-input-fileupload>`;
// INSTANCE
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
@property({
type: String,
})
public label: string = null;
@property({
type: String,
reflect: true,
})
public key: string;
@property({
attribute: false,
})
public value: File[] = [];
@property()
public state: 'idle' | 'dragOver' | 'dropped' | 'uploading' | 'completed' = 'idle';
@property({
type: Boolean,
})
public required: boolean = false;
@property({
type: Boolean,
})
public disabled: boolean = false;
@property({
type: String,
})
public buttonText: string = 'Upload File...';
constructor() {
super();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
position: relative;
display: grid;
margin: 10px 0px;
margin-bottom: 24px;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
.hidden {
display: none;
}
.maincontainer {
position: relative;
border-radius: 3px;
padding: 8px;
background: ${cssManager.bdTheme('#fafafa', '#222222')};
color: ${cssManager.bdTheme('#333', '#ccc')};
border-top: 1px solid #ffffff10;
}
.maincontainer::after {
top: 2px;
right: 2px;
left: 2px;
bottom: 2px;
transform: scale3d(0.98, 0.9, 1);
position: absolute;
content: '';
display: block;
border: 2px dashed rgba(255, 255, 255, 0);
transition: all 0.2s;
pointer-events: none;
background: #00000000;
}
.maincontainer.dragOver::after {
transform: scale3d(1, 1, 1);
border: 2px dashed rgba(255, 255, 255, 0.3);
background: #00000080;
}
.label {
font-size: 14px;
margin-bottom: 8px;
}
.uploadButton {
position: relative;
padding: 8px;
max-width: 600px;
background: ${cssManager.bdTheme('#fafafa', '#333333')};
border-radius: 3px;
text-align: center;
font-size: 14px;
cursor: default;
}
.uploadButton:hover {
color: #fff;
background: ${unsafeCSS(colors.dark.blue)};
}
.uploadCandidate {
display: grid;
grid-template-columns: 48px auto;
background: #333;
padding: 8px 8px 8px 0px;
margin-bottom: 8px;
text-align: left;
border-radius: 3px;
color: ${cssManager.bdTheme('#666', '#ccc')};
font-family: 'Geist Sans', sans-serif;
cursor: default;
transition: all 0.2s;
border-top: 1px solid #ffffff10;
}
.uploadCandidate:last-child {
margin-bottom: 8px;
}
.uploadCandidate .icon {
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
}
.uploadCandidate:hover {
background: #393939;
}
.uploadCandidate .description {
font-size: 14px;
border-left: 1px solid #ffffff10;
padding-left: 8px;
}
`,
];
public render(): TemplateResult {
return html`
<div class="hidden">
<input type="file"></div>
</div>
${this.label ? html`<div class="label">${this.label}</div>` : null}
<div class="maincontainer ${this.state === 'dragOver' ? 'dragOver' : ''}">
${this.value.map(
(fileArg) => html`
<div class="uploadCandidate" @contextmenu=${eventArg => {
DeesContextmenu.openContextMenuWithOptions(eventArg, [{
iconName: 'trash',
name: 'Remove',
action: async () => {
this.value.splice(this.value.indexOf(fileArg), 1);
this.requestUpdate();
}
}]);
}}>
<div class="icon">
<dees-icon .iconFA=${'paperclip'}></dees-icon>
</div>
<div class="description">
<span style="font-weight: 600">${fileArg.name}</span><br />
<span style="font-weight: 400">${fileArg.size}</span>
</div>
</div> `
)}
<div class="uploadButton" @click=${
this.openFileSelector
}>
${this.buttonText}
</div>
</div>
`;
}
public async openFileSelector() {
const inputFile: HTMLInputElement = this.shadowRoot.querySelector('input[type="file"]');
inputFile.click();
this.state = 'idle';
this.buttonText = 'Upload more files...';
}
public async updateValue(eventArg: Event) {
const target: any = eventArg.target;
this.value = target.value;
this.changeSubject.next(this);
}
public firstUpdated() {
const inputFile: HTMLInputElement = this.shadowRoot.querySelector('input[type="file"]');
inputFile.addEventListener('change', (event: Event) => {
const target = event.target as HTMLInputElement;
for (const file of Array.from(target.files)) {
this.value.push(file);
}
this.requestUpdate();
console.log(`Got ${this.value.length} files!`);
// Reset the input value to allow selecting the same file again if needed
target.value = '';
});
// lets handle drag and drop
const dropArea = this.shadowRoot.querySelector('.maincontainer');
const handlerFunction = (eventArg: DragEvent) => {
eventArg.preventDefault();
switch (eventArg.type) {
case 'dragover':
this.state = 'dragOver';
this.buttonText = 'release to upload file...';
break;
case 'dragleave':
this.state = 'idle';
this.buttonText = 'Upload File...';
break;
case 'drop':
this.state = 'idle';
this.buttonText = 'Upload more files...';
}
console.log(eventArg);
for (const file of Array.from(eventArg.dataTransfer.files)) {
this.value.push(file);
this.requestUpdate();
}
console.log(`Got ${this.value.length} files!`);
};
dropArea.addEventListener('dragenter', handlerFunction, false);
dropArea.addEventListener('dragleave', handlerFunction, false);
dropArea.addEventListener('dragover', handlerFunction, false);
dropArea.addEventListener('drop', handlerFunction, false);
}
}

View File

@ -0,0 +1,3 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html`<dees-input-iban .label=${'IBAN'}></dees-input-iban>`;

View File

@ -0,0 +1,98 @@
import {
customElement,
DeesElement,
type TemplateResult,
state,
html,
domtools,
property,
} from '@design.estate/dees-element';
import * as ibantools from 'ibantools';
import { demoFunc } from './dees-input-iban.demo.js';
@customElement('dees-input-iban')
export class DeesInputIban extends DeesElement {
// STATIC
public static demo = demoFunc;
// INSTANCE
@state()
enteredString: string = '';
@state()
enteredIbanIsValid: boolean = false;
@property({
type: Boolean,
})
public disabled = false;
@property({
type: Boolean,
})
public required = false;
@property({
type: String,
})
public label = '';
@property({
type: String,
})
public key = '';
@property({
type: String,
})
public value = '';
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject<DeesInputIban>();
public render(): TemplateResult {
return html`
<style>
input[type='text'] {
line-height: 20px;
padding: 5px;
width: 250px;
}
</style>
<dees-input-text
.label=${'IBAN'}
.value=${this.value}
@input=${(eventArg: InputEvent) => {
this.validateIban(eventArg);
}}
></dees-input-text>
`;
}
public async firstUpdated() {
const deesInputText = this.shadowRoot.querySelector('dees-input-text');
deesInputText.disabled = this.disabled;
deesInputText.required = this.required;
deesInputText.changeSubject.subscribe(valueArg => {
this.value = valueArg.value;
this.changeSubject.next(this);
})
}
public async validateIban(eventArg: InputEvent): Promise<void> {
const inputElement: HTMLInputElement = eventArg.target as HTMLInputElement;
let enteredString = inputElement?.value;
enteredString = enteredString || '';
if (this.enteredString !== enteredString) {
this.enteredString = ibantools.friendlyFormatIBAN(enteredString) || '';
if (inputElement) {
inputElement.value = this.enteredString;
this.value = this.enteredString;
this.changeSubject.next(this);
}
}
this.enteredIbanIsValid = ibantools.isValidIBAN(this.enteredString.replace(/ /g, ''));
const deesInputText = this.shadowRoot.querySelector('dees-input-text');
deesInputText.validationText = `IBAN is valid: ${this.enteredIbanIsValid}`;
}
}

View File

@ -0,0 +1,14 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html`
<dees-input-multitoggle
.options=${['option 1', 'option 2', 'a longer option with multiple words']}
.selectedOption=${'option 2'}
></dees-input-multitoggle>
<dees-input-multitoggle
.type=${'boolean'}
.booleanTrueName=${'enabled'}
.booleanFalseName=${'disabled'}
.selectedOption=${'true'}
></dees-input-multitoggle>
`;

View File

@ -0,0 +1,159 @@
import {
customElement,
DeesElement,
type TemplateResult,
state,
html,
domtools,
property,
css,
cssManager,
} from '@design.estate/dees-element';
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;
@property({
type: String,
})
public label: string;
@property({
type: String,
})
public description: string;
@property()
type: 'boolean' | 'multi' | 'single' = 'multi';
@property()
booleanTrueName: string = 'true';
@property()
booleanFalseName: string = 'false';
@property({
type: Array,
})
options: string[] = [];
@property()
selectedOption: string = '';
@property()
boolValue: boolean = false;
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
color: ${cssManager.bdTheme('#333', '#ccc')};
user-select: none;
margin: 8px 0px 24px 0px;
}
.label {
font-size: 14px;
margin-bottom: 8px;
}
.selections {
position: relative;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
background: ${cssManager.bdTheme('#fff', '#222')};
width: min-content;
border-radius: 20px;
height: 32px;
border-top: 1px solid #ffffff10;
}
.option {
color: #ccc;
position: relative;
padding: 0px 16px;
line-height: 32px;
cursor: default;
width: min-content; /* Make the width as per the content */
white-space: nowrap; /* Prevent text wrapping */
transition: all 0.1s;
font-size: 14px;
transform: translateY(-1px);
}
.option:hover {
color: #fff;
}
.option.selected {
color: #fff;
}
.indicator {
opacity: 0;
position: absolute;
height: 24px;
left: 4px;
top: 3px;
border-radius: 16px;
background: #0050b9;
min-width: 36px;
}
`,
];
public render(): TemplateResult {
return html`
<dees-label .label=${this.label} .description=${this.description}></dees-label>
<div class="mainbox">
<div class="selections">
<div class="indicator"></div>
${this.options.map(
(option) =>
html`<div class="option ${option === this.selectedOption ? 'selected': ''}" @click=${() => this.handleSelection(option)}>
${option}
</div> `
)}
</div>
</div>
`;
}
public async firstUpdated() {
if (this.type === 'boolean') {
this.options = [this.booleanTrueName || 'true', this.booleanFalseName || 'false'];
}
this.setIndicator();
}
public async handleSelection(optionArg: string) {
this.selectedOption = optionArg;
this.setIndicator();
}
public async setIndicator() {
const indicator: HTMLDivElement = this.shadowRoot.querySelector('.indicator');
const option: HTMLDivElement = this.shadowRoot.querySelector(
`.option:nth-child(${this.options.indexOf(this.selectedOption) + 2})`
);
if (indicator && option) {
indicator.style.width = `${option.clientWidth - 8}px`;
indicator.style.left = `${option.offsetLeft + 4}px`;
indicator.style.opacity = '1';
}
setTimeout(() => {
indicator.style.transition = 'all 0.1s';
}, 100);
}
}

View File

@ -0,0 +1,3 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html`<dees-input-phone .label=${'Phone Number'}></dees-input-phone>`;

View File

@ -0,0 +1,29 @@
import {
customElement,
DeesElement,
type TemplateResult,
property,
html,
css,
unsafeCSS,
cssManager,
type CSSResult,
} from '@design.estate/dees-element';
import { demoFunc } from './dees-input-phone.demo.js';
declare global {
interface HTMLElementTagNameMap {
'dees-input-phone': DeesInputPhone;
}
}
@customElement('dees-input-phone')
export class DeesInputPhone extends DeesElement {
// STATIC
public static demo = demoFunc;
// INSTANCE
public render() {
return html`<div>Phone Input</div>`;
}
}

View File

@ -1,11 +1,26 @@
import { customElement, property, html, TemplateResult, LitElement } from 'lit-element';
import * as domtools from '@designestate/dees-domtools';
import { customElement, property, html, type TemplateResult, DeesElement, type CSSResult, } from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
declare global {
interface HTMLElementTagNameMap {
'dees-input-quantityselector': DeesInputQuantitySelector;
}
}
@customElement('dees-input-quantityselector')
export class DeesInputQuantitySelector extends LitElement {
export class DeesInputQuantitySelector extends DeesElement {
public static demo = () => html`<dees-input-quantityselector></dees-input-quantityselector>`;
// INSTANCE
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
@property()
public label: string = 'Label';
@property({
type: String,
reflect: true,
})
public key: string;
@property({
@ -13,6 +28,20 @@ export class DeesInputQuantitySelector extends LitElement {
})
public value: number = 1;
@property({
type: Boolean,
})
public required: boolean = false;
@property({
type: Boolean
})
public disabled: boolean = false;
constructor() {
super();
}
public render(): TemplateResult {
return html`
${domtools.elementBasic.styles}
@ -32,8 +61,14 @@ export class DeesInputQuantitySelector extends LitElement {
line-height: 40px;
padding: 0px;
min-width: 100px;
color: #CCC;
border: 1px solid #CCC;
color: ${this.goBright ? '#666' : '#CCC'};
border: ${this.goBright ? '1px solid #333' : '1px solid #CCC'};
border-radius: 4px;
}
.mainContainer:hover {
color: ${this.goBright ? '#333' : '#fff'};
border: ${this.goBright ? '1px solid #333' : '1px solid #fff'};
}
.minus {
@ -50,17 +85,13 @@ export class DeesInputQuantitySelector extends LitElement {
}
.selector:hover {
cursor: pointer;
}
.quantity {
text-align: center;
}
.mainContainer:hover {
color: #fff;
border: 1px solid #fff;
}
</style>
<div class="maincontainer">
@ -73,6 +104,7 @@ export class DeesInputQuantitySelector extends LitElement {
public increase () {
this.value++;
this.changeSubject.next(this);
}
public decrease () {
@ -81,5 +113,6 @@ export class DeesInputQuantitySelector extends LitElement {
} else {
// nothing to do here
}
this.changeSubject.next(this);
}
}

View File

@ -1,10 +1,23 @@
import {customElement, LitElement, TemplateResult, property, html} from 'lit-element';
import {customElement, DeesElement, type TemplateResult, property, html, type CSSResult,} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
declare global {
interface HTMLElementTagNameMap {
'dees-input-radio': DeesInputRadio;
}
}
@customElement('dees-input-radio')
export class DeesInputRadio extends LitElement {
export class DeesInputRadio extends DeesElement {
public static demo = () => html`<dees-input-radio></dees-input-radio>`;
@property()
// INSTANCE
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
@property({
type: String,
reflect: true,
})
public key: string;
@property()
@ -13,6 +26,20 @@ export class DeesInputRadio extends LitElement {
@property()
public value: boolean = false;
@property({
type: Boolean,
})
public required: boolean = false;
@property({
type: Boolean
})
public disabled: boolean = false;
constructor() {
super();
}
public render(): TemplateResult {
return html `
<style>
@ -24,7 +51,6 @@ export class DeesInputRadio extends LitElement {
display: block;
position: relative;
margin: 20px 0px;
cursor: pointer;
}
.maincontainer {
@ -64,8 +90,8 @@ export class DeesInputRadio extends LitElement {
}
.checkbox.selected {
background: #039BE5;
border: 1px solid #039BE5;
background: #0050b9;
border: 1px solid #0050b9;
}
.maincontainer:hover .checkbox.selected {
@ -96,5 +122,6 @@ export class DeesInputRadio extends LitElement {
detail: this.value,
bubbles: true
}));
this.changeSubject.next(this);
}
}

View File

View File

@ -1,73 +1,254 @@
import {customElement, LitElement, TemplateResult, property, html} from 'lit-element';
import * as colors from './00colors.js';
import {
customElement,
DeesElement,
type TemplateResult,
property,
html,
cssManager,
css,
type CSSResult,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
declare global {
interface HTMLElementTagNameMap {
'dees-input-text': DeesInputText;
}
}
@customElement('dees-input-text')
export class DeesInputText extends LitElement {
public static demo = () => html`<dees-input-text></dees-input-text>`;
export class DeesInputText extends DeesElement {
public static demo = () => html`
<dees-input-text .label=${'this is a label'} .value=${'test'}></dees-input-text>
<dees-input-text .isPasswordBool=${true}></dees-input-text>
`;
@property()
public label: string = 'Label';
// INSTANCE
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject<DeesInputText>();
@property()
@property({
type: String,
})
public label: string;
@property({
type: String,
})
public description: string;
@property({
type: String,
reflect: true,
})
public key: string;
@property()
public value: string;
@property({
type: String,
reflect: true,
})
public value: string = '';
@property({
type: Boolean,
})
public required: boolean = false;
@property({
type: Boolean,
})
public disabled: boolean = false;
@property({
type: Boolean,
reflect: true,
})
public isPasswordBool = false;
@property({
type: Boolean,
reflect: true,
})
public showPasswordBool = false;
@property({
type: Boolean,
reflect: true,
})
public validationState: 'valid' | 'warn' | 'invalid';
@property({
reflect: true,
})
public validationText: string = '';
@property({})
validationFunction: (value: string) => boolean;
public static styles = [
cssManager.defaultStyles,
css`
* {
box-sizing: border-box;
}
:host {
position: relative;
display: grid;
margin: 8px 0px;
margin-bottom: 24px;
z-index: auto;
}
.maincontainer {
color: ${cssManager.bdTheme('#333', '#ccc')};
}
input {
margin-top: 0px;
background: ${cssManager.bdTheme('#fafafa', '#222')};
border-top: ${cssManager.bdTheme('1px solid #CCC', '1px solid #ffffff10')};
border-bottom: ${cssManager.bdTheme('1px solid #CCC', '1px solid #222')};
border-right: ${cssManager.bdTheme('1px solid #CCC', 'none')};
border-left: ${cssManager.bdTheme('1px solid #CCC', 'none')};
padding-left: 10px;
padding-right: 10px;
border-radius: 2px;
width: 100%;
line-height: 36px;
transition: all 0.2s;
outline: none;
font-size: 16px;
position: relative;
z-index: 2;
cursor: default;
}
input:disabled {
background: ${cssManager.bdTheme('#ffffff00', '#11111100')};
border: 1px dashed ${cssManager.bdTheme('#666666', '#666666')};
color: #9b9b9e;
cursor: default;
}
input:focus {
outline: none;
border-bottom: 1px solid ${cssManager.bdTheme( colors.bright.blueActive, colors.dark.blueActive)};
cursor: text;
}
input:hover {
filter: ${cssManager.bdTheme('brightness(0.95)', 'brightness(1.1)')};
}
.showPassword {
position: absolute;
bottom: 7px;
right: 10px;
border: 1px dashed #444;
border-radius: 7px;
padding: 4px 0px;
width: 40px;
z-index: 3;
}
.showPassword:hover {
background: ${cssManager.bdTheme('#00000010', '#ffffff10')};
}
.validationContainer {
text-align: center;
padding: 6px 2px 2px 2px;
margin-top: -4px;
font-size: 12px;
color: #fff;
background: #e4002b;
position: relative;
z-index: 1;
border-radius: 3px;
transition: all 0.2s;
}
`,
];
public render(): TemplateResult {
return html `
return html`
<style>
* {
box-sizing: border-box;
}
:host {
position: relative;
display: grid;
margin: 10px 0px;
margin-bottom: 24px;
}
.maincontainer {
color: #ccc;
}
.label {
font-size: 14px;
margin-bottom: 5px;
}
input {
margin-top: 5px;
background: #222;
border: none;
border-top: 1px solid #444;
border-bottom: 1px solid #333;
padding-left:10px;
padding-right: 10px;
border-radius: 2px;
width: 100%;
line-height: 48px;
transition: all 0.2s;
outline: none;
color: #ccc;
font-size: 16px;
}
input:focus {
outline: none;
border-bottom: 1px solid #e4002b;
font-family: ${this.isPasswordBool ? 'monospace' : 'Geist Sans'};
letter-spacing: ${this.isPasswordBool ? '1px' : 'normal'};
color: ${this.goBright ? '#333' : '#ccc'};
}
${this.validationText ? css`
.validationContainer {
height: 22px;
opacity: 1;
}
` : css`
.validationContainer {
height: 4px;
padding: 2px !important;
opacity: 0;
}
`}
</style>
<div class="maincontainer">
<div class="label">${this.label}</div>
<input type="text" @input="${this.updateValue}" />
<dees-label .label=${this.label} .description=${this.description}></dees-label>
<input
type="${this.isPasswordBool && !this.showPasswordBool ? 'password' : 'text'}"
.value=${this.value}
@input="${this.updateValue}"
.disabled=${this.disabled}
/>
<div class="validationContainer">
${this.validationText}
</div>
${this.isPasswordBool
? html`
<div class="showPassword" @click=${this.togglePasswordView}>
<dees-icon .iconFA=${this.showPasswordBool ? 'eye' : 'eyeSlash'}></dees-icon>
</div>
`
: html``}
</div>
`;
}
firstUpdated() {
const input = this.shadowRoot.querySelector('input');
input.addEventListener('input', (eventArg: InputEvent) => {
});
}
public async updateValue(eventArg: Event) {
const target: any = eventArg.target;
this.value = target.value;
this.changeSubject.next(this);
}
public async freeze() {
this.disabled = true;
}
public async unfreeze() {
this.disabled = false;
}
public async togglePasswordView() {
const domtools = await this.domtoolsPromise;
this.showPasswordBool = !this.showPasswordBool;
console.log(`this.showPasswordBool is: ${this.showPasswordBool}`);
}
public async focus() {
const textInput = this.shadowRoot.querySelector('input');
textInput.focus();
}
public async blur() {
const textInput = this.shadowRoot.querySelector('input');
textInput.blur();
}
}

View File

@ -0,0 +1,15 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html`
<style>
.demoContainer {
max-width: 600px;
margin: auto;
padding: 40px;
background: #000;
}
</style>
<div class="demoContainer">
<dees-input-typelist></dees-input-typelist>
</div>
`;

View File

@ -0,0 +1,98 @@
import {
customElement,
DeesElement,
type TemplateResult,
state,
html,
domtools,
property,
css,
cssManager,
} from '@design.estate/dees-element';
const { demoFunc } = await import('./dees-input-typelist.demo.js');
@customElement('dees-input-typelist')
export class DeesInputTypelist extends DeesElement {
public static demo = demoFunc;
// INSTANCE
@property({
type: String,
})
public label: string;
constructor() {
super();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
color: ${cssManager.bdTheme('#333', '#fff')};
margin: 8px 0px 24px 0px;
}
.label {
font-size: 14px;
margin-bottom: 8px;
}
.mainbox {
border-radius: 3px;
background: #222;
overflow: hidden;
border-top: ${cssManager.bdTheme('1px solid #CCC', '1px solid #444')};
border-bottom: ${cssManager.bdTheme('1px solid #CCC', '1px solid #333')};
border-right: ${cssManager.bdTheme('1px solid #CCC', 'none')};
border-left: ${cssManager.bdTheme('1px solid #CCC', 'none')};
}
.tags {
padding: 16px;
cursor: default;
}
.notags {
text-align: center;
opacity: 0.5;
font-size: 12px;
}
input {
display: block;
box-sizing: border-box;
background: #181818;
width: 100%;
outline: none;
border: none;
color: inherit;
padding: 0px 16px;
overflow: hidden;
line-height: 32px;
height: 0px;
transition: height 0.2s;
}
input:focus {
height: 32px;
}
`,
];
public render(): TemplateResult {
return html`
<div class="label">${this.label}</div>
<div class="mainbox">
<div class="tags" @click=${() => {
this.shadowRoot.querySelector('input').focus();
}}>
<div class="notags">No tags yet</div>
</div>
<input type="text" placeholder="Type, press Enter to add it..." />
</div>
`;
}
}

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

@ -0,0 +1,184 @@
import * as plugins from './00plugins.js';
import {
cssManager,
css,
type CSSResult,
customElement,
DeesElement,
domtools,
html,
property,
} from '@design.estate/dees-element';
import { DeesWindowLayer } from './dees-windowlayer.js';
@customElement('dees-mobilenavigation')
export class DeesMobilenavigation extends DeesElement {
// STATIC
public static demo = () => html`
<dees-button @click=${() => {
DeesMobilenavigation.createAndShow([
{
name: 'Test',
action: async (deesMobileNav) => {
alert('test');
return null;
},
},
]);
}}></dees-button>
`;
private static singletonRef: DeesMobilenavigation;
public static async createAndShow(menuItemsArg: plugins.tsclass.website.IMenuItem<DeesMobilenavigation>[]) {
if (!this.singletonRef) {
this.singletonRef = new DeesMobilenavigation();
document.body.append(this.singletonRef);
await this.singletonRef.init();
}
this.singletonRef.menuItems = menuItemsArg;
await this.singletonRef.readyDeferred.promise;
this.singletonRef.show();
return this.singletonRef;
}
// INSTANCE
@property({
type: Array,
})
public heading: string = `MENU`;
@property({
type: Array,
})
public menuItems: plugins.tsclass.website.IMenuItem[] = [];
readyDeferred: plugins.smartpromise.Deferred<any> = domtools.plugins.smartpromise.defer();
constructor() {
super();
/* this.init().then(() => {
this.show();
}); */
}
/**
* inits the mobile navigation
*/
public async init() {
await this.updateComplete;
this.readyDeferred.resolve();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
}
.main {
transition: all 0.3s cubic-bezier(0.22, 1, 0.36, 1);
will-change: transform;
position: fixed;
height: 100vh;
min-width: 280px;
transform: translateX(200px);
color: ${cssManager.bdTheme('#333', '#fff')};
z-index: 250;
opacity: 0;
padding: 16px 32px;
right: 0px;
top: 0px;
bottom: 0px;
background: ${cssManager.bdTheme('#eeeeeb', '#000')};
border-left: 1px solid ${cssManager.bdTheme('#eeeeeb', '#222')};
pointer-events: none;
}
.main.show {
pointer-events: all;
transform: translateX(0px);
opacity: 1;
}
.menuItem {
text-align: left;
padding: 8px;
margin-left: -8px;
margin-right: -8px;
border-radius: 3px;
}
.menuItem:hover {
background: ${cssManager.bdTheme('#CCC', '#333')};;
}
.heading {
text-align: left;
font-size: 24px;
padding: 8px 0px;
font-family: 'Geist Sans', sans-serif;
font-weight: 300;
border-bottom: 1px dashed #444;
margin-top: 16px;
margin-bottom: 16px;
}
`,
];
public render() {
return html`
<div class="main">
<div class="heading">${this.heading}</div>
${this.menuItems.map((menuItem) => {
return html`
<div
class="menuItem"
@click="${() => {
this.hide();
menuItem.action(this);
}}"
>
${menuItem.name}
</div>
`;
})}
</div>
`;
}
private windowLayer: DeesWindowLayer;
/**
* inits the show
*/
public async show() {
const domtools = await this.domtoolsPromise;
const main = this.shadowRoot.querySelector('.main');
if (!this.windowLayer) {
this.windowLayer = new DeesWindowLayer();
this.windowLayer.options.blur = true;
this.windowLayer.addEventListener('click', () => {
this.hide();
});
}
document.body.append(this.windowLayer);
await domtools.convenience.smartdelay.delayFor(0);
this.windowLayer.show();
await domtools.convenience.smartdelay.delayFor(0);
main.classList.add('show');
}
/**
* inits the hide function
*/
public async hide() {
const domtools = await this.domtoolsPromise;
const main = this.shadowRoot.querySelector('.main');
main.classList.remove('show');
this.windowLayer.hide();
}
async disconnectedCallback() {
document.body.removeChild(this.windowLayer);
}
}

View File

@ -0,0 +1,37 @@
import { html } from '@design.estate/dees-element';
import { DeesModal } from './dees-modal.js';
export const demoFunc = () => html`
<dees-button @click=${() => {
DeesModal.createAndShow({
heading: 'This is a heading',
content: html`
<dees-form>
<dees-input-text
.label=${'Username'}
>
</dees-input-text>
<dees-input-text
.label=${'Password'}
>
</dees-input-text>
</dees-form>
`,
menuOptions: [{
name: 'Cancel',
iconName: null,
action: async (deesModalArg) => {
deesModalArg.destroy();
return null;
}
}, {
name: 'Ok',
iconName: null,
action: async (deesModalArg) => {
deesModalArg.destroy();
return null;
}
}],
});
}}>open modal</dees-button>
`

View File

@ -0,0 +1,214 @@
import * as colors from './00colors.js';
import * as plugins from './00plugins.js';
import { demoFunc } from './dees-modal.demo.js';
import {
customElement,
html,
DeesElement,
property,
type TemplateResult,
cssManager,
css,
type CSSResult,
unsafeCSS,
unsafeHTML,
state,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { DeesWindowLayer } from './dees-windowlayer.js';
declare global {
interface HTMLElementTagNameMap {
'dees-modal': DeesModal;
}
}
@customElement('dees-modal')
export class DeesModal extends DeesElement {
// STATIC
public static demo = demoFunc;
public static async createAndShow(optionsArg: {
heading: string;
content: TemplateResult;
menuOptions: plugins.tsclass.website.IMenuItem<DeesModal>[];
}) {
const body = document.body;
const modal = new DeesModal();
modal.heading = optionsArg.heading;
modal.content = optionsArg.content;
modal.menuOptions = optionsArg.menuOptions;
modal.windowLayer = await DeesWindowLayer.createAndShow({
blur: true,
});
modal.windowLayer.addEventListener('click', async () => {
await modal.destroy();
});
body.append(modal.windowLayer);
body.append(modal);
}
// INSTANCE
@property({
type: String,
})
public heading = '';
@state({})
public content: TemplateResult;
@state({})
public menuOptions: plugins.tsclass.website.IMenuItem<DeesModal>[] = [];
constructor() {
super();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
font-family: 'Geist Sans', sans-serif;
color: ${cssManager.bdTheme('#333', '#fff')};
will-change: transform;
}
.modalContainer {
display: flex;
position: fixed;
top: 0px;
left: 0px;
width: 100vw;
height: 100vh;
box-sizing: border-box;
align-items: center;
justify-content: center;
z-index: 2000;
}
.modal {
will-change: transform;
transform: translateY(0px) scale(0.95);
opacity: 0;
width: 480px;
min-height: 120px;
background: #111;
border-radius: 8px;
border: 1px solid #222;
transition: all 0.2s;
overflow: hidden;
box-shadow: 0px 2px 5px #00000080;
}
.modal.show {
opacity: 1;
transform: translateY(0px) scale(1);
}
.modal.show.predestroy {
opacity: 0;
transform: translateY(10px) scale(1);
}
.modal .heading {
height: 32px;
font-family: 'Geist Sans', sans-serif;
line-height: 32px;
text-align: center;
font-weight: 600;
font-size: 12px;
border-bottom: 1px solid #222;
}
.modal .content {
padding: 16px;
}
.modal .bottomButtons {
display: flex;
flex-direction: row;
border-top: 1px solid #222;
justify-content: flex-end;
}
.modal .bottomButtons .bottomButton {
margin: 8px 0px;
padding: 8px 12px;
border-radius: 4px;
line-height: 16px;
text-align: center;
font-size: 14px;
cursor: default;
user-select: none;
}
.modal .bottomButtons .bottomButton:first-child {
margin-left: 8px;
}
.modal .bottomButtons .bottomButton:last-child {
margin-right: 8px;
}
.modal .bottomButtons .bottomButton:hover {
background: ${cssManager.bdTheme(colors.bright.blue, colors.dark.blue)};
}
.modal .bottomButtons .bottomButton:active {
background: ${cssManager.bdTheme(colors.bright.blueActive, colors.dark.blueActive)};
}
.modal .bottomButtons .bottomButton:last-child {
border-right: none;
}
`,
];
public render(): TemplateResult {
return html`
<style>
.modal .bottomButtons {
grid-template-columns: ${cssManager.cssGridColumns(this.menuOptions.length, 0)};
}
</style>
<div class="modalContainer" @click=${this.handleOutsideClick}>
<div class="modal">
<div class="heading">${this.heading}</div>
<div class="content">${this.content}</div>
<div class="bottomButtons">
${this.menuOptions.map(
(actionArg) => html`
<div class="bottomButton" @click=${() => {
actionArg.action(this);
}}>${actionArg.name}</div>
`
)}
</div>
</div>
</div>
`;
}
private windowLayer: DeesWindowLayer;
public async firstUpdated(_changedProperties: Map<string | number | symbol, unknown>) {
super.firstUpdated(_changedProperties);
const domtools = await this.domtoolsPromise;
await domtools.convenience.smartdelay.delayFor(30);
const modal = this.shadowRoot.querySelector('.modal');
modal.classList.add('show');
}
public async handleOutsideClick(eventArg: MouseEvent) {
eventArg.stopPropagation();
const modalContainer = this.shadowRoot.querySelector('.modalContainer');
if (eventArg.target === modalContainer) {
await this.destroy();
}
}
public async destroy() {
const domtools = await this.domtoolsPromise;
const modal = this.shadowRoot.querySelector('.modal');
modal.classList.add('predestroy');
await domtools.convenience.smartdelay.delayFor(200);
document.body.removeChild(this);
await this.windowLayer.destroy();
}
}

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,
})
);
}
}

110
ts_web/elements/dees-pdf.ts Normal file
View File

@ -0,0 +1,110 @@
import { DeesElement, property, html, customElement, domtools, type TemplateResult, type CSSResult, } from '@design.estate/dees-element';
import { Deferred } from '@push.rocks/smartpromise';
// import type pdfjsTypes from 'pdfjs-dist';
declare global {
interface HTMLElementTagNameMap {
'dees-pdf': DeesPdf;
}
}
@customElement('dees-pdf')
export class DeesPdf extends DeesElement {
// DEMO
public static demo = () => html` <dees-pdf></dees-pdf> `;
// INSTANCE
@property()
public pdfUrl: string =
'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf';
constructor() {
super();
// you have access to all kinds of things through this.
// this.setAttribute('gotIt','true');
}
public render(): TemplateResult {
return html`
<style>
:host {
font-family: 'Geist Sans', sans-serif;
display: block;
box-sizing: border-box;
max-width: 800px;
}
:host([hidden]) {
display: none;
}
#pdfcanvas {
box-shadow: 0px 0px 5px #ccc;
width: 100%;
}
</style>
<canvas id="pdfcanvas" .height=${0} .width=${0}></canvas>
`;
}
public static pdfJsReady: Promise<any>;
public static pdfjsLib: any // typeof pdfjsTypes;
public async connectedCallback() {
super.connectedCallback();
if (!DeesPdf.pdfJsReady) {
const pdfJsReadyDeferred = domtools.plugins.smartpromise.defer();
DeesPdf.pdfJsReady = pdfJsReadyDeferred.promise;
// @ts-ignore
DeesPdf.pdfjsLib = await import('https://cdn.jsdelivr.net/npm/pdfjs-dist@4.0.379/+esm');
DeesPdf.pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@4.0.379/build/pdf.worker.mjs';
pdfJsReadyDeferred.resolve();
}
await DeesPdf.pdfJsReady;
this.displayContent();
}
public async displayContent() {
await DeesPdf.pdfJsReady;
// Asynchronous download of PDF
const loadingTask = DeesPdf.pdfjsLib.getDocument(this.pdfUrl);
loadingTask.promise.then(
(pdf) => {
console.log('PDF loaded');
// Fetch the first page
const pageNumber = 1;
pdf.getPage(pageNumber).then((page) => {
console.log('Page loaded');
const scale = 10;
const viewport = page.getViewport({ scale: scale });
// Prepare canvas using PDF page dimensions
const canvas: any = this.shadowRoot.querySelector('#pdfcanvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
const renderContext = {
canvasContext: context,
viewport: viewport,
};
const renderTask = page.render(renderContext);
renderTask.promise.then(function () {
console.log('Page rendered');
});
});
},
(reason) => {
// PDF loading error
console.error(reason);
}
);
}
}

View File

View File

@ -0,0 +1,11 @@
import { html } from '@design.estate/dees-element';
import { DeesProgressbar } from './dees-progressbar.js';
export const demoFunc = () => {
return html`
<dees-progressbar
.percentage=${50}
></dees-progressbar>
`;
}

View File

@ -0,0 +1,95 @@
import * as plugins from './00plugins.js';
import * as colors from './00colors.js';
import { demoFunc } from './dees-progressbar.demo.js';
import {
customElement,
html,
DeesElement,
property,
type TemplateResult,
cssManager,
css,
type CSSResult,
unsafeCSS,
unsafeHTML,
state,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
@customElement('dees-progressbar')
export class DeesProgressbar extends DeesElement {
// STATIC
public static demo = demoFunc;
// INSTANCE
@property({
type: Number,
})
public percentage = 0;
public static styles = [
cssManager.defaultStyles,
css`
:host {
color: ${cssManager.bdTheme(colors.bright.text, colors.dark.text)};
}
.progressBarContainer {
padding: 8px;
min-width: 200px;
}
.progressBar {
background: ${cssManager.bdTheme('#eeeeeb', '#444')};
height: 8px;
width: 100%;
border-radius: 4px;
border-top: 0.5px solid ${cssManager.bdTheme('none', '#555')};
}
.progressBarFill {
background: ${cssManager.bdTheme(colors.dark.blueActive, colors.bright.blueActive)};
height: 8px;
margin-top: -0.5px;
transition: 0.2s width;
border-radius: 4px;
width: 0px;
border-top: 0.5 solid ${cssManager.bdTheme('none', '#398fff')};
}
.progressText {
padding: 8px;
text-align: center;
}
`
];
public render() {
return html`
<div class="progressBarContainer">
<div class="progressBar">
<div class="progressBarFill"></div>
<div class="progressText">
${this.percentage}%
<div>
</div>
</div>
`
}
firstUpdated (_changedProperties: Map<string | number | symbol, unknown>): void {
super.firstUpdated(_changedProperties);
this.updateComplete.then(() => {
this.updatePercentage();
});
}
public async updatePercentage() {
const progressBarFill = this.shadowRoot.querySelector('.progressBarFill') as HTMLElement;
progressBarFill.style.width = `${this.percentage}%`;
}
updated(){
this.updatePercentage();
}
}

View File

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

@ -0,0 +1,21 @@
import { html } from '@design.estate/dees-element';
import type { IView } from './dees-simple-appdash.js';
export const demoFunc = () => html`
<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

@ -0,0 +1,234 @@
import { demoFunc } from './dees-simple-appdash.demo.js';
import * as colors from './00colors.js';
import {
customElement,
html,
DeesElement,
property,
type TemplateResult,
cssManager,
css,
unsafeCSS,
type CSSResult,
state,
domtools,
} from '@design.estate/dees-element';
import { DeesTerminal } from './dees-terminal.js';
declare global {
interface HTMLElementTagNameMap {
'dees-simple-appdash': DeesSimpleAppDash;
}
}
export interface IView {
name: string;
element: DeesElement['constructor']['prototype'];
}
@customElement('dees-simple-appdash')
export class DeesSimpleAppDash extends DeesElement {
// STATIC
public static demo = demoFunc;
// INSTANCE
@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', '#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: 0px;
left: 0px;
height: calc(100% - 24px);
width: 200px;
background: ${cssManager.bdTheme('#eeeeeb', '#000')};
border-right: 1px solid ${cssManager.bdTheme('#ccc', '#ffffff20')};
font-size: 14px;
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: 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="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>
`;
}
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

@ -0,0 +1,3 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html` <dees-simple-login name="someapp"> Hello there </dees-simple-login> `;

View File

@ -0,0 +1,130 @@
import { demoFunc } from './dees-simple-login.demo.js';
import {
customElement,
html,
DeesElement,
property,
type TemplateResult,
cssManager,
css,
unsafeCSS,
type CSSResult,
state,
} from '@design.estate/dees-element';
declare global {
interface HTMLElementTagNameMap {
'dees-simple-login': DeesSimpleLogin;
}
}
@customElement('dees-simple-login')
export class DeesSimpleLogin extends DeesElement {
// STATIC
public static demo = demoFunc
// INSTANCE
@property()
public name = 'Dees Simple Login';
public static styles = [
cssManager.defaultStyles,
css`
:host {
color: ${cssManager.bdTheme('#333', '#fff')};
user-select: none;
}
.loginContainer {
position: absolute;
display: flex;
justify-content: center; /* aligns horizontally */
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;
background: ${cssManager.bdTheme('#eeeeeb', '#111')};
box-shadow: ${cssManager.bdTheme('0px 1px 4px rgba(0,0,0,0.3)', 'none')};
border-radius: 8px;
padding: 24px;
transition: opacity 0.3s, transform 0.3s;
}
.header {
text-align: center;
}
.slotContainer {
opacity:0;
transition: opacity 0.3s, transform 0.3s;
pointer-events: none;
}
`,
];
public render(): TemplateResult {
return html`
<div class="loginContainer">
<div class="login">
<dees-form>
<div class="header">Login to ${this.name}</div>
<dees-input-text key="username" label="username" required></dees-input-text>
<dees-input-text key="password" label="password" isPasswordBool required></dees-input-text>
<dees-form-submit disabled>login</dees-form-submit>
</dees-form>
</div>
</div>
<div class="slotContainer">
<slot></slot>
</div>
`;
}
public async firstUpdated(_changedProperties): Promise<void> {
const domtools = await this.domtoolsPromise;
super.firstUpdated(_changedProperties);
const form = this.shadowRoot.querySelector('dees-form');
await form.readyDeferred.promise;
const username = this.shadowRoot.querySelector('dees-input-text[label="username"]');
const password = this.shadowRoot.querySelector('dees-input-text[label="password"]');
const submit = this.shadowRoot.querySelector('dees-form-submit');
form.addEventListener('formData', (event: CustomEvent) => {
this.dispatchEvent(new CustomEvent('login', { detail: event.detail }));
});
}
/**
* allows switching to slotted content
*/
public async switchToSlottedContent() {
const domtools = await this.domtoolsPromise;
const loginDiv: HTMLDivElement = this.shadowRoot.querySelector('.login');
const loginContainerDiv: HTMLDivElement = this.shadowRoot.querySelector('.loginContainer');
const slotContainerDiv: HTMLDivElement = this.shadowRoot.querySelector('.slotContainer');
loginDiv.style.opacity = '0';
loginDiv.style.transform = 'translateY(20px)';
loginContainerDiv.style.pointerEvents = 'none';
slotContainerDiv.style.transform = 'translateY(20px)';
await domtools.convenience.smartdelay.delayFor(300);
slotContainerDiv.style.opacity = '1';
slotContainerDiv.style.transform = 'translateY(0px)';
await domtools.convenience.smartdelay.delayFor(300);
slotContainerDiv.style.pointerEvents = 'all';
}
}

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

@ -0,0 +1,229 @@
import * as colors from './00colors.js';
import * as plugins from './00plugins.js';
import { demoFunc } from './dees-speechbubble.demo.js';
import {
customElement,
html,
DeesElement,
property,
type TemplateResult,
cssManager,
css,
type CSSResult,
unsafeCSS,
domtools,
directives,
unsafeHTML,
} from '@design.estate/dees-element';
import { DeesWindowLayer } from './dees-windowlayer.js';
declare global {
interface HTMLElementTagNameMap {
'dees-speechbubble': DeesSpeechbubble;
}
}
@customElement('dees-speechbubble')
export class DeesSpeechbubble extends DeesElement {
public static demo = demoFunc;
// 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 wave: boolean = false;
@property({
type: Boolean,
})
public manifested = false;
@property({
type: String,
})
public status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
public windowLayer: DeesWindowLayer;
constructor() {
super();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
box-sizing: border-box;
color: ${cssManager.bdTheme('#333', '#fff')};
user-select: none;
}
.maincontainer {
position: relative;
will-change: transform;
transition: transform 0.2s;
transform: translateX(0px);
transition: all 0.2s;
margin-left: 0px;
filter: drop-shadow(0px 0px 2px rgba(0, 0, 0, 0.2));
pointer-events: none;
opacity: 0;
transition: all 0.2s;
}
.arrow {
position: absolute;
transform: rotate(45deg);
background: ${cssManager.bdTheme('#fff', '#333')};
height: 15px;
width: 15px;
left: 2px;
top: 12px;
border-radius: 3px;
}
.speechbubble {
background: ${cssManager.bdTheme('#fff', '#333')};
padding: 0px 16px;
border-radius: 3px;
position: absolute;
min-width: 240px;
font-size: 12px;
top: 0px;
left: 8px;
}
.wave {
animation-name: wave-animation; /* Refers to the name of your @keyframes element below */
animation-duration: 2.5s; /* Change to speed up or slow down */
animation-iteration-count: infinite; /* Never stop waving :) */
transform-origin: 70% 70%; /* Pivot around the bottom-left palm */
display: inline-block;
}
@keyframes wave-animation {
0% {
transform: rotate(0deg);
}
10% {
transform: rotate(14deg);
} /* The following five values can be played with to make the waving more or less extreme */
20% {
transform: rotate(-8deg);
}
30% {
transform: rotate(14deg);
}
40% {
transform: rotate(-4deg);
}
50% {
transform: rotate(10deg);
}
60% {
transform: rotate(0deg);
} /* Reset for the last half to pause */
100% {
transform: rotate(0deg);
}
}
`,
];
public render(): TemplateResult {
return html`
${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() {
console.log('speechbubble got clicked.');
}
public async firstUpdated() {
// 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,131 @@
import {
customElement,
html,
DeesElement,
property,
type TemplateResult,
cssManager,
css,
type CSSResult,
unsafeCSS,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
declare global {
interface HTMLElementTagNameMap {
'dees-spinner': DeesSpinner;
}
}
@customElement('dees-spinner')
export class DeesSpinner extends DeesElement {
public static demo = () => html`
<dees-spinner></dees-spinner>
<dees-spinner status="success"></dees-spinner>
<dees-spinner status="error"></dees-spinner>
<dees-spinner size=${64} status="success"></dees-spinner>
<dees-spinner .size=${64} status="error"></dees-spinner>
`;
@property({
type: Number,
})
public size = 20;
@property({
type: String,
})
public bnw: boolean = false;
@property()
public status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
constructor() {
super();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
}
#loading {
position: relative;
transition: none;
display: flex;
justify-content: center;
align-content: center;
background: #8bc34a00;
border: 3px solid ${cssManager.bdTheme('rgba(0, 0, 0, 0.1)', 'rgba(255, 255, 255, 0.3)')};
border-radius: 50%;
border-top-color: ${cssManager.bdTheme('#333', '#fff')};
animation: spin 1s ease-in-out infinite;
-webkit-animation: spin 1s ease-in-out infinite;
}
#loading.success {
border: none;
border-radius: 50%;
animation: none;
-webkit-animation: none;
}
#loading.error {
border: none;
border-radius: 50%;
animation: none;
-webkit-animation: none;
}
@keyframes spin {
to {
-webkit-transform: rotate(360deg);
}
}
@-webkit-keyframes spin {
to {
-webkit-transform: rotate(360deg);
}
}
dees-icon {
position: absolute;
height: 100%;
width: 100%;
}
`,
];
render() {
return html`
<style>
#loading {
width: ${this.size}px;
height: ${this.size}px;
}
#loading.success {
color: ${cssManager.bdTheme(this.bnw ? '#333': `#8bc34a`, this.bnw ? '#fff' : `#8bc34a`)};
}
#loading.error {
color: ${cssManager.bdTheme(this.bnw ? '#333': `#e64a19`, this.bnw ? '#fff' : `#e64a19`)};
}
dees-icon {
font-size: ${this.size}px;
}
</style>
<div class="${this.status}" id="loading">
${(() => {
if (this.status === 'success') {
return html`<dees-icon style="transform: translateX(1%) translateY(3%);" .iconFA=${'circleCheck' as any}></dees-icon>`;
} else if (this.status === 'error') {
return html`<dees-icon .iconFA=${'circleXmark' as any}></dees-icon>`;
}
})()}
</div>
`;
}
}

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

@ -0,0 +1,283 @@
import * as plugins from './00plugins.js';
import * as colors from './00colors.js';
import {
DeesElement,
customElement,
html,
css,
unsafeCSS,
type CSSResult,
cssManager,
property,
type TemplateResult,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
export interface IStep {
title: string;
content: TemplateResult;
validationFunc?: (stepper: DeesStepper, htmlElement: HTMLElement) => Promise<any>;
onReturnToStepFunc?: (stepper: DeesStepper, htmlElement: HTMLElement) => Promise<any>;
validationFuncCalled?: boolean;
}
declare global {
interface HTMLElementTagNameMap {
'dees-stepper': DeesStepper;
}
}
@customElement('dees-stepper')
export class DeesStepper extends DeesElement {
public static demo = () =>
html`
<dees-stepper
.steps=${[
{
title: 'Whats your name?',
content: html`
<dees-form>
<dees-input-text
key="email"
label="Your Email"
value="hello@something.com"
disabled
></dees-input-text>
<dees-input-text key="firstName" required label="Vorname"></dees-input-text>
<dees-input-text key="lastName" required label="Nachname"></dees-input-text>
<dees-form-submit>Next</dees-form-submit>
</dees-form>
`,
validationFunc: async (stepperArg, elementArg) => {
const deesForm = elementArg.querySelector('dees-form');
deesForm.addEventListener('formData', (eventArg) => {
stepperArg.goNext();
});
},
},
{
title: 'Whats your mobile number?',
content: html``,
},
] as IStep[]}
></dees-stepper>
`;
@property({
type: Array,
})
public steps: IStep[] = [];
@property({
type: Object,
})
public selectedStep: IStep;
constructor() {
super();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
position: absolute;
width: 100%;
height: 100%;
}
.stepperContainer {
position: absolute;
width: 100%;
height: 100%;
background: ${cssManager.bdTheme('#eeeeeb', '#000')};
overflow: hidden;
}
.step {
position: relative;
pointer-events: none;
overflow: hidden;
transition: all 0.7s ease-in-out;
max-width: 500px;
min-height: 300px;
border-radius: 16px;
background: ${cssManager.bdTheme('#ffffff', '#181818')};
border-top: 1px solid ${cssManager.bdTheme('#ffffff', '#181818')};
color: ${cssManager.bdTheme('#333', '#fff')};
margin: auto;
margin-bottom: 20px;
filter: opacity(0.5) grayscale(1);
box-shadow: 0px 0px 3px #00000010;
user-select: none;
}
.step.selected {
border-top: 1px solid #e4002b;
pointer-events: all;
filter: opacity(1) grayscale(0);
box-shadow: 0px 0px 5px #00000010;
user-select: auto;
}
.step.hiddenStep {
filter: opacity(0);
}
.step:last-child {
margin-bottom: 100vh;
}
.step .stepCounter {
color: #999;
position: absolute;
top: 0px;
right: 0px;
padding: 10px 15px;
font-size: 12px;
border-bottom-left-radius: 3px;
background: ${cssManager.bdTheme('#00000008', '#ffffff08')};
}
.step .goBack {
color: #999;
cursor: default;
position: absolute;
top: 0px;
left: 0px;
padding: 10px 15px;
font-size: 12px;
border-bottom-right-radius: 3px;
background: ${cssManager.bdTheme('#00000008', '#ffffff08')};
}
.step .goBack:hover {
color: ${cssManager.bdTheme('#333', '#fff')};
background: ${cssManager.bdTheme('#00000012', colors.dark.blue)};
}
.step .goBack:active {
color: ${cssManager.bdTheme('#333', '#fff')};
background: ${cssManager.bdTheme('#00000012', colors.dark.blueActive)};
}
.step .goBack span {
transition: all 0.2s;
display: inline-block;
}
.step .goBack:hover span {
transform: translateX(-2px);
}
.step .title {
text-align: center;
padding-top: 50px;
font-family: 'Geist Sans', sans-serif;
font-size: 22px;
font-weight: 500;
}
.step .content {
padding: 20px;
}
`,
];
public render() {
return html`
<div class="stepperContainer">
${this.steps.map(
(stepArg) =>
html`<div
class="step ${stepArg === this.selectedStep
? 'selected'
: null} ${this.getIndexOfStep(stepArg) > this.getIndexOfStep(this.selectedStep)
? 'hiddenStep'
: ''}"
>
${this.getIndexOfStep(stepArg) > 0
? html`<div class="goBack" @click=${this.goBack}><span style="font-family: Inter"><-</span> go to previous step</div>`
: ``}
<div class="stepCounter">
Step ${this.steps.findIndex((elementArg) => elementArg === stepArg) + 1} of
${this.steps.length}
</div>
<div class="title">${stepArg.title}</div>
<div class="content">${stepArg.content}</div>
</div> `
)}
</div>
`;
}
public getIndexOfStep = (stepArg: IStep): number => {
return this.steps.findIndex((stepArg2) => stepArg === stepArg2);
};
public async firstUpdated() {
await this.domtoolsPromise;
await this.domtools.convenience.smartdelay.delayFor(0);
this.selectedStep = this.steps[0];
this.setScrollStatus();
}
public async updated() {
this.setScrollStatus();
}
public scroller: typeof domtools.plugins.SweetScroll.prototype;
public async setScrollStatus() {
const stepperContainer: HTMLElement = this.shadowRoot.querySelector('.stepperContainer');
const firstStepElement: HTMLElement = this.shadowRoot.querySelector('.step');
const selectedStepElement: HTMLElement = this.shadowRoot.querySelector('.selected');
if (!selectedStepElement) {
return;
}
if (!stepperContainer.style.paddingTop) {
stepperContainer.style.paddingTop = `${
stepperContainer.offsetHeight / 2 - selectedStepElement.offsetHeight / 2
}px`;
}
console.log('Setting scroll status');
console.log(selectedStepElement);
const scrollPosition =
selectedStepElement.offsetTop -
stepperContainer.offsetHeight / 2 +
selectedStepElement.offsetHeight / 2;
console.log(scrollPosition);
const domtoolsInstance = await domtools.DomTools.setupDomTools();
if (!this.scroller) {
this.scroller = new domtools.plugins.SweetScroll(
{
vertical: true,
horizontal: false,
easing: 'easeInOutExpo',
duration: 700,
},
stepperContainer
);
}
if (!this.selectedStep.validationFuncCalled && this.selectedStep.validationFunc) {
this.selectedStep.validationFuncCalled = true;
await this.selectedStep.validationFunc(this, selectedStepElement);
}
this.scroller.to(scrollPosition);
}
public async goBack() {
const currentIndex = this.steps.findIndex((stepArg) => stepArg === this.selectedStep);
this.selectedStep = this.steps[currentIndex - 1];
await this.domtoolsPromise;
await this.domtools.convenience.smartdelay.delayFor(100);
this.selectedStep.onReturnToStepFunc?.(this, this.shadowRoot.querySelector('.selected'));
}
public goNext() {
const currentIndex = this.steps.findIndex((stepArg) => stepArg === this.selectedStep);
this.selectedStep = this.steps[currentIndex + 1];
}
}

View File

@ -0,0 +1,129 @@
import { type ITableAction } from './dees-table.js';
import * as plugins from './00plugins.js';
import { html } from '@design.estate/dees-element';
interface ITableDemoData {
date: string;
amount: string;
description: string;
}
export const demoFunc = () => html`
<style>
.demoWrapper {
box-sizing: border-box;
position: absolute;
width: 100%;
height: 100%;
padding: 20px;
background: #000000;
}
</style>
<div class="demoWrapper">
<dees-table
heading1="Current Account Statement"
heading2="Bunq - Payment Account 2 - April 2021"
.editableFields="${['description']}"
.data=${[
{
date: '2021-04-01',
amount: '2464.65 €',
description: 'Printing Paper (Office Supplies) - STAPLES BREMEN',
},
{
date: '2021-04-02',
amount: '165.65 €',
description: 'Logitech Mouse (Hardware) - logi.com OnlineShop',
},
{
date: '2021-04-03',
amount: '2999,00 €',
description: 'Macbook Pro 16inch (Hardware) - Apple.de OnlineShop',
},
{
date: '2021-04-01',
amount: '2464.65 €',
description: 'Office-Supplies - STAPLES BREMEN',
},
{
date: '2021-04-01',
amount: '2464.65 €',
description: 'Office-Supplies - STAPLES BREMEN',
},
]}
dataName="transactions"
.dataActions="${[
{
name: 'upload',
iconName: 'bell',
useTableBehaviour: 'upload',
type: ['inRow'],
actionFunc: async (optionsArg) => {
alert(optionsArg.item.amount);
},
},
{
name: 'visibility',
iconName: 'copy',
type: ['inRow'],
useTableBehaviour: 'preview',
actionFunc: async (itemArg: any) => {},
},
{
name: 'create new',
iconName: 'instagram',
type: ['header'],
useTableBehaviour: 'preview',
actionFunc: async (itemArg: any) => {},
},
{
name: 'to gallery',
iconName: 'message',
type: ['footer'],
useTableBehaviour: 'preview',
actionFunc: async (itemArg: any) => {},
},
{
name: 'copy',
iconName: 'copySolid',
type: ['contextmenu', 'inRow'],
action: async () => {
return null;
},
},
{
name: 'edit (from demo)',
iconName: 'penToSquare',
type: ['contextmenu'],
action: async () => {
return null;
},
},
{
name: 'paste',
iconName: 'pasteSolid',
type: ['contextmenu'],
action: async () => {
return null;
},
},
{
name: 'preview',
type: ['doubleClick', 'contextmenu'],
iconName: 'eye',
actionFunc: async (itemArg) => {
alert(itemArg.item.amount);
return null;
},
}
] as (ITableAction<ITableDemoData>)[] as any}"
.displayFunction=${(itemArg) => {
return {
...itemArg,
onlyDisplayProp: 'onlyDisplay',
};
}}
>This is a slotted Text</dees-table
>
</div>
`;

View File

@ -0,0 +1,784 @@
import * as colors from './00colors.js';
import * as plugins from './00plugins.js';
import { demoFunc } from './dees-table.demo.js';
import {
customElement,
html,
DeesElement,
property,
type TemplateResult,
cssManager,
css,
unsafeCSS,
type CSSResult,
state,
directives,
} from '@design.estate/dees-element';
import { DeesContextmenu } from './dees-contextmenu.js';
import * as domtools from '@design.estate/dees-domtools';
import { type TIconKey } from './dees-icon.js';
declare global {
interface HTMLElementTagNameMap {
'dees-table': DeesTable<any>;
}
}
// interfaces
export interface ITableAction<T = any> {
name: string;
iconName: TIconKey;
/**
* the table behaviour to use for this action
* e.g. upload: allows to upload files to the table
*/
useTableBehaviour?: 'upload' | 'cancelUpload' | 'none';
/**
* the type of the action
*/
type: (
| 'inRow'
| 'contextmenu'
| 'doubleClick'
| 'footer'
| 'header'
| 'preview'
| 'keyCombination'
)[];
/**
* allows to check if the action is relevant for the given item
* @param itemArg
* @returns
*/
actionRelevancyCheckFunc?: (itemArg: T) => boolean;
/**
* the actual action function implementation
* @param itemArg
* @returns
*/
actionFunc: (actionDataArg: ITableActionDataArg<T>) => Promise<any>;
}
export interface ITableActionDataArg<T> {
item: T;
table: DeesTable<T>;
}
export type TDisplayFunction<T = any> = (itemArg: T) => object;
// the table implementation
@customElement('dees-table')
export class DeesTable<T> extends DeesElement {
public static demo = demoFunc;
// INSTANCE
@property({
type: String,
})
public heading1: string = 'heading 1';
@property({
type: String,
})
public heading2: string = 'heading 2';
@property({
type: Array,
})
public data: T[] = [];
// dees-form compatibility -----------------------------------------
@property({
type: String,
})
public key: string;
@property({
type: String,
})
public label: string;
@property({
type: Boolean,
})
public disabled: boolean = false;
@property({
type: Boolean,
})
public required: boolean = false;
get value() {
return this.data;
}
set value(valueArg) {}
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,
})
public dataActions: ITableAction<T>[] = [];
@property({
attribute: false,
})
public displayFunction: TDisplayFunction = (itemArg: T) => itemArg as any;
@property({
attribute: false,
})
public reverseDisplayFunction: (itemArg: any) => T = (itemArg: any) => itemArg as T;
@property({
type: Object,
})
public selectedDataRow: T;
@property({
type: Array,
})
public editableFields: string[] = [];
public files: File[] = [];
public fileWeakMap = new WeakMap();
public dataChangeSubject = new domtools.plugins.smartrx.rxjs.Subject();
constructor() {
super();
}
public static styles = [
cssManager.defaultStyles,
css`
.mainbox {
color: ${cssManager.bdTheme('#333', '#fff')};
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', '#222222')};
border-radius: 3px;
border-top: 1px solid ${cssManager.bdTheme('#fff', '#ffffff10')};
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3);
overflow-x: auto;
cursor: default;
}
.header {
display: flex;
justify-content: flex-end;
align-items: center;
font-family: 'Geist Sans', sans-serif;
}
.headingContainer {
}
.heading {
}
.heading1 {
font-weight: 600;
}
.heading2 {
opacity: 0.6;
}
.headingSeparation {
margin-top: 7px;
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 {
color: ${cssManager.bdTheme('#555', '#fff')};
}
.headerAction dees-icon {
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;
color: ${cssManager.bdTheme('#333', '#fff')};
border-collapse: collapse;
width: 100%;
}
.noDataSet {
text-align: center;
}
tr {
border-bottom: 1px dashed ${cssManager.bdTheme('#999', '#808080')};
text-align: left;
}
tr:last-child {
border-bottom: none;
text-align: left;
}
tr:hover {
}
tr:hover td {
background: ${cssManager.bdTheme('#22222210', '#ffffff10')};
}
tr:first-child:hover {
cursor: auto;
}
tr:first-child:hover .innerCellContainer {
background: none;
}
tr.selected td {
background: ${cssManager.bdTheme('#22222220', '#ffffff20')};
}
tr.hasAttachment td {
background: ${cssManager.bdTheme('#0098847c', '#0098847c')};
}
th {
text-transform: none;
font-family: 'Geist Sans', sans-serif;
font-weight: 500;
}
th,
td {
position: relative;
vertical-align: top;
padding: 0px;
border-right: 1px dashed ${cssManager.bdTheme('#999', '#808080')};
}
.innerCellContainer {
min-height: 36px;
position: relative;
height: 100%;
width: 100%;
padding: 6px 8px;
line-height: 24px;
}
th:first-child .innerCellContainer,
td:first-child .innerCellContainer {
padding-left: 0px;
}
th:last-child .innerCellContainer,
td:last-child .innerCellContainer {
padding-right: 0px;
}
th:last-child,
td:last-child {
border-right: none;
}
td input {
width: 100%;
height: 100%;
outline: none;
border: 2px solid #fa6101;
top: 0px;
bottom: 0px;
right: 0px;
left: 0px;
position: absolute;
background: #fa610140;
color: ${cssManager.bdTheme('#333', '#fff')};
font-family: inherit;
font-size: inherit;
font-weight: inherit;
padding: 0px 6px;
}
.actionsContainer {
display: flex;
flex-direction: row;
height: 24px;
transform: translateY(-4px);
margin-left: -6px;
}
.action {
position: relative;
padding: 8px 10px;
line-height: 24px;
height: 32px;
size: 16px;
border-radius: 8px;
}
.action:hover {
background: ${cssManager.bdTheme(colors.bright.blue, colors.dark.blue)};
}
.action:active {
background: ${cssManager.bdTheme(colors.bright.blue, colors.dark.blueActive)};
}
.action:hover dees-icon {
filter: ${cssManager.bdTheme('invert(1) brightness(3)', '')};
}
.footer {
font-family: 'Geist Sans', sans-serif;
font-size: 14px;
color: ${cssManager.bdTheme('#111', '#ffffff90')};
background: ${cssManager.bdTheme('#eeeeeb', '#00000050')};
margin: 16px -16px -16px -16px;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
display: flex;
}
.tableStatistics {
padding: 8px 16px;
}
.footerActions {
margin-left: auto;
}
.footerActions .footerAction {
padding: 8px 16px;
display: flex;
user-select: none;
}
.footerActions .footerAction:hover {
background: ${cssManager.bdTheme(colors.bright.blue, colors.dark.blue)};
color: #fff;
}
.footerActions .footerAction dees-icon {
display: flex;
margin-right: 8px;
}
.footerActions .footerAction:hover dees-icon {
}
`,
];
public render(): TemplateResult {
return html`
<div class="mainbox">
<!-- the heading part -->
<div class="header">
<div class="headingContainer">
<div class="heading heading1">${this.label || this.heading1}</div>
<div class="heading heading2">${this.heading2}</div>
</div>
<div class="headerActions">
${directives.resolveExec(async () => {
const resultArray: TemplateResult[] = [];
for (const action of this.dataActions) {
if (!action.type.includes('header')) continue;
resultArray.push(
html`<div
class="headerAction"
@click=${() => {
action.actionFunc({
item: this.selectedDataRow,
table: this,
});
}}
>
${action.iconName
? html`<dees-icon .iconSize=${14} .iconFA=${action.iconName}></dees-icon>
${action.name}`
: action.name}
</div>`
);
}
return resultArray;
})}
</div>
</div>
<div class="headingSeparation"></div>
<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>
${this.data.length > 0
? (() => {
// Only pick up the keys from the first transformed data object
// as all data objects are assumed to have the same structure
const firstTransformedItem = this.displayFunction(this.data[0]);
const headings: string[] = Object.keys(firstTransformedItem);
return html`
<table>
<tr>
${headings.map(
(headingArg) => html`
<th>
<div class="innerCellContainer">${headingArg}</div>
</th>
`
)}
${(() => {
if (this.dataActions && this.dataActions.length > 0) {
return html`
<th>
<div class="innerCellContainer">Actions</div>
</th>
`;
}
})()}
</tr>
${this.data.map((itemArg) => {
const transformedItem = this.displayFunction(itemArg);
const getTr = (elementArg: HTMLElement): HTMLElement => {
if (elementArg.tagName === 'TR') {
return elementArg;
} else {
return getTr(elementArg.parentElement);
}
};
return html`
<tr
@click=${() => {
this.selectedDataRow = itemArg;
}}
@dragenter=${async (eventArg: DragEvent) => {
eventArg.preventDefault();
eventArg.stopPropagation();
const realTarget = getTr(eventArg.target as HTMLElement);
console.log('dragenter');
console.log(realTarget);
setTimeout(() => {
realTarget.classList.add('hasAttachment');
}, 0);
}}
@dragleave=${async (eventArg: DragEvent) => {
eventArg.preventDefault();
eventArg.stopPropagation();
const realTarget = getTr(eventArg.target as HTMLElement);
realTarget.classList.remove('hasAttachment');
}}
@dragover=${async (eventArg: DragEvent) => {
eventArg.preventDefault();
}}
@drop=${async (eventArg: DragEvent) => {
eventArg.preventDefault();
const newFiles = [];
for (const file of Array.from(eventArg.dataTransfer.files)) {
this.files.push(file);
newFiles.push(file);
this.requestUpdate();
}
const result: File[] = this.fileWeakMap.get(itemArg as object);
if (!result) {
this.fileWeakMap.set(itemArg as object, newFiles);
} else {
result.push(...newFiles);
}
}}
@contextmenu=${async (eventArg: MouseEvent) => {
DeesContextmenu.openContextMenuWithOptions(
eventArg,
this.getActionsForType('contextmenu').map((action) => {
const menuItem: plugins.tsclass.website.IMenuItem = {
name: action.name,
iconName: action.iconName as any,
action: async () => {
await action.actionFunc({
item: itemArg,
table: this,
});
return null;
},
};
return menuItem;
})
);
}}
class="${itemArg === this.selectedDataRow ? 'selected' : ''}"
>
${headings.map(
(headingArg) => html`
<td
@dblclick=${(e: Event) => {
if (this.editableFields.includes(headingArg)) {
this.handleCellEditing(e, itemArg, headingArg);
} else {
const wantedAction = this.dataActions.find((actionArg) =>
actionArg.type.includes('doubleClick')
);
if (wantedAction) {
wantedAction.actionFunc({
item: itemArg,
table: this,
});
}
}
}}
>
<div class="innerCellContainer">${transformedItem[headingArg]}</div>
</td>
`
)}
${(() => {
if (this.dataActions && this.dataActions.length > 0) {
return html`
<td>
<div class="innerCellContainer">
<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>
`;
}
})()}
</tr>
`;
})}
</table>
`;
})()
: html` <div class="noDataSet">No data set!</div> `}
<div class="footer">
<div class="tableStatistics">
${this.data.length} ${this.dataName || 'data rows'} (total) |
${this.selectedDataRow ? '# ' + `${this.data.indexOf(this.selectedDataRow) + 1}` : `No`}
selected
</div>
<div class="footerActions">
${directives.resolveExec(async () => {
const resultArray: TemplateResult[] = [];
for (const action of this.dataActions) {
if (!action.type.includes('footer')) continue;
resultArray.push(
html`<div
class="footerAction"
@click=${() => {
action.actionFunc({
item: this.selectedDataRow,
table: this,
});
}}
>
${action.iconName
? html`<dees-icon .iconSize=${14} .iconFA=${action.iconName}></dees-icon>
${action.name}`
: action.name}
</div>`
);
}
return resultArray;
})}
</div>
</div>
</div>
`;
}
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() {
const domtools = await this.domtoolsPromise;
await domtools.convenience.smartdelay.delayFor(0);
// Get the table element
const table = this.shadowRoot.querySelector('table');
if (!table) return;
// Get the first row's cells to measure the widths
const cells = table.rows[0].cells;
const handleColumnByIndex = async (i: number, waitForRenderArg: boolean = false) => {
const done = plugins.smartpromise.defer();
const cell = cells[i];
// Get computed width
const width = window.getComputedStyle(cell).width;
if (cell.textContent.includes('Actions')) {
const neededWidth =
this.dataActions.filter((actionArg) => actionArg.type.includes('inRow')).length * 36;
cell.style.width = `${Math.max(neededWidth, 68)}px`;
} else {
cell.style.width = width;
}
if (waitForRenderArg) {
requestAnimationFrame(() => {
done.resolve();
});
await done.promise;
}
};
if (cells[cells.length - 1].textContent.includes('Actions')) {
await handleColumnByIndex(cells.length - 1, true);
}
for (let i = 0; i < cells.length; i++) {
if (cells[i].textContent.includes('Actions')) {
continue;
}
await handleColumnByIndex(i);
}
table.style.tableLayout = 'fixed';
}
getActionsForType(typeArg: ITableAction['type'][0]) {
const actions: ITableAction[] = [];
for (const action of this.dataActions) {
if (!action.type.includes(typeArg)) continue;
actions.push(action);
}
return actions;
}
async handleCellEditing(event: Event, itemArg: T, key: string) {
const domtools = await this.domtoolsPromise;
const target = event.target as HTMLElement;
const originalColor = target.style.color;
target.style.color = 'transparent';
const transformedItem = this.displayFunction(itemArg);
const initialValue = (transformedItem[key] as unknown as string) || '';
// Create an input element
const input = document.createElement('input');
input.type = 'text';
input.value = initialValue;
const blurInput = async (blurArg = true, saveArg = false) => {
if (blurArg) {
input.blur();
}
if (saveArg) {
itemArg[key] = input.value as any; // Convert string to T (you might need better type casting depending on your data structure)
this.changeSubject.next(this);
}
input.remove();
target.style.color = originalColor;
this.requestUpdate();
};
// When the input loses focus or the Enter key is pressed, update the data
input.addEventListener('blur', () => {
blurInput(false, false);
});
input.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.key === 'Enter') {
blurInput(true, true); // This will trigger the blur event handler above
}
});
// Replace the cell's content with the input
target.appendChild(input);
input.focus();
}
}

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

@ -0,0 +1,5 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = async () => {
return html`<dees-toast></dees-toast>`;
}

View File

@ -1,9 +1,17 @@
import { customElement, LitElement, TemplateResult, html } from 'lit-element';
import { customElement, DeesElement, type TemplateResult, html, type CSSResult, } from '@design.estate/dees-element';
import * as domtools from '@designestate/dees-domtools';
import * as domtools from '@design.estate/dees-domtools';
import { demoFunc } from './dees-toast.demo.js';
declare global {
interface HTMLElementTagNameMap {
'dees-toast': DeesToast;
}
}
@customElement('dees-toast')
export class DeesToast extends LitElement {
export class DeesToast extends DeesElement {
public static demo = demoFunc;
constructor() {
super();

Some files were not shown because too many files have changed in this diff Show More