@solidrt/core 0.0.2 → 0.0.3

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/core",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "license": "MIT",
5
5
  "author": "Antoine van Wel",
6
6
  "type": "module",
@@ -19,7 +19,7 @@
19
19
  "colord": "^2.9.3"
20
20
  },
21
21
  "devDependencies": {
22
- "@solidrt/flux-types": "0.0.2"
22
+ "@solidrt/flux-types": "0.0.3"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "@solidjs/signals": "2.0.0-beta.13",
package/src/focus.ts CHANGED
@@ -3,6 +3,7 @@ import { getEventHandler } from "./events"
3
3
  // Currently-focused node id. Reset to null automatically across engine
4
4
  // reloads because the JS environment is rebuilt from scratch.
5
5
  let focusedNodeId: number | null = null
6
+ let textInputActive = false
6
7
 
7
8
  export function setFocus(nodeId: number | null): void {
8
9
  if (nodeId === focusedNodeId) return
@@ -14,6 +15,11 @@ export function setFocus(nodeId: number | null): void {
14
15
  if (nodeId != null) {
15
16
  getEventHandler(nodeId, "onFocus")?.()
16
17
  }
18
+ let wantActive = nodeId != null && getEventHandler(nodeId, "onTextInput") != null
19
+ if (wantActive !== textInputActive) {
20
+ textInputActive = wantActive
21
+ ffi.setTextInputActive(wantActive)
22
+ }
17
23
  }
18
24
 
19
25
  export function getFocusedNodeId(): number | null {
package/src/index.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from "./renderer"
2
2
  export { setFocus, getFocusedNodeId } from "./focus"
3
- export { onRender, onResize, onWindowFocus, onWindowBlur } from "./window"
3
+ export { onRender, onLayout, onResize, onWindowFocus, onWindowBlur } from "./window"
4
+ export { measureText } from "./text"
5
+ export type { MeasureTextOptions } from "./types"
package/src/renderer.ts CHANGED
@@ -3,6 +3,7 @@ import { createRenderer } from "@solidjs/universal"
3
3
  import { attachWindow } from "./window"
4
4
  import { parseColorToU32, isColorProp } from "./color"
5
5
  import { setEventHandler, cleanupNodeHandlers } from "./events"
6
+ import { getFocusedNodeId, setFocus } from "./focus"
6
7
 
7
8
  export { getEventHandler } from "./events"
8
9
 
@@ -124,9 +125,11 @@ export let {
124
125
 
125
126
  ffi.deleteNode(parent.id, node.id)
126
127
 
127
- // Recursively clean up node and all descendants
128
+ // Recursively clean up node and all descendants. Clear focus before
129
+ // dropping handlers so onBlur still fires for a focused descendant.
128
130
  let cleanup = (n: ProxyNode) => {
129
131
  for (let child of n.children) cleanup(child)
132
+ if (n.id === getFocusedNodeId()) setFocus(null)
130
133
  nodes.delete(n.id)
131
134
  cleanupNodeHandlers(n.id)
132
135
  }
package/src/text.ts ADDED
@@ -0,0 +1,5 @@
1
+ import type { MeasureTextOptions } from "./types"
2
+
3
+ export function measureText(text: string, options?: MeasureTextOptions): { width: number, height: number } {
4
+ return ffi.measureText(text, options)
5
+ }
package/src/types.d.ts CHANGED
@@ -10,9 +10,19 @@ declare global {
10
10
  insertNode(parentId: number, nodeId: number, anchorId?: number): void
11
11
  deleteNode(parentId: number, nodeId: number): void
12
12
  setProperty(nodeId: number, name: string, value: unknown): void
13
+ setTextInputActive(active: boolean): void
14
+ measureText(text: string, options?: MeasureTextOptions): { width: number, height: number }
13
15
  }
14
16
  }
15
17
 
18
+ export interface MeasureTextOptions {
19
+ fontFamily?: "sans" | "mono" | (string & {})
20
+ fontSize?: number
21
+ fontStyle?: "normal" | "italic"
22
+ fontWeight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
23
+ maxLines?: number
24
+ }
25
+
16
26
  type Children = SolidJSX.Element
17
27
 
18
28
  type OA<T> = T | Accessor<T>
@@ -101,6 +111,8 @@ export interface TransformProps {
101
111
  y?: OA<number>
102
112
  cx?: OA<number>
103
113
  cy?: OA<number>
114
+ scrollX?: OA<number>
115
+ scrollY?: OA<number>
104
116
  }
105
117
 
106
118
  export interface PointerProps {
@@ -167,6 +179,7 @@ export interface TextProps extends PaintProps {
167
179
  children?: Children
168
180
  fontFamily?: "sans" | "mono" | (string & {})
169
181
  fontSize?: number
182
+ lineHeight?: number
170
183
  fontStyle?: "normal" | "italic"
171
184
  fontWeight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
172
185
  textAlign?: "left" | "right" | "center" | "justify"
package/src/window.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { onCleanup, onSettled } from "@solidjs/signals"
2
2
  import { getEventHandler } from "./events"
3
- import { getFocusedNodeId } from "./focus"
3
+ import { getFocusedNodeId, setFocus } from "./focus"
4
4
 
5
5
  // ------ Animation frames ----------------
6
6
 
@@ -50,6 +50,16 @@ export function onResize(fn: (data: ResizeEvent) => void) {
50
50
  return unsubscribe
51
51
  }
52
52
 
53
+ // Fires after layout has been computed for the current frame but before paint.
54
+ // Setting properties that affect layout from this callback will be picked up
55
+ // by a re-layout pass before painting (one extra pass; cascades beyond that
56
+ // paint stale).
57
+ export function onLayout(fn: () => void) {
58
+ let unsubscribe = Flux.on("postLayout", fn)
59
+ onCleanup(unsubscribe)
60
+ return unsubscribe
61
+ }
62
+
53
63
  export function onWindowFocus(fn: () => void) {
54
64
  let unsubscribe = Flux.on("windowFocus", fn)
55
65
  onCleanup(unsubscribe)
@@ -74,6 +84,8 @@ export function attachWindow(_nodeId: number) {
74
84
  let unsubWheel: () => void = null!
75
85
  let unsubKeyDown: () => void = null!
76
86
  let unsubKeyUp: () => void = null!
87
+ let unsubTextInput: () => void = null!
88
+ let unsubKeyboardVisibility: () => void = null!
77
89
 
78
90
  onSettled(() => {
79
91
  unsubscribe = Flux.on("render", ({ time, frame }: { time: number, frame: number }) => {
@@ -92,6 +104,12 @@ export function attachWindow(_nodeId: number) {
92
104
  for (let nodeId of targets) {
93
105
  getEventHandler(nodeId, "onPointerDown")?.(e)
94
106
  }
107
+ // Outside-tap blur. Read focus AFTER per-node handlers so a tap that
108
+ // moves focus to a new node is not immediately blurred again.
109
+ let focused = getFocusedNodeId()
110
+ if (focused != null && !targets.includes(focused)) {
111
+ setFocus(null)
112
+ }
95
113
  })
96
114
 
97
115
  unsubUp = Flux.on("pointerUp", ({ targets, ...e }: { targets: number[], [k: string]: any }) => {
@@ -138,6 +156,19 @@ export function attachWindow(_nodeId: number) {
138
156
  }
139
157
  })
140
158
 
159
+ unsubTextInput = Flux.on("textInput", (e: any) => {
160
+ let id = getFocusedNodeId()
161
+ if (id != null) {
162
+ getEventHandler(id, "onTextInput")?.(e)
163
+ }
164
+ })
165
+
166
+ // When the user dismisses the on-screen keyboard (swipe down, "Done",
167
+ // back button), blur the focused node so the app's UI state catches up.
168
+ unsubKeyboardVisibility = Flux.on("keyboardVisibility", ({ shown }: { shown: boolean }) => {
169
+ if (!shown) setFocus(null)
170
+ })
171
+
141
172
  // trigger first draw
142
173
  draw()
143
174
  })
@@ -152,5 +183,7 @@ export function attachWindow(_nodeId: number) {
152
183
  if (unsubWheel) unsubWheel()
153
184
  if (unsubKeyDown) unsubKeyDown()
154
185
  if (unsubKeyUp) unsubKeyUp()
186
+ if (unsubTextInput) unsubTextInput()
187
+ if (unsubKeyboardVisibility) unsubKeyboardVisibility()
155
188
  })
156
189
  }