Skip to content

Commit

Permalink
use abort signals, improve architecture, and fix #125
Browse files Browse the repository at this point in the history
  • Loading branch information
bbohlender committed Mar 6, 2025
1 parent c7c5d8c commit 440e672
Show file tree
Hide file tree
Showing 53 changed files with 2,715 additions and 2,526 deletions.
3 changes: 2 additions & 1 deletion examples/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"type": "module",
"dependencies": {
"@react-three/uikit": "workspace:^",
"@react-three/uikit-lucide": "workspace:^"
"@react-three/uikit-lucide": "workspace:^",
"zustand": "^4.5.2"
},
"scripts": {
"dev": "vite --host",
Expand Down
73 changes: 53 additions & 20 deletions examples/auth/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Canvas } from '@react-three/fiber'
import { Canvas, useFrame } from '@react-three/fiber'
import {
Container,
Fullscreen,
Expand All @@ -12,22 +12,26 @@ import { Defaults, colors } from '@/theme.js'
import { Button } from '@/button.js'
import { UserAuthForm } from './components/user-auth-form.js'
import { noEvents, PointerEvents } from '@react-three/xr'
import { create } from 'zustand'

setPreferredColorScheme('light')

export default function App() {
return (
<Canvas
flat
frameloop="demand"
camera={{ position: [0, 0, 18], fov: 35 }}
style={{ height: '100dvh', touchAction: 'none' }}
gl={{ localClippingEnabled: true }}
events={noEvents}
{...canvasInputProps}
>
<PointerEvents />
{/*<Root backgroundColor={0xffffff} sizeX={8.34} sizeY={5.58} pixelSize={0.01}>
<>
<FrameCounter />
<Canvas
flat
frameloop="demand"
camera={{ position: [0, 0, 18], fov: 35 }}
style={{ height: '100dvh', touchAction: 'none' }}
gl={{ localClippingEnabled: true }}
events={noEvents}
{...canvasInputProps}
>
<CountFrames />
<PointerEvents />
{/*<Root backgroundColor={0xffffff} sizeX={8.34} sizeY={5.58} pixelSize={0.01}>
<Defaults>
<DialogAnchor>
<MarketPage />
Expand All @@ -39,14 +43,43 @@ export default function App() {
<TiltShift2 blur={0.25} />
</EffectComposer>
<OrbitControls makeDefault />*/}
<Fullscreen backgroundColor={colors.background}>
<Defaults>
<DefaultProperties scrollbarWidth={8} scrollbarOpacity={0.1} scrollbarBorderRadius={4}>
<AuthenticationPage />
</DefaultProperties>
</Defaults>
</Fullscreen>
</Canvas>
<Fullscreen backgroundColor={colors.background}>
<Defaults>
<DefaultProperties scrollbarWidth={8} scrollbarOpacity={0.1} scrollbarBorderRadius={4}>
<AuthenticationPage />
</DefaultProperties>
</Defaults>
</Fullscreen>
</Canvas>
</>
)
}

const useFrameCounter = create(() => 0)

function CountFrames() {
useFrame(() => useFrameCounter.setState(useFrameCounter.getState() + 1))
return null
}

function FrameCounter() {
const counter = useFrameCounter()
return (
<div
style={{
position: 'absolute',
top: 0,
right: 0,
backgroundColor: 'black',
fontSize: '2rem',
padding: '0.5rem 1rem',
color: 'white',
fontFamily: 'sans-serif',
zIndex: 100,
}}
>
{counter}
</div>
)
}

Expand Down
4 changes: 2 additions & 2 deletions examples/dashboard/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import { noEvents, PointerEvents } from '@react-three/xr'

setPreferredColorScheme('light')

const useFrameCounter = create(() => 0)

export default function App() {
const [open, setOpen] = useState(false)
return (
Expand Down Expand Up @@ -51,6 +49,8 @@ export default function App() {
)
}

const useFrameCounter = create(() => 0)

function CountFrames() {
useFrame(() => useFrameCounter.setState(useFrameCounter.getState() + 1))
return null
Expand Down
29 changes: 19 additions & 10 deletions packages/react/src/container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import { ParentProvider, useParent } from './context.js'
import { AddHandlers, R3FEventMap, usePropertySignals } from './utils.js'
import {
ContainerProperties as BaseContainerProperties,
createContainer,
unsubscribeSubscriptions,
Subscriptions,
initialize,
createContainerState,
setupContainer,
} from '@pmndrs/uikit/internals'
import { ComponentInternals, useComponentInternals } from './ref.js'
import { DefaultProperties } from './default.js'
Expand All @@ -25,25 +23,36 @@ export const Container: (props: ContainerProperties & RefAttributes<ContainerRef
const outerRef = useRef<Object3D>(null)
const innerRef = useRef<Object3D>(null)
const propertySignals = usePropertySignals(properties)

const internals = useMemo(
() =>
createContainer<R3FEventMap>(
createContainerState<R3FEventMap>(
parent,
outerRef,
propertySignals.style,
propertySignals.properties,
propertySignals.default,
outerRef,
innerRef,
),
[parent, propertySignals],
)

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

useEffect(() => {
const subscriptions: Subscriptions = []
initialize(internals.initializers, subscriptions)
return () => unsubscribeSubscriptions(subscriptions)
if (outerRef.current == null || innerRef.current == null) {
return
}
const abortController = new AbortController()
setupContainer<R3FEventMap>(
internals,
parent,
propertySignals.style,
propertySignals.properties,
outerRef.current,
innerRef.current,
abortController.signal,
)
return () => abortController.abort()
}, [parent, propertySignals, internals])

useComponentInternals(ref, parent.root.pixelSize, propertySignals.style, internals, internals.interactionPanel)
Expand Down
25 changes: 17 additions & 8 deletions packages/react/src/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import { forwardRef, ReactNode, RefAttributes, useEffect, useMemo, useRef } from
import { Object3D } from 'three'
import { ParentProvider, useParent } from './context.js'
import { AddHandlers, R3FEventMap, usePropertySignals } from './utils.js'
import { createContent, initialize, Subscriptions, unsubscribeSubscriptions } from '@pmndrs/uikit/internals'
import { ComponentInternals, useComponentInternals } from './ref.js'
import { ContentProperties as BaseContentProperties } from '../../uikit/dist/components/content.js'
import { ContentProperties as BaseContentProperties, createContentState, setupContent } from '@pmndrs/uikit/internals'

export type ContentProperties = {
name?: string
Expand All @@ -21,12 +20,11 @@ export const Content: (props: ContentProperties & RefAttributes<ContentRef>) =>
const propertySignals = usePropertySignals(properties)
const internals = useMemo(
() =>
createContent<R3FEventMap>(
createContentState<R3FEventMap>(
parent,
propertySignals.style,
propertySignals.properties,
propertySignals.default,
outerRef,
innerRef,
),
[parent, propertySignals],
Expand All @@ -35,10 +33,21 @@ export const Content: (props: ContentProperties & RefAttributes<ContentRef>) =>
internals.interactionPanel.name = properties.name ?? ''

useEffect(() => {
const subscriptions: Subscriptions = []
initialize(internals.initializers, subscriptions)
return () => unsubscribeSubscriptions(subscriptions)
}, [internals])
if (outerRef.current == null || innerRef.current == null) {
return
}
const abortController = new AbortController()
setupContent<R3FEventMap>(
internals,
parent,
propertySignals.style,
propertySignals.properties,
outerRef.current,
innerRef.current,
abortController.signal,
)
return () => abortController.abort()
}, [internals, parent, propertySignals])

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

Expand Down
34 changes: 21 additions & 13 deletions packages/react/src/custom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ import { Material, Mesh, Object3D } from 'three'
import { ParentProvider, useParent } from './context.js'
import { AddHandlers, R3FEventMap, usePropertySignals } from './utils.js'
import {
createCustomContainer,
CustomContainerProperties as BaseCustomContainerProperties,
initialize,
createCustomContainerState,
panelGeometry,
Subscriptions,
unsubscribeSubscriptions,
setupCustomContainer,
} from '@pmndrs/uikit/internals'
import { ComponentInternals, useComponentInternals } from './ref.js'

Expand All @@ -29,28 +27,38 @@ export const CustomContainer: (props: CustomContainerProperties & RefAttributes<
const propertySignals = usePropertySignals(properties)
const internals = useMemo(
() =>
createCustomContainer<R3FEventMap>(
createCustomContainerState<R3FEventMap>(
parent,
propertySignals.style,
propertySignals.properties,
propertySignals.default,
outerRef,
innerRef,
),
[parent, propertySignals],
)
useEffect(() => {
const subscriptions: Subscriptions = []
initialize(internals.initializers, subscriptions)
return () => unsubscribeSubscriptions(subscriptions)
}, [internals])
if (outerRef.current == null || innerRef.current == null) {
return
}
const abortController = new AbortController()
setupCustomContainer<R3FEventMap>(
internals,
parent,
propertySignals.style,
propertySignals.properties,
outerRef.current,
innerRef.current,
abortController.signal,
)
return () => abortController.abort()
}, [internals, parent, propertySignals])

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

useEffect(() => {
if (innerRef.current && properties.name) {
innerRef.current.name = properties.name
if (innerRef.current == null) {
return
}
innerRef.current.name = properties.name ?? ''
}, [properties.name])

return (
Expand Down
17 changes: 4 additions & 13 deletions packages/react/src/font.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,9 @@ import {
FontFamilyWeightMap,
FontWeight,
GlyphLayoutProperties,
Initializers,
MergedProperties,
Subscriptions,
computedFont,
initialize,
measureGlyphLayout,
unsubscribeSubscriptions,
} from '@pmndrs/uikit/internals'
import { signal } from '@preact/signals-core'
import { useThree } from '@react-three/fiber'
Expand Down Expand Up @@ -44,15 +40,10 @@ export function useMeasureText(fontFamily?: string, fontWeight?: FontWeight) {
const renderer = useThree((state) => state.gl)
const fontFamilies = useMemo(() => signal<FontFamilies | undefined>(undefined as any), [])
fontFamilies.value = useFontFamilies()
const { font, initializers } = useMemo(() => {
const initializers: Initializers = []
return { font: computedFont(propertiesSignal, fontFamilies, renderer, initializers), initializers }
}, [fontFamilies, propertiesSignal, renderer])
useEffect(() => {
const subscriptions: Subscriptions = []
initialize(initializers, subscriptions)
return () => unsubscribeSubscriptions(subscriptions)
}, [initializers])
const font = useMemo(
() => computedFont(propertiesSignal, fontFamilies, renderer),
[fontFamilies, propertiesSignal, renderer],
)
return useCallback(
async (properties: Omit<GlyphLayoutProperties, 'font'> & { availableWidth?: number }) => {
let fontValue = font.peek()
Expand Down
29 changes: 16 additions & 13 deletions packages/react/src/icon.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import {
IconProperties as BaseIconProperties,
Subscriptions,
createIcon,
initialize,
unsubscribeSubscriptions,
} from '@pmndrs/uikit/internals'
import { IconProperties as BaseIconProperties, createIconState, setupIcon } from '@pmndrs/uikit/internals'
import { ReactNode, RefAttributes, forwardRef, useEffect, useMemo, useRef } from 'react'
import { Object3D } from 'three'
import { AddHandlers, R3FEventMap, usePropertySignals } from './utils.js'
Expand All @@ -27,26 +21,35 @@ export const Icon: (props: IconProperties & RefAttributes<IconRef>) => ReactNode
const propertySignals = usePropertySignals(properties)
const internals = useMemo(
() =>
createIcon<R3FEventMap>(
createIconState<R3FEventMap>(
parent,
properties.text,
properties.svgWidth,
properties.svgHeight,
propertySignals.style,
propertySignals.properties,
propertySignals.default,
outerRef,
),
[parent, properties.svgHeight, properties.svgWidth, properties.text, propertySignals],
)

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

useEffect(() => {
const subscriptions: Subscriptions = []
initialize(internals.initializers, subscriptions)
return () => unsubscribeSubscriptions(subscriptions)
}, [internals])
if (outerRef.current == null) {
return
}
const abortController = new AbortController()
setupIcon<R3FEventMap>(
internals,
parent,
propertySignals.style,
propertySignals.properties,
outerRef.current,
abortController.signal,
)
return () => abortController.abort()
}, [parent, propertySignals, internals])

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

Expand Down
Loading

0 comments on commit 440e672

Please sign in to comment.