@lavalogic/scoria 0.40.10 → 0.40.12

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.
@@ -99,6 +99,7 @@
99
99
  </script>
100
100
 
101
101
  <script lang="ts">
102
+ import { loaderDefaults } from '../Helpers/loaderDefaults.svelte.js';
102
103
  import type { LoadingAnimationProps } from './LoadingAnimationProps.js';
103
104
 
104
105
  const {
@@ -114,7 +115,16 @@
114
115
  // The word the box spells: the caller's first name, or the product name as a
115
116
  // friendly fallback. Block capitals tumble cleanly and dodge lowercase
116
117
  // descenders clipping the baseline on landing.
117
- const word = $derived((name.trim() || 'FloWMS').toUpperCase());
118
+ // Explicit `name` prop wins; otherwise fall back to the app-wide default
119
+ // (set by the host so internally-rendered loaders also show the user), then
120
+ // finally the product name.
121
+ const word = $derived(
122
+ (name.trim() || loaderDefaults.name.trim() || 'FloWMS').toUpperCase()
123
+ );
124
+
125
+ // Caption text with any trailing dots/ellipsis stripped, since the pill
126
+ // appends its own animated "..." — avoids "moment……" style doubling.
127
+ const captionText = $derived(caption.replace(/[\s.…]+$/, ''));
118
128
 
119
129
  // Per-letter render data: each letter is a stack of DEPTH layers (painted
120
130
  // back-to-front) topped by the lit face, so it has genuine 3D thickness.
@@ -764,7 +774,7 @@
764
774
  whose opacity cycles, so the pill width stays fixed as they animate. -->
765
775
  {#if showText}
766
776
  <div class="caption">
767
- <span class="caption-label">{caption}</span><span
777
+ <span class="caption-label">{captionText}</span><span
768
778
  class="caption-dots"
769
779
  aria-hidden="true"><span>.</span><span>.</span><span>.</span></span
770
780
  >
@@ -786,17 +796,20 @@
786
796
  --kraft-inner: #8a6b43;
787
797
  --seam: rgba(0, 0, 0, 0.14);
788
798
  --ink: #3c4045;
789
- /* brand-blue confetti */
790
- --fill-1: #259fc7;
791
- --fill-2: #1b87b0;
792
- --fill-3: #5cc4e6;
793
- --fill-4: #0f6e93;
794
- --fill-5: #86d6ef;
799
+ /* subtle white confetti — shades of white/off-white */
800
+ --fill-1: #ffffff;
801
+ --fill-2: #eef1f4;
802
+ --fill-3: #f8fafb;
803
+ --fill-4: #e2e7ec;
804
+ --fill-5: #fbfcfd;
795
805
  --hw: calc(var(--w) / 2);
796
806
  --hd: calc(var(--d) / 2);
797
807
  --hh: calc(var(--h) / 2);
798
808
  --y-outer: calc(var(--hh) * -1 + 0.5px);
799
809
  --y-inner: calc(var(--hh) * -1 + 1.5px);
810
+ /* Push the box (and its letters) down within the footprint so the caption
811
+ pill has clear headroom above the opened flaps. */
812
+ --stage-shift: 40px;
800
813
  display: grid;
801
814
  place-items: center;
802
815
  position: relative;
@@ -806,6 +819,7 @@
806
819
  .flowms-loader .scene {
807
820
  width: var(--w);
808
821
  height: var(--h);
822
+ translate: 0 var(--stage-shift);
809
823
  perspective: 820px;
810
824
  perspective-origin: 50% 40%;
811
825
  transform-style: preserve-3d;
@@ -1021,7 +1035,7 @@
1021
1035
  margin-top: -48px;
1022
1036
  border-radius: 50%;
1023
1037
  opacity: 0;
1024
- background: radial-gradient(circle, rgba(127, 212, 236, 0.6666666667) 0%, rgba(37, 159, 199, 0.3333333333) 35%, rgba(37, 159, 199, 0) 70%);
1038
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0.3) 35%, rgba(255, 255, 255, 0) 70%);
1025
1039
  }
1026
1040
  .flowms-loader .fill {
1027
1041
  position: absolute;
@@ -1033,6 +1047,8 @@
1033
1047
  margin-top: -6.5px;
1034
1048
  border-radius: 3px;
1035
1049
  opacity: 0;
1050
+ /* faint edge so the white pieces stay visible on light backgrounds */
1051
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
1036
1052
  }
1037
1053
  .flowms-loader .fill:nth-child(5n+1) {
1038
1054
  background: var(--fill-1);
@@ -1064,7 +1080,7 @@
1064
1080
  .flowms-loader .letterstage {
1065
1081
  position: absolute;
1066
1082
  left: 50%;
1067
- top: 50%;
1083
+ top: calc(50% + var(--stage-shift)); /* track the shifted box */
1068
1084
  width: var(--w);
1069
1085
  height: var(--h);
1070
1086
  transform: translate(-50%, -50%);
@@ -1097,21 +1113,30 @@
1097
1113
  top: 0;
1098
1114
  }
1099
1115
  .flowms-loader {
1100
- /* Caption pill: sits above the box and on top of every other layer. */
1116
+ /* Caption pill: sits above the box and on top of every other layer. Its
1117
+ bottom edge is anchored just above the opened flaps, so longer messages
1118
+ wrap and grow UPWARD into the headroom rather than down over the box. */
1101
1119
  }
1102
1120
  .flowms-loader .caption {
1103
1121
  position: absolute;
1104
1122
  left: 50%;
1105
- top: 22px; /* above the box, clear of the eruption */
1123
+ bottom: calc(100% - 50px);
1106
1124
  transform: translateX(-50%);
1107
- white-space: nowrap;
1125
+ /* `width: max-content` opts out of the half-container shrink that
1126
+ `left: 50%` would otherwise impose, so the pill sizes to its text up
1127
+ to the max-width (then wraps). */
1128
+ width: max-content;
1129
+ max-width: min(380px, 90vw);
1130
+ white-space: normal;
1131
+ text-align: center;
1132
+ text-wrap: balance;
1108
1133
  padding: 7px 16px;
1109
1134
  border-radius: 999px;
1110
1135
  background: rgba(255, 255, 255, 0.82);
1111
1136
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
1112
1137
  -webkit-backdrop-filter: blur(3px);
1113
1138
  backdrop-filter: blur(3px);
1114
- font: 600 14px/1.2 system-ui, -apple-system, "Segoe UI", sans-serif;
1139
+ font: 600 14px/1.3 system-ui, -apple-system, "Segoe UI", sans-serif;
1115
1140
  color: #44515a;
1116
1141
  letter-spacing: 0.2px;
1117
1142
  }
@@ -4,7 +4,7 @@
4
4
  import LoadingAnimation from './LoadingAnimation.svelte';
5
5
  import type { LoadingModalProps } from './LoadingModalProps.js';
6
6
 
7
- const { showModal, message, name }: LoadingModalProps = $props();
7
+ const { showModal, message, name, caption }: LoadingModalProps = $props();
8
8
 
9
9
  let dialog: HTMLDialogElement | undefined = $state();
10
10
 
@@ -35,12 +35,9 @@
35
35
  aria-live="polite"
36
36
  >
37
37
  <div class="modal-root">
38
- {#if message}
39
- <p>{message}</p>
40
- {/if}
41
38
  <LoadingAnimation
42
39
  {name}
43
- showText={!message}
40
+ caption={message ?? caption}
44
41
  />
45
42
  </div>
46
43
  </dialog>
@@ -309,17 +306,4 @@ dialog .modal-root {
309
306
  align-items: center;
310
307
  background: rgba(0, 0, 0, 0);
311
308
  position: relative;
312
- }
313
- dialog .modal-root p {
314
- position: absolute;
315
- left: 50%;
316
- top: 50%;
317
- z-index: 99999;
318
- translate: -50% -80%;
319
- user-select: none;
320
- text-shadow: 0px 0px 3px #000000;
321
- background-color: rgba(0, 0, 0, 0.4);
322
- border-radius: 4px;
323
- padding: 1rem;
324
- color: #ffffff;
325
309
  }</style>
@@ -19,4 +19,11 @@ export interface LoadingModalProps {
19
19
  * erupts. When omitted the loader falls back to the product name.
20
20
  */
21
21
  name?: string | undefined;
22
+ /**
23
+ * Optional caption shown in the pill above the box (an animated `...` is
24
+ * appended automatically). Use this instead of {@link message} to keep the
25
+ * loader's own pill rather than rendering a separate message bar. Ignored
26
+ * when {@link message} is set. Defaults to `'Loading'`.
27
+ */
28
+ caption?: string | undefined;
22
29
  }
@@ -4,7 +4,7 @@
4
4
  import LoadingAnimation from './LoadingAnimation.svelte';
5
5
  import type { LoadingOverlayProps } from './LoadingOverlayProps.js';
6
6
 
7
- const { message, absolute, borderRadius, name }: LoadingOverlayProps =
7
+ const { message, absolute, borderRadius, name, caption }: LoadingOverlayProps =
8
8
  $props();
9
9
  </script>
10
10
 
@@ -17,12 +17,9 @@
17
17
  ? `border-radius: ${borderRadius};`
18
18
  : undefined}
19
19
  >
20
- {#if message}
21
- <p>{message}</p>
22
- {/if}
23
20
  <LoadingAnimation
24
21
  {name}
25
- showText={!message}
22
+ caption={message ?? caption}
26
23
  />
27
24
  </div>
28
25
 
@@ -42,19 +39,6 @@
42
39
  flex-flow: column;
43
40
  gap: 2rem;
44
41
  }
45
- .loading-overlay p {
46
- position: absolute;
47
- left: 50%;
48
- top: 50%;
49
- z-index: 99999;
50
- translate: -50% -80%;
51
- user-select: none;
52
- text-shadow: 0px 0px 3px #000000;
53
- background-color: rgba(0, 0, 0, 0.4);
54
- border-radius: 4px;
55
- padding: 1rem;
56
- color: #ffffff;
57
- }
58
42
  .loading-overlay.absolute {
59
43
  position: absolute;
60
44
  }</style>
@@ -13,6 +13,13 @@ export interface LoadingOverlayProps {
13
13
  * erupts. When omitted the loader falls back to the product name.
14
14
  */
15
15
  name?: string | undefined;
16
+ /**
17
+ * Optional caption shown in the pill above the box (an animated `...` is
18
+ * appended automatically). Use this instead of {@link message} to keep the
19
+ * loader's own pill rather than rendering a separate message bar. Ignored
20
+ * when {@link message} is set. Defaults to `'Loading'`.
21
+ */
22
+ caption?: string | undefined;
16
23
  /**
17
24
  * When `true` the overlay uses `position: absolute` and fills its
18
25
  * nearest positioned ancestor instead of the viewport. Use this
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Global fallback configuration for all loaders.
3
+ *
4
+ * Loaders rendered *internally* by components like {@link Table},
5
+ * {@link DesktopModal} and {@link AppointmentCalendar} can't be handed a `name`
6
+ * prop at the call site. So the host app sets {@link loaderDefaults.name} once —
7
+ * typically in its root layout inside a browser `$effect` — and every loader
8
+ * (including those internal ones) spells that name. An explicit `name` prop on a
9
+ * specific loader still takes precedence.
10
+ *
11
+ * Only assign this on the client (inside an `$effect` / `onMount`). Assigning it
12
+ * during SSR would share one request's value with concurrent requests.
13
+ */
14
+ export declare const loaderDefaults: {
15
+ name: string;
16
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Global fallback configuration for all loaders.
3
+ *
4
+ * Loaders rendered *internally* by components like {@link Table},
5
+ * {@link DesktopModal} and {@link AppointmentCalendar} can't be handed a `name`
6
+ * prop at the call site. So the host app sets {@link loaderDefaults.name} once —
7
+ * typically in its root layout inside a browser `$effect` — and every loader
8
+ * (including those internal ones) spells that name. An explicit `name` prop on a
9
+ * specific loader still takes precedence.
10
+ *
11
+ * Only assign this on the client (inside an `$effect` / `onMount`). Assigning it
12
+ * during SSR would share one request's value with concurrent requests.
13
+ */
14
+ export const loaderDefaults = $state({ name: '' });
package/dist/index.d.ts CHANGED
@@ -64,6 +64,7 @@ export type { InfoAlertProps } from './Components/InfoAlertProps.js';
64
64
  export type { InputProps } from './Components/InputProps.js';
65
65
  export { default as LoadingAnimation } from './Components/LoadingAnimation.svelte';
66
66
  export type { LoadingAnimationProps } from './Components/LoadingAnimationProps.js';
67
+ export { loaderDefaults } from './Helpers/loaderDefaults.svelte.js';
67
68
  export { default as LoadingModal } from './Components/LoadingModal.svelte';
68
69
  export type { LoadingModalProps } from './Components/LoadingModalProps.js';
69
70
  export { default as LoadingOverlay } from './Components/LoadingOverlay.svelte';
package/dist/index.js CHANGED
@@ -40,6 +40,7 @@ export {} from './Components/IconProps.js';
40
40
  export { icons } from './Components/Icons.js';
41
41
  export { default as InfoAlert } from './Components/InfoAlert.svelte';
42
42
  export { default as LoadingAnimation } from './Components/LoadingAnimation.svelte';
43
+ export { loaderDefaults } from './Helpers/loaderDefaults.svelte.js';
43
44
  export { default as LoadingModal } from './Components/LoadingModal.svelte';
44
45
  export { default as LoadingOverlay } from './Components/LoadingOverlay.svelte';
45
46
  export { default as Modal } from './Components/Modal.svelte';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lavalogic/scoria",
3
3
  "description": "Svelte components used for the FloWMS Web Frontend",
4
- "version": "0.40.10",
4
+ "version": "0.40.12",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },