From a0ee4ab74e290d84b031adf58d0e0d4252b34f77 Mon Sep 17 00:00:00 2001 From: Bela Bohlender Date: Fri, 5 Apr 2024 17:42:27 +0200 Subject: [PATCH] feat: ref.current.setStyle --- examples/uikit/src/App.tsx | 9 +- packages/react/src/container.tsx | 12 +- packages/react/src/content.tsx | 4 +- packages/react/src/custom.tsx | 11 +- packages/react/src/fullscreen.tsx | 2 +- packages/react/src/icon.tsx | 8 +- packages/react/src/image.tsx | 13 ++- packages/react/src/index.ts | 9 ++ packages/react/src/input.tsx | 9 +- packages/react/src/portal.tsx | 129 +++++++++++---------- packages/react/src/ref.ts | 9 +- packages/react/src/root.tsx | 10 +- packages/react/src/suspending.tsx | 20 ++-- packages/react/src/svg.tsx | 13 ++- packages/react/src/text.tsx | 13 ++- packages/react/src/utilts.tsx | 2 +- packages/uikit/src/active.ts | 17 +-- packages/uikit/src/components/container.ts | 11 +- packages/uikit/src/components/content.ts | 10 +- packages/uikit/src/components/custom.ts | 11 +- packages/uikit/src/components/icon.ts | 13 ++- packages/uikit/src/components/image.ts | 14 ++- packages/uikit/src/components/input.ts | 26 +++-- packages/uikit/src/components/root.ts | 10 +- packages/uikit/src/components/svg.ts | 10 +- packages/uikit/src/components/text.ts | 11 +- packages/uikit/src/components/utils.ts | 12 +- packages/uikit/src/hover.ts | 19 +-- packages/uikit/src/index.ts | 11 ++ packages/uikit/src/listeners.ts | 12 +- packages/uikit/src/properties/default.ts | 13 ++- packages/uikit/src/properties/merged.ts | 5 +- packages/uikit/src/scroll.ts | 4 +- packages/uikit/src/vanilla/container.ts | 21 ++-- packages/uikit/src/vanilla/content.ts | 36 ++++-- packages/uikit/src/vanilla/custom.ts | 32 +++-- packages/uikit/src/vanilla/icon.ts | 22 ++-- packages/uikit/src/vanilla/image.ts | 24 ++-- packages/uikit/src/vanilla/input.ts | 27 +++-- packages/uikit/src/vanilla/root.ts | 23 ++-- packages/uikit/src/vanilla/svg.ts | 26 +++-- packages/uikit/src/vanilla/text.ts | 21 ++-- 42 files changed, 455 insertions(+), 259 deletions(-) diff --git a/examples/uikit/src/App.tsx b/examples/uikit/src/App.tsx index 607026e3..0da89574 100644 --- a/examples/uikit/src/App.tsx +++ b/examples/uikit/src/App.tsx @@ -1,4 +1,4 @@ -import { Suspense, useMemo, useState } from 'react' +import { Suspense, useMemo, useRef, useState } from 'react' import { Canvas } from '@react-three/fiber' import { Gltf, Box, PerspectiveCamera, RenderTexture } from '@react-three/drei' import { signal } from '@preact/signals-core' @@ -14,6 +14,8 @@ import { SuspendingImage, Input, FontFamilyProvider, + ComponentInternals, + ImageProperties, } from '@react-three/uikit' import { Texture } from 'three' import { Skeleton } from '../../../packages/kits/default/skeleton.js' @@ -24,6 +26,7 @@ export default function App() { const s = useMemo(() => signal(5), []) const x = useMemo(() => signal('red'), []) const t = useMemo(() => signal('X X\nX X'), []) + const ref = useRef>(null) return ( @@ -118,9 +121,11 @@ export default function App() { }> ref.current?.setStyle({ borderOpacity: hovered ? 10 : 0.2 })} borderOpacity={0.2} borderRadius={10} flexDirection="column" diff --git a/packages/react/src/container.tsx b/packages/react/src/container.tsx index 0354fd8d..19a3cbe0 100644 --- a/packages/react/src/container.tsx +++ b/packages/react/src/container.tsx @@ -11,14 +11,22 @@ export const Container: ( children?: ReactNode } & ContainerProperties & EventHandlers & - RefAttributes, + RefAttributes>, ) => ReactNode = forwardRef((properties, ref) => { const parent = useParent() const outerRef = useRef(null) const innerRef = useRef(null) const propertySignals = usePropertySignals(properties) const internals = useMemo( - () => createContainer(parent, propertySignals.properties, propertySignals.default, outerRef, innerRef), + () => + createContainer( + parent, + propertySignals.style, + propertySignals.properties, + propertySignals.default, + outerRef, + innerRef, + ), [parent, propertySignals], ) useEffect(() => () => unsubscribeSubscriptions(internals.subscriptions), [internals]) diff --git a/packages/react/src/content.tsx b/packages/react/src/content.tsx index 31286a65..a93d6891 100644 --- a/packages/react/src/content.tsx +++ b/packages/react/src/content.tsx @@ -12,14 +12,14 @@ export const Content: ( children?: ReactNode } & ContentProperties & EventHandlers & - RefAttributes, + RefAttributes>, ) => ReactNode = forwardRef((properties, ref) => { const parent = useParent() const outerRef = useRef(null) const innerRef = useRef(null) const propertySignals = usePropertySignals(properties) const internals = useMemo( - () => createContent(parent, propertySignals.properties, propertySignals.default, outerRef), + () => createContent(parent, propertySignals.style, propertySignals.properties, propertySignals.default, outerRef), [parent, propertySignals], ) useEffect(() => { diff --git a/packages/react/src/custom.tsx b/packages/react/src/custom.tsx index b3aa9e09..47203db7 100644 --- a/packages/react/src/custom.tsx +++ b/packages/react/src/custom.tsx @@ -18,14 +18,21 @@ export const CustomContainer: ( customDistanceMaterial?: Material } & CustomContainerProperties & EventHandlers & - RefAttributes, + RefAttributes>, ) => ReactNode = forwardRef((properties, ref) => { const parent = useParent() const outerRef = useRef(null) const innerRef = useRef(null) const propertySignals = usePropertySignals(properties) const internals = useMemo( - () => createCustomContainer(parent, propertySignals.properties, propertySignals.default, outerRef), + () => + createCustomContainer( + parent, + propertySignals.style, + propertySignals.properties, + propertySignals.default, + outerRef, + ), [parent, propertySignals], ) useEffect(() => { diff --git a/packages/react/src/fullscreen.tsx b/packages/react/src/fullscreen.tsx index ebb99e2e..7beb1086 100644 --- a/packages/react/src/fullscreen.tsx +++ b/packages/react/src/fullscreen.tsx @@ -12,7 +12,7 @@ export const Fullscreen: ( children?: ReactNode attachCamera?: boolean } & EventHandlers & - RefAttributes, + RefAttributes>, ) => ReactNode = forwardRef((properties, ref) => { const store = useStore() const pixelSize = properties.pixelSize ?? 0.01 diff --git a/packages/react/src/icon.tsx b/packages/react/src/icon.tsx index 8fb97ee7..43e72588 100644 --- a/packages/react/src/icon.tsx +++ b/packages/react/src/icon.tsx @@ -9,7 +9,12 @@ import type { EventHandlers } from '@react-three/fiber/dist/declarations/src/cor export const Icon: ( props: IconProperties & EventHandlers & - RefAttributes & { text: string; svgWidth: number; svgHeight: number; children?: ReactNode }, + RefAttributes> & { + text: string + svgWidth: number + svgHeight: number + children?: ReactNode + }, ) => ReactNode = forwardRef((properties, ref) => { const parent = useParent() const outerRef = useRef(null) @@ -21,6 +26,7 @@ export const Icon: ( properties.text, properties.svgWidth, properties.svgHeight, + propertySignals.style, propertySignals.properties, propertySignals.default, outerRef, diff --git a/packages/react/src/image.tsx b/packages/react/src/image.tsx index 4a72bcaf..3083576e 100644 --- a/packages/react/src/image.tsx +++ b/packages/react/src/image.tsx @@ -10,7 +10,7 @@ import { Signal, signal } from '@preact/signals-core' export const Image: ( props: ImageProperties & EventHandlers & - RefAttributes & { + RefAttributes> & { src?: Signal | string | Texture | Signal children?: ReactNode }, @@ -25,7 +25,16 @@ export const Image: ( ) srcSignal.value = properties.src const internals = useMemo( - () => createImage(parent, srcSignal, propertySignals.properties, propertySignals.default, outerRef, innerRef), + () => + createImage( + parent, + srcSignal, + propertySignals.style, + propertySignals.properties, + propertySignals.default, + outerRef, + innerRef, + ), [parent, propertySignals, srcSignal], ) useEffect(() => () => unsubscribeSubscriptions(internals.subscriptions), [internals]) diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 4fb9728f..4da8e442 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -12,6 +12,15 @@ export { type ViewportListeners, type Listeners, type AllOptionalProperties, + type ImageProperties, + type ContainerProperties, + type ContentProperties, + type CustomContainerProperties, + type IconProperties, + type InputProperties, + type RootProperties, + type SvgProperties, + type TextProperties, } from '@pmndrs/uikit' export { DefaultProperties } from './default.js' export type { ComponentInternals } from './ref.js' diff --git a/packages/react/src/input.tsx b/packages/react/src/input.tsx index 7aff6993..52bfafcc 100644 --- a/packages/react/src/input.tsx +++ b/packages/react/src/input.tsx @@ -14,7 +14,10 @@ import { ComponentInternals, useComponentInternals } from './ref.js' import { computed, ReadonlySignal, Signal, signal } from '@preact/signals-core' import { useFontFamilies } from './font.js' -export type InputInternals = ComponentInternals & { current: ReadonlySignal; focus: () => void } +export type InputInternals = ComponentInternals & { + current: ReadonlySignal + focus: () => void +} export const Input: ( props: { @@ -45,10 +48,12 @@ export const Input: ( if (!controlled.current) { valueSignal.value = newValue } - propertySignals.properties.peek().onValueChange?.(newValue) + propertySignals.style.peek()?.onValueChange?.(newValue) + propertySignals.properties.peek()?.onValueChange?.(newValue) }, properties.multiline ?? false, fontFamilies, + propertySignals.style, propertySignals.properties, propertySignals.default, outerRef, diff --git a/packages/react/src/portal.tsx b/packages/react/src/portal.tsx index 24d9d2c0..9deffe96 100644 --- a/packages/react/src/portal.tsx +++ b/packages/react/src/portal.tsx @@ -17,76 +17,77 @@ import type { DomEvent, EventHandlers } from '@react-three/fiber/dist/declaratio import type { ImageProperties } from '@pmndrs/uikit/internals' import type { ComponentInternals } from './ref.js' +export type PortalProperties = { + frames?: number + renderPriority?: number + eventPriority?: number + resolution?: number + children?: ReactNode +} & Omit, 'src' | 'fit'> + export const Portal: ( - props: Omit & + props: PortalProperties & EventHandlers & - RefAttributes & { + RefAttributes> & { children?: ReactNode }, -) => ReactNode = forwardRef< - ComponentInternals, - { - frames?: number - renderPriority?: number - eventPriority?: number - resolution?: number - children?: ReactNode - } & Omit, 'src' | 'fit'> ->(({ children, resolution = 1, frames = Infinity, renderPriority = 0, eventPriority = 0, ...props }, ref) => { - // eslint-disable-next-line react-hooks/exhaustive-deps - const fbo = useMemo( - () => - new WebGLRenderTarget(1, 1, { - minFilter: LinearFilter, - magFilter: LinearFilter, - type: HalfFloatType, +) => ReactNode = forwardRef( + ({ children, resolution = 1, frames = Infinity, renderPriority = 0, eventPriority = 0, ...props }, ref) => { + // eslint-disable-next-line react-hooks/exhaustive-deps + const fbo = useMemo( + () => + new WebGLRenderTarget(1, 1, { + minFilter: LinearFilter, + magFilter: LinearFilter, + type: HalfFloatType, + }), + [], + ) + const imageRef = useRef>(null) + const injectState = useMemo( + () => ({ + events: { compute: uvCompute.bind(null, imageRef), priority: eventPriority }, + size: { width: 1, height: 1, left: 0, top: 0 }, }), - [], - ) - const imageRef = useRef(null) - const injectState = useMemo( - () => ({ - events: { compute: uvCompute.bind(null, imageRef), priority: eventPriority }, - size: { width: 1, height: 1, left: 0, top: 0 }, - }), - [eventPriority], - ) - useEffect(() => { - if (imageRef.current == null) { - return - } - const { size } = imageRef.current - const unsubscribeSetSize = effect(() => { - const [width, height] = size.value - fbo.setSize(width, height) - injectState.size!.width = width - injectState.size!.height = height - }) - return () => { - unsubscribeSetSize() - fbo.dispose() - } - }, [fbo, injectState]) - useImperativeHandle(ref, () => imageRef.current!, []) - const vScene = useMemo(() => new Scene(), []) - return ( - <> - {createPortal( - - {children} - {/* Without an element that receives pointer events state.pointer will always be 0/0 */} - null} /> - , - vScene, - injectState, - )} - - - ) -}) + [eventPriority], + ) + useEffect(() => { + if (imageRef.current == null) { + return + } + const { size } = imageRef.current + const unsubscribeSetSize = effect(() => { + const [width, height] = size.value + fbo.setSize(width, height) + injectState.size!.width = width + injectState.size!.height = height + }) + return () => { + unsubscribeSetSize() + fbo.dispose() + } + }, [fbo, injectState]) + useImperativeHandle(ref, () => imageRef.current!, []) + const vScene = useMemo(() => new Scene(), []) + return ( + <> + {createPortal( + + {children} + {/* Without an element that receives pointer events state.pointer will always be 0/0 */} + null} /> + , + vScene, + injectState, + )} + + + ) + }, +) function uvCompute( - { current }: RefObject, + { current }: RefObject>, event: DomEvent, state: RootState, previous?: RootState, @@ -116,7 +117,7 @@ function ChildrenToFBO({ renderPriority: number children: ReactNode fbo: WebGLRenderTarget - imageRef: RefObject + imageRef: RefObject> }) { const store = useStore() useEffect(() => { diff --git a/packages/react/src/ref.ts b/packages/react/src/ref.ts index 02a4e99e..12bc2767 100644 --- a/packages/react/src/ref.ts +++ b/packages/react/src/ref.ts @@ -12,7 +12,7 @@ import { import { ForwardedRef, RefObject, useImperativeHandle } from 'react' import { Vector2Tuple, Mesh } from 'three' -export type ComponentInternals = { +export type ComponentInternals = { pixelSize: number size: ReadonlySignal center: ReadonlySignal @@ -21,11 +21,12 @@ export type ComponentInternals = { scrollPosition?: Signal maxScrollPosition?: Signal> interactionPanel: Mesh + setStyle(style: T | undefined): void } export function useComponentInternals( - ref: ForwardedRef, - styleSignal: Signal, + ref: ForwardedRef & O>, + styleSignal: Signal, internals: ReturnType< | typeof createContainer | typeof createImage @@ -45,7 +46,7 @@ export function useComponentInternals( () => { const { scrollPosition, node, root } = internals return { - setStyle: (style: T) => (styleSignal.value = style), + setStyle: (style: T | undefined) => (styleSignal.value = style), pixelSize: root.pixelSize, borderInset: node.borderInset, paddingInset: node.paddingInset, diff --git a/packages/react/src/root.tsx b/packages/react/src/root.tsx index b3554861..354f1a7f 100644 --- a/packages/react/src/root.tsx +++ b/packages/react/src/root.tsx @@ -3,12 +3,7 @@ import { EventHandlers } from '@react-three/fiber/dist/declarations/src/core/eve import { forwardRef, ReactNode, RefAttributes, useEffect, useMemo, useRef } from 'react' import { ParentProvider } from './context.js' import { AddHandlers, usePropertySignals } from './utilts.js' -import { - RootProperties, - createRoot, - reversePainterSortStable, - unsubscribeSubscriptions, -} from '@pmndrs/uikit/internals' +import { RootProperties, createRoot, reversePainterSortStable, unsubscribeSubscriptions } from '@pmndrs/uikit/internals' import { Object3D } from 'three' import { ComponentInternals, useComponentInternals } from './ref.js' @@ -16,7 +11,7 @@ export const Root: ( props: RootProperties & { children?: ReactNode } & EventHandlers & - RefAttributes, + RefAttributes>, ) => ReactNode = forwardRef((properties, ref) => { const renderer = useThree((state) => state.gl) @@ -28,6 +23,7 @@ export const Root: ( const internals = useMemo( () => createRoot( + propertySignals.style, propertySignals.properties, propertySignals.default, outerRef, diff --git a/packages/react/src/suspending.tsx b/packages/react/src/suspending.tsx index 108c7e66..250178be 100644 --- a/packages/react/src/suspending.tsx +++ b/packages/react/src/suspending.tsx @@ -1,14 +1,20 @@ -import { ComponentPropsWithoutRef } from 'react' +import { ReactNode, RefAttributes, forwardRef } from 'react' import { Image } from './image.js' import { useLoader } from '@react-three/fiber' import { SRGBColorSpace, TextureLoader } from 'three' +import { ComponentInternals } from './ref.js' +import { EventHandlers, ImageProperties } from '@pmndrs/uikit/internals' -export function SuspendingImage({ - src, - ...props -}: Omit, 'src'> & { src: string }) { +export const SuspendingImage: ( + props: ImageProperties & + EventHandlers & + RefAttributes> & { + src: string + children?: ReactNode + }, +) => ReactNode = forwardRef(({ src, ...props }, ref) => { const texture = useLoader(TextureLoader, src) texture.colorSpace = SRGBColorSpace texture.matrixAutoUpdate = false - return -} + return +}) diff --git a/packages/react/src/svg.tsx b/packages/react/src/svg.tsx index b116fd82..fca38a12 100644 --- a/packages/react/src/svg.tsx +++ b/packages/react/src/svg.tsx @@ -11,7 +11,7 @@ import {} from '@react-three/fiber' export const Svg: ( props: SvgProperties & EventHandlers & - RefAttributes & { src: string | Signal; children?: ReactNode }, + RefAttributes> & { src: string | Signal; children?: ReactNode }, ) => ReactNode = forwardRef((properties, ref) => { const parent = useParent() const outerRef = useRef(null) @@ -20,7 +20,16 @@ export const Svg: ( const srcSignal = useMemo(() => signal>(''), []) srcSignal.value = properties.src const internals = useMemo( - () => createSvg(parent, srcSignal, propertySignals.properties, propertySignals.default, outerRef, innerRef), + () => + createSvg( + parent, + srcSignal, + propertySignals.style, + propertySignals.properties, + propertySignals.default, + outerRef, + innerRef, + ), [parent, propertySignals, srcSignal], ) useEffect(() => () => unsubscribeSubscriptions(internals.subscriptions), [internals]) diff --git a/packages/react/src/text.tsx b/packages/react/src/text.tsx index 8f438dc9..664b4990 100644 --- a/packages/react/src/text.tsx +++ b/packages/react/src/text.tsx @@ -13,7 +13,7 @@ export const Text: ( children: string | Array> | Signal } & TextProperties & EventHandlers & - RefAttributes, + RefAttributes>, ) => ReactNode = forwardRef((properties, ref) => { const parent = useParent() const outerRef = useRef(null) @@ -26,7 +26,16 @@ export const Text: ( const fontFamilies = useMemo(() => signal(undefined as any), []) fontFamilies.value = useFontFamilies() const internals = useMemo( - () => createText(parent, textSignal, fontFamilies, propertySignals.properties, propertySignals.default, outerRef), + () => + createText( + parent, + textSignal, + fontFamilies, + propertySignals.style, + propertySignals.properties, + propertySignals.default, + outerRef, + ), [fontFamilies, parent, propertySignals, textSignal], ) useEffect(() => () => unsubscribeSubscriptions(internals.subscriptions), [internals]) diff --git a/packages/react/src/utilts.tsx b/packages/react/src/utilts.tsx index 84a19f79..20b1cb60 100644 --- a/packages/react/src/utilts.tsx +++ b/packages/react/src/utilts.tsx @@ -57,7 +57,7 @@ export function usePropertySignals(properties: T) { const propertySignals = useMemo( () => ({ style: signal(undefined), - properties: signal(undefined as any), + properties: signal(undefined as any), default: signal(undefined), }), [], diff --git a/packages/uikit/src/active.ts b/packages/uikit/src/active.ts index 6bc1b70b..af5c2786 100644 --- a/packages/uikit/src/active.ts +++ b/packages/uikit/src/active.ts @@ -13,36 +13,39 @@ export type ActiveEventHandlers = Pick> & EventHandlers, + style: (WithClasses> & EventHandlers) | undefined, + properties: (WithClasses> & EventHandlers) | undefined, defaultProperties: AllOptionalProperties | undefined, activeSignal: Signal>, ): void { let activePropertiesExist = false - traverseProperties(defaultProperties, properties, (p) => { + traverseProperties(style, defaultProperties, properties, (p) => { if ('active' in p) { activePropertiesExist = true } }) - if (!activePropertiesExist && properties.onActiveChange == null) { + if (!activePropertiesExist && style?.onActiveChange == null && properties?.onActiveChange == null) { //no need to listen to hover activeSignal.value.length = 0 return } const onLeave = ({ nativeEvent }: ThreeEvent) => { activeSignal.value = activeSignal.value.filter((id) => id != nativeEvent.pointerId) - if (properties.onActiveChange == null || activeSignal.value.length > 0) { + if (activeSignal.value.length > 0) { return } - properties.onActiveChange(false) + properties?.onActiveChange?.(false) + style?.onActiveChange?.(false) } addHandler('onPointerDown', target, ({ nativeEvent }) => { activeSignal.value = [nativeEvent.pointerId, ...activeSignal.value] - if (properties.onActiveChange == null || activeSignal.value.length != 1) { + if (activeSignal.value.length != 1) { return } - properties.onActiveChange(true) + properties?.onActiveChange?.(true) + style?.onActiveChange?.(true) }) addHandler('onPointerUp', target, onLeave) addHandler('onPointerLeave', target, onLeave) diff --git a/packages/uikit/src/components/container.ts b/packages/uikit/src/components/container.ts index 4aeae0ca..fa51cea7 100644 --- a/packages/uikit/src/components/container.ts +++ b/packages/uikit/src/components/container.ts @@ -50,7 +50,8 @@ export type ContainerProperties = InheritableContainerProperties & Listeners export function createContainer( parentContext: ParentContext, - properties: Signal, + style: Signal, + properties: Signal, defaultProperties: Signal, object: Object3DRef, childrenContainer: Object3DRef, @@ -62,7 +63,7 @@ export function createContainer( setupCursorCleanup(hoveredSignal, subscriptions) //properties - const mergedProperties = computedMergedProperties(properties, defaultProperties, { + const mergedProperties = computedMergedProperties(style, properties, defaultProperties, { ...darkPropertyTransformers, ...createResponsivePropertyTransformers(parentContext.root.node.size), ...createHoverPropertyTransformers(hoveredSignal), @@ -123,8 +124,8 @@ export function createContainer( subscriptions, ) - setupLayoutListeners(properties, node.size, subscriptions) - setupViewportListeners(properties, isClipped, subscriptions) + setupLayoutListeners(style, properties, node.size, subscriptions) + setupViewportListeners(style, properties, isClipped, subscriptions) return { clippingRect: computedClippingRect( @@ -147,7 +148,7 @@ export function createContainer( parentContext.clippingRect, subscriptions, ), - handlers: computedHandlers(properties, defaultProperties, hoveredSignal, activeSignal, scrollHandlers), + handlers: computedHandlers(style, properties, defaultProperties, hoveredSignal, activeSignal, scrollHandlers), subscriptions, } } diff --git a/packages/uikit/src/components/content.ts b/packages/uikit/src/components/content.ts index 3a64cb74..7fa4fb30 100644 --- a/packages/uikit/src/components/content.ts +++ b/packages/uikit/src/components/content.ts @@ -60,7 +60,8 @@ export type ContentProperties = InheritableContentProperties & Listeners export function createContent( parentContext: ParentContext, - properties: Signal, + style: Signal, + properties: Signal, defaultProperties: Signal, object: Object3DRef, ) { @@ -75,6 +76,7 @@ export function createContent( //properties const mergedProperties = computedMergedProperties( + style, properties, defaultProperties, { @@ -118,8 +120,8 @@ export function createContent( const orderInfo = computedOrderInfo(undefined, ElementType.Object, undefined, backgroundorderInfo) - setupLayoutListeners(properties, node.size, subscriptions) - setupViewportListeners(properties, isClipped, subscriptions) + setupLayoutListeners(style, properties, node.size, subscriptions) + setupViewportListeners(style, properties, isClipped, subscriptions) return { setupContent: createSetupContent( @@ -148,7 +150,7 @@ export function createContent( parentContext.clippingRect, subscriptions, ), - handlers: computedHandlers(properties, defaultProperties, hoveredSignal, activeSignal), + handlers: computedHandlers(style, properties, defaultProperties, hoveredSignal, activeSignal), subscriptions, } } diff --git a/packages/uikit/src/components/custom.ts b/packages/uikit/src/components/custom.ts index 6b53d062..08d6b6a8 100644 --- a/packages/uikit/src/components/custom.ts +++ b/packages/uikit/src/components/custom.ts @@ -42,7 +42,8 @@ export type CustomContainerProperties = InheritableCustomContainerProperties & L export function createCustomContainer( parentContext: ParentContext, - properties: Signal, + style: Signal, + properties: Signal, defaultProperties: Signal, object: Object3DRef, ) { @@ -53,7 +54,7 @@ export function createCustomContainer( setupCursorCleanup(hoveredSignal, subscriptions) //properties - const mergedProperties = computedMergedProperties(properties, defaultProperties, { + const mergedProperties = computedMergedProperties(style, properties, defaultProperties, { ...darkPropertyTransformers, ...createResponsivePropertyTransformers(parentContext.root.node.size), ...createHoverPropertyTransformers(hoveredSignal), @@ -104,15 +105,15 @@ export function createCustomContainer( material.shadowSide = FrontSide } - setupLayoutListeners(properties, node.size, subscriptions) - setupViewportListeners(properties, isClipped, subscriptions) + setupLayoutListeners(style, properties, node.size, subscriptions) + setupViewportListeners(style, properties, isClipped, subscriptions) return { root: parentContext.root, setupMesh, setupMaterial, node, - handlers: computedHandlers(properties, defaultProperties, hoveredSignal, activeSignal), + handlers: computedHandlers(style, properties, defaultProperties, hoveredSignal, activeSignal), subscriptions, } } diff --git a/packages/uikit/src/components/icon.ts b/packages/uikit/src/components/icon.ts index 46c95f04..a173daff 100644 --- a/packages/uikit/src/components/icon.ts +++ b/packages/uikit/src/components/icon.ts @@ -18,7 +18,7 @@ import { createNode, keepAspectRatioPropertyTransformer, } from './utils.js' -import { ColorRepresentation, Subscriptions, fitNormalizedContentInside } from '../utils.js' +import { Subscriptions, fitNormalizedContentInside } from '../utils.js' import { makeClippedRaycast } from '../panel/interaction-panel-mesh.js' import { computedIsClipped, createGlobalClippingPlanes } from '../clipping.js' import { setupLayoutListeners, setupViewportListeners } from '../listeners.js' @@ -31,7 +31,6 @@ import { MergedProperties, PanelGroupProperties, computedPanelGroupDependencies, - computedProperty, darkPropertyTransformers, getDefaultPanelMaterialConfig, } from '../internals.js' @@ -60,7 +59,8 @@ export function createIcon( text: string, svgWidth: number, svgHeight: number, - properties: Signal, + style: Signal, + properties: Signal, defaultProperties: Signal, object: Object3DRef, ) { @@ -70,6 +70,7 @@ export function createIcon( setupCursorCleanup(hoveredSignal, subscriptions) const mergedProperties = computedMergedProperties( + style, properties, defaultProperties, { @@ -128,15 +129,15 @@ export function createIcon( subscriptions, ) - setupLayoutListeners(properties, node.size, subscriptions) - setupViewportListeners(properties, isClipped, subscriptions) + setupLayoutListeners(style, properties, node.size, subscriptions) + setupViewportListeners(style, properties, isClipped, subscriptions) return { root: parentContext.root, node, subscriptions, iconGroup, - handlers: computedHandlers(properties, defaultProperties, hoveredSignal, activeSignal), + handlers: computedHandlers(style, properties, defaultProperties, hoveredSignal, activeSignal), interactionPanel: createInteractionPanel( node, orderInfo, diff --git a/packages/uikit/src/components/image.ts b/packages/uikit/src/components/image.ts index ec0ab1dd..4bde4959 100644 --- a/packages/uikit/src/components/image.ts +++ b/packages/uikit/src/components/image.ts @@ -88,7 +88,8 @@ export type ImageProperties = InheritableImageProperties & Listeners export function createImage( parentContext: ParentContext, srcSignal: Signal | string | Texture | Signal | undefined>, - properties: Signal, + style: Signal, + properties: Signal, defaultProperties: Signal, object: Object3DRef, childrenContainer: Object3DRef, @@ -112,6 +113,7 @@ export function createImage( }) const mergedProperties = computedMergedProperties( + style, properties, defaultProperties, { @@ -160,8 +162,8 @@ export function createImage( subscriptions, ) - setupLayoutListeners(properties, node.size, subscriptions) - setupViewportListeners(properties, isClipped, subscriptions) + setupLayoutListeners(style, properties, node.size, subscriptions) + setupViewportListeners(style, properties, isClipped, subscriptions) const ctx: ParentContext = { clippingRect: computedClippingRect( @@ -179,7 +181,7 @@ export function createImage( } return Object.assign(ctx, { subscriptions, - handlers: computedHandlers(properties, defaultProperties, hoveredSignal, activeSignal, scrollHandlers), + handlers: computedHandlers(style, properties, defaultProperties, hoveredSignal, activeSignal, scrollHandlers), interactionPanel: createImageMesh(mergedProperties, texture, parentContext, ctx, isHidden, subscriptions), }) } @@ -208,13 +210,13 @@ function createImageMesh( propertiesSignal: Signal, texture: Signal, parent: ParentContext, - { node, orderInfo, root, clippingRect }: ParentContext, + { node, orderInfo, root }: ParentContext, isHidden: Signal, subscriptions: Subscriptions, ) { const mesh = new Mesh(panelGeometry) mesh.matrixAutoUpdate = false - const clippingPlanes = createGlobalClippingPlanes(root, clippingRect, subscriptions) + const clippingPlanes = createGlobalClippingPlanes(root, parent.clippingRect, subscriptions) const isVisible = getImageMaterialConfig().computedIsVisibile(propertiesSignal, node.borderInset, node.size, isHidden) setupImageMaterials(propertiesSignal, mesh, node.size, node.borderInset, isVisible, clippingPlanes, subscriptions) mesh.raycast = makeClippedRaycast(mesh, makePanelRaycast(mesh), root.object, parent.clippingRect, orderInfo) diff --git a/packages/uikit/src/components/input.ts b/packages/uikit/src/components/input.ts index f9e21f2a..f80ec106 100644 --- a/packages/uikit/src/components/input.ts +++ b/packages/uikit/src/components/input.ts @@ -34,6 +34,7 @@ import { createInstancedText, darkPropertyTransformers, getDefaultPanelMaterialConfig, + traverseProperties, } from '../internals.js' import { Vector2Tuple, Vector2, Vector3Tuple } from 'three' import { CaretProperties, createCaret } from '../caret.js' @@ -95,7 +96,8 @@ export function createInput( onChange: (newValue: string) => void, multiline: boolean, fontFamilies: Signal | undefined, - properties: Signal, + style: Signal, + properties: Signal, defaultProperties: Signal, object: Object3DRef, ) { @@ -106,6 +108,7 @@ export function createInput( setupCursorCleanup(hoveredSignal, subscriptions) const mergedProperties = computedMergedProperties( + style, properties, defaultProperties, { @@ -117,9 +120,10 @@ export function createInput( }, undefined, (m) => { - const { value } = properties - m.add('caretOpacity', value.opacity) - m.add('caretColor', value.color) + traverseProperties(style.value, properties.value, defaultProperties.value, (p) => { + m.add('caretOpacity', p.opacity) + m.add('caretColor', p.color) + }) }, ) @@ -199,8 +203,8 @@ export function createInput( ) subscriptions.push(node.setMeasureFunc(measureFunc)) - setupLayoutListeners(properties, node.size, subscriptions) - setupViewportListeners(properties, isClipped, subscriptions) + setupLayoutListeners(style, properties, node.size, subscriptions) + setupViewportListeners(style, properties, isClipped, subscriptions) const disabled = computedProperty(mergedProperties, 'disabled', false) @@ -226,7 +230,15 @@ export function createInput( parentContext.clippingRect, subscriptions, ), - handlers: computedHandlers(properties, defaultProperties, hoveredSignal, activeSignal, selectionHandlers, 'text'), + handlers: computedHandlers( + style, + properties, + defaultProperties, + hoveredSignal, + activeSignal, + selectionHandlers, + 'text', + ), subscriptions, } } diff --git a/packages/uikit/src/components/root.ts b/packages/uikit/src/components/root.ts index a42a2f05..b4d050a5 100644 --- a/packages/uikit/src/components/root.ts +++ b/packages/uikit/src/components/root.ts @@ -62,7 +62,8 @@ const vectorHelper = new Vector3() const planeHelper = new Plane() export function createRoot( - properties: Signal, + style: Signal, + properties: Signal, defaultProperties: Signal, object: Object3DRef, childrenContainer: Object3DRef, @@ -74,11 +75,12 @@ export function createRoot( const activeSignal = signal>([]) const subscriptions = [] as Subscriptions setupCursorCleanup(hoveredSignal, subscriptions) - const pixelSize = untracked(() => properties.value.pixelSize ?? DEFAULT_PIXEL_SIZE) + const pixelSize = properties.peek()?.pixelSize ?? DEFAULT_PIXEL_SIZE const onFrameSet = new Set<(delta: number) => void>() const mergedProperties = computedMergedProperties( + style, properties, defaultProperties, { @@ -165,7 +167,7 @@ export function createRoot( subscriptions, ) - setupLayoutListeners(properties, node.size, subscriptions) + setupLayoutListeners(style, properties, node.size, subscriptions) const gylphGroupManager = new GlyphGroupManager(pixelSize, ctx, object) onFrameSet.add(gylphGroupManager.onFrame) @@ -188,7 +190,7 @@ export function createRoot( return Object.assign(rootCtx, { subscriptions, interactionPanel: createInteractionPanel(node, orderInfo, rootCtx, undefined, subscriptions), - handlers: computedHandlers(properties, defaultProperties, hoveredSignal, activeSignal, scrollHandlers), + handlers: computedHandlers(style, properties, defaultProperties, hoveredSignal, activeSignal, scrollHandlers), root: rootCtx, }) } diff --git a/packages/uikit/src/components/svg.ts b/packages/uikit/src/components/svg.ts index 97ce2c95..f183512f 100644 --- a/packages/uikit/src/components/svg.ts +++ b/packages/uikit/src/components/svg.ts @@ -71,7 +71,8 @@ export type SvgProperties = InheritableSvgProperties & Listeners export function createSvg( parentContext: ParentContext, srcSignal: Signal | string>, - properties: Signal, + style: Signal, + properties: Signal, defaultProperties: Signal, object: Object3DRef, childrenContainer: Object3DRef, @@ -84,6 +85,7 @@ export function createSvg( const aspectRatio = signal(undefined) const mergedProperties = computedMergedProperties( + style, properties, defaultProperties, { @@ -172,8 +174,8 @@ export function createSvg( subscriptions, ) - setupLayoutListeners(properties, node.size, subscriptions) - setupViewportListeners(properties, isClipped, subscriptions) + setupLayoutListeners(style, properties, node.size, subscriptions) + setupViewportListeners(style, properties, isClipped, subscriptions) return { clippingRect: computedClippingRect( @@ -190,7 +192,7 @@ export function createSvg( root: parentContext.root, subscriptions, centerGroup, - handlers: computedHandlers(properties, defaultProperties, hoveredSignal, activeSignal, scrollHandlers), + handlers: computedHandlers(style, properties, defaultProperties, hoveredSignal, activeSignal, scrollHandlers), interactionPanel: createInteractionPanel( node, orderInfo, diff --git a/packages/uikit/src/components/text.ts b/packages/uikit/src/components/text.ts index 76a2b628..f3242ee4 100644 --- a/packages/uikit/src/components/text.ts +++ b/packages/uikit/src/components/text.ts @@ -54,7 +54,8 @@ export function createText( parentContext: ParentContext, textSignal: Signal | Array>>, fontFamilies: Signal | undefined, - properties: Signal, + style: Signal, + properties: Signal, defaultProperties: Signal, object: Object3DRef, ) { @@ -63,7 +64,7 @@ export function createText( const subscriptions = [] as Subscriptions setupCursorCleanup(hoveredSignal, subscriptions) - const mergedProperties = computedMergedProperties(properties, defaultProperties, { + const mergedProperties = computedMergedProperties(style, properties, defaultProperties, { ...darkPropertyTransformers, ...createResponsivePropertyTransformers(parentContext.root.node.size), ...createHoverPropertyTransformers(hoveredSignal), @@ -122,8 +123,8 @@ export function createText( ) subscriptions.push(node.setMeasureFunc(measureFunc)) - setupLayoutListeners(properties, node.size, subscriptions) - setupViewportListeners(properties, isClipped, subscriptions) + setupLayoutListeners(style, properties, node.size, subscriptions) + setupViewportListeners(style, properties, isClipped, subscriptions) return { root: parentContext.root, @@ -135,7 +136,7 @@ export function createText( parentContext.clippingRect, subscriptions, ), - handlers: computedHandlers(properties, defaultProperties, hoveredSignal, activeSignal), + handlers: computedHandlers(style, properties, defaultProperties, hoveredSignal, activeSignal), subscriptions, } } diff --git a/packages/uikit/src/components/utils.ts b/packages/uikit/src/components/utils.ts index e50e2421..6ec5b85c 100644 --- a/packages/uikit/src/components/utils.ts +++ b/packages/uikit/src/components/utils.ts @@ -84,7 +84,8 @@ export const keepAspectRatioPropertyTransformer: PropertyTransformers = { } export function computedHandlers( - properties: Signal, + style: Signal, + properties: Signal, defaultProperties: Signal, hoveredSignal: Signal>, activeSignal: Signal>, @@ -94,8 +95,8 @@ export function computedHandlers( return computed(() => { const handlers: EventHandlers = {} addHandlers(handlers, dynamicHandlers?.value) - addHoverHandlers(handlers, properties.value, defaultProperties.value, hoveredSignal, defaultCursor) - addActiveHandlers(handlers, properties.value, defaultProperties.value, activeSignal) + addHoverHandlers(handlers, style.value, properties.value, defaultProperties.value, hoveredSignal, defaultCursor) + addActiveHandlers(handlers, style.value, properties.value, defaultProperties.value, activeSignal) return handlers }) } @@ -129,7 +130,8 @@ export function addHandler void }, K e } export function computedMergedProperties( - properties: Signal, + style: Signal, + properties: Signal, defaultProperties: Signal, postTransformers: PropertyTransformers, preTransformers?: PropertyTransformers, @@ -138,7 +140,7 @@ export function computedMergedProperties( return computed(() => { const merged = new MergedProperties(preTransformers) onInit?.(merged) - merged.addAll(defaultProperties.value, properties.value, postTransformers) + merged.addAll(style.value, properties.value, defaultProperties.value, postTransformers) return merged }) } diff --git a/packages/uikit/src/hover.ts b/packages/uikit/src/hover.ts index 28eb57f4..8d900126 100644 --- a/packages/uikit/src/hover.ts +++ b/packages/uikit/src/hover.ts @@ -19,28 +19,30 @@ export function setupCursorCleanup(hoveredSignal: Signal>, subscri export function addHoverHandlers( target: EventHandlers, - properties: WithHover<{}>, + style: WithHover<{}> | undefined, + properties: WithHover<{}> | undefined, defaultProperties: AllOptionalProperties | undefined, hoveredSignal: Signal>, defaultCursor?: string, ): void { let hoverPropertiesExist = false - traverseProperties(defaultProperties, properties, (p) => { + traverseProperties(style, properties, defaultProperties, (p) => { if ('hover' in p) { hoverPropertiesExist = true } }) - const cursor = properties.cursor ?? defaultCursor - if (!hoverPropertiesExist && properties.onHoverChange == null && cursor == null) { + const cursor = style?.cursor ?? properties?.cursor ?? defaultCursor + if (!hoverPropertiesExist && style?.onHoverChange == null && properties?.onHoverChange == null && cursor == null) { //no need to listen to hover hoveredSignal.value.length = 0 return } addHandler('onPointerOver', target, ({ nativeEvent }) => { hoveredSignal.value = [nativeEvent.pointerId, ...hoveredSignal.value] - if (properties.onHoverChange != null && hoveredSignal.value.length === 1) { - properties.onHoverChange(true) + if (hoveredSignal.value.length === 1) { + properties?.onHoverChange?.(true) + style?.onHoverChange?.(true) } if (cursor != null) { setCursorType(hoveredSignal, cursor) @@ -48,8 +50,9 @@ export function addHoverHandlers( }) addHandler('onPointerOut', target, ({ nativeEvent }) => { hoveredSignal.value = hoveredSignal.value.filter((id) => id != nativeEvent.pointerId) - if (properties.onHoverChange != null && hoveredSignal.value.length === 0) { - properties.onHoverChange(false) + if (hoveredSignal.value.length === 0) { + properties?.onHoverChange?.(false) + style?.onHoverChange?.(false) } unsetCursorType(hoveredSignal) }) diff --git a/packages/uikit/src/index.ts b/packages/uikit/src/index.ts index 0e1f98cb..bdb5e48d 100644 --- a/packages/uikit/src/index.ts +++ b/packages/uikit/src/index.ts @@ -10,4 +10,15 @@ export { export type { MaterialClass } from './panel/panel-material.js' export type { Listeners, LayoutListeners, ScrollListeners, ViewportListeners } from './listeners.js' export type { AllOptionalProperties } from './properties/default.js' +export type { + ImageProperties, + ContainerProperties, + ContentProperties, + CustomContainerProperties, + IconProperties, + InputProperties, + RootProperties, + SvgProperties, + TextProperties, +} from './components/index.js' export * from './vanilla/index.js' diff --git a/packages/uikit/src/listeners.ts b/packages/uikit/src/listeners.ts index b32fca3d..4924b0af 100644 --- a/packages/uikit/src/listeners.ts +++ b/packages/uikit/src/listeners.ts @@ -31,7 +31,8 @@ export type ViewportListeners = { } export function setupLayoutListeners( - listeners: Signal, + l1: Signal, + l2: Signal, size: Signal, subscriptions: Subscriptions, ) { @@ -43,13 +44,15 @@ export function setupLayoutListeners( first = false return } - listeners.peek().onSizeChange?.(...s) + l1.peek()?.onSizeChange?.(...s) + l2.peek()?.onSizeChange?.(...s) }), ) } export function setupViewportListeners( - listeners: Signal, + l1: Signal, + l2: Signal, isClipped: Signal, subscriptions: Subscriptions, ) { @@ -61,7 +64,8 @@ export function setupViewportListeners( first = false return } - listeners.peek().onIsInViewportChange?.(isInViewport) + l1.peek()?.onIsInViewportChange?.(isInViewport) + l2.peek()?.onIsInViewportChange?.(isInViewport) }), ) } diff --git a/packages/uikit/src/properties/default.ts b/packages/uikit/src/properties/default.ts index e4ca7d9e..3f831afd 100644 --- a/packages/uikit/src/properties/default.ts +++ b/packages/uikit/src/properties/default.ts @@ -32,16 +32,23 @@ export type Properties = Record export type WithClasses = T & { classes?: T | Array } export function traverseProperties( + style: WithClasses | undefined, + properties: WithClasses | undefined, defaultProperties: AllOptionalProperties | undefined, - properties: WithClasses, fn: (properties: T) => void, ): void { if (defaultProperties != null) { traverseClasses(defaultProperties.classes as any, fn) fn(defaultProperties as T) } - traverseClasses(properties.classes as any, fn) - fn(properties) + if (properties != null) { + traverseClasses(properties.classes as any, fn) + fn(properties) + } + if (style != null) { + traverseClasses(style.classes as any, fn) + fn(style) + } } function traverseClasses(classes: WithClasses['classes'], fn: (properties: T) => void) { diff --git a/packages/uikit/src/properties/merged.ts b/packages/uikit/src/properties/merged.ts index a1c247b8..f7394c6d 100644 --- a/packages/uikit/src/properties/merged.ts +++ b/packages/uikit/src/properties/merged.ts @@ -117,11 +117,12 @@ export class MergedProperties { } addAll( + style: WithClasses | undefined, + properties: WithClasses | undefined, defaultProperties: AllOptionalProperties | undefined, - properties: WithClasses, postTransformers: PropertyTransformers, ): void { - traverseProperties(defaultProperties, properties, (p) => { + traverseProperties(style, properties, defaultProperties, (p) => { for (const key in p) { this.add(key, p[key]) } diff --git a/packages/uikit/src/scroll.ts b/packages/uikit/src/scroll.ts index b6b162e0..c94bca01 100644 --- a/packages/uikit/src/scroll.ts +++ b/packages/uikit/src/scroll.ts @@ -53,7 +53,7 @@ export function setupScrollHandler( node: FlexNode, scrollPosition: Signal, object: Object3DRef, - listeners: Signal, + listeners: Signal, pixelSize: number, onFrameSet: Set<(delta: number) => void>, subscriptions: Subscriptions, @@ -95,7 +95,7 @@ export function setupScrollHandler( wasScrolledY || Math.min(y, (maxY ?? 0) - y) > 5, ) } - const preventScroll = listeners.peek().onScroll?.(newX, newY, scrollPosition, event) + const preventScroll = listeners.peek()?.onScroll?.(newX, newY, scrollPosition, event) if (preventScroll === false || (x === newX && y === newY)) { return } diff --git a/packages/uikit/src/vanilla/container.ts b/packages/uikit/src/vanilla/container.ts index ef4a0c7e..6dceb984 100644 --- a/packages/uikit/src/vanilla/container.ts +++ b/packages/uikit/src/vanilla/container.ts @@ -12,10 +12,11 @@ export class Container extends Object3D { public readonly internals: ReturnType public readonly fontFamiliesSignal: Signal - private readonly propertiesSignal: Signal + private readonly styleSignal: Signal = signal(undefined) + private readonly propertiesSignal: Signal private readonly defaultPropertiesSignal: Signal - constructor(parent: Parent, properties: ContainerProperties = {}, defaultProperties?: AllOptionalProperties) { + constructor(parent: Parent, properties?: ContainerProperties, defaultProperties?: AllOptionalProperties) { super() this.fontFamiliesSignal = parent.fontFamiliesSignal this.propertiesSignal = signal(properties) @@ -30,6 +31,7 @@ export class Container extends Object3D { //setting up the container this.internals = createContainer( parent.internals, + this.styleSignal, this.propertiesSignal, this.defaultPropertiesSignal, { current: this }, @@ -42,11 +44,16 @@ export class Container extends Object3D { bindHandlers(handlers, this, subscriptions) } - setProperties(properties: Properties, defaultProperties?: AllOptionalProperties) { - batch(() => { - this.propertiesSignal.value = properties - this.defaultPropertiesSignal.value = defaultProperties - }) + setStyle(style: ContainerProperties | undefined) { + this.styleSignal.value = style + } + + setProperties(properties: ContainerProperties | undefined) { + this.propertiesSignal.value = properties + } + + setDefaultProperties(properties: AllOptionalProperties) { + this.defaultPropertiesSignal.value = properties } destroy() { diff --git a/packages/uikit/src/vanilla/content.ts b/packages/uikit/src/vanilla/content.ts index cdcdbb52..a50589ca 100644 --- a/packages/uikit/src/vanilla/content.ts +++ b/packages/uikit/src/vanilla/content.ts @@ -7,15 +7,16 @@ import { Subscriptions, unsubscribeSubscriptions } from '../utils.js' import { ContentProperties, createContent, FontFamilies } from '../internals.js' export class Content extends Object3D { - private contentContainer: Object3D public readonly internals: ReturnType public readonly fontFamiliesSignal: Signal - private readonly propertiesSignal: Signal + private readonly contentContainer: Object3D + private readonly styleSignal: Signal = signal(undefined) + private readonly propertiesSignal: Signal private readonly defaultPropertiesSignal: Signal - private contentSubscriptions: Subscriptions = [] + private readonly contentSubscriptions: Subscriptions = [] - constructor(parent: Parent, properties: ContentProperties = {}, defaultProperties?: AllOptionalProperties) { + constructor(parent: Parent, properties?: ContentProperties, defaultProperties?: AllOptionalProperties) { super() this.fontFamiliesSignal = parent.fontFamiliesSignal this.propertiesSignal = signal(properties) @@ -28,9 +29,15 @@ export class Content extends Object3D { parent.add(this) //setting up the container - this.internals = createContent(parent.internals, this.propertiesSignal, this.defaultPropertiesSignal, { - current: this, - }) + this.internals = createContent( + parent.internals, + this.styleSignal, + this.propertiesSignal, + this.defaultPropertiesSignal, + { + current: this, + }, + ) //setup events const { handlers, interactionPanel, subscriptions } = this.internals @@ -45,11 +52,16 @@ export class Content extends Object3D { this.internals.setupContent(this.contentContainer, this.contentSubscriptions) } - setProperties(properties: Properties, defaultProperties?: AllOptionalProperties) { - batch(() => { - this.propertiesSignal.value = properties - this.defaultPropertiesSignal.value = defaultProperties - }) + setStyle(style: ContentProperties | undefined) { + this.styleSignal.value = style + } + + setProperties(properties: ContentProperties | undefined) { + this.propertiesSignal.value = properties + } + + setDefaultProperties(properties: AllOptionalProperties) { + this.defaultPropertiesSignal.value = properties } destroy() { diff --git a/packages/uikit/src/vanilla/custom.ts b/packages/uikit/src/vanilla/custom.ts index 078d8dd8..df0d50d9 100644 --- a/packages/uikit/src/vanilla/custom.ts +++ b/packages/uikit/src/vanilla/custom.ts @@ -10,10 +10,11 @@ export class CustomContainer extends Object3D { public readonly internals: ReturnType public readonly fontFamiliesSignal: Signal - private readonly propertiesSignal: Signal + private readonly styleSignal: Signal = signal(undefined) + private readonly propertiesSignal: Signal private readonly defaultPropertiesSignal: Signal - constructor(parent: Parent, properties: CustomContainerProperties = {}, defaultProperties?: AllOptionalProperties) { + constructor(parent: Parent, properties?: CustomContainerProperties, defaultProperties?: AllOptionalProperties) { super() this.fontFamiliesSignal = parent.fontFamiliesSignal this.propertiesSignal = signal(properties) @@ -23,9 +24,15 @@ export class CustomContainer extends Object3D { parent.add(this) //setting up the container - this.internals = createCustomContainer(parent.internals, this.propertiesSignal, this.defaultPropertiesSignal, { - current: this, - }) + this.internals = createCustomContainer( + parent.internals, + this.styleSignal, + this.propertiesSignal, + this.defaultPropertiesSignal, + { + current: this, + }, + ) //setup events const { handlers, subscriptions, setupMesh, setupMaterial } = this.internals @@ -37,11 +44,16 @@ export class CustomContainer extends Object3D { bindHandlers(handlers, this, subscriptions) } - setProperties(properties: Properties, defaultProperties?: AllOptionalProperties) { - batch(() => { - this.propertiesSignal.value = properties - this.defaultPropertiesSignal.value = defaultProperties - }) + setStyle(style: CustomContainerProperties | undefined) { + this.styleSignal.value = style + } + + setProperties(properties: CustomContainerProperties | undefined) { + this.propertiesSignal.value = properties + } + + setDefaultProperties(properties: AllOptionalProperties) { + this.defaultPropertiesSignal.value = properties } destroy() { diff --git a/packages/uikit/src/vanilla/icon.ts b/packages/uikit/src/vanilla/icon.ts index 4381b130..35bda418 100644 --- a/packages/uikit/src/vanilla/icon.ts +++ b/packages/uikit/src/vanilla/icon.ts @@ -9,7 +9,8 @@ import { IconProperties, createIcon } from '../components/icon.js' export class Icon extends Object3D { public readonly internals: ReturnType - private readonly propertiesSignal: Signal + private readonly styleSignal: Signal = signal(undefined) + private readonly propertiesSignal: Signal private readonly defaultPropertiesSignal: Signal constructor( @@ -17,7 +18,7 @@ export class Icon extends Object3D { text: string, svgWidth: number, svgHeight: number, - properties: IconProperties = {}, + properties?: IconProperties, defaultProperties?: AllOptionalProperties, ) { super() @@ -30,11 +31,11 @@ export class Icon extends Object3D { text, svgWidth, svgHeight, + this.styleSignal, this.propertiesSignal, this.defaultPropertiesSignal, { current: this }, ) - this.setProperties(properties, defaultProperties) const { handlers, iconGroup, interactionPanel, subscriptions } = this.internals this.add(interactionPanel) @@ -42,11 +43,16 @@ export class Icon extends Object3D { bindHandlers(handlers, this, subscriptions) } - setProperties(properties: IconProperties, defaultProperties?: AllOptionalProperties) { - batch(() => { - this.propertiesSignal.value = properties - this.defaultPropertiesSignal.value = defaultProperties - }) + setStyle(style: IconProperties | undefined) { + this.styleSignal.value = style + } + + setProperties(properties: IconProperties | undefined) { + this.propertiesSignal.value = properties + } + + setDefaultProperties(properties: AllOptionalProperties) { + this.defaultPropertiesSignal.value = properties } destroy() { diff --git a/packages/uikit/src/vanilla/image.ts b/packages/uikit/src/vanilla/image.ts index d30c540a..33ba6e47 100644 --- a/packages/uikit/src/vanilla/image.ts +++ b/packages/uikit/src/vanilla/image.ts @@ -11,8 +11,9 @@ export class Image extends Object3D { public readonly internals: ReturnType public readonly fontFamiliesSignal: Signal - private childrenContainer: Object3D - private readonly propertiesSignal: Signal + private readonly childrenContainer: Object3D + private readonly styleSignal: Signal = signal(undefined) + private readonly propertiesSignal: Signal private readonly defaultPropertiesSignal: Signal private readonly srcSignal: Signal< Signal | string | Texture | Signal | undefined @@ -21,7 +22,7 @@ export class Image extends Object3D { constructor( parent: Parent, src: string | Signal, - properties: ImageProperties, + properties?: ImageProperties, defaultProperties?: AllOptionalProperties, ) { super() @@ -39,12 +40,12 @@ export class Image extends Object3D { this.internals = createImage( parent.internals, this.srcSignal, + this.styleSignal, this.propertiesSignal, this.defaultPropertiesSignal, { current: this }, { current: this.childrenContainer }, ) - this.setProperties(properties, defaultProperties) //setting up events const { handlers, interactionPanel, subscriptions } = this.internals @@ -56,11 +57,16 @@ export class Image extends Object3D { this.srcSignal.value = src } - setProperties(properties: ImageProperties, defaultProperties?: AllOptionalProperties) { - batch(() => { - this.propertiesSignal.value = properties - this.defaultPropertiesSignal.value = defaultProperties - }) + setStyle(style: ImageProperties | undefined) { + this.styleSignal.value = style + } + + setProperties(properties: ImageProperties | undefined) { + this.propertiesSignal.value = properties + } + + setDefaultProperties(properties: AllOptionalProperties) { + this.defaultPropertiesSignal.value = properties } destroy() { diff --git a/packages/uikit/src/vanilla/input.ts b/packages/uikit/src/vanilla/input.ts index 2c73a314..1f9b1eff 100644 --- a/packages/uikit/src/vanilla/input.ts +++ b/packages/uikit/src/vanilla/input.ts @@ -9,17 +9,17 @@ import { InputProperties, createInput } from '../components/input.js' export class Input extends Object3D { public readonly internals: ReturnType - private readonly propertiesSignal: Signal + private readonly styleSignal: Signal = signal(undefined) + private readonly propertiesSignal: Signal private readonly defaultPropertiesSignal: Signal - - private valueSignal: Signal | string> + private readonly valueSignal: Signal | string> constructor( parent: Parent, value: string | Signal = '', private readonly controlled: boolean = false, multiline: boolean = false, - properties: InputProperties = {}, + properties?: InputProperties, defaultProperties?: AllOptionalProperties, ) { super() @@ -42,10 +42,12 @@ export class Input extends Object3D { if (!controlled) { this.valueSignal.value = newValue } - this.propertiesSignal.peek().onValueChange?.(newValue) + this.propertiesSignal.peek()?.onValueChange?.(newValue) + this.styleSignal.peek()?.onValueChange?.(newValue) }, multiline, parent.fontFamiliesSignal, + this.styleSignal, this.propertiesSignal, this.defaultPropertiesSignal, { current: this }, @@ -68,11 +70,16 @@ export class Input extends Object3D { this.valueSignal.value = text } - setProperties(properties: Properties, defaultProperties?: AllOptionalProperties) { - batch(() => { - this.propertiesSignal.value = properties - this.defaultPropertiesSignal.value = defaultProperties - }) + setStyle(style: InputProperties | undefined) { + this.styleSignal.value = style + } + + setProperties(properties: InputProperties | undefined) { + this.propertiesSignal.value = properties + } + + setDefaultProperties(properties: AllOptionalProperties) { + this.defaultPropertiesSignal.value = properties } destroy() { diff --git a/packages/uikit/src/vanilla/root.ts b/packages/uikit/src/vanilla/root.ts index 919cbcff..63bdc299 100644 --- a/packages/uikit/src/vanilla/root.ts +++ b/packages/uikit/src/vanilla/root.ts @@ -9,16 +9,17 @@ import { FontFamilies } from '../internals.js' export class Root extends Object3D { public readonly internals: ReturnType public readonly fontFamiliesSignal: Signal - private childrenContainer: Object3D - private readonly propertiesSignal: Signal + private readonly childrenContainer: Object3D + private readonly styleSignal: Signal = signal(undefined) + private readonly propertiesSignal: Signal private readonly defaultPropertiesSignal: Signal constructor( camera: Camera | (() => Camera), renderer: WebGLRenderer, fontFamilies?: FontFamilies, - properties: RootProperties = {}, + properties?: RootProperties, defaultProperties?: AllOptionalProperties, ) { super() @@ -31,6 +32,7 @@ export class Root extends Object3D { this.matrixAutoUpdate = false this.internals = createRoot( + this.styleSignal, this.propertiesSignal, this.defaultPropertiesSignal, { current: this }, @@ -55,11 +57,16 @@ export class Root extends Object3D { this.fontFamiliesSignal.value = fontFamilies } - setProperties(properties: RootProperties, defaultProperties?: AllOptionalProperties) { - batch(() => { - this.propertiesSignal.value = properties - this.defaultPropertiesSignal.value = defaultProperties - }) + setStyle(style: RootProperties | undefined) { + this.styleSignal.value = style + } + + setProperties(properties: RootProperties | undefined) { + this.propertiesSignal.value = properties + } + + setDefaultProperties(properties: AllOptionalProperties) { + this.defaultPropertiesSignal.value = properties } destroy() { diff --git a/packages/uikit/src/vanilla/svg.ts b/packages/uikit/src/vanilla/svg.ts index 3507e77d..abeaca1f 100644 --- a/packages/uikit/src/vanilla/svg.ts +++ b/packages/uikit/src/vanilla/svg.ts @@ -11,15 +11,16 @@ export class Svg extends Object3D { public readonly internals: ReturnType public readonly fontFamiliesSignal: Signal - private childrenContainer: Object3D - private readonly propertiesSignal: Signal + private readonly childrenContainer: Object3D + private readonly styleSignal: Signal = signal(undefined) + private readonly propertiesSignal: Signal private readonly defaultPropertiesSignal: Signal - private srcSignal: Signal> + private readonly srcSignal: Signal> constructor( parent: Parent, src: string | Signal, - properties: SvgProperties = {}, + properties?: SvgProperties, defaultProperties?: AllOptionalProperties, ) { super() @@ -35,12 +36,12 @@ export class Svg extends Object3D { this.internals = createSvg( parent.internals, this.srcSignal, + this.styleSignal, this.propertiesSignal, this.defaultPropertiesSignal, { current: this }, { current: this.childrenContainer }, ) - this.setProperties(properties, defaultProperties) const { handlers, centerGroup, interactionPanel, subscriptions } = this.internals this.add(interactionPanel) @@ -52,11 +53,16 @@ export class Svg extends Object3D { this.srcSignal.value = src } - setProperties(properties: SvgProperties, defaultProperties?: AllOptionalProperties) { - batch(() => { - this.propertiesSignal.value = properties - this.defaultPropertiesSignal.value = defaultProperties - }) + setStyle(style: SvgProperties | undefined) { + this.styleSignal.value = style + } + + setProperties(properties: SvgProperties | undefined) { + this.propertiesSignal.value = properties + } + + setDefaultProperties(properties: AllOptionalProperties) { + this.defaultPropertiesSignal.value = properties } destroy() { diff --git a/packages/uikit/src/vanilla/text.ts b/packages/uikit/src/vanilla/text.ts index 43e39b46..7bc8fefc 100644 --- a/packages/uikit/src/vanilla/text.ts +++ b/packages/uikit/src/vanilla/text.ts @@ -9,14 +9,15 @@ import { unsubscribeSubscriptions } from '../internals.js' export class Text extends Object3D { public readonly internals: ReturnType - private readonly propertiesSignal: Signal + private readonly styleSignal: Signal = signal(undefined) + private readonly propertiesSignal: Signal private readonly defaultPropertiesSignal: Signal private readonly textSignal: Signal | Array>> constructor( parent: Parent, text: string | Signal | Array> = '', - properties: TextProperties = {}, + properties?: TextProperties, defaultProperties?: AllOptionalProperties, ) { super() @@ -32,6 +33,7 @@ export class Text extends Object3D { parent.internals, this.textSignal, parent.fontFamiliesSignal, + this.styleSignal, this.propertiesSignal, this.defaultPropertiesSignal, { current: this }, @@ -47,11 +49,16 @@ export class Text extends Object3D { this.textSignal.value = text } - setProperties(properties: Properties, defaultProperties?: AllOptionalProperties) { - batch(() => { - this.propertiesSignal.value = properties - this.defaultPropertiesSignal.value = defaultProperties - }) + setStyle(style: TextProperties | undefined) { + this.styleSignal.value = style + } + + setProperties(properties: TextProperties | undefined) { + this.propertiesSignal.value = properties + } + + setDefaultProperties(properties: AllOptionalProperties) { + this.defaultPropertiesSignal.value = properties } destroy() {