elbe-ui 0.2.11 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ import { Icons } from "../..";
2
+
3
+ export function Spinner({ padding = 2 }: { padding?: number }) {
4
+ return (
5
+ <div style={{ padding: `${padding}rem` }} class="centered">
6
+ <div class="rotate-box">
7
+ <Icons.LoaderCircle />
8
+ </div>
9
+ </div>
10
+ );
11
+ }
@@ -1,6 +1,7 @@
1
- import { applyProps, Icons, type ElbeProps } from "../..";
1
+ import { Icons, applyProps } from "../..";
2
2
  import { _ElbeErr } from "../util/error_view";
3
3
  import type { ElbeChild } from "../util/util";
4
+ import type { ElbeProps } from "./box";
4
5
  import { Card } from "./card";
5
6
  import { Column, FlexSpace } from "./flex";
6
7
 
@@ -1,4 +1,4 @@
1
- import { CircleXIcon } from "lucide-react";
1
+ import { Icons } from "../..";
2
2
 
3
3
  export function _ElbeErr(msg: string) {
4
4
  return (
@@ -6,7 +6,7 @@ export function _ElbeErr(msg: string) {
6
6
  class="row text-s gap-half"
7
7
  style="background: #ee0044; color: white; border-radius: 4px; text-align: left; padding: .5rem"
8
8
  >
9
- <CircleXIcon />
9
+ <Icons.CircleX />
10
10
  <div class="column gap-none cross-stretch">
11
11
  <b class="text-s">elbe error</b>
12
12
  <span style="margin-top: -.125rem">{msg}</span>
@@ -1,4 +1,36 @@
1
1
  import type React from "preact/compat";
2
+ import { showToast } from "./toast";
2
3
 
3
4
  export type ElbeChild = React.ReactNode;
4
5
  export type ElbeChildren = ElbeChild[] | ElbeChild;
6
+
7
+ /**
8
+ * use the web share api if available, otherwise copy the data to the clipboard
9
+ * @param data the data you want to share
10
+ * @param toastMsg the message to show in the toast if the share api is not available
11
+ */
12
+ export function share(
13
+ data: { title: string; text?: string; url: string },
14
+ toastMsg = "copied to clipboard. Share it with others."
15
+ ) {
16
+ const msg = `${data.title}\n${data.text ?? ""}\n\n${data.url}`;
17
+
18
+ if (navigator.share) {
19
+ navigator.share(data).catch(() => copyToClipboard(msg, toastMsg));
20
+ } else {
21
+ copyToClipboard(msg, toastMsg);
22
+ }
23
+ }
24
+
25
+ /**
26
+ * copy the text to the clipboard
27
+ * @param text the text to copy to the clipboard
28
+ * @param toastMsg the message to show in the toast
29
+ */
30
+ export function copyToClipboard(
31
+ text: string,
32
+ toastMsg = "copied to clipboard"
33
+ ) {
34
+ navigator.clipboard.writeText(text);
35
+ if (toastMsg) showToast(toastMsg);
36
+ }
@@ -1,80 +1,47 @@
1
- #hide-site {
2
- position: fixed;
3
- left: 0px;
4
- right: 0px;
5
- top: 0px;
6
- bottom: 0px;
7
- background-color: white;
8
- z-index: 99; /* Higher than anything else in the document */
9
- }
10
-
11
- .margin-none {
12
- margin: 0;
13
- }
14
-
15
- .padded {
16
- padding: 1rem;
17
- }
1
+ // =============== MARKUP ===============
18
2
 
19
- .padding-none {
20
- padding: 0rem;
21
- }
3
+ i, .i {font-style: italic;}
4
+ b, .b {font-weight: bold;}
5
+ .u {text-decoration: underline;}
6
+ .ni {font-style: normal;}
7
+ .nb {font-weight: normal;}
8
+ .nu {text-decoration: none;}
9
+ .striked {text-decoration: line-through;}
10
+ .code {font-family: monospace;}
22
11
 
23
- .padded_v {
24
- padding-top: 1rem;
25
- padding-bottom: 1rem;
26
- }
12
+ // =============== SPACING ===============
27
13
 
28
- .padded_h {
29
- padding-left: 1rem;
30
- padding-right: 1rem;
31
- }
14
+ .padded {padding: 1rem;}
15
+ .padding-none {padding: 0rem;}
32
16
 
33
- .margined {
34
- margin: 1rem;
35
- }
17
+ .margined {margin: 1rem;}
18
+ .margined-none {margin: 0rem;}
36
19
 
37
- .margined_v {
38
- margin-top: 1rem;
39
- margin-bottom: 1rem;
40
- }
20
+ // =============== FLEX-Layouts ===============
41
21
 
42
- .margined_h {
43
- margin-left: 1rem;
44
- margin-right: 1rem;
22
+ .flex {
23
+ display: flex;
24
+ flex-direction: column;
45
25
  }
46
26
 
47
- .box {
48
- @extend .padded;
49
-
50
- @extend .text-m;
51
-
52
- @extend .primary;
53
-
54
- &.primary {
55
- @extend .primary;
56
- }
57
-
58
- &.secondary {
59
- @extend .secondary;
60
- }
61
-
62
- &.inverse {
63
- @extend .inverse;
27
+ @for $i from 1 through 12 {
28
+ .flex-#{$i} {
29
+ @extend .flex;
30
+ flex: $i;
64
31
  }
65
32
  }
66
33
 
67
- .flex {
34
+ .centered {
68
35
  display: flex;
69
- flex-direction: column;
36
+ align-items: center;
37
+ justify-content: center;
70
38
  }
71
39
 
72
- .card {
73
- @extend .box;
74
- border-width: 2px;
75
- border-style: solid;
76
- border-radius: $g-radius;
77
- //overflow: hidden;
40
+ .column {
41
+ display: flex;
42
+ flex-direction: column;
43
+ align-items: center;
44
+ gap: 1rem;
78
45
  }
79
46
 
80
47
  .row {
@@ -83,25 +50,7 @@
83
50
  align-items: center;
84
51
  gap: 1rem;
85
52
 
86
- &.wrap {
87
- flex-wrap: wrap;
88
- }
89
- }
90
-
91
- .if-wide {
92
- @media (max-width: 700px) {
93
- & {
94
- display: none;
95
- }
96
- }
97
- }
98
-
99
- .if-narrow {
100
- @media (min-width: 700px) {
101
- & {
102
- display: none;
103
- }
104
- }
53
+ &.wrap {flex-wrap: wrap;}
105
54
  }
106
55
 
107
56
  .row-resp,
@@ -119,165 +68,108 @@
119
68
  align-items: stretch;
120
69
  }
121
70
 
122
- &.row-resp-rev {
123
- flex-direction: column-reverse;
124
- }
71
+ &.row-resp-rev {flex-direction: column-reverse;}
125
72
  }
126
73
  }
127
74
 
128
- .column {
129
- display: flex;
130
- flex-direction: column;
131
- align-items: center;
132
- gap: 1rem;
133
- }
134
-
135
- // ================ GAP ================
75
+ .gap-none {gap: 0;}
76
+ .gap-quarter {gap: 0.25rem;}
77
+ .gap-half {gap: 0.5rem;}
78
+ .gap {gap: 1rem;}
136
79
 
137
- .gap-none {
138
- gap: 0;
80
+ @for $i from 0 through 6 {
81
+ .gap-#{$i} {gap: #{$i}rem;}
139
82
  }
140
83
 
141
- .gap-quarter {
142
- gap: 0.25rem;
84
+ $cross_modes: (stretch,start,end,center);
85
+ @each $name in $cross_modes {
86
+ .cross-#{$name} {align-items: $name;}
143
87
  }
144
88
 
145
- .gap-half {
146
- gap: 0.5rem;
89
+ $main_modes: (stretch,start,end,center,space-between);
90
+ @each $name in $main_modes {
91
+ .main-#{$name} {justify-content: $name;}
147
92
  }
148
93
 
149
- .gap {
150
- gap: 1rem;
151
- }
94
+ // =============== conditionals ===============
152
95
 
153
- @for $i from 0 through 6 {
154
- .gap-#{$i} {
155
- gap: #{$i}rem;
96
+ .if-wide {
97
+ @media (max-width: 700px) {
98
+ & {display: none;}
156
99
  }
157
100
  }
158
101
 
159
- $cross_modes: (stretch, start, end, center);
160
-
161
- @each $name in $cross_modes {
162
- .cross-#{$name} {
163
- align-items: $name;
102
+ .if-narrow {
103
+ @media (min-width: 700px) {
104
+ & {display: none;}
164
105
  }
165
106
  }
166
107
 
167
- $main_modes: (stretch, start, end, center, space-between);
108
+ // =============== element style ===============
168
109
 
169
- @each $name in $main_modes {
170
- .main-#{$name} {
171
- justify-content: $name;
172
- }
173
- }
174
-
175
- .column.cross-stretch-fill {
176
- align-items: stretch;
177
- width: 100%;
178
- }
110
+ .sharp {border-radius: 0;}
111
+ .rounded {border-radius: $g-radius;}
112
+ .round {border-radius: 50%;}
179
113
 
180
- @for $i from 1 through 12 {
181
- .flex-#{$i} {
182
- @extend .flex;
183
- flex: $i;
184
- }
185
- }
186
-
187
- .i {
188
- font-style: italic;
189
- }
190
- b,
191
- .b {
192
- font-weight: bold;
193
- }
114
+ img.round,
115
+ img.rounded {object-fit: cover;}
194
116
 
195
- .u {
196
- text-decoration: underline;
197
- }
117
+ .borderless {border: none;}
118
+ .raised {box-shadow: 0 0.1rem 1rem #00000033;}
198
119
 
199
- .ni {
200
- font-style: normal;
120
+ .scrollbars-none {
121
+ scrollbar-width: none;
122
+ -ms-overflow-style: none;
123
+ &::-webkit-scrollbar {display: none;}
201
124
  }
202
125
 
203
- .nb {
204
- font-weight: normal;
205
- }
126
+ .pointer {cursor: pointer;}
206
127
 
207
- .nu {
208
- text-decoration: none;
209
- }
128
+ .text-centered {text-align: center;}
129
+ .text-start {text-align: start;}
130
+ .text-end {text-align: end;}
210
131
 
211
- .striked {
212
- text-decoration: line-through;
213
- }
132
+ body:has(dialog[open=""]) {overflow: hidden;}
214
133
 
215
- .code {
216
- font-family: monospace;
217
- }
218
134
 
219
- .header {
220
- @extend .box;
221
- @extend .row;
222
- width: 100%;
223
- position: fixed;
224
- left: 0;
225
- top: 0;
226
- display: flex;
227
- border-width: 20px;
228
- border-style: solid;
229
- border-width: 0 0 2px 0;
230
- //border-width: 0 0 2px 0;
231
- background-color: transparent;
232
- backdrop-filter: blur(10px);
233
- -webkit-backdrop-filter: blur(10px);
234
- z-index: 20;
235
- // place border inside the element:
236
- box-sizing: border-box;
237
- height: 4rem;
238
- }
135
+ // =============== animations ===============
239
136
 
240
- .centered {
137
+ .rotate-box {
138
+ @extend .rotate;
241
139
  display: flex;
242
140
  align-items: center;
243
141
  justify-content: center;
244
142
  }
245
143
 
246
- .sharp {
247
- border-radius: 0;
144
+ .rotate {
145
+ animation: rotation 1.5s infinite linear;
248
146
  }
249
147
 
250
- .rounded {
251
- border-radius: $g-radius;
148
+ @keyframes rotation {
149
+ from {transform: rotate(0deg);}
150
+ to {transform: rotate(359deg);}
252
151
  }
253
152
 
254
- .round {
255
- border-radius: 50%;
256
- }
153
+ // =============== COMPONENTS ===============
257
154
 
258
- img.round,
259
- img.rounded {
260
- object-fit: cover;
261
- }
155
+ .box {
156
+ @extend .padded;
157
+ @extend .text-m;
158
+ @extend .primary;
262
159
 
263
- .base-limited {
264
- @extend .box;
265
- max-width: 700px;
266
- width: 100%;
267
- margin: 0 auto;
160
+ &.primary {@extend .primary;}
161
+ &.secondary {@extend .secondary;}
162
+ &.inverse {@extend .inverse;}
268
163
  }
269
164
 
270
- .scrollbars-none {
271
- scrollbar-width: none;
272
- -ms-overflow-style: none;
273
- &::-webkit-scrollbar {
274
- display: none;
275
- }
165
+ .card {
166
+ @extend .box;
167
+ border-width: 2px;
168
+ border-style: solid;
169
+ border-radius: $g-radius;
170
+ //overflow: hidden;
276
171
  }
277
172
 
278
- .pointer {
279
- cursor: pointer;
280
- }
281
173
 
282
174
  button {
283
175
  @extend .card;
@@ -327,17 +219,24 @@ button {
327
219
  }
328
220
  }
329
221
 
330
- input[type="text"],
331
- input[type="number"],
332
- input[type="password"],
333
- input[type="email"],
334
- input[type="date"],
335
- input[type="time"] {
222
+ .icon {
223
+ aspect-ratio: 1;
224
+ border-radius: 25%;
225
+ object-fit: cover;
226
+ }
227
+
228
+ .toast {
336
229
  @extend .card;
337
- padding: 0 0.75rem;
338
- height: 3rem;
339
- min-width: 12rem;
340
- width: 100%;
230
+ @extend .inverse;
231
+ @extend .b;
232
+ @extend .raised;
233
+ position: fixed;
234
+ bottom: 1rem;
235
+ right: 1rem;
236
+ z-index: 200;
237
+ left: 50%;
238
+ width: min(40rem, 90%);
239
+ transform: translateX(-50%);
341
240
  }
342
241
 
343
242
  select {
@@ -358,17 +257,6 @@ textarea {
358
257
  width: 100%;
359
258
  }
360
259
 
361
- .text-centered {
362
- text-align: center;
363
- }
364
-
365
- .text-left {
366
- text-align: left;
367
- }
368
-
369
- body:has(dialog[open=""]) {
370
- overflow: hidden;
371
- }
372
260
 
373
261
  dialog {
374
262
  @extend .column;
@@ -406,32 +294,30 @@ dialog {
406
294
  }
407
295
  }
408
296
 
409
- .raised {
410
- box-shadow: 0 0.1rem 1rem #00000033;
297
+ .expandable {
298
+ max-height: 0px;
299
+ transition: max-height 0.25s ease-out;
300
+ overflow: hidden;
411
301
  }
412
302
 
413
- .icon {
414
- aspect-ratio: 1;
415
- border-radius: 25%;
416
- object-fit: cover;
303
+ .expandable:has(*) {
304
+ max-height: 500px;
305
+ transition: max-height 0.25s ease-in;
417
306
  }
418
307
 
419
- .toast {
420
- @extend .card;
421
- @extend .inverse;
422
- @extend .b;
423
- @extend .raised;
424
- position: fixed;
425
- bottom: 1rem;
426
- right: 1rem;
427
- z-index: 200;
428
- left: 50%;
429
- width: min(40rem, 90%);
430
- transform: translateX(-50%);
431
- }
308
+ // =============== INPUTS ===============
432
309
 
433
- .borderless {
434
- border: none;
310
+ input[type="text"],
311
+ input[type="number"],
312
+ input[type="password"],
313
+ input[type="email"],
314
+ input[type="date"],
315
+ input[type="time"] {
316
+ @extend .card;
317
+ padding: 0 0.75rem;
318
+ height: 3rem;
319
+ min-width: 12rem;
320
+ width: 100%;
435
321
  }
436
322
 
437
323
  input[type="range"] {
@@ -441,28 +327,34 @@ input[type="range"] {
441
327
  cursor: pointer;
442
328
  width: 15rem;
443
329
 
444
- &::-webkit-slider-runnable-track,
445
- &::-moz-range-track {
330
+ &::-moz-range-track{
331
+ background-color: #707070;
332
+ border-radius: 100px;
333
+ border: none;
334
+ height: 0.5rem;
335
+ }
336
+
337
+ &::-webkit-slider-runnable-track
338
+ {
446
339
  @extend .secondary;
447
340
  border-radius: 100px;
448
341
  border: none;
449
342
  height: 0.5rem;
450
343
  }
451
344
 
452
- &::-webkit-slider-thumb,
453
- &::-moz-range-thumb {
345
+ &::-webkit-slider-thumb
346
+ {
454
347
  @extend .accent;
455
348
  border: none;
456
- -webkit-appearance: none; /* Override default look */
349
+ -webkit-appearance: none; // Override default look
457
350
  appearance: none;
458
- margin-top: -12px;
351
+ margin-top: -6px;
459
352
  border-radius: 100px;
460
353
  height: 1.2rem;
461
354
  width: 1.2rem;
462
355
  }
463
356
  }
464
357
 
465
- // ============ CHECKBOXES ============
466
358
 
467
359
  input[type="checkbox"] {
468
360
  -webkit-appearance: none;
@@ -518,34 +410,8 @@ input[type="checkbox"]:disabled {
518
410
  cursor: not-allowed;
519
411
  }
520
412
 
521
- .code {
522
- @extend .card;
523
- @extend .secondary;
524
- padding: 0.4rem 0.5rem;
525
- font-family: monospace;
526
- white-space: pre-wrap;
527
- }
528
-
529
- .rotate-box {
530
- @extend .rotate;
531
- display: flex;
532
- align-items: center;
533
- justify-content: center;
534
- }
535
-
536
- .rotate {
537
- animation: rotation 1.5s infinite linear;
538
- }
539
-
540
- @keyframes rotation {
541
- from {
542
- transform: rotate(0deg);
543
- }
413
+ // =============== TOOLTIP ===============
544
414
 
545
- to {
546
- transform: rotate(359deg);
547
- }
548
- }
549
415
 
550
416
  [data-tooltip]:hover:after {
551
417
  opacity: 1;
@@ -572,3 +438,13 @@ input[type="checkbox"]:disabled {
572
438
  [data-tooltip] {
573
439
  position: relative;
574
440
  }
441
+
442
+ // =============== LAYOUT ===============
443
+
444
+
445
+ .base-limited {
446
+ @extend .box;
447
+ max-width: 700px;
448
+ width: 100%;
449
+ margin: 0 auto;
450
+ }