@solidrt/components 0.0.4 → 0.0.6

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
@@ -12,33 +12,128 @@ bun add @solidrt/components
12
12
 
13
13
  Appearance (colors, spacing, border, font size) is controlled via the shared theme. Call `setTheme` from `@solidrt/components` to override defaults.
14
14
 
15
+ ## Layout and style
16
+
17
+ Most components group their props into two objects:
18
+
19
+ - `layout` - properties that feed the layout engine (flexbox/grid, sizing, padding, margin, position). Changing them triggers a relayout. This is the core `LayoutProps` set.
20
+ - `style` - paint-only properties that never affect layout: `color`, `backgroundColor`, `borderColor`, `borderWidth`, `borderRadius`, and the transform `x`, `y`, `rotate`, `scale`.
21
+
22
+ Event handlers (`onPointerDown`, `onKeyDown`, etc.) are passed directly as top-level props.
23
+
24
+ `StyleProps`:
25
+
26
+ | Prop | Type | Description |
27
+ | ----------------- | ------------------------------------------ | ------------------------------------ |
28
+ | `color` | `string` | Text color (used by `Text`). |
29
+ | `backgroundColor` | `string` | Fill color. |
30
+ | `borderColor` | `string` | Stroke color (when `borderWidth` set). |
31
+ | `borderWidth` | `number` | Border stroke width. |
32
+ | `borderRadius` | `number \| [number, number, number, number]` | Corner radius. |
33
+ | `x` / `y` | `number` | Translation offset. |
34
+ | `rotate` | `number` | Rotation. |
35
+ | `scale` | `number` | Scale factor. |
36
+
15
37
  ## Components
16
38
 
17
- ### SafeArea
39
+ ### Window
18
40
 
19
- Wraps its children in a view that applies padding to avoid system UI intrusions (status bars, home indicators, notches, etc.).
41
+ The root surface of an app. Accepts `layout` and the `backgroundColor` from `style`; a window cannot be transformed or bordered.
20
42
 
21
43
  ```jsx
22
- import { SafeArea } from "@solidrt/components"
44
+ import { Window } from "@solidrt/components"
23
45
 
24
46
  function App() {
25
47
  return (
26
- <window>
27
- <SafeArea>
28
- <text>Content clear of system UI</text>
29
- </SafeArea>
30
- </window>
48
+ <Window title="My App" style={{ backgroundColor: "#111" }}>
49
+ {/* ... */}
50
+ </Window>
31
51
  )
32
52
  }
33
53
  ```
34
54
 
35
55
  **Props**
36
56
 
37
- | Prop | Type | Default | Description |
38
- | ---------- | -------------------------------------------- | ------------------- | -------------------------------------------------------------- |
39
- | `edges` | `("top" \| "bottom" \| "left" \| "right")[]` | `["top", "bottom"]` | Which edges to apply safe area insets on |
40
- | `minimum` | `number` | `0` | Minimum padding applied even if the safe area inset is smaller |
41
- | `children` | `any` | - | Content to render inside the safe area |
57
+ | Prop | Type | Default | Description |
58
+ | ------------ | ------------- | ------- | ------------------------------------ |
59
+ | `title` | `string` | - | Window title. |
60
+ | `fullscreen` | `boolean` | - | Open fullscreen. |
61
+ | `vsync` | `boolean` | - | Enable vsync. |
62
+ | `fps` | `boolean` | - | Show an FPS counter. |
63
+ | `layout` | `LayoutProps` | - | Layout properties. |
64
+ | `style` | `StyleProps` | - | Only `backgroundColor` is applied. |
65
+ | `children` | `any` | - | Content. |
66
+
67
+ ### View
68
+
69
+ A general-purpose box. Spreads `layout` onto the underlying view, applies the transform from `style`, and draws a background and/or border when those style props are set.
70
+
71
+ ```jsx
72
+ import { View } from "@solidrt/components"
73
+
74
+ <View
75
+ layout={{ padding: 16, flexDirection: "column", gap: 8 }}
76
+ style={{ backgroundColor: "#222", borderRadius: 8 }}
77
+ >
78
+ {/* ... */}
79
+ </View>
80
+ ```
81
+
82
+ **Props**
83
+
84
+ Accepts all pointer event props, plus:
85
+
86
+ | Prop | Type | Description |
87
+ | ---------- | ----------------------------- | --------------------- |
88
+ | `layout` | `LayoutProps` | Layout properties. |
89
+ | `style` | `StyleProps` | Paint properties. |
90
+ | `ref` | `(node: { id: number }) => void` | Node reference. |
91
+ | `children` | `any` | Content. |
92
+
93
+ ### Text
94
+
95
+ Renders text inside a layout box. Font properties live in `layout` (they affect measurement); `color` lives in `style`.
96
+
97
+ ```jsx
98
+ import { Text } from "@solidrt/components"
99
+
100
+ <Text layout={{ fontSize: 18, maxLines: 2 }} style={{ color: "#fff" }}>
101
+ Hello
102
+ </Text>
103
+ ```
104
+
105
+ `layout` accepts all `LayoutProps` plus the font fields `fontFamily`, `fontSize`, `lineHeight`, `fontStyle`, `fontWeight`, `textAlign`, and `maxLines`.
106
+
107
+ **Props**
108
+
109
+ Accepts all pointer event props, plus:
110
+
111
+ | Prop | Type | Description |
112
+ | ---------- | ----------------------------- | --------------------------------- |
113
+ | `layout` | `TextLayoutProps` | Layout properties plus font fields. |
114
+ | `style` | `StyleProps` | `color` and transform. |
115
+ | `ref` | `(node: { id: number }) => void` | Node reference. |
116
+ | `children` | `any` | Text content. |
117
+
118
+ ### Image
119
+
120
+ Loads and displays an image from a URL or raw bytes.
121
+
122
+ ```jsx
123
+ import { Image } from "@solidrt/components"
124
+
125
+ function Avatar() {
126
+ return <Image src="https://example.com/avatar.png" layout={{ width: 64, height: 64 }} />
127
+ }
128
+ ```
129
+
130
+ **Props**
131
+
132
+ Accepts `layout`, `style`, and all pointer event props, plus:
133
+
134
+ | Prop | Type | Description |
135
+ | ----- | ---------------------- | ------------------------------------------ |
136
+ | `src` | `string \| Uint8Array` | URL to fetch, or raw image bytes to decode |
42
137
 
43
138
  ### TextInput
44
139
 
@@ -56,7 +151,7 @@ function NameField() {
56
151
  onInput={setName}
57
152
  onSubmit={(v) => console.log("submitted", v)}
58
153
  placeholder="Your name"
59
- width={240}
154
+ layout={{ width: 240 }}
60
155
  />
61
156
  )
62
157
  }
@@ -64,19 +159,61 @@ function NameField() {
64
159
 
65
160
  **Props**
66
161
 
67
- | Prop | Type | Default | Description |
68
- | -------------- | -------------------------- | ------- | ------------------------------------------------------------ |
69
- | `value` | `string` | - | Controlled value. If omitted, the component is uncontrolled. |
70
- | `defaultValue` | `string` | `""` | Initial value for uncontrolled use |
71
- | `onInput` | `(value: string) => void` | - | Fires on every change |
72
- | `onSubmit` | `(value: string) => void` | - | Fires on Enter |
73
- | `onFocus` | `() => void` | - | Fires when the field gains focus |
74
- | `onBlur` | `() => void` | - | Fires when the field loses focus |
75
- | `placeholder` | `string` | - | Shown when value is empty and the field is not focused |
76
- | `maxLength` | `number` | - | Truncates input to this length |
77
- | `disabled` | `boolean` | `false` | Ignores pointer and key events when true |
78
- | `autoFocus` | `boolean` | `false` | Focuses on mount |
79
- | `width` | `number \| "auto" \| "N%"` | - | Field width |
162
+ | Prop | Type | Default | Description |
163
+ | -------------- | ------------------------- | ------- | ------------------------------------------------------------ |
164
+ | `value` | `string` | - | Controlled value. If omitted, the component is uncontrolled. |
165
+ | `defaultValue` | `string` | `""` | Initial value for uncontrolled use. |
166
+ | `onInput` | `(value: string) => void` | - | Fires on every change. |
167
+ | `onSubmit` | `(value: string) => void` | - | Fires on Enter. |
168
+ | `onFocus` | `() => void` | - | Fires when the field gains focus. |
169
+ | `onBlur` | `() => void` | - | Fires when the field loses focus. |
170
+ | `placeholder` | `string` | - | Shown when value is empty and the field is not focused. |
171
+ | `maxLength` | `number` | - | Truncates input to this length. |
172
+ | `disabled` | `boolean` | `false` | Ignores pointer and key events when true. |
173
+ | `autoFocus` | `boolean` | `false` | Focuses on mount. |
174
+ | `layout` | `LayoutProps` | - | Layout properties (e.g. `width`). |
175
+ | `style` | `StyleProps` | - | Overrides theme colors, border, and radius. |
176
+
177
+ ### SafeArea
178
+
179
+ Wraps its children in a view that applies padding to avoid system UI intrusions (status bars, home indicators, notches, etc.).
180
+
181
+ ```jsx
182
+ import { SafeArea } from "@solidrt/components"
183
+
184
+ function App() {
185
+ return (
186
+ <Window>
187
+ <SafeArea top bottom>
188
+ <Text>Content clear of system UI</Text>
189
+ </SafeArea>
190
+ </Window>
191
+ )
192
+ }
193
+ ```
194
+
195
+ Top and bottom insets are applied by default. Pass `false` to opt out of an edge, or a number to apply the inset with a minimum padding.
196
+
197
+ ```jsx
198
+ // top only
199
+ <SafeArea bottom={false}>
200
+
201
+ // apply top and bottom insets, with a minimum of 16px each
202
+ <SafeArea top={16} bottom={16}>
203
+
204
+ // all four edges
205
+ <SafeArea top bottom left right>
206
+ ```
207
+
208
+ **Props**
209
+
210
+ | Prop | Type | Default | Description |
211
+ | ---------- | ------------------- | ------- | ------------------------------------------------------ |
212
+ | `top` | `boolean \| number` | `true` | Apply top inset. A number sets the minimum padding. |
213
+ | `bottom` | `boolean \| number` | `true` | Apply bottom inset. A number sets the minimum padding. |
214
+ | `left` | `boolean \| number` | `false` | Apply left inset. A number sets the minimum padding. |
215
+ | `right` | `boolean \| number` | `false` | Apply right inset. A number sets the minimum padding. |
216
+ | `children` | `any` | - | Content to render inside the safe area. |
80
217
 
81
218
  ## License
82
219
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidrt/components",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "license": "MIT",
5
5
  "author": "Antoine van Wel",
6
6
  "type": "module",
@@ -14,7 +14,7 @@
14
14
  "LICENSE"
15
15
  ],
16
16
  "peerDependencies": {
17
- "@solidjs/signals": "2.0.0-beta.13",
18
- "@solidrt/core": "0.0.4"
17
+ "@solidjs/signals": "2.0.0-beta.14",
18
+ "@solidrt/core": "0.0.6"
19
19
  }
20
20
  }
package/src/image.tsx ADDED
@@ -0,0 +1,69 @@
1
+ import { createSignal, onCleanup } from "@solidjs/signals"
2
+ import { decodeImage, createTexture } from "@solidrt/core/gpu"
3
+ import type { LayoutProps, PointerProps } from "@solidrt/core"
4
+ import type { StyleProps } from "./types"
5
+
6
+ export interface ImageProps extends PointerProps {
7
+ src: string | Uint8Array
8
+ layout?: LayoutProps
9
+ style?: StyleProps
10
+ }
11
+
12
+ //TODO onLoad, onError
13
+ //TODO release texture in onCleanup
14
+ export function Image(props: ImageProps) {
15
+ let [res] = createSignal(async () => {
16
+ let bytes: Uint8Array
17
+ if (typeof props.src === "string") {
18
+ let response = await fetch(props.src)
19
+ bytes = await response.bytes()
20
+ } else {
21
+ bytes = props.src
22
+ }
23
+ let { data, width, height } = decodeImage(bytes)
24
+ let id = createTexture(data, width, height)
25
+ return { id, width, height }
26
+ })
27
+
28
+ let src = () => res()?.id
29
+ let hasBorder = () => (props.style?.borderWidth ?? 0) > 0
30
+
31
+ onCleanup(() => {
32
+ //TODO release texture
33
+ })
34
+
35
+ return (
36
+ <view
37
+ {...props.layout}
38
+ x={props.style?.x}
39
+ y={props.style?.y}
40
+ scale={props.style?.scale}
41
+ rotate={props.style?.rotate}
42
+ onPointerEnter={props.onPointerEnter}
43
+ onPointerLeave={props.onPointerLeave}
44
+ onPointerDown={props.onPointerDown}
45
+ onPointerUp={props.onPointerUp}
46
+ onPointerMove={props.onPointerMove}
47
+ onWheel={props.onWheel}
48
+ onFocus={props.onFocus}
49
+ onBlur={props.onBlur}
50
+ onKeyDown={props.onKeyDown}
51
+ onKeyUp={props.onKeyUp}
52
+ onTextInput={props.onTextInput}
53
+ pointerEvents={props.pointerEvents}
54
+ >
55
+ {props.style?.backgroundColor != null ? (
56
+ <d-rect color={props.style.backgroundColor} radius={props.style?.borderRadius} />
57
+ ) : null}
58
+ <texture src={src()} />
59
+ {hasBorder() ? (
60
+ <d-rect
61
+ drawStyle="stroke"
62
+ color={props.style?.borderColor ?? "transparent"}
63
+ strokeWidth={props.style?.borderWidth}
64
+ radius={props.style?.borderRadius}
65
+ />
66
+ ) : null}
67
+ </view>
68
+ )
69
+ }
package/src/index.ts CHANGED
@@ -1,3 +1,8 @@
1
+ export { Window, type WindowProps } from "./window"
2
+ export { View, type ViewProps } from "./view"
3
+ export { Text, type TextProps } from "./text"
4
+ export { Image, type ImageProps } from "./image"
1
5
  export { SafeArea } from "./safe-area"
2
6
  export { TextInput, type TextInputProps } from "./text-input"
3
- export { theme, setTheme, type Theme } from "./theme"
7
+ export { theme, setTheme, type Theme } from "./theme"
8
+ export type { StyleProps, TextLayoutProps } from "./types"
package/src/safe-area.tsx CHANGED
@@ -2,8 +2,11 @@ import { createSignal } from "@solidjs/signals"
2
2
  import { onResize } from "@solidrt/core"
3
3
 
4
4
  export function SafeArea(props: {
5
- edges?: ("top" | "bottom" | "left" | "right")[]
6
- minimum?: number
5
+ top?: boolean | number
6
+ bottom?: boolean | number
7
+ left?: boolean | number
8
+ right?: boolean | number
9
+ relative?: boolean
7
10
  children?: any
8
11
  }) {
9
12
  let [insets, setInsets] = createSignal({ top: 0, bottom: 0, left: 0, right: 0 })
@@ -15,13 +18,18 @@ export function SafeArea(props: {
15
18
  right: width - safeArea.right,
16
19
  })
17
20
  })
18
- let has = (e: string) => (props.edges ?? ["top", "bottom"]).includes(e as any)
19
- let pad = (edge: "top" | "bottom" | "left" | "right") =>
20
- has(edge) ? Math.max(insets()[edge], props.minimum ?? 0) : 0
21
+ let pad = (edge: "top" | "bottom" | "left" | "right") => {
22
+ let defaultOn = edge === "top" || edge === "bottom"
23
+ let p = props[edge] ?? defaultOn
24
+ if (p === false) return 0
25
+ if (p === true) return insets()[edge]
26
+ return Math.max(insets()[edge], p as number)
27
+ }
21
28
  return (
22
29
  <view
23
30
  flex={1}
24
31
  flexDirection="column"
32
+ position={props.relative !== false ? "relative" : undefined}
25
33
  marginTop={pad("top")}
26
34
  marginBottom={pad("bottom")}
27
35
  marginLeft={pad("left")}
@@ -1,9 +1,9 @@
1
- import { createSignal, onCleanup } from "@solidjs/signals"
2
- import { measureText, setFocus } from "@solidrt/core"
1
+ import { createSignal, flush, onCleanup } from "@solidjs/signals"
2
+ import { getBoundingBox, measureText, onLayout, setFocus } from "@solidrt/core"
3
+ import type { LayoutProps } from "@solidrt/core"
4
+ import type { StyleProps } from "./types"
3
5
  import { theme } from "./theme"
4
6
 
5
- type Dimension = number | "auto" | `${number}%`
6
-
7
7
  export interface TextInputProps {
8
8
  value?: string
9
9
  defaultValue?: string
@@ -17,7 +17,8 @@ export interface TextInputProps {
17
17
  disabled?: boolean
18
18
  autoFocus?: boolean
19
19
 
20
- width?: Dimension
20
+ layout?: LayoutProps
21
+ style?: StyleProps
21
22
  }
22
23
 
23
24
  // V1: single-line, caret-at-end only, no selection, no mid-string editing.
@@ -27,8 +28,10 @@ export function TextInput(props: TextInputProps) {
27
28
  let [internalValue, setInternalValue] = createSignal(props.defaultValue ?? "")
28
29
  let [focused, setFocused] = createSignal(false)
29
30
  let [caretOn, setCaretOn] = createSignal(true)
31
+ let [viewportWidth, setViewportWidth] = createSignal(0)
30
32
 
31
33
  let node: { id: number } | undefined
34
+ let viewport: { id: number } | undefined
32
35
  let blinkId: any = null
33
36
 
34
37
  let value = () => props.value ?? internalValue()
@@ -87,13 +90,30 @@ export function TextInput(props: TextInputProps) {
87
90
  if (blinkId != null) clearInterval(blinkId)
88
91
  })
89
92
 
93
+ // Style overrides fall back to theme defaults.
94
+ let textColor = () => props.style?.color ?? theme.color.text
95
+ let surfaceColor = () => props.style?.backgroundColor ?? theme.color.surface
96
+ let borderColor = () => props.style?.borderColor ?? theme.color.border
97
+ let borderWidth = () => props.style?.borderWidth ?? theme.borderWidth.sm
98
+ let borderRadius = () => props.style?.borderRadius ?? theme.radius.sm
99
+
90
100
  let showPlaceholder = () => !focused() && value().length === 0 && (props.placeholder ?? "").length > 0
91
101
  let displayText = () => (showPlaceholder() ? (props.placeholder ?? "") : value())
92
- let displayColor = () => (showPlaceholder() ? theme.color.textMuted : theme.color.text)
102
+ let displayColor = () => (showPlaceholder() ? theme.color.textMuted : textColor())
103
+
104
+ // Viewport width is the inner scroll container's own laid-out width, read
105
+ // after layout. This works for numeric, "auto" and "%" widths alike (no need
106
+ // to subtract padding: the padding lives on the outer view). getBoundingBox
107
+ // is a snapshot, so we push it into a signal from onLayout. The flush drains
108
+ // the resulting scrollX update synchronously, before paint, so the scroll
109
+ // tracks the width in the same frame instead of trailing it by one.
110
+ onLayout(() => {
111
+ if (!viewport) return
112
+ let w = getBoundingBox(viewport)?.width ?? 0
113
+ if (w !== viewportWidth()) setViewportWidth(w)
114
+ flush()
115
+ })
93
116
 
94
- // V1: viewport width derived from numeric props.width minus padding.
95
- // For "auto" / "%" widths, fall back to 0 (no scroll, caret may overflow).
96
- let viewportWidth = () => (typeof props.width === "number" ? props.width - 2 * theme.spacing.md : 0)
97
117
  let caretWidth = () => (focused() && !showPlaceholder() ? 1 : 0)
98
118
  let scrollX = () => {
99
119
  if (showPlaceholder()) return 0
@@ -111,25 +131,30 @@ export function TextInput(props: TextInputProps) {
111
131
  }}
112
132
  flexDirection="row"
113
133
  alignItems="center"
114
- width={props.width}
115
134
  paddingLeft={theme.spacing.md}
116
135
  paddingRight={theme.spacing.md}
117
136
  paddingTop={theme.spacing.sm}
118
137
  paddingBottom={theme.spacing.sm}
138
+ {...props.layout}
139
+ x={props.style?.x}
140
+ y={props.style?.y}
141
+ scale={props.style?.scale}
142
+ rotate={props.style?.rotate}
119
143
  onPointerDown={handlePointerDown}
120
144
  onFocus={handleFocus}
121
145
  onBlur={handleBlur}
122
146
  onKeyDown={handleKeyDown}
123
147
  onTextInput={handleTextInput}
124
148
  >
125
- <d-rect color={theme.color.surface} radius={theme.radius.sm} />
149
+ <d-rect color={surfaceColor()} radius={borderRadius()} />
126
150
  <d-rect
127
151
  drawStyle="stroke"
128
- color={theme.color.border}
129
- strokeWidth={theme.borderWidth.sm}
130
- radius={theme.radius.sm}
152
+ color={borderColor()}
153
+ strokeWidth={borderWidth()}
154
+ radius={borderRadius()}
131
155
  />
132
156
  <view
157
+ ref={(n: { id: number }) => (viewport = n)}
133
158
  flex={1}
134
159
  flexDirection="row"
135
160
  alignItems="center"
package/src/text.tsx ADDED
@@ -0,0 +1,70 @@
1
+ import { createMemo } from "@solidjs/signals"
2
+ import type { PointerProps } from "@solidrt/core"
3
+ import type { StyleProps, TextLayoutProps } from "./types"
4
+
5
+ export interface TextProps extends PointerProps {
6
+ children?: any
7
+ ref?: (node: { id: number }) => void
8
+ layout?: TextLayoutProps
9
+ style?: StyleProps
10
+ }
11
+
12
+ // Font keys live in layout (they affect measurement) but belong on the inner
13
+ // <text> node, not the box <view>. Strip them out before spreading layout.
14
+ const FONT_KEYS = [
15
+ "fontFamily",
16
+ "fontSize",
17
+ "lineHeight",
18
+ "fontStyle",
19
+ "fontWeight",
20
+ "textAlign",
21
+ "maxLines",
22
+ ]
23
+
24
+ export function Text(props: TextProps) {
25
+ let box = createMemo(() => {
26
+ let l = props.layout
27
+ if (!l) return {}
28
+ let out: Record<string, unknown> = {}
29
+ for (let key in l) {
30
+ if (!FONT_KEYS.includes(key)) out[key] = (l as Record<string, unknown>)[key]
31
+ }
32
+ return out
33
+ })
34
+
35
+ return (
36
+ <view
37
+ ref={props.ref}
38
+ {...box()}
39
+ x={props.style?.x}
40
+ y={props.style?.y}
41
+ scale={props.style?.scale}
42
+ rotate={props.style?.rotate}
43
+ onPointerEnter={props.onPointerEnter}
44
+ onPointerLeave={props.onPointerLeave}
45
+ onPointerDown={props.onPointerDown}
46
+ onPointerUp={props.onPointerUp}
47
+ onPointerMove={props.onPointerMove}
48
+ onWheel={props.onWheel}
49
+ onFocus={props.onFocus}
50
+ onBlur={props.onBlur}
51
+ onKeyDown={props.onKeyDown}
52
+ onKeyUp={props.onKeyUp}
53
+ onTextInput={props.onTextInput}
54
+ pointerEvents={props.pointerEvents}
55
+ >
56
+ <text
57
+ color={props.style?.color}
58
+ fontFamily={props.layout?.fontFamily}
59
+ fontSize={props.layout?.fontSize}
60
+ lineHeight={props.layout?.lineHeight}
61
+ fontStyle={props.layout?.fontStyle}
62
+ fontWeight={props.layout?.fontWeight}
63
+ textAlign={props.layout?.textAlign}
64
+ maxLines={props.layout?.maxLines}
65
+ >
66
+ {props.children}
67
+ </text>
68
+ </view>
69
+ )
70
+ }
package/src/types.ts ADDED
@@ -0,0 +1,33 @@
1
+ import type { Color, LayoutProps } from "@solidrt/core"
2
+
3
+ // The split between layout and style follows one rule: layout properties feed
4
+ // into Taffy and changing them triggers a relayout; style properties are
5
+ // paint-only and can change without affecting layout.
6
+
7
+ // Paint-only props. None of these change the box Taffy computes. Borders are
8
+ // drawn as a stroke overlay (not part of the box model), and the transform is
9
+ // applied at paint time, so both live here.
10
+ export interface StyleProps {
11
+ color?: Color
12
+ backgroundColor?: Color
13
+ borderColor?: Color
14
+ borderWidth?: number
15
+ borderRadius?: number | [number, number, number, number]
16
+ x?: number
17
+ y?: number
18
+ rotate?: number
19
+ scale?: number
20
+ }
21
+
22
+ // Text shaping affects measurement, so font props belong with layout rather
23
+ // than style. These end up on the inner <text> node, while the box layout
24
+ // fields go on the wrapping <view>.
25
+ export interface TextLayoutProps extends LayoutProps {
26
+ fontFamily?: "sans" | "mono" | (string & {})
27
+ fontSize?: number
28
+ lineHeight?: number
29
+ fontStyle?: "normal" | "italic"
30
+ fontWeight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
31
+ textAlign?: "left" | "right" | "center" | "justify"
32
+ maxLines?: number
33
+ }
package/src/view.tsx ADDED
@@ -0,0 +1,54 @@
1
+ import type { LayoutProps, PointerProps } from "@solidrt/core"
2
+ import type { StyleProps } from "./types"
3
+
4
+ export interface ViewProps extends PointerProps {
5
+ children?: any
6
+ ref?: (node: { id: number }) => void
7
+ layout?: LayoutProps
8
+ style?: StyleProps
9
+ }
10
+
11
+ export function View(props: ViewProps) {
12
+ let hasBackground = () =>
13
+ props.style?.backgroundColor != null || props.style?.borderRadius != null
14
+ let hasBorder = () => (props.style?.borderWidth ?? 0) > 0
15
+
16
+ return (
17
+ <view
18
+ ref={props.ref}
19
+ {...props.layout}
20
+ x={props.style?.x}
21
+ y={props.style?.y}
22
+ scale={props.style?.scale}
23
+ rotate={props.style?.rotate}
24
+ onPointerEnter={props.onPointerEnter}
25
+ onPointerLeave={props.onPointerLeave}
26
+ onPointerDown={props.onPointerDown}
27
+ onPointerUp={props.onPointerUp}
28
+ onPointerMove={props.onPointerMove}
29
+ onWheel={props.onWheel}
30
+ onFocus={props.onFocus}
31
+ onBlur={props.onBlur}
32
+ onKeyDown={props.onKeyDown}
33
+ onKeyUp={props.onKeyUp}
34
+ onTextInput={props.onTextInput}
35
+ pointerEvents={props.pointerEvents}
36
+ >
37
+ {hasBackground() ? (
38
+ <d-rect
39
+ color={props.style?.backgroundColor ?? "transparent"}
40
+ radius={props.style?.borderRadius}
41
+ />
42
+ ) : null}
43
+ {props.children}
44
+ {hasBorder() ? (
45
+ <d-rect
46
+ drawStyle="stroke"
47
+ color={props.style?.borderColor ?? "transparent"}
48
+ strokeWidth={props.style?.borderWidth}
49
+ radius={props.style?.borderRadius}
50
+ />
51
+ ) : null}
52
+ </view>
53
+ )
54
+ }
package/src/window.tsx ADDED
@@ -0,0 +1,31 @@
1
+ import type { LayoutProps } from "@solidrt/core"
2
+ import type { StyleProps } from "./types"
3
+
4
+ export interface WindowProps {
5
+ children?: any
6
+ title?: string
7
+ fullscreen?: boolean
8
+ vsync?: boolean
9
+ fps?: boolean
10
+ layout?: LayoutProps
11
+ style?: StyleProps
12
+ }
13
+
14
+ // A window is the root surface: it can't be transformed or bordered, so only
15
+ // the paint-only backgroundColor from style applies here.
16
+ export function Window(props: WindowProps) {
17
+ return (
18
+ <window
19
+ {...props.layout}
20
+ title={props.title}
21
+ fullscreen={props.fullscreen}
22
+ vsync={props.vsync}
23
+ fps={props.fps}
24
+ >
25
+ {props.style?.backgroundColor != null ? (
26
+ <d-rect color={props.style.backgroundColor} />
27
+ ) : null}
28
+ {props.children}
29
+ </window>
30
+ )
31
+ }