@swiftbrowser/react 0.17.4 → 0.17.5

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/README.md CHANGED
@@ -403,7 +403,13 @@ An image is inert, as a SwiftUI image is. `<AsyncImage url={…}
403
403
  webInteractions />` gives a remote picture the browser's own image
404
404
  interactions back: a long press's Save / Share / Copy on iOS, dragging it
405
405
  out of the page, the right-click menu. Use it on an image with no tap,
406
- gesture or context menu of its own.
406
+ gesture or context menu of its own. `placeholder` shows until the image
407
+ loaded (a box at the picture's aspect ratio keeps the row from shifting),
408
+ `failure` when it could not, and `loadingIndicator` goes over the
409
+ placeholder once a load has run for `loadingIndicatorDelay` ms (300 by
410
+ default): a `<ProgressView />` for a big picture on a slow connection,
411
+ nothing for one that comes quickly or from the cache. All three show in a
412
+ SwiftBrowser Native app too.
407
413
 
408
414
  `keyboardType`, `textContentType`, `textInputAutocapitalization` and
409
415
  `autocorrectionDisabled` are SwiftUI's text input hints, for the field they
@@ -1,5 +1,7 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import { type ViewModifiers } from '../modifiers';
3
+ /** How long a load runs before `loadingIndicator` shows. */
4
+ export declare const LOADING_INDICATOR_DELAY_MS = 300;
3
5
  export interface AsyncImageProps extends ViewModifiers {
4
6
  url: string;
5
7
  /** `.resizable()` on the loaded image. */
@@ -8,6 +10,13 @@ export interface AsyncImageProps extends ViewModifiers {
8
10
  placeholder?: ReactNode;
9
11
  /** Shown when the image failed to load (the placeholder by default). */
10
12
  failure?: ReactNode;
13
+ /**
14
+ * Shown over the placeholder once a load has run for `loadingIndicatorDelay`
15
+ * ms (300 by default): a `<ProgressView />` for a big picture on a slow
16
+ * connection, nothing for one that comes quickly or from the cache.
17
+ */
18
+ loadingIndicator?: ReactNode;
19
+ loadingIndicatorDelay?: number;
11
20
  /**
12
21
  * Keep the browser's own image interactions: a long press's Save / Share /
13
22
  * Copy on iOS, dragging the image out, the right-click menu. Off by
@@ -21,9 +30,11 @@ export interface AsyncImageProps extends ViewModifiers {
21
30
  /**
22
31
  * `AsyncImage(url:)`: the page loads the image to learn its size (the
23
32
  * engine lays a remote image out by its pixel size, as the Swift core does
24
- * after its `image` request), then shows it. In a SwiftBrowser Native app,
25
- * which has no page to load it with, the URL goes to the renderer at once
26
- * and SwiftUI's own `AsyncImage` loads it.
33
+ * after its `image` request), then shows it. A SwiftBrowser Native app's
34
+ * runtime gives it an `Image` too (@swiftbrowser/native's image.ts, over
35
+ * the host's image loader), so the placeholder shows there as well; on a
36
+ * host without one the URL goes to the renderer at once and SwiftUI's own
37
+ * `AsyncImage` loads it.
27
38
  */
28
39
  export declare function AsyncImage(props: AsyncImageProps): ReactNode;
29
40
  //# sourceMappingURL=images.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../../src/components/images.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,EAA8B,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAE9E,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,yDAAyD;IACzD,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,wEAAwE;IACxE,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oEAAoE;IACpE,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAmBD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,CAgC5D"}
1
+ {"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../../src/components/images.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,EAA8B,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAG9E,4DAA4D;AAC5D,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,yDAAyD;IACzD,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,wEAAwE;IACxE,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oEAAoE;IACpE,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAmBD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,CAqD5D"}
package/dist/index.js CHANGED
@@ -3404,6 +3404,7 @@ function EditButton(props) {
3404
3404
  const [editing, setEditing] = readBinding(own.editMode, own.onChange);
3405
3405
  return applyModifiers(/* @__PURE__ */ jsx(Button, { title: editing ? "Done" : "Edit", editMode: editing ? "active" : "inactive", action: () => setEditing(!editing) }), modifiers);
3406
3406
  }
3407
+ const LOADING_INDICATOR_DELAY_MS = 300;
3407
3408
  const loaded = /* @__PURE__ */ new Map();
3408
3409
  function inNativeApp() {
3409
3410
  return typeof Image === "undefined" && globalThis.__sbNative !== void 0;
@@ -3434,10 +3435,28 @@ function AsyncImage(props) {
3434
3435
  live = false;
3435
3436
  };
3436
3437
  }, [own.url]);
3438
+ const hasIndicator = own.loadingIndicator !== void 0 && own.loadingIndicator !== null;
3439
+ const delay = own.loadingIndicatorDelay ?? LOADING_INDICATOR_DELAY_MS;
3440
+ const [slow, setSlow] = useState(false);
3441
+ useEffect(() => {
3442
+ if (state !== null || !hasIndicator) {
3443
+ setSlow(false);
3444
+ return;
3445
+ }
3446
+ const timer = setTimeout(() => setSlow(true), delay);
3447
+ return () => clearTimeout(timer);
3448
+ }, [state, hasIndicator, delay]);
3437
3449
  if (inNativeApp()) {
3438
3450
  return applyModifiers(sb("image", { ...imageProps(own), url: own.url, width: 0, height: 0 }), modifiers);
3439
3451
  }
3440
- if (state === null) return own.placeholder ?? null;
3452
+ if (state === null) {
3453
+ const placeholder2 = own.placeholder ?? null;
3454
+ if (!slow || !hasIndicator) return placeholder2;
3455
+ return /* @__PURE__ */ jsxs(ZStack, { children: [
3456
+ placeholder2,
3457
+ own.loadingIndicator
3458
+ ] });
3459
+ }
3441
3460
  if (state === "failed") return own.failure ?? own.placeholder ?? null;
3442
3461
  return applyModifiers(sb("image", { ...imageProps(own), url: state.url, width: state.width, height: state.height }), modifiers);
3443
3462
  }
@@ -4623,6 +4642,7 @@ export {
4623
4642
  Host,
4624
4643
  Image$1 as Image,
4625
4644
  KeyframeAnimator,
4645
+ LOADING_INDICATOR_DELAY_MS,
4626
4646
  Label,
4627
4647
  LabeledContent,
4628
4648
  Layout,