@lavalogic/scoria 0.37.7 → 0.37.9

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.
@@ -4,6 +4,7 @@
4
4
  lang="ts"
5
5
  module
6
6
  >
7
+ import { browser } from '$app/environment';
7
8
  import { DATAMatrix } from '../Helpers/Datamatrix.js';
8
9
 
9
10
  // Frozen const map of scanner shortcut codes recognised by AlertModal.
@@ -16,18 +17,17 @@
16
17
  } as const);
17
18
  type AlertShortcut = (typeof AlertShortcuts)[keyof typeof AlertShortcuts];
18
19
 
19
- // Barcode icons are derived from literal constants. Build them once at
20
- // module load so they are not re-encoded on every render.
20
+ // Barcode icons are encoded once at module load (rather than per
21
+ // component instance) so opening multiple alerts is cheap.
21
22
  //
22
- // `DATAMatrix` reaches for `document.createElement` internally, so we
23
- // guard the calls against SSR module evaluation - the modal itself is
24
- // `dialog.open === false` during SSR (no user-visible barcode rendered),
25
- // and the client-side module re-evaluates with a real `document` so the
26
- // icons get their proper SVG payload on the browser side.
27
- const HAS_DOM = typeof document !== 'undefined';
28
- const yesIcon = HAS_DOM ? DATAMatrix(AlertShortcuts.yes) : '';
29
- const noIcon = HAS_DOM ? DATAMatrix(AlertShortcuts.no) : '';
30
- const otherIcon = HAS_DOM ? DATAMatrix(AlertShortcuts.other) : '';
23
+ // `DATAMatrix` calls `document.createElement` internally, which does
24
+ // not exist during SvelteKit's server-render pass. The module loads
25
+ // in both environments; we gate the encoding on the `browser` flag so
26
+ // the server gets empty strings (the modal is hidden during SSR
27
+ // anyway) and the client gets the real SVGs after hydration.
28
+ const yesIcon = browser ? DATAMatrix(AlertShortcuts.yes) : '';
29
+ const noIcon = browser ? DATAMatrix(AlertShortcuts.no) : '';
30
+ const otherIcon = browser ? DATAMatrix(AlertShortcuts.other) : '';
31
31
  </script>
32
32
 
33
33
  <script lang="ts">
@@ -4,6 +4,7 @@
4
4
  module
5
5
  lang="ts"
6
6
  >
7
+ import { browser } from '$app/environment';
7
8
  import { DATAMatrix } from '../Helpers/Datamatrix.js';
8
9
  import { passthrough } from '../Helpers/Helpers.svelte.js';
9
10
  import { ColourSet } from '../scss/colours.js';
@@ -53,8 +54,16 @@
53
54
  // Pre-compute barcode SVGs once per `buttons` array. Building a DataMatrix
54
55
  // barcode is non-trivial; rebuilding inside the each-block re-runs the
55
56
  // encoder on every render.
57
+ //
58
+ // `DATAMatrix` calls `document.createElement` internally, which is
59
+ // undefined during SvelteKit's server render. Returning `null` on the
60
+ // server keeps the SVG slots empty for SSR; the client re-renders with
61
+ // the real encoded payload after hydration.
56
62
  const barcodeIcons = $derived.by(
57
- () => buttons?.map((b) => (b.barcodeValue != null ? DATAMatrix(b.barcodeValue) : null)) ?? []
63
+ () =>
64
+ buttons?.map((b) =>
65
+ b.barcodeValue != null && browser ? DATAMatrix(b.barcodeValue) : null
66
+ ) ?? []
58
67
  );
59
68
 
60
69
  onMount(() => {
@@ -1,7 +1,7 @@
1
1
  <svelte:options runes />
2
2
 
3
3
  <script lang="ts">
4
- import { cssLength, passthrough } from '../Helpers/Helpers.svelte.js';
4
+ import { cssLength } from '../Helpers/Helpers.svelte.js';
5
5
  import { InputVariant } from '../Types/Internal/InputVariant.js';
6
6
  import { Size } from '../Types/Internal/Size.js';
7
7
  import Icon from './Icon.svelte';
@@ -28,7 +28,10 @@
28
28
 
29
29
  onblur,
30
30
  onfocus,
31
- onkeydown = passthrough,
31
+ // No default `onkeydown` - see TextInput.svelte for the rationale.
32
+ // The legacy default (`passthrough`) blurred the input on every
33
+ // alphanumeric keystroke and made the field unusable.
34
+ onkeydown,
32
35
  oninput,
33
36
 
34
37
  inputRef = $bindable(),
@@ -1,7 +1,7 @@
1
1
  <svelte:options runes />
2
2
 
3
3
  <script lang="ts">
4
- import { cssLength, passthrough } from '../Helpers/Helpers.svelte.js';
4
+ import { cssLength } from '../Helpers/Helpers.svelte.js';
5
5
  import { ColourSet } from '../scss/colours.js';
6
6
  import { Size } from '../Types/Internal/Size.js';
7
7
  import Action from './Action.svelte';
@@ -19,7 +19,10 @@
19
19
  autocomplete = 'current-password',
20
20
  onblur,
21
21
  onfocus,
22
- onkeydown = passthrough,
22
+ // No default `onkeydown` - see TextInput.svelte for the rationale.
23
+ // The legacy default (`passthrough`) blurred the input on every
24
+ // alphanumeric keystroke and made the field unusable.
25
+ onkeydown,
23
26
  }: PasswordInputProps = $props();
24
27
 
25
28
  function toggleVisibility(): void {
@@ -1,7 +1,7 @@
1
1
  <svelte:options runes />
2
2
 
3
3
  <script lang="ts">
4
- import { cssLength, passthrough } from '../Helpers/Helpers.svelte.js';
4
+ import { cssLength } from '../Helpers/Helpers.svelte.js';
5
5
  import { InputVariant } from '../Types/Internal/InputVariant.js';
6
6
  import { Size } from '../Types/Internal/Size.js';
7
7
  import Icon from './Icon.svelte';
@@ -33,7 +33,10 @@
33
33
  spellcheck,
34
34
  onblur,
35
35
  onfocus,
36
- onkeydown = passthrough,
36
+ // No default `onkeydown` - see TextInput.svelte for the rationale.
37
+ // The legacy default (`passthrough`) blurred the textarea on every
38
+ // alphanumeric keystroke and made the field unusable.
39
+ onkeydown,
37
40
  oninput,
38
41
  }: TextAreaProps = $props();
39
42
 
@@ -1,7 +1,7 @@
1
1
  <svelte:options runes />
2
2
 
3
3
  <script lang="ts">
4
- import { cssLength, isValidEmailAddress, passthrough } from '../Helpers/Helpers.svelte.js';
4
+ import { cssLength, isValidEmailAddress } from '../Helpers/Helpers.svelte.js';
5
5
  import { Colour, ColourSet } from '../scss/colours.js';
6
6
  import { InputVariant } from '../Types/Internal/InputVariant.js';
7
7
  import { Size } from '../Types/Internal/Size.js';
@@ -34,7 +34,17 @@
34
34
  spellcheck,
35
35
  onblur,
36
36
  onfocus,
37
- onkeydown = passthrough,
37
+ // No default `onkeydown`. The legacy default was `passthrough`,
38
+ // a helper that BLURS the target on every alphanumeric key (it is
39
+ // a barcode-scanner aid intended for buttons - note the
40
+ // `as HTMLButtonElement` cast inside it). Applied to a text input
41
+ // it makes the field unusable: each keystroke blurs the input
42
+ // before the value can update, so users see nothing as they type.
43
+ // Inputs should accept keystrokes by default; consumers that want
44
+ // the barcode-blur behaviour explicitly pass
45
+ // `onkeydown={passthrough}` (preferably renamed to
46
+ // `blurOnAlphanumeric` in a future patch).
47
+ onkeydown,
38
48
  oninput,
39
49
  }: TextInputProps = $props();
40
50
 
@@ -4,6 +4,7 @@
4
4
  module
5
5
  lang="ts"
6
6
  >
7
+ import { browser } from '$app/environment';
7
8
  import { DATAMatrix } from '../../Helpers/Datamatrix.js';
8
9
  import { useAsyncFlag } from '../../Helpers/AsyncFlag.svelte.js';
9
10
  import { passthrough } from '../../Helpers/Helpers.svelte.js';
@@ -51,8 +52,16 @@
51
52
  // Pre-compute barcode SVGs once per `buttons` array. Building a DataMatrix
52
53
  // barcode is non-trivial; rebuilding inside the each-block re-runs the
53
54
  // encoder on every render.
55
+ //
56
+ // `DATAMatrix` calls `document.createElement` internally, which is
57
+ // undefined during SvelteKit's server render. Returning `null` on the
58
+ // server keeps the SVG slots empty for SSR; the client re-renders with
59
+ // the real encoded payload after hydration.
54
60
  const barcodeIcons = $derived.by(
55
- () => buttons?.map((b) => (b.barcodeValue != null ? DATAMatrix(b.barcodeValue) : null)) ?? []
61
+ () =>
62
+ buttons?.map((b) =>
63
+ b.barcodeValue != null && browser ? DATAMatrix(b.barcodeValue) : null
64
+ ) ?? []
56
65
  );
57
66
 
58
67
  // Resolve each button's `isValid` once at the component level rather than
@@ -136,11 +136,21 @@ export declare function SQLFriendlyWithLocalTime(date: InputDate | InputDateWith
136
136
  */
137
137
  export declare const localTimeFormat: Intl.DateTimeFormat;
138
138
  /**
139
- * Default `onkeydown` handler that blurs the focused element when
140
- * the user presses an alphanumeric key. Used on input wrappers
141
- * and buttons so that scanner-driven barcode characters do not
142
- * accumulate inside the focused field; instead the buffer goes
143
- * to `UntypedInputContext`.
139
+ * Barcode-scanner helper. Wire as an `onkeydown` on **buttons and
140
+ * other non-text focusable elements** that should yield focus when a
141
+ * scanner-driven keystream begins. On every alphanumeric key it
142
+ * calls `e.target.blur()` so the buffered keystrokes flow up to
143
+ * `UntypedInputContext` instead of being captured by the focused
144
+ * element.
145
+ *
146
+ * Do NOT apply to text inputs / textareas. Blurring the field on each
147
+ * keypress makes it impossible to type into - the field deselects
148
+ * before the browser commits the character.
149
+ *
150
+ * Despite the name, this is not a "pass the event through" no-op; it
151
+ * actively blurs the target. Renaming is deferred for backwards
152
+ * compatibility, but consumer-facing prop docs should describe the
153
+ * behaviour as "blur on alphanumeric".
144
154
  */
145
155
  export declare function passthrough(e: KeyboardEvent): void;
146
156
  /**
@@ -248,11 +248,21 @@ export const localTimeFormat = new Intl.DateTimeFormat('en-GB', {
248
248
  timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
249
249
  });
250
250
  /**
251
- * Default `onkeydown` handler that blurs the focused element when
252
- * the user presses an alphanumeric key. Used on input wrappers
253
- * and buttons so that scanner-driven barcode characters do not
254
- * accumulate inside the focused field; instead the buffer goes
255
- * to `UntypedInputContext`.
251
+ * Barcode-scanner helper. Wire as an `onkeydown` on **buttons and
252
+ * other non-text focusable elements** that should yield focus when a
253
+ * scanner-driven keystream begins. On every alphanumeric key it
254
+ * calls `e.target.blur()` so the buffered keystrokes flow up to
255
+ * `UntypedInputContext` instead of being captured by the focused
256
+ * element.
257
+ *
258
+ * Do NOT apply to text inputs / textareas. Blurring the field on each
259
+ * keypress makes it impossible to type into - the field deselects
260
+ * before the browser commits the character.
261
+ *
262
+ * Despite the name, this is not a "pass the event through" no-op; it
263
+ * actively blurs the target. Renaming is deferred for backwards
264
+ * compatibility, but consumer-facing prop docs should describe the
265
+ * behaviour as "blur on alphanumeric".
256
266
  */
257
267
  export function passthrough(e) {
258
268
  if (!e.key.match(/^[a-zA-Z0-9[\]]$/gm)) {
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.37.7",
4
+ "version": "0.37.9",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },
@@ -23,9 +23,9 @@
23
23
  "svelte": "./dist/index.js"
24
24
  },
25
25
  "./scss/*": {
26
- "sass": "./dist/scss/*",
27
- "style": "./dist/scss/*",
28
- "default": "./dist/scss/*"
26
+ "sass": "./dist/scss/_*.scss",
27
+ "style": "./dist/scss/_*.scss",
28
+ "default": "./dist/scss/_*.scss"
29
29
  }
30
30
  },
31
31
  "peerDependencies": {