@solidrt/components 0.0.28 → 0.0.29

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidrt/components",
3
- "version": "0.0.28",
3
+ "version": "0.0.29",
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.28"
21
+ "@solidrt/core": "0.0.29"
22
22
  }
23
23
  }
package/src/badge.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { Show } from "@solidrt/core"
1
+ import { Show, children } from "@solidrt/core"
2
2
  import type { LayoutProps } from "@solidrt/core"
3
3
  import { theme } from "./theme"
4
4
  import { typeStyle, lightOnDark } from "./typography"
@@ -38,7 +38,10 @@ export function Badge(props: BadgeProps) {
38
38
  let bg = () => props.style?.backgroundColor ?? colors().bg
39
39
  let fg = () => props.style?.color ?? colors().fg
40
40
  let radius = () => props.style?.borderRadius ?? RADIUS
41
- let isText = () => typeof props.children === "string" || typeof props.children === "number"
41
+ // Resolved once via children(): the typeof probe and the mount sites must
42
+ // share one build - reading the raw getter again would orphan native nodes.
43
+ let resolved = children(() => props.children)
44
+ let isText = () => typeof resolved() === "string" || typeof resolved() === "number"
42
45
  let labelOnDark = () => lightOnDark(fg(), bg())
43
46
 
44
47
  return (
@@ -58,9 +61,9 @@ export function Badge(props: BadgeProps) {
58
61
  opacity={props.style?.opacity}
59
62
  >
60
63
  <d-rect color={bg()} radius={radius()} />
61
- <Show when={isText()} fallback={props.children}>
64
+ <Show when={isText()} fallback={resolved()}>
62
65
  <text color={fg()} {...typeStyle("label", labelOnDark())}>
63
- {props.children}
66
+ {resolved()}
64
67
  </text>
65
68
  </Show>
66
69
  </view>
package/src/button.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { Show } from "@solidrt/core"
1
+ import { Show, children } from "@solidrt/core"
2
2
  import { Pressable, type PressState } from "./pressable"
3
3
  import { theme } from "./theme"
4
4
  import { policy } from "./policy"
@@ -55,7 +55,11 @@ export function Button(props: ButtonProps) {
55
55
  : colors().fill)
56
56
  let radius = () => props.style?.borderRadius ?? theme.radius.sm
57
57
  let label = () => (props.disabled ? theme.color.textMuted : colors().label)
58
- let isText = () => typeof props.children === "string" || typeof props.children === "number"
58
+ // Resolved once via children(): reading the raw children getter builds a new
59
+ // subtree per read, so the typeof probe and the two mount sites below must
60
+ // share this single memoized build (an unmounted build leaks native nodes).
61
+ let resolved = children(() => props.children)
62
+ let isText = () => typeof resolved() === "string" || typeof resolved() === "number"
59
63
  // The label's polarity against the idle fill: onPrimary on a saturated fill
60
64
  // is light-on-dark even in a light theme, so it needs the low-DPI weight
61
65
  // compensation there too.
@@ -92,9 +96,9 @@ export function Button(props: ButtonProps) {
92
96
  scale: (props.style?.scale ?? 1) * (s.pressed && policy.motion !== "none" ? 0.97 : 1),
93
97
  })}
94
98
  >
95
- <Show when={isText()} fallback={props.children}>
99
+ <Show when={isText()} fallback={resolved()}>
96
100
  <text color={label()} {...typeStyle("body", labelOnDark())}>
97
- {props.children}
101
+ {resolved()}
98
102
  </text>
99
103
  </Show>
100
104
  </Pressable>
package/src/image.tsx CHANGED
@@ -20,10 +20,12 @@ export interface ImageProps extends PointerProps {
20
20
 
21
21
  // Renders the fallback source when the main one failed. Its own <Errored>
22
22
  // keeps a broken fallback from escaping: the placeholder stays instead.
23
+ // Both fallbacks here take the error argument: an arity >= 1 fallback tells
24
+ // <Errored> the error is handled, so dev builds do not console.error it.
23
25
  function FallbackTexture(props: { src: ImageSource; width?: number; height?: number }) {
24
26
  let tex = createImage(() => props.src)
25
27
  return (
26
- <Errored fallback={null}>
28
+ <Errored fallback={(_err: unknown) => null}>
27
29
  <texture src={tex()} width={props.width} height={props.height} />
28
30
  </Errored>
29
31
  )
@@ -79,7 +81,7 @@ export function Image(props: ImageProps) {
79
81
  ) : null}
80
82
  <Loading fallback={null}>
81
83
  <Errored
82
- fallback={() =>
84
+ fallback={(_err: unknown) =>
83
85
  props.fallback != null ? <FallbackTexture src={props.fallback} width={texW()} height={texH()} /> : null
84
86
  }
85
87
  >
package/src/pressable.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { createSignal } from "@solidjs/signals"
1
+ import { createSignal, children } from "@solidrt/core"
2
2
  import type { LayoutProps, PointerEvent, PointerProps } from "@solidrt/core"
3
3
  import type { StyleProps } from "./types"
4
4
 
@@ -26,7 +26,17 @@ export function Pressable(props: PressableProps) {
26
26
 
27
27
  let state = (): PressState => ({ pressed: pressed(), hovered: hovered() })
28
28
  let style = () => (typeof props.style === "function" ? props.style(state()) : props.style)
29
- let kids = () => (typeof props.children === "function" ? props.children(state()) : props.children)
29
+ // Element-valued props build a fresh native subtree on every read, and a
30
+ // subtree that is never inserted is never destroyed - probing the raw getter
31
+ // with typeof would orphan one full copy per evaluation. children() memoizes
32
+ // the resolve so probe and mount share one build; a render-prop child
33
+ // ((state) => ...) passes through it intact because flatten only unwraps
34
+ // zero-arg functions.
35
+ let resolved = children(() => props.children)
36
+ let kids = () => {
37
+ let c = resolved()
38
+ return typeof c === "function" ? c(state()) : c
39
+ }
30
40
 
31
41
  let handleDown = (e: PointerEvent) => {
32
42
  if (e.button != null && e.button !== 0) return
@@ -1,5 +1,4 @@
1
- import { createSignal } from "@solidjs/signals"
2
- import { onFrame, onLayout, getBoundingBox, Show } from "@solidrt/core"
1
+ import { createSignal, onFrame, onLayout, getBoundingBox, Show } from "@solidrt/core"
3
2
  import type { LayoutProps } from "@solidrt/core"
4
3
  import { theme } from "./theme"
5
4
  import { policy } from "./policy"
package/src/qrcode.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { createMemo } from "@solidjs/signals"
1
+ import { createMemo } from "@solidrt/core"
2
2
  import type { LayoutProps } from "@solidrt/core"
3
3
  import qrcode from "qrcode-generator"
4
4
 
package/src/radio.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { createSignal, createContext, useContext, Show } from "@solidrt/core"
1
+ import { createSignal, createContext, useContext, Show, children } from "@solidrt/core"
2
2
  import type { LayoutProps } from "@solidrt/core"
3
3
  import { Pressable } from "./pressable"
4
4
  import { theme } from "./theme"
@@ -72,7 +72,10 @@ export function Radio(props: RadioProps) {
72
72
  let selected = () => ctx.value() === props.value
73
73
  let disabled = () => props.disabled || ctx.disabled()
74
74
  let ringColor = () => (selected() ? theme.color.primary : theme.color.border)
75
- let isText = () => typeof props.children === "string" || typeof props.children === "number"
75
+ // Resolved once via children(): the typeof probe and the mount sites must
76
+ // share one build - reading the raw getter again would orphan native nodes.
77
+ let resolved = children(() => props.children)
78
+ let isText = () => typeof resolved() === "string" || typeof resolved() === "number"
76
79
 
77
80
  let ring = () => Math.round(RING * densityScale())
78
81
  // Inner dot inset as a fraction of the ring, so it scales with the density.
@@ -91,9 +94,9 @@ export function Radio(props: RadioProps) {
91
94
  <d-oval x={inset()} y={inset()} w={ring() - inset() * 2} h={ring() - inset() * 2} color={theme.color.primary} />
92
95
  </Show>
93
96
  </view>
94
- <Show when={isText()} fallback={props.children}>
97
+ <Show when={isText()} fallback={resolved()}>
95
98
  <text color={theme.color.text} {...typeStyle("body")}>
96
- {props.children}
99
+ {resolved()}
97
100
  </text>
98
101
  </Show>
99
102
  </Pressable>
package/src/slider.tsx CHANGED
@@ -1,5 +1,4 @@
1
- import { createSignal } from "@solidjs/signals"
2
- import { getBoundingBox, onLayout, setPointerCapture } from "@solidrt/core"
1
+ import { createSignal, getBoundingBox, onLayout, setPointerCapture } from "@solidrt/core"
3
2
  import type { LayoutProps, PointerEvent } from "@solidrt/core"
4
3
  import { theme } from "./theme"
5
4
  import { densityScale } from "./policy"
package/src/spinner.tsx CHANGED
@@ -1,5 +1,4 @@
1
- import { createSignal } from "@solidjs/signals"
2
- import { onFrame, Show } from "@solidrt/core"
1
+ import { createSignal, onFrame, Show } from "@solidrt/core"
3
2
  import type { LayoutProps } from "@solidrt/core"
4
3
  import { theme } from "./theme"
5
4
  import { policy } from "./policy"
package/src/switch.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { createSignal } from "@solidjs/signals"
1
+ import { createSignal } from "@solidrt/core"
2
2
  import type { LayoutProps } from "@solidrt/core"
3
3
  import { Pressable } from "./pressable"
4
4
  import { theme } from "./theme"
@@ -1,5 +1,4 @@
1
- import { createEffect, createSignal, onCleanup } from "@solidjs/signals"
2
- import { measureText, setFocus } from "@solidrt/core"
1
+ import { createEffect, createSignal, onCleanup, measureText, setFocus } from "@solidrt/core"
3
2
  import { createCaretScroll, createTextBuffer } from "@solidrt/core/text-input"
4
3
  import type { Color, Gradient, LayoutProps } from "@solidrt/core"
5
4
  import type { StyleProps } from "./types"
package/src/text.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { createMemo } from "@solidjs/signals"
1
+ import { createMemo } from "@solidrt/core"
2
2
  import type { PointerProps } from "@solidrt/core"
3
3
  import type { StyleProps, TextLayoutProps } from "./types"
4
4
  import { theme, type TextVariant } from "./theme"
package/src/tooltip.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { createSignal, onCleanup, createPortal, onLayout, getBoundingBox, Show, env } from "@solidrt/core"
1
+ import { createSignal, onCleanup, createPortal, onLayout, getBoundingBox, Show, env, children } from "@solidrt/core"
2
2
  import type { LayoutProps, PointerEvent } from "@solidrt/core"
3
3
  import { theme } from "./theme"
4
4
  import { policy } from "./policy"
@@ -66,7 +66,10 @@ export function Tooltip(props: TooltipProps) {
66
66
  let cur = pos()
67
67
  if (!cur || cur.x !== x || cur.y !== y) setPos({ x, y })
68
68
  })
69
- let isText = () => typeof props.content === "string" || typeof props.content === "number"
69
+ // Resolved once via children(): the typeof probe and the mount sites must
70
+ // share one build - reading the raw getter again would orphan native nodes.
71
+ let content = children(() => props.content)
72
+ let isText = () => typeof content() === "string" || typeof content() === "number"
70
73
  return createPortal(
71
74
  <view
72
75
  ref={(n: { id: number }) => (bubble = n)}
@@ -83,9 +86,9 @@ export function Tooltip(props: TooltipProps) {
83
86
  pointerEvents="none"
84
87
  >
85
88
  <d-rect color={theme.color.surfaceAlt} radius={theme.radius.sm} />
86
- <Show when={isText()} fallback={props.content}>
89
+ <Show when={isText()} fallback={content()}>
87
90
  <text color={theme.color.text} {...typeStyle("body")}>
88
- {props.content}
91
+ {content()}
89
92
  </text>
90
93
  </Show>
91
94
  <d-rect