@solidrt/components 0.0.26 → 0.0.28

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
@@ -151,7 +151,7 @@ import { Text } from "@solidrt/components"
151
151
  </Text>
152
152
  ```
153
153
 
154
- `layout` accepts all `LayoutProps` plus the font fields `fontFamily`, `fontSize`, `lineHeight`, `fontStyle`, `fontWeight`, `textAlign`, and `maxLines`.
154
+ `layout` accepts all `LayoutProps` plus the font fields `fontFamily`, `fontSize`, `lineHeight`, `fontStyle`, `fontWeight`, `textAlign`, and `maxLines`. Note that `lineHeight` is a multiplier of `fontSize` (the theme uses 1.3-1.6), not a pixel value.
155
155
 
156
156
  **Props**
157
157
 
@@ -166,13 +166,24 @@ Accepts all pointer event props, plus:
166
166
 
167
167
  ### Image
168
168
 
169
- Loads and displays an image from a URL or raw bytes.
169
+ Loads and displays an image from a URL or raw bytes. URL loads are shared
170
+ runtime-wide: mounts of the same URL reuse one fetch and one texture, and the
171
+ bytes are cached on disk (fetched with `cache: "force-cache"` - no freshness
172
+ check, so use versioned URLs for content that changes). The runtime keeps
173
+ concurrent asset fetches polite with a per-host limit; a failed load rejects
174
+ the mounts sharing it and a later remount retries.
170
175
 
171
176
  ```jsx
172
177
  import { Image } from "@solidrt/components"
173
178
 
174
179
  function Avatar() {
175
- return <Image src="https://example.com/avatar.png" layout={{ width: 64, height: 64 }} />
180
+ return (
181
+ <Image
182
+ src="https://example.com/avatar.png"
183
+ fallback={PLACEHOLDER_PNG}
184
+ layout={{ width: 64, height: 64 }}
185
+ />
186
+ )
176
187
  }
177
188
  ```
178
189
 
@@ -180,9 +191,15 @@ function Avatar() {
180
191
 
181
192
  Accepts `layout`, `style`, and all pointer event props, plus:
182
193
 
183
- | Prop | Type | Description |
184
- | ----- | ---------------------- | ------------------------------------------ |
185
- | `src` | `string \| Uint8Array` | URL to fetch, or raw image bytes to decode |
194
+ | Prop | Type | Description |
195
+ | ---------- | ------------------------ | ------------------------------------------------------------------------------------ |
196
+ | `src` | `string \| Uint8Array` | URL to fetch, or raw image bytes to decode |
197
+ | `fallback` | `string \| Uint8Array` | Source shown when `src` fails; if it also fails, the `backgroundColor` placeholder stays |
198
+ | `onLoad` | `() => void` | Called each time a source finishes loading |
199
+ | `onError` | `(err: unknown) => void` | Called when `src` fails to load or decode |
200
+
201
+ A failing `src` is contained by the component (the fallback or placeholder
202
+ shows); it does not propagate to an outer `<Errored>` boundary.
186
203
 
187
204
  ### TextInput
188
205
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidrt/components",
3
- "version": "0.0.26",
3
+ "version": "0.0.28",
4
4
  "license": "MIT",
5
5
  "author": "Antoine van Wel",
6
6
  "type": "module",
@@ -18,6 +18,6 @@
18
18
  },
19
19
  "peerDependencies": {
20
20
  "@solidjs/signals": "2.0.0-beta.17",
21
- "@solidrt/core": "0.0.26"
21
+ "@solidrt/core": "0.0.28"
22
22
  }
23
23
  }
package/src/image.tsx CHANGED
@@ -1,14 +1,34 @@
1
- import { createImage, Loading } from "@solidrt/core"
2
- import type { LayoutProps, PointerProps } from "@solidrt/core"
1
+ import { createImage, createEffect, Loading, Errored } from "@solidrt/core"
2
+ import type { ImageSource, LayoutProps, PointerProps } from "@solidrt/core"
3
3
  import type { StyleProps } from "./types"
4
4
 
5
5
  export interface ImageProps extends PointerProps {
6
6
  src: string | Uint8Array
7
+ /** Image source to show when `src` fails to load. If this also fails, only
8
+ * the `backgroundColor` placeholder remains. */
9
+ fallback?: ImageSource
10
+ /** Called each time a source finishes loading (including reloads on a
11
+ * reactive `src`). */
12
+ onLoad?: () => void
13
+ /** Called when `src` fails to load or decode. The fallback (if any) is shown
14
+ * regardless; without one the error stays contained here instead of
15
+ * propagating to an outer boundary. */
16
+ onError?: (err: unknown) => void
7
17
  layout?: LayoutProps
8
18
  style?: StyleProps
9
19
  }
10
20
 
11
- //TODO onLoad, onError
21
+ // Renders the fallback source when the main one failed. Its own <Errored>
22
+ // keeps a broken fallback from escaping: the placeholder stays instead.
23
+ function FallbackTexture(props: { src: ImageSource; width?: number; height?: number }) {
24
+ let tex = createImage(() => props.src)
25
+ return (
26
+ <Errored fallback={null}>
27
+ <texture src={tex()} width={props.width} height={props.height} />
28
+ </Errored>
29
+ )
30
+ }
31
+
12
32
  export function Image(props: ImageProps) {
13
33
  // createImage fetches/decodes/uploads, swaps the texture when src changes, and
14
34
  // frees it on cleanup, returning the texture id. Pass an accessor so a reactive
@@ -17,6 +37,14 @@ export function Image(props: ImageProps) {
17
37
  let src = createImage(() => props.src)
18
38
  let hasBorder = () => (props.style?.borderWidth ?? 0) > 0
19
39
 
40
+ // Load/error callbacks ride a separate effect read of the same async value;
41
+ // the error handler also keeps the failure out of the console when the
42
+ // render side already contains it with a fallback.
43
+ createEffect(() => src(), {
44
+ effect: () => props.onLoad?.(),
45
+ error: (err: unknown) => props.onError?.(err),
46
+ })
47
+
20
48
  // A texture sizes from its own width/height (not the box around it), so the
21
49
  // numeric layout dimensions are forwarded to it. Omitting height lets the
22
50
  // texture follow the image's intrinsic aspect ratio.
@@ -50,7 +78,13 @@ export function Image(props: ImageProps) {
50
78
  <d-rect color={props.style?.backgroundColor} radius={props.style?.borderRadius} />
51
79
  ) : null}
52
80
  <Loading fallback={null}>
53
- <texture src={src()} width={texW()} height={texH()} />
81
+ <Errored
82
+ fallback={() =>
83
+ props.fallback != null ? <FallbackTexture src={props.fallback} width={texW()} height={texH()} /> : null
84
+ }
85
+ >
86
+ <texture src={src()} width={texW()} height={texH()} />
87
+ </Errored>
54
88
  </Loading>
55
89
  {hasBorder() ? (
56
90
  <d-rect
package/src/modal.tsx CHANGED
@@ -20,7 +20,9 @@ export interface ModalProps {
20
20
  * escapes the layout and stacking of its surrounding tree. It fills the window
21
21
  * with a dimming backdrop and centers `children` on top. Control visibility by
22
22
  * mounting/unmounting it, e.g. `<Show when={open()}><Modal .../></Show>`: the
23
- * portal's onCleanup removes it when the surrounding scope disposes.
23
+ * portal's onCleanup removes it when the surrounding scope disposes. The
24
+ * gating signal must start false: portals cannot mount during the app's
25
+ * initial render (see createPortal), so a modal visible at startup throws.
24
26
  *
25
27
  * Pressing the backdrop calls `onClose`; pressing the content does not. This
26
28
  * works because pointer events dispatch to the whole hit path with no
@@ -1,7 +1,7 @@
1
1
  import { createEffect, createSignal, onCleanup } from "@solidjs/signals"
2
2
  import { measureText, setFocus } from "@solidrt/core"
3
3
  import { createCaretScroll, createTextBuffer } from "@solidrt/core/text-input"
4
- import type { LayoutProps } from "@solidrt/core"
4
+ import type { Color, Gradient, LayoutProps } from "@solidrt/core"
5
5
  import type { StyleProps } from "./types"
6
6
  import { theme } from "./theme"
7
7
  import { policy } from "./policy"
@@ -166,7 +166,7 @@ export function TextInput(props: TextInputProps) {
166
166
  }),
167
167
  )
168
168
 
169
- let textStyle = (color: string) => ({
169
+ let textStyle = (color: Color | Gradient) => ({
170
170
  w: TEXT_SHAPE_WIDTH,
171
171
  fontSize: fontSize(),
172
172
  lineHeight: theme.text.body.lineHeight,