From e56fc3e86cf6607dcbdc2547590eea25d4843750 Mon Sep 17 00:00:00 2001 From: Vatroslav Vrbanic Date: Mon, 20 Jun 2022 05:39:14 +0200 Subject: [PATCH] Refactor shadow DOM creation, closes #72 --- src/components/AmbientLight.svelte | 157 ++++++++++------- src/components/CubeCamera.svelte | 157 ++++++++++------- src/components/DirectionalLight.svelte | 157 ++++++++++------- src/components/Empty.svelte | 201 ++++++++++++--------- src/components/HemisphereLight.svelte | 157 ++++++++++------- src/components/LoadedGLTF.svelte | 71 ++------ src/components/Mesh.svelte | 201 ++++++++++++--------- src/components/OrthographicCamera.svelte | 157 ++++++++++------- src/components/PerspectiveCamera.svelte | 157 ++++++++++------- src/components/PointLight.svelte | 157 ++++++++++------- src/components/Points.svelte | 212 ++++++++++++++--------- src/components/RectAreaLight.svelte | 157 ++++++++++------- src/components/Scene.svelte | 198 ++++++++++++--------- src/components/SpotLight.svelte | 157 ++++++++++------- 14 files changed, 1348 insertions(+), 948 deletions(-) diff --git a/src/components/AmbientLight.svelte b/src/components/AmbientLight.svelte index 636418d..38440c5 100644 --- a/src/components/AmbientLight.svelte +++ b/src/components/AmbientLight.svelte @@ -50,10 +50,6 @@ AmbientLight cannot be used to cast shadows as it doesn't have a direction. Posi const self = get_current_component() const c_name = get_comp_name(self) - const shadow_root: Writable<{ element: HTMLDivElement }> = getContext("shadow_root") - let shadow_root_el: HTMLDivElement - $: shadow_root_el = $shadow_root.element - const verbose: boolean = verbose_mode() export let log_all: boolean = false @@ -74,6 +70,16 @@ AmbientLight cannot be used to cast shadows as it doesn't have a direction. Posi /** The (three) instance that was shared to this component as it's 'parent'. */ let our_parent: Object3D = undefined + /** Shadow DOM element generated by our parent scene / root scene. Used as fallback if this component has no non-`Scene` component as parent. */ + let scene_shadow_dom_el: SvelthreeShadowDOMElement = getContext("scene_shadow_dom_el") + + /** Shadow DOM element generated by our parent component (_not `Canvas`_) shared with this component (child) via context. + Fallback is `scene_shadow_dom_el` or `shadow_root_el` in case of the `Scene` component. */ + let our_parent_shadow_dom_el: SvelthreeShadowDOMElement = undefined + + /** Shadow DOM element generated by this component. Shared by this component (as parent) to it's children via context as "parent_shadow_dom_el". */ + let shadow_dom_el: SvelthreeShadowDOMElement = undefined + /** Returns the `light` instance created by the component & allows providing (_injection_) of (_already created / premade_) `THREE.AmbientLight` instances. */ export let light: AmbientLight = undefined let light_uuid: string = undefined @@ -103,11 +109,28 @@ AmbientLight cannot be used to cast shadows as it doesn't have a direction. Posi // Determining 'parent' on initialization if 'light' instance was provided ('create' is false). if (!create) { - // get the instance that was shared to us as our 'parent'. + // get the instance that was shared to us as our 'parent' or use fallback. our_parent = getContext("parent") || scene + // get the shadow DOM element that was shared to us by our parent component or use fallback. + our_parent_shadow_dom_el = getContext("parent_shadow_dom_el") || scene_shadow_dom_el // share created object (three) instance to all children (slots) as 'parent'. setContext("parent", light) + + // share our own shadow_dom_el as parent_shadow_dom_el + if (shadow_dom_el) { + // recreate shadow_dom_el + remove_shadow_dom_el() + create_shadow_dom_el() + } else { + create_shadow_dom_el() + } + + if (shadow_dom_el) { + setContext("parent_shadow_dom_el", shadow_dom_el) + } else { + console.error(`SVELTHREE > ${c_name} > 'shadow_dom_el' not available!`, shadow_dom_el) + } } // GENERATOR REMARK: 'reactive_re_creation_logic' not implemented for 'AmbientLight'! @@ -129,16 +152,79 @@ AmbientLight cannot be used to cast shadows as it doesn't have a direction. Posi } // Determining 'parent' if 'light' instance has to be created first / was not provided on initialization ('create' is true). - $: if (light && create && !our_parent) set_parent() + $: if (light && create && scene && !our_parent) set_parent() function set_parent() { - // get the instance that was shared to us as our 'parent'. + // get the instance that was shared to us as our 'parent' or use fallback. our_parent = getContext("parent") || scene // share created object (three) instance to all children (slots) as 'parent'. setContext("parent", light) } + $: if (light && create && !our_parent_shadow_dom_el) set_parent_shadow_dom_el() + + function set_parent_shadow_dom_el() { + our_parent_shadow_dom_el = getContext("parent_shadow_dom_el") || scene_shadow_dom_el + + // share our own shadow_dom_el as parent_shadow_dom_el + if (shadow_dom_el) { + // recreate shadow_dom_el + remove_shadow_dom_el() + create_shadow_dom_el() + } else { + create_shadow_dom_el() + } + + if (shadow_dom_el) { + setContext("parent_shadow_dom_el", shadow_dom_el) + } else { + console.error(`SVELTHREE > ${c_name} : 'shadow_dom_el' not available!`, shadow_dom_el) + } + } + + function remove_shadow_dom_el() { + shadow_dom_el.parentNode.removeChild(shadow_dom_el) + } + + function create_shadow_dom_el(): void { + shadow_dom_el = document.createElement("div") + + shadow_dom_el.dataset.kind = `${c_name}` + + if (our_parent_shadow_dom_el) { + our_parent_shadow_dom_el.appendChild(shadow_dom_el) + //console.log(`SVELTHREE > ${c_name} > create_shadow_dom_el > shadow dom appended!:`, our_parent_shadow_dom_el) + } else { + console.error( + `SVELTHREE > ${c_name} > create_shadow_dom_el > could'nt append shadow dom, no 'our_parent_shadow_dom_el'!`, + our_parent_shadow_dom_el + ) + } + } + + // accessability -> shadow dom focusable + export let tabindex: number = undefined + + $: if (shadow_dom_el && tabindex !== undefined) { + shadow_dom_el.tabIndex = tabindex + } + + // accessability -> shadow dom wai-aria + export let aria: Partial = undefined + + $: if (shadow_dom_el && aria !== undefined) { + for (const key in aria) { + if (key === "ariaLabel") { + // add specified `ariaLabel` as text to generated shadow DOM `
` element (for better reader support / indexing (?)) + // TODO RECONSIDER needs to be tested more, may be obsolete (?). + shadow_dom_el.innerText += `${aria[key]}` + } + + shadow_dom_el[key] = aria[key] + } + } + // this statement is being triggered on creation / recreation $: if (light && ((light_uuid && light_uuid !== light.uuid) || light.parent !== our_parent)) add_instance_to() @@ -191,68 +277,15 @@ AmbientLight cannot be used to cast shadows as it doesn't have a direction. Posi } } - // accessability -> shadow dom element - - /** Shadow DOM element created by the component, needed for accessability features, event propagation etc. */ - export let shadow_dom_el: SvelthreeShadowDOMElement = undefined - - $: if (shadow_root_el && light && !shadow_dom_el) create_shadow_dom_target() - - async function create_shadow_dom_target() { - if (browser) { - // DUCKTAPE `getContext()` wrong order fix, see [#72](https://github.com/vatro/svelthree/issues/72) - await tick() - - shadow_dom_el = document.createElement("div") - - shadow_dom_el.dataset.kind = "AmbientLight" - if (name) shadow_dom_el.dataset.name = name - - const parent_shadow_dom_target = our_parent?.userData.svelthreeComponent.shadow_dom_el - const shadow_target: SvelthreeShadowDOMElement = parent_shadow_dom_target || shadow_root_el - - if (shadow_target) { - shadow_target.appendChild(shadow_dom_el) - } else { - console.error( - "SVELTHREE > AmbientLight > create_shadow_dom_target > Wasn't able to append shadow DOM element, no 'shadow_target'!", - { shadow_target }, - { parent_shadow_dom_target }, - { our_parent } - ) - } - } - } - - // accessability -> shadow dom focusable - export let tabindex: number = undefined - - $: if (shadow_dom_el && tabindex !== undefined) { - shadow_dom_el.tabIndex = tabindex - } - - // accessability -> shadow dom wai-aria - export let aria: Partial = undefined - - $: if (shadow_dom_el && aria !== undefined) { - for (const key in aria) { - if (key === "ariaLabel") { - // add specified `ariaLabel` as text to generated shadow DOM `
` element (for better reader support / indexing (?)) - // TODO RECONSIDER needs to be tested more, may be obsolete (?). - shadow_dom_el.innerText += `${aria[key]}` - } - - shadow_dom_el[key] = aria[key] - } - } - /** Override object's `.matrixAutoUpdate` set (*on initialzation*) by scene's `.matrixAutoUpdate` (*default is `true`*). Also: `mau` can be changed on-the-fly.*/ export let mau: boolean = undefined $: if (light) light.matrixAutoUpdate = scene.matrixAutoUpdate $: if (light && mau !== undefined) light.matrixAutoUpdate = mau export let name: string = undefined + $: if (light && name) light.name = name + $: if (shadow_dom_el && name) shadow_dom_el.dataset.name = name const w_sh = PropUtils.getShortHandAttrWarnings(`SVELTHREE > ${c_name} >`) diff --git a/src/components/CubeCamera.svelte b/src/components/CubeCamera.svelte index 38c199a..8daf550 100644 --- a/src/components/CubeCamera.svelte +++ b/src/components/CubeCamera.svelte @@ -88,10 +88,6 @@ Renders a CubeMap for usage with **non-PBR** materials which have an `.envMap` p const self = get_current_component() const c_name = get_comp_name(self) - const shadow_root: Writable<{ element: HTMLDivElement }> = getContext("shadow_root") - let shadow_root_el: HTMLDivElement - $: shadow_root_el = $shadow_root.element - const verbose: boolean = verbose_mode() export let log_all: boolean = false @@ -156,6 +152,16 @@ Renders a CubeMap for usage with **non-PBR** materials which have an `.envMap` p /** The (three) instance that was shared to this component as it's 'parent'. */ let our_parent: Object3D = undefined + /** Shadow DOM element generated by our parent scene / root scene. Used as fallback if this component has no non-`Scene` component as parent. */ + let scene_shadow_dom_el: SvelthreeShadowDOMElement = getContext("scene_shadow_dom_el") + + /** Shadow DOM element generated by our parent component (_not `Canvas`_) shared with this component (child) via context. + Fallback is `scene_shadow_dom_el` or `shadow_root_el` in case of the `Scene` component. */ + let our_parent_shadow_dom_el: SvelthreeShadowDOMElement = undefined + + /** Shadow DOM element generated by this component. Shared by this component (as parent) to it's children via context as "parent_shadow_dom_el". */ + let shadow_dom_el: SvelthreeShadowDOMElement = undefined + /** Returns the `camera` instance created by the component & allows providing (_injection_) of (_already created / premade_) `THREE.CubeCamera` instances. */ export let camera: CubeCamera = undefined @@ -197,11 +203,28 @@ Renders a CubeMap for usage with **non-PBR** materials which have an `.envMap` p // Determining 'parent' on initialization if 'camera' instance was provided ('create' is false). if (!create) { - // get the instance that was shared to us as our 'parent'. + // get the instance that was shared to us as our 'parent' or use fallback. our_parent = getContext("parent") || scene + // get the shadow DOM element that was shared to us by our parent component or use fallback. + our_parent_shadow_dom_el = getContext("parent_shadow_dom_el") || scene_shadow_dom_el // share created object (three) instance to all children (slots) as 'parent'. setContext("parent", camera) + + // share our own shadow_dom_el as parent_shadow_dom_el + if (shadow_dom_el) { + // recreate shadow_dom_el + remove_shadow_dom_el() + create_shadow_dom_el() + } else { + create_shadow_dom_el() + } + + if (shadow_dom_el) { + setContext("parent_shadow_dom_el", shadow_dom_el) + } else { + console.error(`SVELTHREE > ${c_name} > 'shadow_dom_el' not available!`, shadow_dom_el) + } } // GENERATOR REMARK: 'reactive_re_creation_logic' not implemented for 'CubeCamera'! @@ -253,16 +276,79 @@ Renders a CubeMap for usage with **non-PBR** materials which have an `.envMap` p } // Determining 'parent' if 'camera' instance has to be created first / was not provided on initialization ('create' is true). - $: if (camera && create && !our_parent) set_parent() + $: if (camera && create && scene && !our_parent) set_parent() function set_parent() { - // get the instance that was shared to us as our 'parent'. + // get the instance that was shared to us as our 'parent' or use fallback. our_parent = getContext("parent") || scene // share created object (three) instance to all children (slots) as 'parent'. setContext("parent", camera) } + $: if (camera && create && !our_parent_shadow_dom_el) set_parent_shadow_dom_el() + + function set_parent_shadow_dom_el() { + our_parent_shadow_dom_el = getContext("parent_shadow_dom_el") || scene_shadow_dom_el + + // share our own shadow_dom_el as parent_shadow_dom_el + if (shadow_dom_el) { + // recreate shadow_dom_el + remove_shadow_dom_el() + create_shadow_dom_el() + } else { + create_shadow_dom_el() + } + + if (shadow_dom_el) { + setContext("parent_shadow_dom_el", shadow_dom_el) + } else { + console.error(`SVELTHREE > ${c_name} : 'shadow_dom_el' not available!`, shadow_dom_el) + } + } + + function remove_shadow_dom_el() { + shadow_dom_el.parentNode.removeChild(shadow_dom_el) + } + + function create_shadow_dom_el(): void { + shadow_dom_el = document.createElement("div") + + shadow_dom_el.dataset.kind = `${c_name}` + + if (our_parent_shadow_dom_el) { + our_parent_shadow_dom_el.appendChild(shadow_dom_el) + //console.log(`SVELTHREE > ${c_name} > create_shadow_dom_el > shadow dom appended!:`, our_parent_shadow_dom_el) + } else { + console.error( + `SVELTHREE > ${c_name} > create_shadow_dom_el > could'nt append shadow dom, no 'our_parent_shadow_dom_el'!`, + our_parent_shadow_dom_el + ) + } + } + + // accessability -> shadow dom focusable + export let tabindex: number = undefined + + $: if (shadow_dom_el && tabindex !== undefined) { + shadow_dom_el.tabIndex = tabindex + } + + // accessability -> shadow dom wai-aria + export let aria: Partial = undefined + + $: if (shadow_dom_el && aria !== undefined) { + for (const key in aria) { + if (key === "ariaLabel") { + // add specified `ariaLabel` as text to generated shadow DOM `
` element (for better reader support / indexing (?)) + // TODO RECONSIDER needs to be tested more, may be obsolete (?). + shadow_dom_el.innerText += `${aria[key]}` + } + + shadow_dom_el[key] = aria[key] + } + } + // this statement is being triggered on creation / recreation $: if (camera && ((camera_uuid && camera_uuid !== camera.uuid) || (root_scene && camera.parent !== root_scene))) add_instance_to() @@ -413,68 +499,15 @@ Renders a CubeMap for usage with **non-PBR** materials which have an `.envMap` p return wp } - // accessability -> shadow dom element - - /** Shadow DOM element created by the component, needed for accessability features, event propagation etc. */ - export let shadow_dom_el: SvelthreeShadowDOMElement = undefined - - $: if (shadow_root_el && camera && !shadow_dom_el) create_shadow_dom_target() - - async function create_shadow_dom_target() { - if (browser) { - // DUCKTAPE `getContext()` wrong order fix, see [#72](https://github.com/vatro/svelthree/issues/72) - await tick() - - shadow_dom_el = document.createElement("div") - - shadow_dom_el.dataset.kind = "CubeCamera" - if (name) shadow_dom_el.dataset.name = name - - const parent_shadow_dom_target = our_parent?.userData.svelthreeComponent.shadow_dom_el - const shadow_target: SvelthreeShadowDOMElement = parent_shadow_dom_target || shadow_root_el - - if (shadow_target) { - shadow_target.appendChild(shadow_dom_el) - } else { - console.error( - "SVELTHREE > CubeCamera > create_shadow_dom_target > Wasn't able to append shadow DOM element, no 'shadow_target'!", - { shadow_target }, - { parent_shadow_dom_target }, - { our_parent } - ) - } - } - } - - // accessability -> shadow dom focusable - export let tabindex: number = undefined - - $: if (shadow_dom_el && tabindex !== undefined) { - shadow_dom_el.tabIndex = tabindex - } - - // accessability -> shadow dom wai-aria - export let aria: Partial = undefined - - $: if (shadow_dom_el && aria !== undefined) { - for (const key in aria) { - if (key === "ariaLabel") { - // add specified `ariaLabel` as text to generated shadow DOM `
` element (for better reader support / indexing (?)) - // TODO RECONSIDER needs to be tested more, may be obsolete (?). - shadow_dom_el.innerText += `${aria[key]}` - } - - shadow_dom_el[key] = aria[key] - } - } - /** Override object's `.matrixAutoUpdate` set (*on initialzation*) by scene's `.matrixAutoUpdate` (*default is `true`*). Also: `mau` can be changed on-the-fly.*/ export let mau: boolean = undefined $: if (camera) camera.matrixAutoUpdate = scene.matrixAutoUpdate $: if (camera && mau !== undefined) camera.matrixAutoUpdate = mau export let name: string = undefined + $: if (camera && name) camera.name = name + $: if (shadow_dom_el && name) shadow_dom_el.dataset.name = name /** ☝️ `matrix` **shorthand** attribute overrides ( *are ignored* ) `pos`, `rot`, `quat`, `scale` and `lookAt` 'shorthand' attributes! */ export let matrix: Matrix4 | Parameters = undefined diff --git a/src/components/DirectionalLight.svelte b/src/components/DirectionalLight.svelte index 3a4f7c8..0c1a60c 100644 --- a/src/components/DirectionalLight.svelte +++ b/src/components/DirectionalLight.svelte @@ -78,10 +78,6 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` const self = get_current_component() const c_name = get_comp_name(self) - const shadow_root: Writable<{ element: HTMLDivElement }> = getContext("shadow_root") - let shadow_root_el: HTMLDivElement - $: shadow_root_el = $shadow_root.element - const verbose: boolean = verbose_mode() export let log_all: boolean = false @@ -102,6 +98,16 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` /** The (three) instance that was shared to this component as it's 'parent'. */ let our_parent: Object3D = undefined + /** Shadow DOM element generated by our parent scene / root scene. Used as fallback if this component has no non-`Scene` component as parent. */ + let scene_shadow_dom_el: SvelthreeShadowDOMElement = getContext("scene_shadow_dom_el") + + /** Shadow DOM element generated by our parent component (_not `Canvas`_) shared with this component (child) via context. + Fallback is `scene_shadow_dom_el` or `shadow_root_el` in case of the `Scene` component. */ + let our_parent_shadow_dom_el: SvelthreeShadowDOMElement = undefined + + /** Shadow DOM element generated by this component. Shared by this component (as parent) to it's children via context as "parent_shadow_dom_el". */ + let shadow_dom_el: SvelthreeShadowDOMElement = undefined + /** Returns the `light` instance created by the component & allows providing (_injection_) of (_already created / premade_) `THREE.DirectionalLight` instances. */ export let light: DirectionalLight = undefined let light_uuid: string = undefined @@ -131,11 +137,28 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` // Determining 'parent' on initialization if 'light' instance was provided ('create' is false). if (!create) { - // get the instance that was shared to us as our 'parent'. + // get the instance that was shared to us as our 'parent' or use fallback. our_parent = getContext("parent") || scene + // get the shadow DOM element that was shared to us by our parent component or use fallback. + our_parent_shadow_dom_el = getContext("parent_shadow_dom_el") || scene_shadow_dom_el // share created object (three) instance to all children (slots) as 'parent'. setContext("parent", light) + + // share our own shadow_dom_el as parent_shadow_dom_el + if (shadow_dom_el) { + // recreate shadow_dom_el + remove_shadow_dom_el() + create_shadow_dom_el() + } else { + create_shadow_dom_el() + } + + if (shadow_dom_el) { + setContext("parent_shadow_dom_el", shadow_dom_el) + } else { + console.error(`SVELTHREE > ${c_name} > 'shadow_dom_el' not available!`, shadow_dom_el) + } } // GENERATOR REMARK: 'reactive_re_creation_logic' not implemented for 'DirectionalLight'! @@ -158,16 +181,79 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` } // Determining 'parent' if 'light' instance has to be created first / was not provided on initialization ('create' is true). - $: if (light && create && !our_parent) set_parent() + $: if (light && create && scene && !our_parent) set_parent() function set_parent() { - // get the instance that was shared to us as our 'parent'. + // get the instance that was shared to us as our 'parent' or use fallback. our_parent = getContext("parent") || scene // share created object (three) instance to all children (slots) as 'parent'. setContext("parent", light) } + $: if (light && create && !our_parent_shadow_dom_el) set_parent_shadow_dom_el() + + function set_parent_shadow_dom_el() { + our_parent_shadow_dom_el = getContext("parent_shadow_dom_el") || scene_shadow_dom_el + + // share our own shadow_dom_el as parent_shadow_dom_el + if (shadow_dom_el) { + // recreate shadow_dom_el + remove_shadow_dom_el() + create_shadow_dom_el() + } else { + create_shadow_dom_el() + } + + if (shadow_dom_el) { + setContext("parent_shadow_dom_el", shadow_dom_el) + } else { + console.error(`SVELTHREE > ${c_name} : 'shadow_dom_el' not available!`, shadow_dom_el) + } + } + + function remove_shadow_dom_el() { + shadow_dom_el.parentNode.removeChild(shadow_dom_el) + } + + function create_shadow_dom_el(): void { + shadow_dom_el = document.createElement("div") + + shadow_dom_el.dataset.kind = `${c_name}` + + if (our_parent_shadow_dom_el) { + our_parent_shadow_dom_el.appendChild(shadow_dom_el) + //console.log(`SVELTHREE > ${c_name} > create_shadow_dom_el > shadow dom appended!:`, our_parent_shadow_dom_el) + } else { + console.error( + `SVELTHREE > ${c_name} > create_shadow_dom_el > could'nt append shadow dom, no 'our_parent_shadow_dom_el'!`, + our_parent_shadow_dom_el + ) + } + } + + // accessability -> shadow dom focusable + export let tabindex: number = undefined + + $: if (shadow_dom_el && tabindex !== undefined) { + shadow_dom_el.tabIndex = tabindex + } + + // accessability -> shadow dom wai-aria + export let aria: Partial = undefined + + $: if (shadow_dom_el && aria !== undefined) { + for (const key in aria) { + if (key === "ariaLabel") { + // add specified `ariaLabel` as text to generated shadow DOM `
` element (for better reader support / indexing (?)) + // TODO RECONSIDER needs to be tested more, may be obsolete (?). + shadow_dom_el.innerText += `${aria[key]}` + } + + shadow_dom_el[key] = aria[key] + } + } + // this statement is being triggered on creation / recreation $: if (light && ((light_uuid && light_uuid !== light.uuid) || light.parent !== our_parent)) add_instance_to() @@ -220,61 +306,6 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` } } - // accessability -> shadow dom element - - /** Shadow DOM element created by the component, needed for accessability features, event propagation etc. */ - export let shadow_dom_el: SvelthreeShadowDOMElement = undefined - - $: if (shadow_root_el && light && !shadow_dom_el) create_shadow_dom_target() - - async function create_shadow_dom_target() { - if (browser) { - // DUCKTAPE `getContext()` wrong order fix, see [#72](https://github.com/vatro/svelthree/issues/72) - await tick() - - shadow_dom_el = document.createElement("div") - - shadow_dom_el.dataset.kind = "DirectionalLight" - if (name) shadow_dom_el.dataset.name = name - - const parent_shadow_dom_target = our_parent?.userData.svelthreeComponent.shadow_dom_el - const shadow_target: SvelthreeShadowDOMElement = parent_shadow_dom_target || shadow_root_el - - if (shadow_target) { - shadow_target.appendChild(shadow_dom_el) - } else { - console.error( - "SVELTHREE > DirectionalLight > create_shadow_dom_target > Wasn't able to append shadow DOM element, no 'shadow_target'!", - { shadow_target }, - { parent_shadow_dom_target }, - { our_parent } - ) - } - } - } - - // accessability -> shadow dom focusable - export let tabindex: number = undefined - - $: if (shadow_dom_el && tabindex !== undefined) { - shadow_dom_el.tabIndex = tabindex - } - - // accessability -> shadow dom wai-aria - export let aria: Partial = undefined - - $: if (shadow_dom_el && aria !== undefined) { - for (const key in aria) { - if (key === "ariaLabel") { - // add specified `ariaLabel` as text to generated shadow DOM `
` element (for better reader support / indexing (?)) - // TODO RECONSIDER needs to be tested more, may be obsolete (?). - shadow_dom_el.innerText += `${aria[key]}` - } - - shadow_dom_el[key] = aria[key] - } - } - /** Defaults to `true` which means that components / objects with targets (_`DirectionalLight`, `SpotLight`, `OrthographicCamera` and `PerspectiveCamera`_) * will add the built-in 'blank' target-Object3D to component's / object's parent on initialization. `target` can be either set to `false` ( TODO ) (_which will remove the target from parent only if it's * not the built-in 'blank' `Object3D`_) or some other object in the scene (_any `Object3D`, an `Empty` component or a `Mesh` component_) */ @@ -339,7 +370,9 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` $: if (light && mau !== undefined) light.matrixAutoUpdate = mau export let name: string = undefined + $: if (light && name) light.name = name + $: if (shadow_dom_el && name) shadow_dom_el.dataset.name = name /** ☝️ `matrix` **shorthand** attribute overrides ( *are ignored* ) `pos`, `rot`, `quat`, `scale` and `lookAt` 'shorthand' attributes! */ export let matrix: Matrix4 | Parameters = undefined diff --git a/src/components/Empty.svelte b/src/components/Empty.svelte index 2bdcb2e..8892a2a 100644 --- a/src/components/Empty.svelte +++ b/src/components/Empty.svelte @@ -77,10 +77,6 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` const self = get_current_component() const c_name = get_comp_name(self) - const shadow_root: Writable<{ element: HTMLDivElement }> = getContext("shadow_root") - let shadow_root_el: HTMLDivElement - $: shadow_root_el = $shadow_root.element - const verbose: boolean = verbose_mode() export let log_all: boolean = false @@ -102,6 +98,16 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` /** The (three) instance that was shared to this component as it's 'parent'. */ let our_parent: Object3D = undefined + /** Shadow DOM element generated by our parent scene / root scene. Used as fallback if this component has no non-`Scene` component as parent. */ + let scene_shadow_dom_el: SvelthreeShadowDOMElement = getContext("scene_shadow_dom_el") + + /** Shadow DOM element generated by our parent component (_not `Canvas`_) shared with this component (child) via context. + Fallback is `scene_shadow_dom_el` or `shadow_root_el` in case of the `Scene` component. */ + let our_parent_shadow_dom_el: SvelthreeShadowDOMElement = undefined + + /** Shadow DOM element generated by this component. Shared by this component (as parent) to it's children via context as "parent_shadow_dom_el". */ + let shadow_dom_el: SvelthreeShadowDOMElement = undefined + /** Returns the `empty` instance created by the component & allows providing (_injection_) of (_already created / premade_) `THREE.Empty` instances. */ export let empty: Object3D = undefined let empty_uuid: string = undefined @@ -131,11 +137,28 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` // Determining 'parent' on initialization if 'empty' instance was provided ('create' is false). if (!create) { - // get the instance that was shared to us as our 'parent'. + // get the instance that was shared to us as our 'parent' or use fallback. our_parent = getContext("parent") || scene + // get the shadow DOM element that was shared to us by our parent component or use fallback. + our_parent_shadow_dom_el = getContext("parent_shadow_dom_el") || scene_shadow_dom_el // share created object (three) instance to all children (slots) as 'parent'. setContext("parent", empty) + + // share our own shadow_dom_el as parent_shadow_dom_el + if (shadow_dom_el) { + // recreate shadow_dom_el + remove_shadow_dom_el() + create_shadow_dom_el() + } else { + create_shadow_dom_el() + } + + if (shadow_dom_el) { + setContext("parent_shadow_dom_el", shadow_dom_el) + } else { + console.error(`SVELTHREE > ${c_name} > 'shadow_dom_el' not available!`, shadow_dom_el) + } } // GENERATOR REMARK: 'reactive_re_creation_logic' not implemented for 'Empty'! @@ -151,16 +174,101 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` } // Determining 'parent' if 'empty' instance has to be created first / was not provided on initialization ('create' is true). - $: if (empty && create && !our_parent) set_parent() + $: if (empty && create && scene && !our_parent) set_parent() function set_parent() { - // get the instance that was shared to us as our 'parent'. + // get the instance that was shared to us as our 'parent' or use fallback. our_parent = getContext("parent") || scene // share created object (three) instance to all children (slots) as 'parent'. setContext("parent", empty) } + /** Specify the component / three.js object instance to act as an HTML `