Skip to content

Commit 440e672

Browse files
committed
use abort signals, improve architecture, and fix #125
1 parent c7c5d8c commit 440e672

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+2715
-2526
lines changed

examples/auth/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"type": "module",
33
"dependencies": {
44
"@react-three/uikit": "workspace:^",
5-
"@react-three/uikit-lucide": "workspace:^"
5+
"@react-three/uikit-lucide": "workspace:^",
6+
"zustand": "^4.5.2"
67
},
78
"scripts": {
89
"dev": "vite --host",

examples/auth/src/App.tsx

+53-20
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Canvas } from '@react-three/fiber'
1+
import { Canvas, useFrame } from '@react-three/fiber'
22
import {
33
Container,
44
Fullscreen,
@@ -12,22 +12,26 @@ import { Defaults, colors } from '@/theme.js'
1212
import { Button } from '@/button.js'
1313
import { UserAuthForm } from './components/user-auth-form.js'
1414
import { noEvents, PointerEvents } from '@react-three/xr'
15+
import { create } from 'zustand'
1516

1617
setPreferredColorScheme('light')
1718

1819
export default function App() {
1920
return (
20-
<Canvas
21-
flat
22-
frameloop="demand"
23-
camera={{ position: [0, 0, 18], fov: 35 }}
24-
style={{ height: '100dvh', touchAction: 'none' }}
25-
gl={{ localClippingEnabled: true }}
26-
events={noEvents}
27-
{...canvasInputProps}
28-
>
29-
<PointerEvents />
30-
{/*<Root backgroundColor={0xffffff} sizeX={8.34} sizeY={5.58} pixelSize={0.01}>
21+
<>
22+
<FrameCounter />
23+
<Canvas
24+
flat
25+
frameloop="demand"
26+
camera={{ position: [0, 0, 18], fov: 35 }}
27+
style={{ height: '100dvh', touchAction: 'none' }}
28+
gl={{ localClippingEnabled: true }}
29+
events={noEvents}
30+
{...canvasInputProps}
31+
>
32+
<CountFrames />
33+
<PointerEvents />
34+
{/*<Root backgroundColor={0xffffff} sizeX={8.34} sizeY={5.58} pixelSize={0.01}>
3135
<Defaults>
3236
<DialogAnchor>
3337
<MarketPage />
@@ -39,14 +43,43 @@ export default function App() {
3943
<TiltShift2 blur={0.25} />
4044
</EffectComposer>
4145
<OrbitControls makeDefault />*/}
42-
<Fullscreen backgroundColor={colors.background}>
43-
<Defaults>
44-
<DefaultProperties scrollbarWidth={8} scrollbarOpacity={0.1} scrollbarBorderRadius={4}>
45-
<AuthenticationPage />
46-
</DefaultProperties>
47-
</Defaults>
48-
</Fullscreen>
49-
</Canvas>
46+
<Fullscreen backgroundColor={colors.background}>
47+
<Defaults>
48+
<DefaultProperties scrollbarWidth={8} scrollbarOpacity={0.1} scrollbarBorderRadius={4}>
49+
<AuthenticationPage />
50+
</DefaultProperties>
51+
</Defaults>
52+
</Fullscreen>
53+
</Canvas>
54+
</>
55+
)
56+
}
57+
58+
const useFrameCounter = create(() => 0)
59+
60+
function CountFrames() {
61+
useFrame(() => useFrameCounter.setState(useFrameCounter.getState() + 1))
62+
return null
63+
}
64+
65+
function FrameCounter() {
66+
const counter = useFrameCounter()
67+
return (
68+
<div
69+
style={{
70+
position: 'absolute',
71+
top: 0,
72+
right: 0,
73+
backgroundColor: 'black',
74+
fontSize: '2rem',
75+
padding: '0.5rem 1rem',
76+
color: 'white',
77+
fontFamily: 'sans-serif',
78+
zIndex: 100,
79+
}}
80+
>
81+
{counter}
82+
</div>
5083
)
5184
}
5285

examples/dashboard/src/App.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ import { noEvents, PointerEvents } from '@react-three/xr'
1919

2020
setPreferredColorScheme('light')
2121

22-
const useFrameCounter = create(() => 0)
23-
2422
export default function App() {
2523
const [open, setOpen] = useState(false)
2624
return (
@@ -51,6 +49,8 @@ export default function App() {
5149
)
5250
}
5351

52+
const useFrameCounter = create(() => 0)
53+
5454
function CountFrames() {
5555
useFrame(() => useFrameCounter.setState(useFrameCounter.getState() + 1))
5656
return null

packages/react/src/container.tsx

+19-10
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@ import { ParentProvider, useParent } from './context.js'
44
import { AddHandlers, R3FEventMap, usePropertySignals } from './utils.js'
55
import {
66
ContainerProperties as BaseContainerProperties,
7-
createContainer,
8-
unsubscribeSubscriptions,
9-
Subscriptions,
10-
initialize,
7+
createContainerState,
8+
setupContainer,
119
} from '@pmndrs/uikit/internals'
1210
import { ComponentInternals, useComponentInternals } from './ref.js'
1311
import { DefaultProperties } from './default.js'
@@ -25,25 +23,36 @@ export const Container: (props: ContainerProperties & RefAttributes<ContainerRef
2523
const outerRef = useRef<Object3D>(null)
2624
const innerRef = useRef<Object3D>(null)
2725
const propertySignals = usePropertySignals(properties)
26+
2827
const internals = useMemo(
2928
() =>
30-
createContainer<R3FEventMap>(
29+
createContainerState<R3FEventMap>(
3130
parent,
31+
outerRef,
3232
propertySignals.style,
3333
propertySignals.properties,
3434
propertySignals.default,
35-
outerRef,
36-
innerRef,
3735
),
3836
[parent, propertySignals],
3937
)
4038

4139
internals.interactionPanel.name = properties.name ?? ''
4240

4341
useEffect(() => {
44-
const subscriptions: Subscriptions = []
45-
initialize(internals.initializers, subscriptions)
46-
return () => unsubscribeSubscriptions(subscriptions)
42+
if (outerRef.current == null || innerRef.current == null) {
43+
return
44+
}
45+
const abortController = new AbortController()
46+
setupContainer<R3FEventMap>(
47+
internals,
48+
parent,
49+
propertySignals.style,
50+
propertySignals.properties,
51+
outerRef.current,
52+
innerRef.current,
53+
abortController.signal,
54+
)
55+
return () => abortController.abort()
4756
}, [parent, propertySignals, internals])
4857

4958
useComponentInternals(ref, parent.root.pixelSize, propertySignals.style, internals, internals.interactionPanel)

packages/react/src/content.tsx

+17-8
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ import { forwardRef, ReactNode, RefAttributes, useEffect, useMemo, useRef } from
22
import { Object3D } from 'three'
33
import { ParentProvider, useParent } from './context.js'
44
import { AddHandlers, R3FEventMap, usePropertySignals } from './utils.js'
5-
import { createContent, initialize, Subscriptions, unsubscribeSubscriptions } from '@pmndrs/uikit/internals'
65
import { ComponentInternals, useComponentInternals } from './ref.js'
7-
import { ContentProperties as BaseContentProperties } from '../../uikit/dist/components/content.js'
6+
import { ContentProperties as BaseContentProperties, createContentState, setupContent } from '@pmndrs/uikit/internals'
87

98
export type ContentProperties = {
109
name?: string
@@ -21,12 +20,11 @@ export const Content: (props: ContentProperties & RefAttributes<ContentRef>) =>
2120
const propertySignals = usePropertySignals(properties)
2221
const internals = useMemo(
2322
() =>
24-
createContent<R3FEventMap>(
23+
createContentState<R3FEventMap>(
2524
parent,
2625
propertySignals.style,
2726
propertySignals.properties,
2827
propertySignals.default,
29-
outerRef,
3028
innerRef,
3129
),
3230
[parent, propertySignals],
@@ -35,10 +33,21 @@ export const Content: (props: ContentProperties & RefAttributes<ContentRef>) =>
3533
internals.interactionPanel.name = properties.name ?? ''
3634

3735
useEffect(() => {
38-
const subscriptions: Subscriptions = []
39-
initialize(internals.initializers, subscriptions)
40-
return () => unsubscribeSubscriptions(subscriptions)
41-
}, [internals])
36+
if (outerRef.current == null || innerRef.current == null) {
37+
return
38+
}
39+
const abortController = new AbortController()
40+
setupContent<R3FEventMap>(
41+
internals,
42+
parent,
43+
propertySignals.style,
44+
propertySignals.properties,
45+
outerRef.current,
46+
innerRef.current,
47+
abortController.signal,
48+
)
49+
return () => abortController.abort()
50+
}, [internals, parent, propertySignals])
4251

4352
useComponentInternals(ref, parent.root.pixelSize, propertySignals.style, internals, internals.interactionPanel)
4453

packages/react/src/custom.tsx

+21-13
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@ import { Material, Mesh, Object3D } from 'three'
33
import { ParentProvider, useParent } from './context.js'
44
import { AddHandlers, R3FEventMap, usePropertySignals } from './utils.js'
55
import {
6-
createCustomContainer,
76
CustomContainerProperties as BaseCustomContainerProperties,
8-
initialize,
7+
createCustomContainerState,
98
panelGeometry,
10-
Subscriptions,
11-
unsubscribeSubscriptions,
9+
setupCustomContainer,
1210
} from '@pmndrs/uikit/internals'
1311
import { ComponentInternals, useComponentInternals } from './ref.js'
1412

@@ -29,28 +27,38 @@ export const CustomContainer: (props: CustomContainerProperties & RefAttributes<
2927
const propertySignals = usePropertySignals(properties)
3028
const internals = useMemo(
3129
() =>
32-
createCustomContainer<R3FEventMap>(
30+
createCustomContainerState<R3FEventMap>(
3331
parent,
3432
propertySignals.style,
3533
propertySignals.properties,
3634
propertySignals.default,
37-
outerRef,
38-
innerRef,
3935
),
4036
[parent, propertySignals],
4137
)
4238
useEffect(() => {
43-
const subscriptions: Subscriptions = []
44-
initialize(internals.initializers, subscriptions)
45-
return () => unsubscribeSubscriptions(subscriptions)
46-
}, [internals])
39+
if (outerRef.current == null || innerRef.current == null) {
40+
return
41+
}
42+
const abortController = new AbortController()
43+
setupCustomContainer<R3FEventMap>(
44+
internals,
45+
parent,
46+
propertySignals.style,
47+
propertySignals.properties,
48+
outerRef.current,
49+
innerRef.current,
50+
abortController.signal,
51+
)
52+
return () => abortController.abort()
53+
}, [internals, parent, propertySignals])
4754

4855
useComponentInternals(ref, parent.root.pixelSize, propertySignals.style, internals, innerRef)
4956

5057
useEffect(() => {
51-
if (innerRef.current && properties.name) {
52-
innerRef.current.name = properties.name
58+
if (innerRef.current == null) {
59+
return
5360
}
61+
innerRef.current.name = properties.name ?? ''
5462
}, [properties.name])
5563

5664
return (

packages/react/src/font.tsx

+4-13
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@ import {
44
FontFamilyWeightMap,
55
FontWeight,
66
GlyphLayoutProperties,
7-
Initializers,
87
MergedProperties,
9-
Subscriptions,
108
computedFont,
11-
initialize,
129
measureGlyphLayout,
13-
unsubscribeSubscriptions,
1410
} from '@pmndrs/uikit/internals'
1511
import { signal } from '@preact/signals-core'
1612
import { useThree } from '@react-three/fiber'
@@ -44,15 +40,10 @@ export function useMeasureText(fontFamily?: string, fontWeight?: FontWeight) {
4440
const renderer = useThree((state) => state.gl)
4541
const fontFamilies = useMemo(() => signal<FontFamilies | undefined>(undefined as any), [])
4642
fontFamilies.value = useFontFamilies()
47-
const { font, initializers } = useMemo(() => {
48-
const initializers: Initializers = []
49-
return { font: computedFont(propertiesSignal, fontFamilies, renderer, initializers), initializers }
50-
}, [fontFamilies, propertiesSignal, renderer])
51-
useEffect(() => {
52-
const subscriptions: Subscriptions = []
53-
initialize(initializers, subscriptions)
54-
return () => unsubscribeSubscriptions(subscriptions)
55-
}, [initializers])
43+
const font = useMemo(
44+
() => computedFont(propertiesSignal, fontFamilies, renderer),
45+
[fontFamilies, propertiesSignal, renderer],
46+
)
5647
return useCallback(
5748
async (properties: Omit<GlyphLayoutProperties, 'font'> & { availableWidth?: number }) => {
5849
let fontValue = font.peek()

packages/react/src/icon.tsx

+16-13
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
import {
2-
IconProperties as BaseIconProperties,
3-
Subscriptions,
4-
createIcon,
5-
initialize,
6-
unsubscribeSubscriptions,
7-
} from '@pmndrs/uikit/internals'
1+
import { IconProperties as BaseIconProperties, createIconState, setupIcon } from '@pmndrs/uikit/internals'
82
import { ReactNode, RefAttributes, forwardRef, useEffect, useMemo, useRef } from 'react'
93
import { Object3D } from 'three'
104
import { AddHandlers, R3FEventMap, usePropertySignals } from './utils.js'
@@ -27,26 +21,35 @@ export const Icon: (props: IconProperties & RefAttributes<IconRef>) => ReactNode
2721
const propertySignals = usePropertySignals(properties)
2822
const internals = useMemo(
2923
() =>
30-
createIcon<R3FEventMap>(
24+
createIconState<R3FEventMap>(
3125
parent,
3226
properties.text,
3327
properties.svgWidth,
3428
properties.svgHeight,
3529
propertySignals.style,
3630
propertySignals.properties,
3731
propertySignals.default,
38-
outerRef,
3932
),
4033
[parent, properties.svgHeight, properties.svgWidth, properties.text, propertySignals],
4134
)
4235

4336
internals.interactionPanel.name = properties.name ?? ''
4437

4538
useEffect(() => {
46-
const subscriptions: Subscriptions = []
47-
initialize(internals.initializers, subscriptions)
48-
return () => unsubscribeSubscriptions(subscriptions)
49-
}, [internals])
39+
if (outerRef.current == null) {
40+
return
41+
}
42+
const abortController = new AbortController()
43+
setupIcon<R3FEventMap>(
44+
internals,
45+
parent,
46+
propertySignals.style,
47+
propertySignals.properties,
48+
outerRef.current,
49+
abortController.signal,
50+
)
51+
return () => abortController.abort()
52+
}, [parent, propertySignals, internals])
5053

5154
useComponentInternals(ref, parent.root.pixelSize, propertySignals.style, internals, internals.interactionPanel)
5255

0 commit comments

Comments
 (0)