Compare commits

...

338 Commits

Author SHA1 Message Date
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
91 changed files with 10796 additions and 2896 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

@ -10,11 +10,9 @@
/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="preconnect" href="https://rsms.me/">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
<!--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 {

View File

@ -1,6 +1,6 @@
// 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/index.js';

View File

@ -3,10 +3,10 @@
"projectType": "wcc",
"module": {
"githost": "gitlab.com",
"gitscope": "designestate",
"gitscope": "design.estate",
"gitrepo": "dees-catalog",
"description": "a webcomponents catalog for handling daily stuff on the web",
"npmPackagename": "@designestate/dees-catalog",
"npmPackagename": "@design.estate/dees-catalog",
"license": "MIT",
"projectDomain": "design.estate"
}

View File

@ -1,6 +1,6 @@
{
"name": "@designestate/dees-catalog",
"version": "1.0.112",
"name": "@design.estate/dees-catalog",
"version": "1.0.281",
"private": false,
"description": "website for lossless.com",
"main": "dist_ts_web/index.js",
@ -15,25 +15,34 @@
"author": "Lossless GmbH",
"license": "MIT",
"dependencies": {
"@designestate/dees-domtools": "^2.0.28",
"@designestate/dees-element": "^2.0.18",
"@designestate/dees-wcctools": "^1.0.75",
"@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-regular-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1",
"@pushrocks/smartpromise": "^3.1.7",
"@tsclass/tsclass": "^4.0.28",
"pdfjs-dist": "^2.15.349"
"@design.estate/dees-domtools": "^2.0.57",
"@design.estate/dees-element": "^2.0.33",
"@design.estate/dees-wcctools": "^1.0.87",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
"@fortawesome/free-brands-svg-icons": "^6.5.1",
"@fortawesome/free-regular-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@push.rocks/smarti18n": "^1.0.4",
"@push.rocks/smartpromise": "^4.0.3",
"@push.rocks/smartstring": "^4.0.13",
"@tsclass/tsclass": "^4.0.46",
"@webcontainer/api": "^1.1.8",
"apexcharts": "^3.45.2",
"highlight.js": "11.9.0",
"ibantools": "^4.3.9",
"monaco-editor": "^0.45.0",
"pdfjs-dist": "^4.0.379",
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0"
},
"devDependencies": {
"@gitzone/tsbuild": "^2.1.65",
"@gitzone/tsbundle": "^2.0.7",
"@gitzone/tstest": "^1.0.74",
"@gitzone/tswatch": "^2.0.5",
"@pushrocks/projectinfo": "^5.0.1",
"@pushrocks/tapbundle": "^5.0.4",
"@types/node": "^18.11.18"
"@git.zone/tsbuild": "^2.1.72",
"@git.zone/tsbundle": "^2.0.15",
"@git.zone/tstest": "^1.0.86",
"@git.zone/tswatch": "^2.0.21",
"@push.rocks/projectinfo": "^5.0.2",
"@push.rocks/tapbundle": "^5.0.15",
"@types/node": "^20.11.5"
},
"files": [
"ts/**/*",

6561
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,26 @@
# @designestate/dees-catalog
# @design.estate/dees-catalog
a webcomponents catalog for handling daily stuff on the web
## Availabililty and Links
* [npmjs.org (npm package)](https://www.npmjs.com/package/@designestate/dees-catalog)
* [gitlab.com (source)](https://gitlab.com/designestate/dees-catalog)
* [github.com (source mirror)](https://github.com/designestate/dees-catalog)
* [docs (typedoc)](https://designestate.gitlab.io/dees-catalog/)
* [npmjs.org (npm package)](https://www.npmjs.com/package/@design.estate/dees-catalog)
* [gitlab.com (source)](https://gitlab.com/design.estate/dees-catalog)
* [github.com (source mirror)](https://github.com/design.estate/dees-catalog)
* [docs (typedoc)](https://design.estate.gitlab.io/dees-catalog/)
## Status for master
Status Category | Status Badge
-- | --
GitLab Pipelines | [![pipeline status](https://gitlab.com/designestate/dees-catalog/badges/master/pipeline.svg)](https://lossless.cloud)
GitLab Pipline Test Coverage | [![coverage report](https://gitlab.com/designestate/dees-catalog/badges/master/coverage.svg)](https://lossless.cloud)
npm | [![npm downloads per month](https://badgen.net/npm/dy/@designestate/dees-catalog)](https://lossless.cloud)
Snyk | [![Known Vulnerabilities](https://badgen.net/snyk/designestate/dees-catalog)](https://lossless.cloud)
GitLab Pipelines | [![pipeline status](https://gitlab.com/design.estate/dees-catalog/badges/master/pipeline.svg)](https://lossless.cloud)
GitLab Pipline Test Coverage | [![coverage report](https://gitlab.com/design.estate/dees-catalog/badges/master/coverage.svg)](https://lossless.cloud)
npm | [![npm downloads per month](https://badgen.net/npm/dy/@design.estate/dees-catalog)](https://lossless.cloud)
Snyk | [![Known Vulnerabilities](https://badgen.net/snyk/design.estate/dees-catalog)](https://lossless.cloud)
TypeScript Support | [![TypeScript](https://badgen.net/badge/TypeScript/>=%203.x/blue?icon=typescript)](https://lossless.cloud)
node Support | [![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
Code Style | [![Code Style](https://badgen.net/badge/style/prettier/purple)](https://lossless.cloud)
PackagePhobia (total standalone install weight) | [![PackagePhobia](https://badgen.net/packagephobia/install/@designestate/dees-catalog)](https://lossless.cloud)
PackagePhobia (package size on registry) | [![PackagePhobia](https://badgen.net/packagephobia/publish/@designestate/dees-catalog)](https://lossless.cloud)
BundlePhobia (total size when bundled) | [![BundlePhobia](https://badgen.net/bundlephobia/minzip/@designestate/dees-catalog)](https://lossless.cloud)
PackagePhobia (total standalone install weight) | [![PackagePhobia](https://badgen.net/packagephobia/install/@design.estate/dees-catalog)](https://lossless.cloud)
PackagePhobia (package size on registry) | [![PackagePhobia](https://badgen.net/packagephobia/publish/@design.estate/dees-catalog)](https://lossless.cloud)
BundlePhobia (total size when bundled) | [![BundlePhobia](https://badgen.net/bundlephobia/minzip/@design.estate/dees-catalog)](https://lossless.cloud)
## Usage

View File

@ -1,4 +1,4 @@
import { tap, expect, webhelpers } from '@pushrocks/tapbundle';
import { tap, expect, webhelpers } from '@push.rocks/tapbundle';
import * as deesCatalog from '../ts_web';

View File

@ -2,7 +2,7 @@
* autocreated commitinfo by @pushrocks/commitinfo
*/
export const commitinfo = {
name: '@designestate/dees-catalog',
version: '1.0.112',
name: '@design.estate/dees-catalog',
version: '1.0.281',
description: 'website for lossless.com'
}

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: 'Roboto', 'Inter', 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: 'Roboto', 'Inter', sans-serif;
font-weight: 600;
font-size: 14px;
}
.selectionOptions {
position: absolute;
top: 32px;
padding-top: 8px;
left: 0px;
width: 100%;
font-family: 'Roboto', 'Inter', 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

@ -3,11 +3,12 @@ import {
customElement,
DeesElement,
html,
TemplateResult,
type TemplateResult,
css,
type CSSResult,
state,
property
} from '@designestate/dees-element';
} from '@design.estate/dees-element';
@customElement('dees-button-exit')
export class DeesButtonExit extends DeesElement {
@ -20,7 +21,7 @@ export class DeesButtonExit extends DeesElement {
@property({
type: Number
})
public size: number = 20;
public size: number = 24;
public styles = [
cssManager.defaultStyles,
@ -37,12 +38,6 @@ export class DeesButtonExit extends DeesElement {
width: ${this.size}px;
height: ${this.size}px;
}
.firstLine {
position: absolute;
}
.secondLine {
position: absolute;
}
</style>
<div class="maincontainer">
<div class="firstLine"></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,15 +1,17 @@
import { demoFunc } from './dees-button.demo.js';
import {
customElement,
html,
DeesElement,
property,
TemplateResult,
type TemplateResult,
cssManager,
css,
type CSSResult,
unsafeCSS,
} from '@designestate/dees-element';
} from '@design.estate/dees-element';
import * as domtools from '@designestate/dees-domtools';
import * as domtools from '@design.estate/dees-domtools';
declare global {
interface HTMLElementTagNameMap {
@ -19,19 +21,14 @@ declare global {
@customElement('dees-button')
export class DeesButton extends DeesElement {
public static demo = () => 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>
`;
public static demo = demoFunc;
@property()
@property({
reflect: true,
hasChanged() {
return true;
}
})
public text: string;
@property()
@ -68,20 +65,22 @@ export class DeesButton extends DeesElement {
:host {
display: block;
box-sizing: border-box;
font-family: 'Roboto', 'monospace';
}
:host([hidden]) {
display: none;
}
.button {
transition: none;
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('#eee', '#333')};
box-shadow: ${cssManager.bdTheme('0px 0px 5px rgba(0,0,0,0.1)', 'none')};
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;
@ -94,16 +93,15 @@ export class DeesButton extends DeesElement {
}
.button:hover {
cursor: pointer;
background: #039be5;
border: 1px solid #039be5;
border-top: 1px solid #039be5;
background: #0050b9;
color: #ffffff;
border: 1px solid #0050b9;
border-top: 1px solid #0050b9;
}
.button:active {
background: #0277bd;
border-top: 1px solid #0277bd;
background: #0069f2;
border-top: 1px solid #0069f2;
}
.button.disabled {
@ -147,8 +145,8 @@ export class DeesButton extends DeesElement {
}
.button.pending {
border: 1px dashed ${cssManager.bdTheme('#0277bd', '#0277bd70')};
background: ${cssManager.bdTheme('#0277bd', '#0277bd70')};
border: 1px dashed ${cssManager.bdTheme('#0069f2', '#0069f270')};
background: ${cssManager.bdTheme('#0069f2', '#0069f270')};
color: #fff;
}
@ -180,7 +178,7 @@ export class DeesButton extends DeesElement {
@click="${this.dispatchClick}"
>
${this.status === 'normal' ? html``: html`
<dees-spinner status="${this.status}"></dees-spinner>
<dees-spinner .bnw=${true} status="${this.status}"></dees-spinner>
`}
<div class="textbox">${this.text ? this.text : this.textContent}</div>
</div>

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-area
.title=${'System Usage'}
></dees-chart-area>
</div>
`;
};

View File

@ -0,0 +1,192 @@
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 title: string = 'Untitled Chart';
constructor() {
super();
domtools.elementBasic.setup();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
font-family: 'Roboto', sans-serif;
color: #ccc;
font-weight: 600;
font-size: 12px;
}
.mainbox {
position: relative;
width: 100%;
max-width: 600px;
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.title}</div>
<div class="chartContainer"></div>
</div> `;
}
public async firstUpdated() {
const domtoolsInstance = await this.domtoolsPromise;
var options: ApexCharts.ApexOptions = {
series: [
{
name: 'cpu',
data: [31, 40, 28, 51, 42, 109, 100],
},
{
name: 'memory',
data: [11, 32, 45, 32, 34, 52, 41],
},
],
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: {
crosshairs: {
stroke: {
width: 1,
color: '#444',
},
},
type: 'datetime',
categories: [
'2018-09-19T00:00:00.000Z',
'2018-09-19T01:30:00.000Z',
'2018-09-19T02:30:00.000Z',
'2018-09-19T03:30:00.000Z',
'2018-09-19T04:30:00.000Z',
'2018-09-19T05:30:00.000Z',
'2018-09-19T06:30:00.000Z',
],
},
yaxis: {
crosshairs: {
stroke: {
width: 1,
color: '#444',
},
},
},
tooltip: {
x: {
format: 'dd/MM/yy HH:mm',
},
},
grid: {
xaxis: {
lines: {
show: true, // This enables the grid lines along the x-axis
},
},
yaxis: {
lines: {
show: true,
},
},
borderColor: '#666', // Set the color of the grid lines
strokeDashArray: 2, // 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,
},
},
};
this.chart = new ApexCharts(this.shadowRoot.querySelector('.chartContainer'), options);
await this.chart.render();
await this.resizeChart();
}
public async resizeChart() {
const element = this.shadowRoot.querySelector('.chartContainer');
// Get computed style of the element
const style = window.getComputedStyle(element);
// Extract padding values
const paddingTop = parseInt(style.paddingTop, 10);
const paddingBottom = parseInt(style.paddingBottom, 10);
const paddingLeft = parseInt(style.paddingLeft, 10);
const paddingRight = parseInt(style.paddingRight, 10);
// Calculate the actual width and height to use, subtracting padding
const actualWidth = element.clientWidth - paddingLeft - paddingRight;
const actualHeight = element.clientHeight - paddingTop - paddingBottom;
await this.chart.updateOptions({
chart: {
width: actualWidth,
height: actualHeight,
},
});
}
}

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

@ -3,13 +3,15 @@ import {
html,
DeesElement,
property,
TemplateResult,
type TemplateResult,
cssManager,
css,
type CSSResult,
unsafeCSS,
} from '@designestate/dees-element';
} from '@design.estate/dees-element';
import * as domtools from '@designestate/dees-domtools';
import * as domtools from '@design.estate/dees-domtools';
import { demoFunc } from './dees-chips.demo.js';
declare global {
interface HTMLElementTagNameMap {
@ -17,30 +19,32 @@ declare global {
}
}
type Tag = { key: string; value: string };
@customElement('dees-chips')
export class DeesChips extends DeesElement {
public static demo = () => html`
<dees-chips .selectableChips=${['Payment Account 1', 'PaymentAccount2', 'Payment Account 3']}></dees-chips>
<dees-chips selectionMode="multiple" .selectableChips=${['Payment Account 1', 'PaymentAccount2', 'Payment Account 3']}></dees-chips>
`;
public static demo = demoFunc;
@property()
public selectionMode: 'single' | 'multiple' = 'single';
public selectionMode: 'none' | 'single' | 'multiple' = 'single';
@property({
type: Array
type: Boolean,
})
public selectableChips: string[] = [];
public chipsAreRemovable: boolean = false;
@property({
type: Array,
})
public selectableChips: Tag[] = [];
@property()
public selectedChip: string = null;
public selectedChip: Tag = null;
@property({
type: Array
type: Array,
})
public selectedChips: string[] = [];
public selectedChips: Tag[] = [];
constructor() {
super();
@ -49,47 +53,87 @@ export class DeesChips extends DeesElement {
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
box-sizing: border-box;
}
.mainbox {
user-select: none;
}
.chip {
background: #494949;
display: inline-block;
padding: 8px 12px;
font-size: 14px;
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: 8px;
margin-bottom: 4px;
position: relative;
overflow: hidden;
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3);
}
.chip:hover {
background: #666666;
cursor: pointer;
}
.chip.selected {
background: #00A3FF;
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(chipArg => html`
<div @click=${() => this.selectChip(chipArg)} class="chip ${this.selectedChip === chipArg || this.selectedChips.includes(chipArg) ? 'selected' : ''}">
${chipArg}
</div>
`)}
${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>
`;
}
@ -101,23 +145,51 @@ export class DeesChips extends DeesElement {
}
}
public async selectChip(chipArg: string) {
private isSelected(chip: Tag): boolean {
if (this.selectionMode === 'single') {
if (this.selectedChip === chipArg) {
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 = chipArg;
this.selectedChips = [chipArg];
this.selectedChip = chip;
this.selectedChips = [chip];
}
} else if(this.selectionMode === 'multiple') {
if (this.selectedChips.includes(chipArg)) {
this.selectedChips = this.selectedChips.filter(chipArg2 => chipArg !== chipArg2)
} else if (this.selectionMode === 'multiple') {
if (this.isSelected(chip)) {
this.selectedChips = this.selectedChips.filter((selected) => selected.key !== chip.key);
} else {
this.selectedChips.push(chipArg);
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: 'Roboto', 'Inter', 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', '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';
}
`,
];
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 = [
domtools.elementBasic.staticStyles,
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

@ -5,7 +5,8 @@ import {
css,
cssManager,
property,
} from '@designestate/dees-element';
type CSSResult,
} from '@design.estate/dees-element';
import { DeesForm } from './dees-form.js';
declare global {
@ -20,6 +21,7 @@ export class DeesFormSubmit extends DeesElement {
@property({
type: Boolean,
reflect: true,
})
public disabled = false;
@ -29,7 +31,7 @@ export class DeesFormSubmit extends DeesElement {
public text: string;
@property({
type: String
type: String,
})
public status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
@ -41,8 +43,12 @@ export class DeesFormSubmit extends DeesElement {
public render() {
return html`
<dees-button status=${this.status} @click=${this.submit} .disabled=${this.disabled}>
${this.text ? this.text : html`<slot></slot>`}
<dees-button
status=${this.status}
@click=${this.submit}
.disabled=${this.disabled}
.text=${this.text ? this.text : this.textContent}
>
</dees-button>
`;
}
@ -54,4 +60,12 @@ export class DeesFormSubmit extends DeesElement {
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,15 +1,38 @@
import { customElement, html, TemplateResult, DeesElement } from '@designestate/dees-element';
import * as domtools from '@designestate/dees-domtools';
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.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';
export type TFormElement = Array<
DeesInputCheckbox | DeesInputText | DeesInputQuantitySelector | DeesInputRadio
>;
// 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 {
@ -19,29 +42,11 @@ declare global {
@customElement('dees-form')
export class DeesForm extends DeesElement {
public static demo = () => html`
<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-text .required="${true}" key="hello1" label="a text"></dees-input-text>
<dees-input-text .required="${true}" key="hello2" label="also a text"></dees-input-text>
<dees-input-checkbox
.required="${true}"
key="hello3"
label="another text"
></dees-input-checkbox>
<dees-form-submit>Submit</dees-form-submit>
</dees-form>
`;
public static demo = demoFunc;
public name: string = 'myform';
public changeSubject = new domtools.rxjs.Subject();
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
public readyDeferred = domtools.plugins.smartpromise.defer();
public render(): TemplateResult {
return html`
@ -54,51 +59,39 @@ export class DeesForm extends DeesElement {
`;
}
public firstUpdated() {
const formChildren = this.getFormChildren();
this.checkRequiredStatus();
public async firstUpdated() {
const formChildren = this.getFormElements();
this.updateRequiredStatus();
for (const child of formChildren) {
child.changeSubject.subscribe(async (elementArg: TFormElement) => {
const valueObject = await this.gatherData();
child.changeSubject.subscribe(async () => {
const valueObject = await this.collectFormData();
this.changeSubject.next(valueObject);
console.log(valueObject);
this.checkRequiredStatus();
this.updateRequiredStatus();
});
}
await this.addBehaviours();
this.readyDeferred.resolve();
}
public getFormChildren() {
const children: Array<DeesElement> = this.children as any;
const formChildren: TFormElement = [];
for (const child of children) {
if (
child instanceof DeesInputCheckbox ||
child instanceof DeesInputText ||
child instanceof DeesInputQuantitySelector
) {
formChildren.push(child);
}
}
return formChildren;
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() {
const children: Array<DeesElement> = this.children as any;
let submitButton: DeesFormSubmit;
for (const childArg of children) {
if (childArg instanceof DeesFormSubmit) {
submitButton = childArg;
}
}
return submitButton;
public getSubmitButton(): DeesFormSubmit | undefined {
return Array.from(this.children).find(
(child) => child instanceof DeesFormSubmit
) as DeesFormSubmit;
}
public async checkRequiredStatus() {
public async updateRequiredStatus() {
console.log('checking the required status.');
let requiredOK = true;
for (const childArg of this.getFormChildren()) {
for (const childArg of this.getFormElements()) {
if (childArg.required && !childArg.value) {
requiredOK = false;
}
@ -108,17 +101,24 @@ export class DeesForm extends DeesElement {
}
}
public async gatherData() {
const children = this.getFormChildren();
const valueObject: { [key: string]: string | number | boolean } = {};
/**
* 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;
}
public async gatherAndDispatch() {
const valueObject = await this.gatherData();
const valueObject = await this.collectFormData();
const formDataEvent = new CustomEvent('formData', {
detail: {
data: valueObject,
@ -134,7 +134,7 @@ export class DeesForm extends DeesElement {
visualStateArg: 'normal' | 'pending' | 'error' | 'success',
textStateArg: string
) {
const inputChildren = this.getFormChildren();
const inputChildren = this.getFormElements();
const submitButton = this.getSubmitButton();
switch (visualStateArg) {
@ -170,4 +170,36 @@ export class DeesForm extends DeesElement {
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,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,55 @@
import { html } from '@design.estate/dees-element';
import { faIcons } from './dees-icon.js';
export const demoFunc = () => html`
<style>
.demoContainer {
width: 100%;
display: flex;
flex-wrap: wrap;
background: #111111;
padding: 10px; font-size: 30px;
}
dees-icon {
transition: color 0.02s;
color: #ffffff;
}
dees-icon:hover {
color: #e4002b;
}
.iconContainer {
display: block;
padding: 16px 16px 0px 16px;
border: 1px solid #333333;
margin-right: 8px;
margin-bottom: 8px;
}
.iconName {
font-size: 12px;
text-align: center;
color: #ccc;
background: #333333;
padding: 4px 8px;
padding-bottom: 4px;
margin-left: -16px;
margin-right: -16px;
margin-top: 16px;
}
</style>
<div class="demoContainer">
${Object.keys(faIcons).map(
(iconName) => html`
<div class="iconContainer">
<dees-icon .iconFA=${iconName as any}></dees-icon>
<div class="iconName">${iconName}</div>
</div>
`
)}
</div>
`;

View File

@ -1,8 +1,16 @@
import { DeesElement, html, property, customElement } from '@designestate/dees-element';
import {
DeesElement,
html,
property,
customElement,
cssManager,
css,
type CSSResult,
} from '@design.estate/dees-element';
import * as domtools from '@designestate/dees-domtools';
import * as domtools from '@design.estate/dees-domtools';
import { icon, IconDefinition } from '@fortawesome/fontawesome-svg-core';
import { icon, type IconDefinition } from '@fortawesome/fontawesome-svg-core';
import {
faFacebook,
faGoogle,
@ -14,27 +22,109 @@ import {
faTiktok,
} from '@fortawesome/free-brands-svg-icons';
import {} from '@fortawesome/free-regular-svg-icons';
import { faDesktop, faRss, faUsers } from '@fortawesome/free-solid-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';
type TFontAwesomeIcon =
export const faIcons = {
// normal
| 'desktop'
| 'rss'
// brands
| 'facebook'
| 'google'
| 'linkedin'
| 'instagram'
| 'medium'
| 'slack'
| 'tiktok'
| 'twitter'
| 'users';
const faIcons: { [key: string]: IconDefinition } = {
// normal
desktop: faDesktop,
rss: faRss,
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,
@ -44,9 +134,10 @@ const faIcons: { [key: string]: IconDefinition } = {
slack: faSlackHash,
tiktok: faTiktok,
twitter: faTwitter,
users: faUsers,
};
export type TIconKey = keyof typeof faIcons;
declare global {
interface HTMLElementTagNameMap {
'dees-icon': DeesIcon;
@ -55,76 +146,58 @@ declare global {
@customElement('dees-icon')
export class DeesIcon extends DeesElement {
public static demo = () => html`
<dees-icon iconName="visibility"></dees-icon>
<div style="background: #fff; padding: 10px; font-size: 24px">
<dees-icon iconName="visibility"></dees-icon>
<dees-icon brandName="facebook"></dees-icon>
</div>
`;
public static demo = demoFunc;
@property({
type: String
})
public iconFA: keyof typeof faIcons;
@property()
public iconName: string;
@property()
public brandName: TFontAwesomeIcon;
@property()
public svgSize: number = 20;
public iconSize: number;
constructor() {
super();
domtools.elementBasic.setup();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
white-space: nowrap;
display: flex;
align-items: center;
justify-content: center;
}
* {
transition: inherit !important;
}
`,
];
public render() {
return html`
${domtools.elementBasic.styles}
<style>
:host {
display: block;
line-height: inherit;
font-size: inherit;
margin: 0px;
padding: 0px;
white-space: nowrap;
}
#iconContainer svg {
display: inline-block;
height: ${this.svgSize}px;
}
.material-symbols-outlined {
font-family: 'Material Symbols Outlined';
font-weight: normal;
font-style: normal;
line-height: inherit;
font-size: inherit; /* Preferred icon size */
display: inline-block;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 48;
display: block;
height: ${this.iconSize}px;
}
</style>
${this.iconName
? html`
<i
class="material-symbols-outlined"
>
${this.iconName}
</i>`
: html``}
${this.brandName ? html`<div id="iconContainer"></div>` : html``}
<div id="iconContainer"></div>
`;
}
public async firstUpdated() {
if (this.brandName && !this.iconName) {
this.shadowRoot.querySelector('#iconContainer').innerHTML = icon(
faIcons[this.brandName]
).html[0];
public async updated() {
if (!this.iconSize) {
this.iconSize = parseInt(globalThis.getComputedStyle(this).fontSize.replace(/\D/g,''));
}
if (this.iconFA) {
this.shadowRoot.querySelector('#iconContainer').innerHTML = this.iconFA
? icon(faIcons[this.iconFA]).html[0]
: 'icon not found';
}
}
}

View File

@ -1,13 +1,14 @@
import {
customElement,
DeesElement,
TemplateResult,
type TemplateResult,
property,
html,
css,
cssManager
} from '@designestate/dees-element';
import * as domtools from '@designestate/dees-domtools';
cssManager,
type CSSResult,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
declare global {
interface HTMLElementTagNameMap {
@ -21,10 +22,11 @@ export class DeesInputCheckbox extends DeesElement {
public static demo = () => html`<dees-input-checkbox></dees-input-checkbox>`;
// INSTANCE
public changeSubject = new domtools.rxjs.Subject();
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
@property({
type: String,
reflect: true,
})
public key: string;
@ -60,7 +62,10 @@ export class DeesInputCheckbox extends DeesElement {
display: block;
position: relative;
margin: 20px 0px;
cursor: pointer;
cursor: default;
}
:host(:hover) {
filter: ${cssManager.bdTheme('brightness(0.95)', 'brightness(1.1)')};
}
.maincontainer {
@ -98,8 +103,8 @@ export class DeesInputCheckbox extends DeesElement {
}
.checkbox.selected {
background: #039BE5;
border: 1px solid #039BE5;
background: #0050b9;
border: 1px solid #0050b9;
}
.checkbox.disabled {
@ -143,7 +148,7 @@ export class DeesInputCheckbox extends DeesElement {
}
</style>
<div class="maincontainer" @click="${this.toggleSelected}">
<div class="checkbox ${this.value ? 'selected' : ''} ${this.disabled ? 'disabled' : ''}">
<div class="checkbox ${this.value ? 'selected' : ''} ${this.disabled ? 'disabled' : ''}" tabindex="0">
${this.value
? html`
<span class="checkmark">
@ -171,4 +176,11 @@ export class DeesInputCheckbox extends DeesElement {
);
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,5 +1,17 @@
import { customElement, DeesElement, TemplateResult, property, html } from '@designestate/dees-element';
import * as domtools from '@designestate/dees-domtools';
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';
declare global {
interface HTMLElementTagNameMap {
@ -9,129 +21,355 @@ declare global {
@customElement('dees-input-dropdown')
export class DeesInputDropdown extends DeesElement {
public static demo = () => html`<dees-input-dropdown></dees-input-dropdown>`
public static demo = demoFunc;
// INSTANCE
public changeSubject = new domtools.rxjs.Subject();
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
@property()
@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
type: Boolean,
})
public required: boolean = false;
@property({
type: Boolean
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

@ -1,15 +1,20 @@
import * as colors from './00colors.js';
import * as plugins from './00plugins.js';
import { DeesContextmenu } from './dees-contextmenu.js';
import {
customElement,
DeesElement,
TemplateResult,
type TemplateResult,
property,
html,
css,
unsafeCSS,
cssManager,
} from '@designestate/dees-element';
import * as domtools from '@designestate/dees-domtools';
type CSSResult,
domtools,
} from '@design.estate/dees-element';
declare global {
interface HTMLElementTagNameMap {
@ -19,10 +24,11 @@ declare global {
@customElement('dees-input-fileupload')
export class DeesInputFileupload extends DeesElement {
public static demo = () => html`<dees-input-fileupload .label=${'Attachments'}></dees-input-fileupload>`;
public static demo = () =>
html`<dees-input-fileupload .label=${'Attachments'}></dees-input-fileupload>`;
// INSTANCE
public changeSubject = new domtools.rxjs.Subject();
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
@property({
type: String,
@ -31,6 +37,7 @@ export class DeesInputFileupload extends DeesElement {
@property({
type: String,
reflect: true,
})
public key: string;
@ -69,6 +76,7 @@ export class DeesInputFileupload extends DeesElement {
display: grid;
margin: 10px 0px;
margin-bottom: 24px;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
.hidden {
@ -76,31 +84,15 @@ export class DeesInputFileupload extends DeesElement {
}
.maincontainer {
color: ${cssManager.bdTheme('#333', '#ccc')};
}
.label {
font-size: 14px;
margin-bottom: 5px;
}
.uploadButton {
position: relative;
cursor: pointer;
padding: 8px;
max-width: 600px;
background: ${cssManager.bdTheme('#fafafa', '#333333')};
border-radius: 3px;
text-align: center;
padding: 8px;
background: ${cssManager.bdTheme('#fafafa', '#222222')};
color: ${cssManager.bdTheme('#333', '#ccc')};
border-top: 1px solid #ffffff10;
}
.uploadButton:hover {
color: #fff;
background: rgb(3, 155, 229);
}
.uploadButton::after {
.maincontainer::after {
top: 2px;
right: 2px;
left: 2px;
@ -111,28 +103,71 @@ export class DeesInputFileupload extends DeesElement {
display: block;
border: 2px dashed rgba(255, 255, 255, 0);
transition: all 0.2s;
pointer-events: none;
background: #00000000;
}
.uploadButton.dragOver::after {
.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-bottom: 1px dashed #444;
border-radius: 3px;
color: ${cssManager.bdTheme('#666', '#ccc')};
padding: 8px;
font-family: 'Inter';
}
.uploadButton:hover .uploadCandidate {
color: ${cssManager.bdTheme('#fff', '#fff')};
border-bottom: 1px dashed #fff;
font-family: 'Roboto', 'Inter', 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;
}
`,
];
@ -141,10 +176,32 @@ export class DeesInputFileupload extends DeesElement {
<div class="hidden">
<input type="file"></div>
</div>
<div class="maincontainer">
${this.label ? html`<div class="label">${this.label}</div>` : null}
${this.value.map((fileArg) => html` <div class="uploadCandidate">${fileArg.name} | ${fileArg.size}</div> `)}
<div class="uploadButton ${this.state === 'dragOver' ? 'dragOver' : ''}" @click=${this.openFileSelector}>
${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>
@ -154,6 +211,8 @@ export class DeesInputFileupload extends DeesElement {
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) {
@ -163,7 +222,20 @@ export class DeesInputFileupload extends DeesElement {
}
public firstUpdated() {
const dropArea = this.shadowRoot.querySelector('.uploadButton');
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) {

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,153 @@
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');
@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,5 +1,5 @@
import { customElement, property, html, TemplateResult, DeesElement } from '@designestate/dees-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 {
@ -12,9 +12,15 @@ export class DeesInputQuantitySelector extends DeesElement {
public static demo = () => html`<dees-input-quantityselector></dees-input-quantityselector>`;
// INSTANCE
public changeSubject = new domtools.rxjs.Subject();
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
@property()
public label: string = 'Label';
@property({
type: String,
reflect: true,
})
public key: string;
@property({
@ -79,7 +85,6 @@ export class DeesInputQuantitySelector extends DeesElement {
}
.selector:hover {
cursor: pointer;
}
.quantity {

View File

@ -1,5 +1,5 @@
import {customElement, DeesElement, TemplateResult, property, html} from '@designestate/dees-element';
import * as domtools from '@designestate/dees-domtools';
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 {
@ -12,9 +12,12 @@ export class DeesInputRadio extends DeesElement {
public static demo = () => html`<dees-input-radio></dees-input-radio>`;
// INSTANCE
public changeSubject = new domtools.rxjs.Subject();
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
@property()
@property({
type: String,
reflect: true,
})
public key: string;
@property()
@ -48,7 +51,6 @@ export class DeesInputRadio extends DeesElement {
display: block;
position: relative;
margin: 20px 0px;
cursor: pointer;
}
.maincontainer {
@ -88,8 +90,8 @@ export class DeesInputRadio extends DeesElement {
}
.checkbox.selected {
background: #039BE5;
border: 1px solid #039BE5;
background: #0050b9;
border: 1px solid #0050b9;
}
.maincontainer:hover .checkbox.selected {

View File

View File

@ -1,5 +1,16 @@
import {customElement, DeesElement, TemplateResult, property, html, cssManager} from '@designestate/dees-element';
import * as domtools from '@designestate/dees-domtools';
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 {
@ -9,97 +20,208 @@ declare global {
@customElement('dees-input-text')
export class DeesInputText extends DeesElement {
public static demo = () => html`<dees-input-text></dees-input-text>`;
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>
`;
// INSTANCE
public changeSubject = new domtools.rxjs.Subject();
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject<DeesInputText>();
@property({
type: String
type: String,
})
public label: string = 'Label';
public label: string;
@property({
type: String
type: String,
})
public description: string;
@property({
type: String,
reflect: true,
})
public key: string;
@property({
type: String
type: String,
reflect: true,
})
public value: string = '';
@property({
type: Boolean
type: Boolean,
})
public required: boolean = false;
@property({
type: Boolean
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: ${this.goBright ? '#333' : '#ccc'};
}
.label {
font-size: 14px;
margin-bottom: 5px;
}
input {
margin-top: 5px;
background: ${this.goBright ? '#fafafa' : '#222'};
border-top: ${this.goBright ? '1px solid #CCC' : '1px solid #444'};
border-bottom: ${this.goBright ? '1px solid #CCC' : '1px solid #333'};
border-right: ${this.goBright ? '1px solid #CCC' : 'none'};
border-left: ${this.goBright ? '1px solid #CCC' : 'none'};
padding-left:10px;
padding-right: 10px;
border-radius: 2px;
width: 100%;
line-height: 48px;
transition: all 0.2s;
outline: none;
font-size: 16px;
font-family: Inter;
font-family: ${this.isPasswordBool ? 'monospace' : 'Roboto'};
letter-spacing: ${this.isPasswordBool ? '1px' : 'normal'};
color: ${this.goBright ? '#333' : '#ccc'};
}
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 #e4002b;
}
${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" value=${this.value} @input="${this.updateValue}" .disabled=${this.disabled} />
<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;
@ -113,4 +235,20 @@ export class DeesInputText extends DeesElement {
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

@ -1,39 +1,35 @@
import * as plugins from './plugins.js';
import * as plugins from './00plugins.js';
import {
cssManager,
css,
type CSSResult,
customElement,
DeesElement,
domtools,
html,
property,
} from '@designestate/dees-element';
} 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-mobilenavigation
.menuItems="${[
<dees-button @click=${() => {
DeesMobilenavigation.createAndShow([
{
name: 'hello1',
action: async () => {},
name: 'Test',
action: async (deesMobileNav) => {
alert('test');
return null;
},
},
{
name: 'hello2',
action: async () => {},
},
{
name: 'hello3',
action: async () => {},
},
]}"
></dees-mobilenavigation>
]);
}}></dees-button>
`;
private static singletonRef: DeesMobilenavigation;
public static async createAndInit(menuItemsArg: plugins.tsclass.website.IMenuItem[]) {
public static async createAndShow(menuItemsArg: plugins.tsclass.website.IMenuItem<DeesMobilenavigation>[]) {
if (!this.singletonRef) {
this.singletonRef = new DeesMobilenavigation();
document.body.append(this.singletonRef);
@ -56,7 +52,7 @@ export class DeesMobilenavigation extends DeesElement {
})
public menuItems: plugins.tsclass.website.IMenuItem[] = [];
readyDeferred = domtools.plugins.smartpromise.defer();
readyDeferred: plugins.smartpromise.Deferred<any> = domtools.plugins.smartpromise.defer();
constructor() {
super();
@ -93,8 +89,8 @@ export class DeesMobilenavigation extends DeesElement {
right: 0px;
top: 0px;
bottom: 0px;
background: ${cssManager.bdTheme('#eeeeeb', '#000')};;
border-left: 1px dashed #444;
background: ${cssManager.bdTheme('#eeeeeb', '#000')};
border-left: 1px solid ${cssManager.bdTheme('#eeeeeb', '#222')};
pointer-events: none;
}
@ -109,7 +105,6 @@ export class DeesMobilenavigation extends DeesElement {
padding: 8px;
margin-left: -8px;
margin-right: -8px;
cursor: pointer;
border-radius: 3px;
}
.menuItem:hover {
@ -120,7 +115,7 @@ export class DeesMobilenavigation extends DeesElement {
text-align: left;
font-size: 24px;
padding: 8px 0px;
font-family: Inter;
font-family: 'Roboto', 'Inter', sans-serif;
font-weight: 300;
border-bottom: 1px dashed #444;
margin-top: 16px;
@ -139,7 +134,7 @@ export class DeesMobilenavigation extends DeesElement {
class="menuItem"
@click="${() => {
this.hide();
menuItem.action();
menuItem.action(this);
}}"
>
${menuItem.name}
@ -160,6 +155,7 @@ export class DeesMobilenavigation extends DeesElement {
const main = this.shadowRoot.querySelector('.main');
if (!this.windowLayer) {
this.windowLayer = new DeesWindowLayer();
this.windowLayer.options.blur = true;
this.windowLayer.addEventListener('click', () => {
this.hide();
});

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: 'Roboto', 'Inter', 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: 'Roboto', 'Inter', 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

@ -1,8 +1,8 @@
import { DeesElement, property, html, customElement, domtools, TemplateResult } from '@designestate/dees-element';
import { DeesElement, property, html, customElement, domtools, type TemplateResult, type CSSResult, } from '@design.estate/dees-element';
import { Deferred } from '@pushrocks/smartpromise';
import { Deferred } from '@push.rocks/smartpromise';
import type pdfjsTypes from 'pdfjs-dist';
// import type pdfjsTypes from 'pdfjs-dist';
declare global {
interface HTMLElementTagNameMap {
@ -32,7 +32,7 @@ export class DeesPdf extends DeesElement {
return html`
<style>
:host {
font-family: 'Inter', sans-serif;
font-family: 'Roboto', 'Inter', sans-serif;
display: block;
box-sizing: border-box;
max-width: 800px;
@ -51,24 +51,15 @@ export class DeesPdf extends DeesElement {
}
public static pdfJsReady: Promise<any>;
public static pdfjsLib: typeof pdfjsTypes;
public static pdfjsLib: any // typeof pdfjsTypes;
public async connectedCallback() {
super.connectedCallback();
if (!DeesPdf.pdfJsReady) {
const pdfJsReadyDeferred = domtools.plugins.smartpromise.defer();
DeesPdf.pdfJsReady = pdfJsReadyDeferred.promise;
const loadDeferred = domtools.plugins.smartpromise.defer();
const script = document.createElement('script');
script.addEventListener('load', () => {
console.log('pdf.js loaded!');
loadDeferred.resolve();
});
script.src = 'https:////mozilla.github.io/pdf.js/build/pdf.js';
document.getElementsByTagName('head')[0].appendChild(script);
// The workerSrc property shall be specified.
await loadDeferred.promise;
DeesPdf.pdfjsLib = window['pdfjs-dist/build/pdf'];
DeesPdf.pdfjsLib.GlobalWorkerOptions.workerSrc = '//mozilla.github.io/pdf.js/build/pdf.worker.js';
// @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;

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

@ -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,218 @@
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[] = [];
public static styles = [
cssManager.defaultStyles,
css`
:host {
color: ${cssManager.bdTheme('#333', '#ccc')};
user-select: none;
display: block;
position: relative;
height: 100%;
}
.maincontainer {
position: absolute;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
overflow: hidden;
}
.appbar {
position: fixed;
top: 0;
height: 32px;
width: 100%;
background: ${cssManager.bdTheme('#eeeeeb', '#222')};
border-bottom: 1px solid ${cssManager.bdTheme('#ccc', '#ffffff10')};
font-size: 14px;
line-height: 32px;
font-family: 'Roboto', 'Inter', sans-serif;
padding: 0px 16px;
z-index: 2;
box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.8);
display: grid;
grid-template-columns: min-content 1fr auto;
}
.appbar .viewTabs {
padding: 0px 16px;
display: flex;
flex-direction: row;
align-items: center;
}
.viewTab {
padding: 0px 8px;
}
.viewTab:hover {
background: ${cssManager.bdTheme(colors.bright.blue, colors.dark.blue)};
color: ${cssManager.bdTheme('#000', '#fff')};
}
.viewTab:active {
background: ${cssManager.bdTheme(colors.bright.blueActive, colors.dark.blueActive)};
color: ${cssManager.bdTheme('#000', '#fff')};
}
.appName {
white-space: nowrap;
}
.appActions {
display: flex;
}
.appActions .action {
}
.appActions .action:hover {
color: ${cssManager.bdTheme('#000', '#fff')};
}
.appcontent {
z-index: 1;
position: fixed;
top: 32px;
height: calc(100vh - 32px - 24px);
bottom: 24px;
width: 100%;
overflow: auto;
background: ${cssManager.bdTheme('#eeeeeb', '#000')};
}
.controlbar {
color: #fff;
position: absolute;
bottom: 0px;
left: 0px;
width: 100%;
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;
}
.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 async launchTerminal() {
const maincontainer = this.shadowRoot.querySelector('.maincontainer');
const terminal = new DeesTerminal();
maincontainer.appendChild(terminal);
terminal.style.position = 'absolute';
terminal.style.zIndex = '1';
terminal.style.top = '32px';
terminal.style.left = '0px';
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)';
}
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

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

View File

@ -3,13 +3,14 @@ import {
html,
DeesElement,
property,
TemplateResult,
type TemplateResult,
cssManager,
css,
type CSSResult,
unsafeCSS,
} from '@designestate/dees-element';
} from '@design.estate/dees-element';
import * as domtools from '@designestate/dees-domtools';
import * as domtools from '@design.estate/dees-domtools';
declare global {
interface HTMLElementTagNameMap {
@ -23,6 +24,8 @@ export class DeesSpinner extends DeesElement {
<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({
@ -30,6 +33,11 @@ export class DeesSpinner extends DeesElement {
})
public size = 20;
@property({
type: String,
})
public bnw: boolean = false;
@property()
public status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
@ -45,6 +53,7 @@ export class DeesSpinner extends DeesElement {
}
#loading {
position: relative;
transition: none;
display: flex;
justify-content: center;
@ -58,16 +67,14 @@ export class DeesSpinner extends DeesElement {
}
#loading.success {
border: 0px solid rgba(255, 255, 255, 0);
background: #8bc34a;
border: none;
border-radius: 50%;
animation: none;
-webkit-animation: none;
}
#loading.error {
border: 0px solid rgba(255, 255, 255, 0);
background: #e64a19;
border: none;
border-radius: 50%;
animation: none;
-webkit-animation: none;
@ -84,36 +91,10 @@ export class DeesSpinner extends DeesElement {
}
}
#loading .checkmark {
display: inline-block;
width: 22px;
height: 22px;
-ms-transform: rotate(45deg); /* IE 9 */
-webkit-transform: rotate(45deg); /* Chrome, Safari, Opera */
transform: rotate(45deg);
}
#loading .checkmark_stem {
dees-icon {
position: absolute;
width: 3px;
height: 9px;
background-color: #fff;
left: 9px;
top: 5px;
}
#loading .checkmark_kick {
position: absolute;
width: 3px;
height: 3px;
background-color: #fff;
left: 6px;
top: 11px;
}
#loading.disabled .checkmark_stem,
#loading.disabled .checkmark_kick {
background-color: ${cssManager.bdTheme('#333', '#fff')};
height: 100%;
width: 100%;
}
`,
];
@ -125,16 +106,25 @@ export class DeesSpinner extends DeesElement {
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">
${this.status === 'success' || this.status === 'error'
? html`
<span class="checkmark">
<div class="checkmark_stem"></div>
<div class="checkmark_kick"></div>
</span>
`
: null}
${(() => {
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

@ -1,15 +1,19 @@
import * as plugins from './00plugins.js';
import * as colors from './00colors.js';
import {
DeesElement,
customElement,
html,
css,
unsafeCSS,
type CSSResult,
cssManager,
property,
TemplateResult,
} from '@designestate/dees-element';
type TemplateResult,
} from '@design.estate/dees-element';
import * as domtools from '@designestate/dees-domtools';
import * as domtools from '@design.estate/dees-domtools';
export interface IStep {
title: string;
@ -48,15 +52,15 @@ export class DeesStepper extends DeesElement {
`,
validationFunc: async (stepperArg, elementArg) => {
const deesForm = elementArg.querySelector('dees-form');
deesForm.addEventListener('formData', eventArg => {
deesForm.addEventListener('formData', (eventArg) => {
stepperArg.goNext();
})
}
});
},
},
{
title: 'Whats your mobile number?',
content: html``,
}
},
] as IStep[]}
></dees-stepper>
`;
@ -98,7 +102,7 @@ export class DeesStepper extends DeesElement {
transition: all 0.7s ease-in-out;
max-width: 500px;
min-height: 300px;
border-radius: 3px;
border-radius: 8px;
background: ${cssManager.bdTheme('#ffffff', '#181818')};
border-top: 1px solid ${cssManager.bdTheme('#ffffff', '#181818')};
color: ${cssManager.bdTheme('#333', '#fff')};
@ -126,6 +130,7 @@ export class DeesStepper extends DeesElement {
}
.step .stepCounter {
color: #999;
position: absolute;
top: 0px;
right: 0px;
@ -136,6 +141,8 @@ export class DeesStepper extends DeesElement {
}
.step .goBack {
color: #999;
cursor: default;
position: absolute;
top: 0px;
left: 0px;
@ -143,17 +150,31 @@ export class DeesStepper extends DeesElement {
font-size: 12px;
border-bottom-right-radius: 3px;
background: ${cssManager.bdTheme('#00000008', '#ffffff08')};
cursor: pointer;
}
.step .goBack:hover {
background: ${cssManager.bdTheme('#00000012', '#ffffff12')};
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: Inter;
font-family: 'Roboto', 'Inter', sans-serif;
font-size: 25px;
font-weight: 300;
}
@ -177,7 +198,7 @@ export class DeesStepper extends DeesElement {
: ''}"
>
${this.getIndexOfStep(stepArg) > 0
? html`<div class="goBack" @click=${this.goBack}><- go to previous step</div>`
? 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
@ -195,22 +216,26 @@ export class DeesStepper extends DeesElement {
return this.steps.findIndex((stepArg2) => stepArg === stepArg2);
};
public firstUpdated() {
public async firstUpdated() {
await this.domtoolsPromise;
await this.domtools.convenience.smartdelay.delayFor(0);
this.selectedStep = this.steps[0];
this.setScrollStatus();
}
public updated() {
public async updated() {
this.setScrollStatus();
}
public scroller: typeof domtools.plugins.SweetScroll.prototype;
public async setScrollStatus() {
await domtools.plugins.smartdelay.delayFor(50);
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

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

@ -1,16 +1,24 @@
import * as colors from './00colors.js';
import * as plugins from './00plugins.js';
import { demoFunc } from './dees-table.demo.js';
import {
customElement,
html,
DeesElement,
property,
TemplateResult,
type TemplateResult,
cssManager,
css,
unsafeCSS,
type CSSResult,
state,
} from '@designestate/dees-element';
resolveExec,
} from '@design.estate/dees-element';
import * as domtools from '@designestate/dees-domtools';
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 {
@ -18,75 +26,52 @@ declare global {
}
}
export interface IDataAction<T = any> {
// interfaces
export interface ITableAction<T = any> {
name: string;
iconName: 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';
actionFunc: (itemArg: T) => Promise<any>;
/**
* 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 = () => html`
<style>
.demoWrapper {
box-sizing: border-box;
position: absolute;
width: 100%;
height: 100%;
padding: 60px;
background: #000000;
}
</style>
<div class="demoWrapper">
<dees-table
heading1="Current Account Statement"
heading2="Bunq - Payment Account 2 - April 2021"
.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',
},
]}
.dataActions="${[{
name: 'upload',
iconName: 'upload',
useTableBehaviour: 'upload',
actionFunc: async (itemArg: any) => {
},
},{
name: 'visibility',
iconName: 'visibility',
useTableBehaviour: 'preview',
actionFunc: async (itemArg: any) => {
},
}] as IDataAction[]}"
>This is a slotted Text</dees-table>
</div>
`;
public static demo = demoFunc;
// INSTANCE
@property({
@ -104,10 +89,63 @@ export class DeesTable<T> extends DeesElement {
})
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: IDataAction[] = [];
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,
@ -115,18 +153,15 @@ export class DeesTable<T> extends DeesElement {
public selectedDataRow: T;
@property({
type: String,
type: Array,
})
public type: 'normal' | 'highlighted' | 'discreet' | 'big' = 'normal';
@property({
type: String,
})
public status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
public editableFields: string[] = [];
public files: File[] = [];
public fileWeakMap = new WeakMap();
public dataChangeSubject = new domtools.plugins.smartrx.rxjs.Subject();
constructor() {
super();
}
@ -136,20 +171,83 @@ export class DeesTable<T> extends DeesElement {
css`
.mainbox {
color: ${cssManager.bdTheme('#333', '#fff')};
font-family: Inter;
font-size: 16px;
font-family: 'Roboto', 'Inter', sans-serif;
font-weight: 400;
font-size: 14px;
padding: 16px;
display: block;
width: 100%;
min-height: 50px;
background: ${cssManager.bdTheme('#fafafa', '#333333')};
background: ${cssManager.bdTheme('#ffffff', '#222222')};
border-radius: 3px;
border-top: 1px solid ${cssManager.bdTheme('#fff', '#444')};
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
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: 'Roboto', 'Inter', sans-serif;
}
.headingContainer {
}
.heading {
}
.heading1 {
font-weight: 600;
}
.heading2 {
opacity: 0.6;
}
.headingSeparation {
margin-top: 7px;
border-bottom: 1px solid ${cssManager.bdTheme('#bcbcbc', '#bcbcbc')};
border-bottom: 1px solid ${cssManager.bdTheme('#bcbcbc', '#444444')};
}
.headerActions {
user-select: none;
display: flex;
flex-direction: row;
margin-left: auto;
}
.headerAction {
display: flex;
flex-direction: row;
color: ${cssManager.bdTheme('#333', '#ccc')};
margin-left: 16px;
}
.headerAction:hover {
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,
@ -171,10 +269,9 @@ export class DeesTable<T> extends DeesElement {
text-align: left;
}
tr:hover {
cursor: pointer;
}
tr:hover .innerCellContainer {
background: ${cssManager.bdTheme('#22222210', '#ffffff20')};
tr:hover td {
background: ${cssManager.bdTheme('#22222210', '#ffffff10')};
}
tr:first-child:hover {
cursor: auto;
@ -182,19 +279,34 @@ export class DeesTable<T> extends DeesElement {
tr:first-child:hover .innerCellContainer {
background: none;
}
tr.selected .innerCellContainer {
tr.selected td {
background: ${cssManager.bdTheme('#22222220', '#ffffff20')};
}
tr.hasAttachment td {
background: ${cssManager.bdTheme('#0098847c', '#0098847c')};
}
th {
text-transform: uppercase;
text-transform: none;
font-family: 'Roboto', 'Inter', sans-serif;
font-weight: 500;
}
th,
td {
padding: 3px 0px;
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 {
@ -208,31 +320,87 @@ export class DeesTable<T> extends DeesElement {
td:last-child {
border-right: none;
}
.action {
margin: -8px 0px;
padding: 8px;
line-height: 16px;
display: inline-block;
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;
}
.action:first-child {
margin-left: -8px;
width: min-content;
.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('#CCC', '#111')};
background: ${cssManager.bdTheme(colors.bright.blue, colors.dark.blue)};
}
.tableStatistics {
padding: 4px 16px;
font-size: 12px;
.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: 'Roboto', 'Inter', 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 {
}
`,
];
@ -241,15 +409,73 @@ export class DeesTable<T> extends DeesElement {
return html`
<div class="mainbox">
<!-- the heading part -->
<div>${this.heading1}</div>
<div>${this.heading2}</div>
<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">
${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
? (() => {
const headings: string[] = Object.keys(this.data[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>
@ -270,32 +496,42 @@ export class DeesTable<T> extends DeesElement {
}
})()}
</tr>
${this.data.map(
(itemArg) => html`
${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) => {
console.log((eventArg.target as HTMLElement).tagName)
console.log('dragenter');
eventArg.preventDefault();
eventArg.stopPropagation();
(eventArg.target as HTMLElement).parentElement.style.background = '#800000';
const realTarget = getTr(eventArg.target as HTMLElement);
console.log('dragenter');
console.log(realTarget);
setTimeout(() => {
realTarget.classList.add('hasAttachment');
}, 0);
}}
@dragleave=${async (eventArg: DragEvent) => {
console.log((eventArg.target as HTMLElement).tagName)
console.log('dragleave');
eventArg.preventDefault();
eventArg.stopPropagation();
(eventArg.target as HTMLElement).parentElement.style.background = 'none';
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 = []
const newFiles = [];
for (const file of Array.from(eventArg.dataTransfer.files)) {
this.files.push(file);
newFiles.push(file);
@ -303,17 +539,52 @@ export class DeesTable<T> extends DeesElement {
}
const result: File[] = this.fileWeakMap.get(itemArg as object);
if (!result) {
this.fileWeakMap.set(itemArg as object, newFiles)
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>
<div class="innerCellContainer">${itemArg[headingArg]}</div>
<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>
`
)}
@ -322,36 +593,192 @@ export class DeesTable<T> extends DeesElement {
return html`
<td>
<div class="innerCellContainer">
${(() => {
const actions: TemplateResult[] = [];
for (const action of this.dataActions) {
actions.push(html`<div class="action">${action.iconName ? html`
<dees-icon .iconName=${'upload_file'}></dees-icon>
` : action.name}</div>`)
}
return actions;
})()}
<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="tableStatistics">
${this.data.length} data rows (total) |
${this.selectedDataRow
? html`Row ${this.data.indexOf(this.selectedDataRow) + 1} selected`
: html`No row selected`}
<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">
${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 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,320 @@
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></dees-terminal> `;
// INSTANCE
private resizeObserver: ResizeObserver;
@property()
public setupCommand = `pnpm install @git.zone/tsbuild && clear && echo 'welcome'`;
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 = [
domtools.elementBasic.staticStyles,
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, '~/');
input.write(this.setupCommand);
input.write(`\n`);
}
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();
}
private 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)) {
resolve();
return;
}
}
setTimeout(checkPrompt, 100); // check every 100 ms
};
checkPrompt();
});
}
}

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,6 +1,7 @@
import { customElement, DeesElement, TemplateResult, html } from '@designestate/dees-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 {
@ -10,6 +11,7 @@ declare global {
@customElement('dees-toast')
export class DeesToast extends DeesElement {
public static demo = demoFunc;
constructor() {
super();

View File

@ -0,0 +1 @@
import {} from '@design.estate/dees-element';

View File

@ -0,0 +1,10 @@
import { html } from '@design.estate/dees-element';
import { DeesUpdater } from './dees-updater.js';
export const demoFunc = async () => {
const updater = await DeesUpdater.createAndShow();
setTimeout(async () => {
await updater.destroy();
}, 10000);
}

View File

@ -1,10 +1,16 @@
import { customElement, DeesElement, TemplateResult, html, property } from '@designestate/dees-element';
import * as domtools from '@designestate/dees-domtools';
import {
customElement,
DeesElement,
type TemplateResult,
html,
property,
type CSSResult,
domtools,
} from '@design.estate/dees-element';
import { demoFunc } from './dees-updater.demo.js';
import './dees-windowlayer';
import { css, cssManager } from '@designestate/dees-element';
import { css, cssManager } from '@design.estate/dees-element';
declare global {
interface HTMLElementTagNameMap {
@ -14,7 +20,13 @@ declare global {
@customElement('dees-updater')
export class DeesUpdater extends DeesElement {
public static demo = () => html`<dees-updater></dees-updater>`;
public static demo = demoFunc;
public static async createAndShow() {
const updater = new DeesUpdater();
document.body.appendChild(updater);
return updater;
}
@property({
type: String,
@ -35,56 +47,64 @@ export class DeesUpdater extends DeesElement {
cssManager.defaultStyles,
css`
.modalContainer {
will-change: transform;
position: relative;
background: ${cssManager.bdTheme('#eeeeeb', '#222')};
margin: auto;
max-width: 800px;
border-radius: 3px;
border-top: 1px solid ${cssManager.bdTheme('#eeeeeb', '#333')};
}
will-change: transform;
position: relative;
background: ${cssManager.bdTheme('#eeeeeb', '#222')};
max-width: 800px;
border-radius: 8px;
border-top: 1px solid ${cssManager.bdTheme('#eeeeeb', '#333')};
}
.headingContainer {
display: flex;
justify-content: center;
align-items: center;
padding: 40px 40px;
}
.headingContainer {
display: flex;
justify-content: center;
align-items: center;
padding: 40px 40px;
}
h1 {
margin: none;
font-size: 20px;
color: ${cssManager.bdTheme('#333', '#fff')};
margin-left: 20px;
font-weight: normal;
}
h1 {
margin: none;
font-size: 20px;
color: ${cssManager.bdTheme('#333', '#fff')};
margin-left: 20px;
font-weight: normal;
}
.buttonContainer {
display: grid;
grid-template-columns: 50% 50%;
}
`
]
.buttonContainer {
display: grid;
grid-template-columns: 50% 50%;
}
`,
];
public render(): TemplateResult {
return html`
<dees-windowlayer @clicked="${this.windowLayerClicked}">
<dees-windowlayer
@clicked="${this.windowLayerClicked}"
.options=${{
blur: true,
}}
>
<div class="modalContainer">
<div class="headingContainer">
<dees-spinner .size=${60}></dees-spinner>
<h1>Updating the application...</h1>
</div>
<div class="progress">
<dees-progressbar .progress=${0.5}></dees-progressbar>
</div>
<div class="buttonContainer">
<dees-button>More info</dees-button>
<dees-button>Changelog</dees-button>
</div>
</div>
</dees-windowlayer>>
</div> </dees-windowlayer
>>
`;
}
private windowLayerClicked() {
const windowLayer = this.shadowRoot.querySelector('dees-windowlayer');
windowLayer.toggleVisibility();
public async destroy() {
this.parentElement.removeChild(this);
}
private windowLayerClicked() {}
}

View File

@ -0,0 +1,99 @@
import {
customElement,
DeesElement,
domtools,
type TemplateResult,
html,
property,
type CSSResult,
state,
css,
cssManager,
} from '@design.estate/dees-element';
declare global {
interface HTMLElementTagNameMap {
'dees-windowcontrols': DeesWindowControls;
}
}
@customElement('dees-windowcontrols')
export class DeesWindowControls extends DeesElement {
// STATIC
public static demo = () => html`<dees-windowcontrols></dees-windowcontrols>`;
// Instance
@property({
reflect: true,
})
public type: 'mac' | 'linux' | 'windows' = 'mac';
@property({
reflect: true,
})
public position: 'left' | 'right' = 'left';
public static styles = [
cssManager.defaultStyles,
css`
:host {
position: relative;
display: block;
box-sizing: border-box;
padding-left: 16px;
padding-right: 16px;
}
.windowControls {
height: 100%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.windowControls div {
width: 12px;
height: 12px;
display: inline-block;
border-radius: 50%;
margin: 0px;
padding: 0px;
background: #222222;
}
.windowControls div.close {
background: #ff5f57;
margin-right: 12px;
}
.windowControls div.toDock {
background: #ffbd2e;
margin-right: 12px;
}
.windowControls div.minMax {
background: #27c93f;
}
.windowControls div:hover {
background: #333333;
}
`,
];
public render(): TemplateResult {
return html`
${(this.type === 'mac' && this.position === 'left') ||
((this.type === 'linux' || this.type === 'windows') && this.position === 'right')
? html`
<div class="windowControls">
<div class="close"></div>
<div class="toDock"></div>
<div class="minMax"></div>
</div>
`
: html``}
`;
}
}

View File

@ -1,6 +1,4 @@
import { customElement, DeesElement, TemplateResult, html, property } from '@designestate/dees-element';
import * as domtools from '@designestate/dees-domtools';
import { customElement, DeesElement, domtools, type TemplateResult, html, property, type CSSResult, state, } from '@design.estate/dees-element';
declare global {
interface HTMLElementTagNameMap {
@ -8,11 +6,33 @@ declare global {
}
}
export interface IOptions_DeesWindowLayer {
blur: boolean;
}
@customElement('dees-windowlayer')
export class DeesWindowLayer extends DeesElement {
// STATIC
public static demo = () => html`<dees-windowlayer></dees-windowlayer>`;
public static async createAndShow(optionsArg?: IOptions_DeesWindowLayer) {
const domtoolsInstance = domtools.DomTools.getGlobalDomToolsSync();
const windowLayer = new DeesWindowLayer();
windowLayer.options = {
...windowLayer.options,
...optionsArg,
}
document.body.append(windowLayer);
await domtoolsInstance.convenience.smartdelay.delayFor(0);
windowLayer.show();
return windowLayer;
}
@state()
public options: IOptions_DeesWindowLayer = {
blur: false
};
// INSTANCE
@property({
type: Boolean
@ -29,7 +49,7 @@ export class DeesWindowLayer extends DeesElement {
${domtools.elementBasic.styles}
<style>
.windowOverlay {
transition: all 0.3s;
transition: all 0.2s;
will-change: transform;
position: fixed;
top: 0px;
@ -40,18 +60,29 @@ export class DeesWindowLayer extends DeesElement {
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.0);
backdrop-filter: brightness(1);
backdrop-filter: brightness(1) ${this.options.blur ? 'blur(0px)' : ''};
pointer-events: none;
z-index: 200;
}
.slotContent {
position: fixed;
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
z-index: 201;
}
.visible {
background: rgba(0, 0, 0, 0.2);
backdrop-filter: brightness(0.3);
backdrop-filter: brightness(0.9) ${this.options.blur ? 'blur(2px)' : ''};
pointer-events: all;
}
</style>
<div @click=${this.dispatchClicked} class="windowOverlay ${this.visible ? 'visible' : null}">
<div class="windowOverlay ${this.visible ? 'visible' : null}">
</div>
<div @click=${this.dispatchClicked} class="slotContent">
<slot></slot>
</div>
`;
@ -82,4 +113,11 @@ export class DeesWindowLayer extends DeesElement {
await domtools.convenience.smartdelay.delayFor(0);
this.visible = false;
}
public async destroy() {
const domtools = await this.domtoolsPromise;
await this.hide();
await domtools.convenience.smartdelay.delayFor(300);
this.remove();
}
}

View File

@ -0,0 +1,3 @@
export class FormController {
}

View File

@ -1,21 +1,46 @@
export * from './dees-appui-activitylog.js';
export * from './dees-appui-appbar.js';
export * from './dees-appui-base.js';
export * from './dees-appui-maincontent.js';
export * from './dees-appui-mainmenu.js';
export * from './dees-appui-mainselector.js';
export * from './dees-button-exit.js';
export * from './dees-button.js';
export * from './dees-chart-area.js';
export * from './dees-chips.js';
export * from './dees-form.js';
export * from './dees-contextmenu.js';
export * from './dees-dataview-codebox.js';
export * from './dees-dataview-statusobject.js';
export * from './dees-editor.js';
export * from './dees-editor-markdown.js';
export * from './dees-editor-markdownoutlet.js';
export * from './dees-form-submit.js';
export * from './dees-form.js';
export * from './dees-hint.js';
export * from './dees-icon.js';
export * from './dees-input-checkbox.js';
export * from './dees-input-dropdown.js';
export * from './dees-input-fileupload.js';
export * from './dees-input-iban.js';
export * from './dees-input-typelist.js';
export * from './dees-input-phone.js';
export * from './dees-progressbar.js';
export * from './dees-input-quantityselector.js';
export * from './dees-input-radio.js';
export * from './dees-input-text.js';
export * from './dees-label.js';
export * from './dees-mobilenavigation.js';
export * from './dees-modal.js';
export * from './dees-input-multitoggle.js';
export * from './dees-pdf.js';
export * from './dees-simple-appdash.js';
export * from './dees-simple-login.js';
export * from './dees-speechbubble.js';
export * from './dees-spinner.js';
export * from './dees-stepper.js';
export * from './dees-table.js';
export * from './dees-terminal.js';
export * from './dees-toast.js';
export * from './dees-updater.js';
export * from './dees-windowcontrols.js';
export * from './dees-windowlayer.js';

View File

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

View File

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

View File

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

View File

@ -1,5 +0,0 @@
import * as tsclass from '@tsclass/tsclass';
export {
tsclass
}

View File

@ -3,8 +3,12 @@
"experimentalDecorators": true,
"useDefineForClassFields": false,
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "nodenext",
"esModuleInterop": true
}
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"verbatimModuleSyntax": true
},
"exclude": [
"dist_*/**/*.d.ts"
]
}