-
-
Notifications
You must be signed in to change notification settings - Fork 115
/
SheetObject.svelte
179 lines (154 loc) · 4.61 KB
/
SheetObject.svelte
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<!--
This component is responsible for:
- Creating namespaces
- Potentially Providing a sheet object
-->
<script lang="ts">
import { useStudio } from '../studio/useStudio'
import type { ISheetObject, UnknownShorthandCompoundProps } from '@theatre/core'
import {
createRawEventDispatcher,
currentWritable,
watch,
type CurrentWritable,
useThrelte
} from '@threlte/core'
import { getContext, onDestroy, onMount } from 'svelte'
import type { SheetContext } from '../sheet/types'
import Declare from './declare/Declare.svelte'
import Sync from './sync/Sync.svelte'
import Transform from './transform/Transform.svelte'
type Props = $$Generic<UnknownShorthandCompoundProps>
export let key: string
export let detach: boolean = false
export let props: Props | undefined = undefined
const { invalidate } = useThrelte()
let aggregatedProps: UnknownShorthandCompoundProps = { ...props }
const { sheet } = getContext<SheetContext>('theatre-sheet')
const sheetObject: CurrentWritable<ISheetObject<Props>> = currentWritable(
sheet.object(key, aggregatedProps, {
reconfigure: true
}) as any
)
const dispatch = createRawEventDispatcher<{
change: ISheetObject<Props>['value']
}>()
onMount(() => {
// Because the sheet object value subscription is not running before any
// values change, we're emitting the initial value here. Doing this in
// onMount also means that child components which might add props to the
// sheet object have already been mounted.
dispatch('change', sheetObject.current.value)
})
// This flag is used to prevent the sheet object from being created after it
// has been detached.
let detached = false
onDestroy(() => {
if (detach) {
detached = true
sheet.detachObject(key)
}
})
const updateSheetObject = () => {
// if the sheetObject has already been detached, do nothing.
if (detached) return
// first, detach the sheet object.
sheet.detachObject(key)
// create or reconfigure a sheet object here.
sheetObject.set(
sheet.object(key, aggregatedProps, {
reconfigure: true
}) as any
)
}
const addProps = (props: UnknownShorthandCompoundProps) => {
// add props to list of props
aggregatedProps = {
...aggregatedProps,
...props
}
// update sheet object (create or reconfigure)
updateSheetObject()
}
const removeProps = (propNames: string[]) => {
// remove props from sheet object
propNames.forEach((prop) => {
delete aggregatedProps[prop]
})
// if there are no more props, detach sheet object
if (Object.keys(aggregatedProps).length === 0) {
// detach sheet object
if (detach) {
sheet.detachObject(key)
}
} else {
// update sheet object (reconfigure)
updateSheetObject()
}
}
const augmentConstructorArgs = (args: any) => {
return {
...args,
props: {
...args.props,
sheetObject,
addProps,
removeProps
}
}
}
const proxySyncComponent = new Proxy(Sync, {
construct(_target, [args]) {
return new Sync(augmentConstructorArgs(args))
}
})
const proxyTransformComponent = new Proxy(Transform, {
construct(_target, [args]) {
return new Transform(augmentConstructorArgs(args))
}
})
const proxyDeclareComponent = new Proxy(Declare, {
construct(_target, [args]) {
return new Declare(augmentConstructorArgs(args))
}
})
let values = $sheetObject?.value
watch(sheetObject, (sheetObject) => {
return sheetObject.onValuesChange((newValues) => {
dispatch('change', newValues)
values = newValues
// this invalidation also invalidates changes catched by slotted
// components such as <Sync> or <Declare>.
invalidate()
})
})
// Provide a flag to indicate whether this sheet object is selected in the
// Theatre.js studio.
const studio = useStudio()
export let selected = false
watch([studio, sheetObject], ([studio, sheetObject]) => {
return studio?.onSelectionChange((selection) => {
selected = selection.includes(sheetObject)
})
})
// Provide a select function to select this sheet object in the Theatre.js
// studio.
const select = () => {
$studio?.setSelection([sheetObject.current])
}
const deselect = () => {
if ($studio?.selection.includes(sheetObject.current)) {
$studio?.setSelection([])
}
}
</script>
<slot
{values}
{selected}
{select}
{deselect}
sheetObject={$sheetObject}
Sync={proxySyncComponent}
Transform={proxyTransformComponent}
Declare={proxyDeclareComponent}
/>