@streamscloud/kit 0.73.0 → 0.74.0

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.
@@ -1,2 +1,3 @@
1
1
  export { horizontalWheelScroll } from './horizontal-wheel-scroll';
2
2
  export { swallowTouch } from './swallow-touch';
3
+ export { predecodeOnLoad } from './predecode-on-load';
@@ -1,2 +1,3 @@
1
1
  export { horizontalWheelScroll } from './horizontal-wheel-scroll';
2
2
  export { swallowTouch } from './swallow-touch';
3
+ export { predecodeOnLoad } from './predecode-on-load';
@@ -0,0 +1,3 @@
1
+ import type { Action } from 'svelte/action';
2
+ /** Decodes the `<img>` once after every load, so a drag ghost snapshotted right after includes the picture. A broken image is ignored. `false` turns it off. */
3
+ export declare const predecodeOnLoad: Action<HTMLImageElement, boolean | undefined>;
@@ -0,0 +1,31 @@
1
+ /** Decodes the `<img>` once after every load, so a drag ghost snapshotted right after includes the picture. A broken image is ignored. `false` turns it off. */
2
+ export const predecodeOnLoad = (node, enabled = true) => {
3
+ let active = enabled;
4
+ const decode = async () => {
5
+ try {
6
+ await node.decode();
7
+ }
8
+ catch {
9
+ // a broken image has nothing to decode
10
+ }
11
+ };
12
+ const decodeLoaded = () => {
13
+ if (active && node.complete && node.naturalWidth > 0) {
14
+ void decode();
15
+ }
16
+ };
17
+ node.addEventListener('load', decodeLoaded);
18
+ decodeLoaded();
19
+ return {
20
+ update(nextEnabled = true) {
21
+ const turnedOn = nextEnabled && !active;
22
+ active = nextEnabled;
23
+ if (turnedOn) {
24
+ decodeLoaded();
25
+ }
26
+ },
27
+ destroy() {
28
+ node.removeEventListener('load', decodeLoaded);
29
+ }
30
+ };
31
+ };
@@ -1,3 +1 @@
1
1
  export declare const preloadImage: (src: string, throwOnError?: boolean) => Promise<void>;
2
- /** Decodes a loaded `<img>` once, so a drag ghost snapshotted right after includes the picture. A broken image is ignored. */
3
- export declare const predecodeImage: (img: HTMLImageElement) => Promise<void>;
@@ -8,12 +8,3 @@ export const preloadImage = (src, throwOnError = false) => {
8
8
  img.src = src;
9
9
  });
10
10
  };
11
- /** Decodes a loaded `<img>` once, so a drag ghost snapshotted right after includes the picture. A broken image is ignored. */
12
- export const predecodeImage = async (img) => {
13
- try {
14
- await img.decode();
15
- }
16
- catch {
17
- // a broken image has nothing to decode
18
- }
19
- };
@@ -1,4 +1,4 @@
1
- <script lang="ts">import { predecodeImage } from '../../core/utils';
1
+ <script lang="ts">import { predecodeOnLoad } from '../../core/actions';
2
2
  import { Icon } from '../icon';
3
3
  import { calculateImageState } from './image-state';
4
4
  import IconImageOff from '@fluentui/svg-icons/icons/image_off_20_regular.svg?raw';
@@ -9,10 +9,7 @@ const imageState = $derived(calculateImageState(src, hasError, showStubOnError))
9
9
  // down to fit — small pictures upscale too — and the box hugs the picture, rounding its own corners.
10
10
  // `src` must stay unset: it would join srcset as the 1x candidate and win.
11
11
  const srcset = $derived(src ? `${src.replace(/\s/g, (char) => encodeURIComponent(char))} 0.01x` : undefined);
12
- const onLoad = (event) => {
13
- if (predecode && event.currentTarget instanceof HTMLImageElement) {
14
- void predecodeImage(event.currentTarget);
15
- }
12
+ const onLoad = () => {
16
13
  hasError = false;
17
14
  on?.load?.(true);
18
15
  };
@@ -30,6 +27,7 @@ const onLoadError = () => {
30
27
  srcset={srcset}
31
28
  alt={alt || ''}
32
29
  draggable="false"
30
+ use:predecodeOnLoad={predecode}
33
31
  onload={onLoad}
34
32
  onerror={onLoadError} />
35
33
  {/if}
@@ -1,4 +1,4 @@
1
- <script lang="ts">import { predecodeImage } from '../../core/utils';
1
+ <script lang="ts">import { predecodeOnLoad } from '../../core/actions';
2
2
  import { Checkbox } from '../checkbox';
3
3
  import { ToolbarIconButton } from '../page-toolbar';
4
4
  import { PlayIndicator } from '../play-indicator';
@@ -7,11 +7,6 @@ import { TileCardLocalization } from './tile-card-localization';
7
7
  import IconMore from '@fluentui/svg-icons/icons/more_vertical_20_regular.svg?raw';
8
8
  const { title, meta, preview, predecode = false, menu, menuLabel, selected = false, checkboxVisibility = 'hover', on } = $props();
9
9
  const localization = new TileCardLocalization();
10
- const onPictureLoad = (event) => {
11
- if (predecode && event.currentTarget instanceof HTMLImageElement) {
12
- void predecodeImage(event.currentTarget);
13
- }
14
- };
15
10
  // Safari paints no frame for preload="metadata" until a seek target is given.
16
11
  const withFirstFrame = (src) => (src.includes('#') ? src : `${src}#t=0.001`);
17
12
  </script>
@@ -25,7 +20,7 @@ const withFirstFrame = (src) => (src.includes('#') ? src : `${src}#t=0.001`);
25
20
  {#if typeof preview === 'function'}
26
21
  {@render preview()}
27
22
  {:else if 'image' in preview}
28
- <img class="tile-card__picture" src={preview.image} alt="" onload={onPictureLoad} />
23
+ <img class="tile-card__picture" src={preview.image} alt="" use:predecodeOnLoad={predecode} />
29
24
  {:else}
30
25
  <video class="tile-card__picture" src={withFirstFrame(preview.video)} poster={preview.poster} preload="metadata" muted playsinline></video>
31
26
  <span class="tile-card__play"><PlayIndicator /></span>
@@ -6,7 +6,7 @@ type Props = {
6
6
  meta?: string;
7
7
  /** Top area: `image` / `video` render a drag-safe picture or frame (contain, never upscaled; a video gets a play glyph); a snippet renders custom content. The area is `pointer-events: none` — interactive content belongs in `menu` or `on.activate`. */
8
8
  preview?: TileCardPreview;
9
- /** Decodes the `image` preview once right after it loads. Set it when the tile sits in a `DragSource`, or the first drag ghost comes out without the picture — an `<img>` in a preview snippet needs `predecodeImage` from its owner. @default false */
9
+ /** Decodes the `image` preview once right after it loads. Set it when the tile sits in a `DragSource`, or the first drag ghost comes out without the picture — an `<img>` in a preview snippet needs `use:predecodeOnLoad` from its owner. @default false */
10
10
  predecode?: boolean;
11
11
  /** `PopoverItem`s of the kebab menu at the end of the footer. */
12
12
  menu?: Snippet;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/kit",
3
- "version": "0.73.0",
3
+ "version": "0.74.0",
4
4
  "author": "StreamsCloud",
5
5
  "repository": {
6
6
  "type": "git",