elbe-ui 0.2.11 → 0.2.14

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.
package/dist/index.js CHANGED
@@ -15900,7 +15900,6 @@ var index = Object.freeze({
15900
15900
  ZoomIn,
15901
15901
  ZoomOut
15902
15902
  });
15903
- var $CircleXIcon = CircleX;
15904
15903
  var $X = X;
15905
15904
  var $icons = index;
15906
15905
  // node_modules/preact/dist/preact.mjs
@@ -16873,7 +16872,7 @@ function _ElbeErr(msg) {
16873
16872
  class: "row text-s gap-half",
16874
16873
  style: "background: #ee0044; color: white; border-radius: 4px; text-align: left; padding: .5rem",
16875
16874
  children: [
16876
- u3($CircleXIcon, {}, undefined, false, undefined, this),
16875
+ u3(Icons.CircleX, {}, undefined, false, undefined, this),
16877
16876
  u3("div", {
16878
16877
  class: "column gap-none cross-stretch",
16879
16878
  children: [
@@ -17038,7 +17037,6 @@ var _Flex = function(row, p3, elbe) {
17038
17037
  };
17039
17038
  // src/ui/components/icon_button.tsx
17040
17039
  var _btn2 = function({ icon, onTap, ...elbe }, colorManner = "major") {
17041
- console.log("icon", icon);
17042
17040
  return u3("button", {
17043
17041
  ...applyProps(elbe, [
17044
17042
  "row",
@@ -17053,7 +17051,8 @@ var _btn2 = function({ icon, onTap, ...elbe }, colorManner = "major") {
17053
17051
  height: "3rem",
17054
17052
  width: "3rem"
17055
17053
  }),
17056
- onClick: () => onTap && onTap()
17054
+ onClick: () => onTap && onTap(),
17055
+ children: typeof icon === "function" ? icon({}) : icon
17057
17056
  }, undefined, false, undefined, this);
17058
17057
  };
17059
17058
  class IconButton extends Rn.Component {
@@ -17429,12 +17428,28 @@ function showToast(message) {
17429
17428
  toast.remove();
17430
17429
  }, 3000);
17431
17430
  }
17431
+ // src/ui/util/util.ts
17432
+ function share(data, toastMsg = "copied to clipboard. Share it with others.") {
17433
+ const msg = `${data.title}\n${data.text ?? ""}\n\n${data.url}`;
17434
+ if (navigator.share) {
17435
+ navigator.share(data).catch(() => copyToClipboard(msg, toastMsg));
17436
+ } else {
17437
+ copyToClipboard(msg, toastMsg);
17438
+ }
17439
+ }
17440
+ function copyToClipboard(text, toastMsg = "copied to clipboard") {
17441
+ navigator.clipboard.writeText(text);
17442
+ if (toastMsg)
17443
+ showToast(toastMsg);
17444
+ }
17432
17445
 
17433
17446
  // src/index.tsx
17434
17447
  var Icons = $icons;
17435
17448
  export {
17436
17449
  showToast,
17437
17450
  showConfirmDialog,
17451
+ share,
17452
+ copyToClipboard,
17438
17453
  applyProps,
17439
17454
  ToggleButton,
17440
17455
  Text2 as Text,
@@ -1,5 +1,5 @@
1
- import { type ElbeProps } from "../..";
2
1
  import type { ElbeChild } from "../util/util";
2
+ import type { ElbeProps } from "./box";
3
3
  export type ToggleButtonItem<T> = {
4
4
  icon?: (_: any) => ElbeChild;
5
5
  label: string;
@@ -1,3 +1,19 @@
1
1
  import type React from "preact/compat";
2
2
  export type ElbeChild = React.ReactNode;
3
3
  export type ElbeChildren = ElbeChild[] | ElbeChild;
4
+ /**
5
+ * use the web share api if available, otherwise copy the data to the clipboard
6
+ * @param data the data you want to share
7
+ * @param toastMsg the message to show in the toast if the share api is not available
8
+ */
9
+ export declare function share(data: {
10
+ title: string;
11
+ text?: string;
12
+ url: string;
13
+ }, toastMsg?: string): void;
14
+ /**
15
+ * copy the text to the clipboard
16
+ * @param text the text to copy to the clipboard
17
+ * @param toastMsg the message to show in the toast
18
+ */
19
+ export declare function copyToClipboard(text: string, toastMsg?: string): void;
package/elbe.scss CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  @import url("https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap");
7
- @import url("https://fonts.googleapis.com/css2?family=Inter:slnt,wght@-10..0,100..900&display=swap");
7
+ @import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap");
8
8
  @import url("https://fonts.googleapis.com/css2?family=Calistoga&display=swap");
9
9
  @import url("https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&display=swap");
10
10
 
@@ -30,7 +30,7 @@ $c-modes: (
30
30
  border: #ffffff14,
31
31
  ),
32
32
  secondary: (
33
- back: inter($c-accent, #ffffff, 5%),
33
+ back: inter($c-accent, #ffffff, 7%),
34
34
  front: #222222,
35
35
  border: #22222214,
36
36
  ),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "elbe-ui",
3
3
  "type": "module",
4
- "version": "0.2.11",
4
+ "version": "0.2.14",
5
5
  "author": "Robin Naumann",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -27,7 +27,7 @@
27
27
  "build": "rm -r ./dist ; bun build --target=node src/index.tsx --outdir=dist && bun run build:declaration",
28
28
  "build:declaration": "tsc --emitDeclarationOnly --project tsconfig.types.json"
29
29
  },
30
- "module": "src/index.ts",
30
+ "module": "src/index.tsx",
31
31
  "devDependencies": {
32
32
  "@types/bun": "latest"
33
33
  },
@@ -29,7 +29,6 @@ function _btn(
29
29
  { icon, onTap, ...elbe }: IconButtonProps,
30
30
  colorManner: ElbeColorManners = "major"
31
31
  ) {
32
- console.log("icon", icon);
33
32
  return (
34
33
  <button
35
34
  {...applyProps(
@@ -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
+ }
File without changes