angular-three-tweakpane 4.0.0-next.115 → 4.0.0-next.118
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -0
- package/fesm2022/angular-three-tweakpane.mjs +1168 -62
- package/fesm2022/angular-three-tweakpane.mjs.map +1 -1
- package/package.json +3 -3
- package/types/angular-three-tweakpane.d.ts +975 -0
- package/index.d.ts +0 -189
|
@@ -1,21 +1,120 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, booleanAttribute,
|
|
2
|
+
import { inject, ViewContainerRef, signal, Directive, Component, input, booleanAttribute, Injector, untracked, effect, numberAttribute, model, linkedSignal, computed, DestroyRef, InjectionToken, output, DOCUMENT, afterNextRender, isSignal, inputBinding, twoWayBinding } from '@angular/core';
|
|
3
3
|
import { fromEventPattern, debounceTime } from 'rxjs';
|
|
4
4
|
import { ClassName } from '@tweakpane/core';
|
|
5
5
|
import { Pane } from 'tweakpane';
|
|
6
|
+
import { assertInjector } from 'ngxtension/assert-injector';
|
|
6
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Directive that provides an anchor point for the `tweaks()` function to dynamically create Tweakpane controls.
|
|
10
|
+
*
|
|
11
|
+
* Add this directive to your `ngt-canvas` element to enable the `tweaks()` API:
|
|
12
|
+
*
|
|
13
|
+
* ```html
|
|
14
|
+
* <ngt-canvas tweakpaneAnchor>
|
|
15
|
+
* <ng-template #sceneGraph>
|
|
16
|
+
* <!-- your scene -->
|
|
17
|
+
* </ng-template>
|
|
18
|
+
* </ngt-canvas>
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Then use `tweaks()` in any component within the canvas:
|
|
22
|
+
*
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const controls = tweaks('Physics', {
|
|
25
|
+
* gravity: { value: 9.8, min: 0, max: 20 },
|
|
26
|
+
* debug: this.debugMode, // two-way binding with existing signal
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
class TweakpaneAnchor {
|
|
31
|
+
constructor() {
|
|
32
|
+
/**
|
|
33
|
+
* The ViewContainerRef where dynamic components will be created.
|
|
34
|
+
* Injected from the host element.
|
|
35
|
+
*/
|
|
36
|
+
this.vcr = inject(ViewContainerRef);
|
|
37
|
+
/**
|
|
38
|
+
* Reference to the pane's TweakpaneFolder, set by TweakpanePane when it initializes.
|
|
39
|
+
* This is used as the parent folder for dynamically created folders.
|
|
40
|
+
*/
|
|
41
|
+
this.paneFolder = signal(null, ...(ngDevMode ? [{ debugName: "paneFolder" }] : []));
|
|
42
|
+
/**
|
|
43
|
+
* Registry of folder ComponentRefs by folder name.
|
|
44
|
+
* Used to reuse existing folders when multiple `tweaks()` calls use the same folder name.
|
|
45
|
+
*/
|
|
46
|
+
this.folders = {};
|
|
47
|
+
}
|
|
48
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneAnchor, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
49
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: TweakpaneAnchor, isStandalone: true, selector: "[tweakpaneAnchor]", ngImport: i0 }); }
|
|
50
|
+
}
|
|
51
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneAnchor, decorators: [{
|
|
52
|
+
type: Directive,
|
|
53
|
+
args: [{ selector: '[tweakpaneAnchor]' }]
|
|
54
|
+
}] });
|
|
55
|
+
/**
|
|
56
|
+
* A minimal host component used for dynamically creating Tweakpane controls.
|
|
57
|
+
* This component serves as a host for directives like TweakpaneFolder, TweakpaneNumber, etc.
|
|
58
|
+
* when using the `tweaks()` function.
|
|
59
|
+
*
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
class TweakpaneAnchorHost {
|
|
63
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneAnchorHost, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
64
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: TweakpaneAnchorHost, isStandalone: true, selector: "tweakpane-anchor-host", ngImport: i0, template: '', isInline: true }); }
|
|
65
|
+
}
|
|
66
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneAnchorHost, decorators: [{
|
|
67
|
+
type: Component,
|
|
68
|
+
args: [{
|
|
69
|
+
selector: 'tweakpane-anchor-host',
|
|
70
|
+
template: '',
|
|
71
|
+
}]
|
|
72
|
+
}] });
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Directive that provides hidden and disabled state management for Tweakpane blades.
|
|
76
|
+
*
|
|
77
|
+
* This is a base directive used by other Tweakpane components to control
|
|
78
|
+
* visibility and interactivity of controls.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```html
|
|
82
|
+
* <tweakpane-number [hidden]="isHidden" [disabled]="isDisabled" [(value)]="speed" />
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
7
85
|
class TweakpaneBlade {
|
|
8
86
|
constructor() {
|
|
9
|
-
|
|
10
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Whether the blade is hidden.
|
|
89
|
+
* @default false
|
|
90
|
+
*/
|
|
91
|
+
this.hidden = input(false, { ...(ngDevMode ? { debugName: "hidden" } : {}), transform: booleanAttribute });
|
|
92
|
+
/**
|
|
93
|
+
* Whether the blade is disabled (non-interactive).
|
|
94
|
+
* @default false
|
|
95
|
+
*/
|
|
96
|
+
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : {}), transform: booleanAttribute });
|
|
11
97
|
this.injector = inject(Injector);
|
|
12
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Gets the current hidden and disabled values without tracking signal dependencies.
|
|
101
|
+
* Useful for initial configuration when creating Tweakpane components.
|
|
102
|
+
* @returns An object containing the current hidden and disabled states
|
|
103
|
+
*/
|
|
13
104
|
get snapshot() {
|
|
14
105
|
return {
|
|
15
106
|
hidden: untracked(this.hidden),
|
|
16
107
|
disabled: untracked(this.disabled),
|
|
17
108
|
};
|
|
18
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Synchronizes the hidden and disabled properties with a Tweakpane BladeApi.
|
|
112
|
+
* Creates a reactive effect that updates the API's state whenever
|
|
113
|
+
* the input values change.
|
|
114
|
+
*
|
|
115
|
+
* @param api - A function that returns the Tweakpane BladeApi (or null if not ready)
|
|
116
|
+
* @returns The created effect reference
|
|
117
|
+
*/
|
|
19
118
|
sync(api) {
|
|
20
119
|
return effect(() => {
|
|
21
120
|
const _api = api();
|
|
@@ -25,19 +124,44 @@ class TweakpaneBlade {
|
|
|
25
124
|
_api.disabled = this.disabled();
|
|
26
125
|
}, { injector: this.injector });
|
|
27
126
|
}
|
|
28
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
29
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
127
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneBlade, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
128
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TweakpaneBlade, isStandalone: true, selector: "tweakpane-blade", inputs: { hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
30
129
|
}
|
|
31
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
130
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneBlade, decorators: [{
|
|
32
131
|
type: Directive,
|
|
33
132
|
args: [{ selector: 'tweakpane-blade' }]
|
|
34
|
-
}] });
|
|
133
|
+
}], propDecorators: { hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
35
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Directive that provides debounced change event handling for Tweakpane controls.
|
|
137
|
+
*
|
|
138
|
+
* This is a base directive used by binding-based controls to prevent
|
|
139
|
+
* excessive updates when values change rapidly (e.g., during slider dragging).
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```html
|
|
143
|
+
* <!-- Debounce value updates by 300ms -->
|
|
144
|
+
* <tweakpane-number [debounce]="300" [(value)]="speed" />
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
36
147
|
class TweakpaneDebounce {
|
|
37
148
|
constructor() {
|
|
38
|
-
|
|
149
|
+
/**
|
|
150
|
+
* The debounce delay in milliseconds before emitting value changes.
|
|
151
|
+
* @default 150
|
|
152
|
+
*/
|
|
153
|
+
this.debounce = input(150, { ...(ngDevMode ? { debugName: "debounce" } : {}), transform: numberAttribute });
|
|
39
154
|
this.injector = inject(Injector);
|
|
40
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Synchronizes debounced change events from a Tweakpane API with a callback.
|
|
158
|
+
* Creates a reactive effect that subscribes to change events and debounces them.
|
|
159
|
+
*
|
|
160
|
+
* @typeParam T - The type of value being tracked
|
|
161
|
+
* @param api - A function that returns the Tweakpane API object with on/off methods (or null if not ready)
|
|
162
|
+
* @param cb - Callback function invoked with the change event after debounce delay
|
|
163
|
+
* @returns The created effect reference
|
|
164
|
+
*/
|
|
41
165
|
sync(api, cb) {
|
|
42
166
|
return effect((onCleanup) => {
|
|
43
167
|
const _api = api();
|
|
@@ -53,21 +177,51 @@ class TweakpaneDebounce {
|
|
|
53
177
|
});
|
|
54
178
|
}, { injector: this.injector });
|
|
55
179
|
}
|
|
56
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
57
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
180
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneDebounce, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
181
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TweakpaneDebounce, isStandalone: true, inputs: { debounce: { classPropertyName: "debounce", publicName: "debounce", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
58
182
|
}
|
|
59
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
183
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneDebounce, decorators: [{
|
|
60
184
|
type: Directive
|
|
61
|
-
}] });
|
|
185
|
+
}], propDecorators: { debounce: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounce", required: false }] }] } });
|
|
62
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Directive that provides title functionality for Tweakpane components.
|
|
189
|
+
*
|
|
190
|
+
* This is a base directive used by other Tweakpane components (like `TweakpaneFolder`,
|
|
191
|
+
* `TweakpanePane`, `TweakpaneButton`) to manage their titles reactively.
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```html
|
|
195
|
+
* <tweakpane-folder title="Settings">
|
|
196
|
+
* <!-- folder contents -->
|
|
197
|
+
* </tweakpane-folder>
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
63
200
|
class TweakpaneTitle {
|
|
64
201
|
constructor() {
|
|
202
|
+
/**
|
|
203
|
+
* The title text to display.
|
|
204
|
+
* @default 'TweakPane Title'
|
|
205
|
+
*/
|
|
65
206
|
this.title = input('TweakPane Title', ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
66
207
|
this.injector = inject(Injector);
|
|
67
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Gets the current title value without tracking signal dependencies.
|
|
211
|
+
* Useful for initial configuration when creating Tweakpane components.
|
|
212
|
+
* @returns The current title string value
|
|
213
|
+
*/
|
|
68
214
|
get snapshot() {
|
|
69
215
|
return untracked(this.title);
|
|
70
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Synchronizes the title property with a Tweakpane API object.
|
|
219
|
+
* Creates a reactive effect that updates the API's title whenever
|
|
220
|
+
* the input title changes.
|
|
221
|
+
*
|
|
222
|
+
* @param api - A function that returns the Tweakpane API object (or null if not ready)
|
|
223
|
+
* @returns The created effect reference
|
|
224
|
+
*/
|
|
71
225
|
sync(api) {
|
|
72
226
|
return effect(() => {
|
|
73
227
|
const _api = api();
|
|
@@ -76,20 +230,53 @@ class TweakpaneTitle {
|
|
|
76
230
|
_api.title = this.title();
|
|
77
231
|
}, { injector: this.injector });
|
|
78
232
|
}
|
|
79
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
80
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
233
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneTitle, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
234
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TweakpaneTitle, isStandalone: true, inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
81
235
|
}
|
|
82
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
236
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneTitle, decorators: [{
|
|
83
237
|
type: Directive
|
|
84
|
-
}] });
|
|
238
|
+
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }] } });
|
|
85
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Directive for creating collapsible folders within a Tweakpane.
|
|
242
|
+
*
|
|
243
|
+
* Folders are used to organize controls into logical groups. They can be
|
|
244
|
+
* nested within other folders or directly within a pane.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```html
|
|
248
|
+
* <tweakpane-pane>
|
|
249
|
+
* <tweakpane-folder title="Physics" [expanded]="true">
|
|
250
|
+
* <tweakpane-number label="Gravity" [(value)]="gravity" />
|
|
251
|
+
* <tweakpane-number label="Friction" [(value)]="friction" />
|
|
252
|
+
* </tweakpane-folder>
|
|
253
|
+
*
|
|
254
|
+
* <tweakpane-folder title="Appearance">
|
|
255
|
+
* <tweakpane-color label="Color" [(value)]="color" />
|
|
256
|
+
* </tweakpane-folder>
|
|
257
|
+
* </tweakpane-pane>
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
86
260
|
class TweakpaneFolder {
|
|
87
261
|
constructor() {
|
|
262
|
+
/**
|
|
263
|
+
* Whether the folder is expanded (open) or collapsed.
|
|
264
|
+
* Supports two-way binding with `[(expanded)]`.
|
|
265
|
+
* @default false
|
|
266
|
+
*/
|
|
88
267
|
this.expanded = model(false, ...(ngDevMode ? [{ debugName: "expanded" }] : []));
|
|
89
268
|
this.title = inject(TweakpaneTitle);
|
|
90
269
|
this.blade = inject(TweakpaneBlade);
|
|
91
270
|
this.parent = inject(TweakpaneFolder, { skipSelf: true, optional: true });
|
|
92
|
-
|
|
271
|
+
/**
|
|
272
|
+
* Signal containing the parent folder API.
|
|
273
|
+
* Automatically links to the parent folder in the component hierarchy.
|
|
274
|
+
*/
|
|
275
|
+
this.parentFolder = linkedSignal(() => this.parent?.folder(), ...(ngDevMode ? [{ debugName: "parentFolder" }] : []));
|
|
276
|
+
/**
|
|
277
|
+
* Computed signal containing the Tweakpane FolderApi for this folder.
|
|
278
|
+
* Returns null if the parent folder is not yet available.
|
|
279
|
+
*/
|
|
93
280
|
this.folder = computed(() => {
|
|
94
281
|
const parent = this.parentFolder();
|
|
95
282
|
if (!parent)
|
|
@@ -103,6 +290,11 @@ class TweakpaneFolder {
|
|
|
103
290
|
hidden: this.blade.snapshot.hidden,
|
|
104
291
|
});
|
|
105
292
|
}, ...(ngDevMode ? [{ debugName: "folder" }] : []));
|
|
293
|
+
/**
|
|
294
|
+
* Internal flag indicating whether this directive creates its own folder
|
|
295
|
+
* or reuses the parent folder. Set to `false` by `TweakpanePane`.
|
|
296
|
+
* @internal
|
|
297
|
+
*/
|
|
106
298
|
this.isSelf = true;
|
|
107
299
|
this.title.sync(this.folder);
|
|
108
300
|
this.blade.sync(this.folder);
|
|
@@ -122,10 +314,10 @@ class TweakpaneFolder {
|
|
|
122
314
|
this.folder()?.dispose();
|
|
123
315
|
});
|
|
124
316
|
}
|
|
125
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
126
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
317
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneFolder, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
318
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TweakpaneFolder, isStandalone: true, selector: "tweakpane-folder", inputs: { expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange" }, hostDirectives: [{ directive: TweakpaneTitle, inputs: ["title", "title"] }, { directive: TweakpaneBlade, inputs: ["hidden", "hidden", "disabled", "disabled"] }], ngImport: i0 }); }
|
|
127
319
|
}
|
|
128
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
320
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneFolder, decorators: [{
|
|
129
321
|
type: Directive,
|
|
130
322
|
args: [{
|
|
131
323
|
selector: 'tweakpane-folder',
|
|
@@ -134,17 +326,49 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
134
326
|
{ directive: TweakpaneBlade, inputs: ['hidden', 'disabled'] },
|
|
135
327
|
],
|
|
136
328
|
}]
|
|
137
|
-
}], ctorParameters: () => [] });
|
|
329
|
+
}], ctorParameters: () => [], propDecorators: { expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }] } });
|
|
138
330
|
|
|
331
|
+
/**
|
|
332
|
+
* Directive that provides label and tag functionality for Tweakpane controls.
|
|
333
|
+
*
|
|
334
|
+
* This is a base directive used by binding-based controls (like `TweakpaneNumber`,
|
|
335
|
+
* `TweakpaneText`, `TweakpaneCheckbox`) to manage their labels and optional tags.
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* ```html
|
|
339
|
+
* <tweakpane-number label="Speed" tag="m/s" [(value)]="speed" />
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
139
342
|
class TweakpaneLabel {
|
|
140
343
|
constructor() {
|
|
344
|
+
/**
|
|
345
|
+
* The label text displayed next to the control.
|
|
346
|
+
* @default ''
|
|
347
|
+
*/
|
|
141
348
|
this.label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
349
|
+
/**
|
|
350
|
+
* An optional tag displayed alongside the label (e.g., units like "px", "m/s").
|
|
351
|
+
* @default ''
|
|
352
|
+
*/
|
|
142
353
|
this.tag = input('', ...(ngDevMode ? [{ debugName: "tag" }] : []));
|
|
143
354
|
this.injector = inject(Injector);
|
|
144
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Gets the current label and tag values without tracking signal dependencies.
|
|
358
|
+
* Useful for initial configuration when creating Tweakpane controls.
|
|
359
|
+
* @returns An object containing the current label and tag values
|
|
360
|
+
*/
|
|
145
361
|
get snapshot() {
|
|
146
362
|
return { label: untracked(this.label), tag: untracked(this.tag) };
|
|
147
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Synchronizes the label and tag properties with a Tweakpane API object.
|
|
366
|
+
* Creates a reactive effect that updates the API's label and tag
|
|
367
|
+
* whenever the input values change.
|
|
368
|
+
*
|
|
369
|
+
* @param api - A function that returns the Tweakpane API object (or null if not ready)
|
|
370
|
+
* @returns The created effect reference
|
|
371
|
+
*/
|
|
148
372
|
sync(api) {
|
|
149
373
|
return effect(() => {
|
|
150
374
|
const _api = api();
|
|
@@ -156,18 +380,77 @@ class TweakpaneLabel {
|
|
|
156
380
|
}
|
|
157
381
|
}, { injector: this.injector });
|
|
158
382
|
}
|
|
159
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
160
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
383
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
384
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TweakpaneLabel, isStandalone: true, inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, tag: { classPropertyName: "tag", publicName: "tag", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
161
385
|
}
|
|
162
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
386
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneLabel, decorators: [{
|
|
163
387
|
type: Directive
|
|
164
|
-
}] });
|
|
388
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], tag: [{ type: i0.Input, args: [{ isSignal: true, alias: "tag", required: false }] }] } });
|
|
165
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Injection token used to configure value transformation when TweakpaneBinding
|
|
392
|
+
* is used as a host directive.
|
|
393
|
+
*
|
|
394
|
+
* When set to `true`, values pass through unchanged.
|
|
395
|
+
* When set to an object with `in` and `out` functions, values are transformed:
|
|
396
|
+
* - `in`: Transforms the input value before binding to Tweakpane
|
|
397
|
+
* - `out`: Transforms the Tweakpane value before emitting to the model
|
|
398
|
+
*
|
|
399
|
+
* @internal
|
|
400
|
+
*/
|
|
166
401
|
const NGT_TWEAK_BINDING_AS_HOST = new InjectionToken('hostDirective NgtTweakBinding', { factory: () => null });
|
|
402
|
+
/**
|
|
403
|
+
* Provides configuration for TweakpaneBinding when used as a host directive.
|
|
404
|
+
*
|
|
405
|
+
* @typeParam TIn - The input value type (from the component)
|
|
406
|
+
* @typeParam TOut - The output value type (for Tweakpane)
|
|
407
|
+
* @param inOut - Optional object containing `in` and `out` transformation functions
|
|
408
|
+
* @returns A provider configuration object
|
|
409
|
+
*
|
|
410
|
+
* @example
|
|
411
|
+
* ```typescript
|
|
412
|
+
* // Simple passthrough (no transformation)
|
|
413
|
+
* providers: [provideTweakBindingAsHost()]
|
|
414
|
+
*
|
|
415
|
+
* // With value transformation (e.g., array to object)
|
|
416
|
+
* providers: [provideTweakBindingAsHost({
|
|
417
|
+
* in: (value) => ({ x: value[0], y: value[1] }),
|
|
418
|
+
* out: (value) => [value.x, value.y]
|
|
419
|
+
* })]
|
|
420
|
+
* ```
|
|
421
|
+
*/
|
|
167
422
|
function provideTweakBindingAsHost(inOut) {
|
|
168
423
|
return { provide: NGT_TWEAK_BINDING_AS_HOST, useValue: inOut ?? true };
|
|
169
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* Base directive for creating Tweakpane bindings (two-way data binding controls).
|
|
427
|
+
*
|
|
428
|
+
* This directive provides the core functionality for binding Angular values to
|
|
429
|
+
* Tweakpane controls. It handles:
|
|
430
|
+
* - Creating the binding on the parent folder
|
|
431
|
+
* - Syncing label, blade, and debounce settings
|
|
432
|
+
* - Value transformation when used as a host directive
|
|
433
|
+
* - Automatic cleanup on destroy
|
|
434
|
+
*
|
|
435
|
+
* Most commonly used as a host directive by specific control types like
|
|
436
|
+
* `TweakpaneNumber`, `TweakpaneText`, `TweakpaneCheckbox`, etc.
|
|
437
|
+
*
|
|
438
|
+
* @typeParam TValue - The type of value being bound
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* ```html
|
|
442
|
+
* <!-- Direct usage (rarely needed) -->
|
|
443
|
+
* <tweakpane-binding [(value)]="myValue" label="My Value" />
|
|
444
|
+
*
|
|
445
|
+
* <!-- More commonly used via specific controls -->
|
|
446
|
+
* <tweakpane-number [(value)]="speed" label="Speed" />
|
|
447
|
+
* ```
|
|
448
|
+
*/
|
|
170
449
|
class TweakpaneBinding {
|
|
450
|
+
/**
|
|
451
|
+
* Gets the bindable object with optional value transformation.
|
|
452
|
+
* @returns An object with a `value` property suitable for Tweakpane binding
|
|
453
|
+
*/
|
|
171
454
|
get bindableObject() {
|
|
172
455
|
let value = untracked(this.value);
|
|
173
456
|
if (this.asHostDirective && typeof this.asHostDirective === 'object') {
|
|
@@ -176,6 +459,9 @@ class TweakpaneBinding {
|
|
|
176
459
|
return { value };
|
|
177
460
|
}
|
|
178
461
|
constructor() {
|
|
462
|
+
/**
|
|
463
|
+
* The bound value. Supports two-way binding with `[(value)]`.
|
|
464
|
+
*/
|
|
179
465
|
this.value = model.required(...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
180
466
|
this.debounce = inject(TweakpaneDebounce);
|
|
181
467
|
this.label = inject(TweakpaneLabel);
|
|
@@ -219,15 +505,23 @@ class TweakpaneBinding {
|
|
|
219
505
|
this.bindingApi()?.dispose();
|
|
220
506
|
});
|
|
221
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* Synchronizes additional binding parameters with the Tweakpane binding.
|
|
510
|
+
* Called by specific control directives to add control-specific parameters
|
|
511
|
+
* (e.g., min/max for numbers, options for lists).
|
|
512
|
+
*
|
|
513
|
+
* @param params - A function that returns the binding parameters
|
|
514
|
+
* @returns The created effect reference
|
|
515
|
+
*/
|
|
222
516
|
syncBindingParams(params) {
|
|
223
517
|
return effect(() => {
|
|
224
518
|
this.bindingParams.set(params());
|
|
225
519
|
}, { injector: this.injector });
|
|
226
520
|
}
|
|
227
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
228
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
521
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneBinding, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
522
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TweakpaneBinding, isStandalone: true, selector: "tweakpane-binding", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { value: "valueChange" }, hostDirectives: [{ directive: TweakpaneBlade, inputs: ["disabled", "disabled", "hidden", "hidden"] }, { directive: TweakpaneDebounce, inputs: ["debounce", "debounce"] }, { directive: TweakpaneLabel, inputs: ["label", "label", "tag", "tag"] }], ngImport: i0 }); }
|
|
229
523
|
}
|
|
230
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
524
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneBinding, decorators: [{
|
|
231
525
|
type: Directive,
|
|
232
526
|
args: [{
|
|
233
527
|
selector: 'tweakpane-binding',
|
|
@@ -237,10 +531,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
237
531
|
{ directive: TweakpaneLabel, inputs: ['label', 'tag'] },
|
|
238
532
|
],
|
|
239
533
|
}]
|
|
240
|
-
}], ctorParameters: () => [] });
|
|
534
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }, { type: i0.Output, args: ["valueChange"] }] } });
|
|
241
535
|
|
|
536
|
+
/**
|
|
537
|
+
* Directive for creating a clickable button in Tweakpane.
|
|
538
|
+
*
|
|
539
|
+
* Buttons are used to trigger actions (like reset, randomize, etc.)
|
|
540
|
+
* and emit click events when pressed.
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* ```html
|
|
544
|
+
* <tweakpane-pane>
|
|
545
|
+
* <tweakpane-button title="Reset" (click)="onReset()" />
|
|
546
|
+
* <tweakpane-button title="Randomize" label="Action" (click)="onRandomize()" />
|
|
547
|
+
* <tweakpane-button title="Save" [disabled]="!canSave" (click)="onSave()" />
|
|
548
|
+
* </tweakpane-pane>
|
|
549
|
+
* ```
|
|
550
|
+
*/
|
|
242
551
|
class TweakpaneButton {
|
|
243
552
|
constructor() {
|
|
553
|
+
/**
|
|
554
|
+
* Event emitted when the button is clicked.
|
|
555
|
+
* Provides the Tweakpane mouse event with the ButtonApi.
|
|
556
|
+
*/
|
|
244
557
|
this.click = output();
|
|
245
558
|
this.title = inject(TweakpaneTitle);
|
|
246
559
|
this.label = inject(TweakpaneLabel);
|
|
@@ -274,10 +587,10 @@ class TweakpaneButton {
|
|
|
274
587
|
this.buttonApi()?.dispose();
|
|
275
588
|
});
|
|
276
589
|
}
|
|
277
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
278
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "
|
|
590
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneButton, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
591
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: TweakpaneButton, isStandalone: true, selector: "tweakpane-button", outputs: { click: "click" }, hostDirectives: [{ directive: TweakpaneTitle, inputs: ["title", "title"] }, { directive: TweakpaneLabel, inputs: ["label", "label"] }, { directive: TweakpaneBlade, inputs: ["hidden", "hidden", "disabled", "disabled"] }], ngImport: i0 }); }
|
|
279
592
|
}
|
|
280
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
593
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneButton, decorators: [{
|
|
281
594
|
type: Directive,
|
|
282
595
|
args: [{
|
|
283
596
|
selector: 'tweakpane-button',
|
|
@@ -287,47 +600,118 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
287
600
|
{ directive: TweakpaneBlade, inputs: ['hidden', 'disabled'] },
|
|
288
601
|
],
|
|
289
602
|
}]
|
|
290
|
-
}], ctorParameters: () => [] });
|
|
603
|
+
}], ctorParameters: () => [], propDecorators: { click: [{ type: i0.Output, args: ["click"] }] } });
|
|
291
604
|
|
|
605
|
+
/**
|
|
606
|
+
* Directive for creating a boolean checkbox control in Tweakpane.
|
|
607
|
+
*
|
|
608
|
+
* Provides two-way binding for boolean values with a checkbox UI.
|
|
609
|
+
*
|
|
610
|
+
* @example
|
|
611
|
+
* ```html
|
|
612
|
+
* <tweakpane-pane>
|
|
613
|
+
* <tweakpane-checkbox label="Debug Mode" [(value)]="debugMode" />
|
|
614
|
+
* <tweakpane-checkbox label="Wireframe" [(value)]="showWireframe" [disabled]="!debugMode()" />
|
|
615
|
+
* </tweakpane-pane>
|
|
616
|
+
* ```
|
|
617
|
+
*/
|
|
292
618
|
class TweakpaneCheckbox {
|
|
293
619
|
constructor() {
|
|
620
|
+
/**
|
|
621
|
+
* Additional Tweakpane boolean input parameters.
|
|
622
|
+
* @default {}
|
|
623
|
+
*/
|
|
294
624
|
this.params = input({}, ...(ngDevMode ? [{ debugName: "params" }] : []));
|
|
295
625
|
this.binding = inject(TweakpaneBinding);
|
|
296
626
|
this.binding.syncBindingParams(this.params);
|
|
297
627
|
}
|
|
298
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
299
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
628
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneCheckbox, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
629
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TweakpaneCheckbox, isStandalone: true, selector: "tweakpane-checkbox", inputs: { params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideTweakBindingAsHost()], hostDirectives: [{ directive: TweakpaneBinding, inputs: ["value", "value"], outputs: ["valueChange", "valueChange"] }], ngImport: i0 }); }
|
|
300
630
|
}
|
|
301
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
631
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneCheckbox, decorators: [{
|
|
302
632
|
type: Directive,
|
|
303
633
|
args: [{
|
|
304
634
|
selector: 'tweakpane-checkbox',
|
|
305
635
|
hostDirectives: [{ directive: TweakpaneBinding, inputs: ['value'], outputs: ['valueChange'] }],
|
|
306
636
|
providers: [provideTweakBindingAsHost()],
|
|
307
637
|
}]
|
|
308
|
-
}], ctorParameters: () => [] });
|
|
638
|
+
}], ctorParameters: () => [], propDecorators: { params: [{ type: i0.Input, args: [{ isSignal: true, alias: "params", required: false }] }] } });
|
|
309
639
|
|
|
640
|
+
/**
|
|
641
|
+
* Directive for creating a color picker control in Tweakpane.
|
|
642
|
+
*
|
|
643
|
+
* Provides two-way binding for color values (as hex strings) with a
|
|
644
|
+
* color picker UI that supports RGB, HSL, and alpha.
|
|
645
|
+
*
|
|
646
|
+
* @example
|
|
647
|
+
* ```html
|
|
648
|
+
* <tweakpane-pane>
|
|
649
|
+
* <tweakpane-color label="Background" [(value)]="backgroundColor" />
|
|
650
|
+
* <tweakpane-color label="Accent" [(value)]="accentColor" [params]="{ alpha: true }" />
|
|
651
|
+
* </tweakpane-pane>
|
|
652
|
+
* ```
|
|
653
|
+
*/
|
|
310
654
|
class TweakpaneColor {
|
|
311
655
|
constructor() {
|
|
656
|
+
/**
|
|
657
|
+
* Additional Tweakpane color input parameters.
|
|
658
|
+
* Can include options like `alpha`, `color.type`, etc.
|
|
659
|
+
* @default {}
|
|
660
|
+
*/
|
|
312
661
|
this.params = input({}, ...(ngDevMode ? [{ debugName: "params" }] : []));
|
|
313
662
|
this.binding = inject(TweakpaneBinding);
|
|
314
663
|
this.binding.syncBindingParams(this.params);
|
|
315
664
|
}
|
|
316
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
317
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
665
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneColor, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
666
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TweakpaneColor, isStandalone: true, selector: "tweakpane-color", inputs: { params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideTweakBindingAsHost()], hostDirectives: [{ directive: TweakpaneBinding, inputs: ["value", "value"], outputs: ["valueChange", "valueChange"] }], ngImport: i0 }); }
|
|
318
667
|
}
|
|
319
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
668
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneColor, decorators: [{
|
|
320
669
|
type: Directive,
|
|
321
670
|
args: [{
|
|
322
671
|
selector: 'tweakpane-color',
|
|
323
672
|
hostDirectives: [{ directive: TweakpaneBinding, inputs: ['value'], outputs: ['valueChange'] }],
|
|
324
673
|
providers: [provideTweakBindingAsHost()],
|
|
325
674
|
}]
|
|
326
|
-
}], ctorParameters: () => [] });
|
|
675
|
+
}], ctorParameters: () => [], propDecorators: { params: [{ type: i0.Input, args: [{ isSignal: true, alias: "params", required: false }] }] } });
|
|
327
676
|
|
|
677
|
+
/**
|
|
678
|
+
* Directive for creating a dropdown list/select control in Tweakpane.
|
|
679
|
+
*
|
|
680
|
+
* Provides two-way binding for selecting from a list of options.
|
|
681
|
+
* Options can be provided as an array or as a key-value object.
|
|
682
|
+
*
|
|
683
|
+
* @typeParam TOptionValue - The type of values in the list
|
|
684
|
+
*
|
|
685
|
+
* @example
|
|
686
|
+
* ```html
|
|
687
|
+
* <tweakpane-pane>
|
|
688
|
+
* <!-- Options as array -->
|
|
689
|
+
* <tweakpane-list
|
|
690
|
+
* label="Mode"
|
|
691
|
+
* [(value)]="mode"
|
|
692
|
+
* [options]="['normal', 'debug', 'performance']"
|
|
693
|
+
* />
|
|
694
|
+
*
|
|
695
|
+
* <!-- Options as object (label: value mapping) -->
|
|
696
|
+
* <tweakpane-list
|
|
697
|
+
* label="Quality"
|
|
698
|
+
* [(value)]="quality"
|
|
699
|
+
* [options]="{ 'Low': 1, 'Medium': 2, 'High': 3, 'Ultra': 4 }"
|
|
700
|
+
* />
|
|
701
|
+
* </tweakpane-pane>
|
|
702
|
+
* ```
|
|
703
|
+
*/
|
|
328
704
|
class TweakpaneList {
|
|
329
705
|
constructor() {
|
|
706
|
+
/**
|
|
707
|
+
* The currently selected value. Supports two-way binding with `[(value)]`.
|
|
708
|
+
*/
|
|
330
709
|
this.value = model.required(...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
710
|
+
/**
|
|
711
|
+
* The list options. Can be:
|
|
712
|
+
* - An array of values (labels will be stringified values)
|
|
713
|
+
* - An object mapping display labels to values
|
|
714
|
+
*/
|
|
331
715
|
this.options = input.required(...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
332
716
|
this.blade = inject(TweakpaneBlade);
|
|
333
717
|
this.debounce = inject(TweakpaneDebounce);
|
|
@@ -369,10 +753,10 @@ class TweakpaneList {
|
|
|
369
753
|
this.listApi()?.dispose();
|
|
370
754
|
});
|
|
371
755
|
}
|
|
372
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
373
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
756
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneList, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
757
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TweakpaneList, isStandalone: true, selector: "tweakpane-list", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { value: "valueChange" }, hostDirectives: [{ directive: TweakpaneBlade, inputs: ["hidden", "hidden", "disabled", "disabled"] }, { directive: TweakpaneDebounce, inputs: ["debounce", "debounce"] }, { directive: TweakpaneLabel, inputs: ["label", "label"] }], ngImport: i0 }); }
|
|
374
758
|
}
|
|
375
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
759
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneList, decorators: [{
|
|
376
760
|
type: Directive,
|
|
377
761
|
args: [{
|
|
378
762
|
selector: 'tweakpane-list',
|
|
@@ -382,37 +766,126 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
382
766
|
{ directive: TweakpaneLabel, inputs: ['label'] },
|
|
383
767
|
],
|
|
384
768
|
}]
|
|
385
|
-
}], ctorParameters: () => [] });
|
|
769
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }, { type: i0.Output, args: ["valueChange"] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }] } });
|
|
386
770
|
|
|
771
|
+
/**
|
|
772
|
+
* Directive for creating a numeric input control in Tweakpane.
|
|
773
|
+
*
|
|
774
|
+
* Provides two-way binding for number values with optional min/max/step
|
|
775
|
+
* constraints. Displays as a text input with increment buttons, or as
|
|
776
|
+
* a slider if min and max are specified.
|
|
777
|
+
*
|
|
778
|
+
* @example
|
|
779
|
+
* ```html
|
|
780
|
+
* <tweakpane-pane>
|
|
781
|
+
* <!-- Simple number input -->
|
|
782
|
+
* <tweakpane-number label="Count" [(value)]="count" />
|
|
783
|
+
*
|
|
784
|
+
* <!-- With slider (min/max defined) -->
|
|
785
|
+
* <tweakpane-number
|
|
786
|
+
* label="Speed"
|
|
787
|
+
* [(value)]="speed"
|
|
788
|
+
* [params]="{ min: 0, max: 100, step: 0.1 }"
|
|
789
|
+
* />
|
|
790
|
+
*
|
|
791
|
+
* <!-- With custom format -->
|
|
792
|
+
* <tweakpane-number
|
|
793
|
+
* label="Angle"
|
|
794
|
+
* [(value)]="angle"
|
|
795
|
+
* [params]="{ min: 0, max: 360, format: (v) => v.toFixed(0) + '°' }"
|
|
796
|
+
* />
|
|
797
|
+
* </tweakpane-pane>
|
|
798
|
+
* ```
|
|
799
|
+
*/
|
|
387
800
|
class TweakpaneNumber {
|
|
388
801
|
constructor() {
|
|
802
|
+
/**
|
|
803
|
+
* Additional Tweakpane number input parameters.
|
|
804
|
+
* Can include `min`, `max`, `step`, `format`, etc.
|
|
805
|
+
* @default {}
|
|
806
|
+
*/
|
|
389
807
|
this.params = input({}, ...(ngDevMode ? [{ debugName: "params" }] : []));
|
|
390
808
|
this.binding = inject(TweakpaneBinding);
|
|
391
809
|
this.binding.syncBindingParams(this.params);
|
|
392
810
|
}
|
|
393
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
394
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
811
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneNumber, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
812
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TweakpaneNumber, isStandalone: true, selector: "tweakpane-number", inputs: { params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideTweakBindingAsHost()], hostDirectives: [{ directive: TweakpaneBinding, inputs: ["value", "value"], outputs: ["valueChange", "valueChange"] }], ngImport: i0 }); }
|
|
395
813
|
}
|
|
396
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
814
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneNumber, decorators: [{
|
|
397
815
|
type: Directive,
|
|
398
816
|
args: [{
|
|
399
817
|
selector: 'tweakpane-number',
|
|
400
818
|
hostDirectives: [{ directive: TweakpaneBinding, inputs: ['value'], outputs: ['valueChange'] }],
|
|
401
819
|
providers: [provideTweakBindingAsHost()],
|
|
402
820
|
}]
|
|
403
|
-
}], ctorParameters: () => [] });
|
|
821
|
+
}], ctorParameters: () => [], propDecorators: { params: [{ type: i0.Input, args: [{ isSignal: true, alias: "params", required: false }] }] } });
|
|
404
822
|
|
|
823
|
+
/**
|
|
824
|
+
* Main Tweakpane container directive that creates the root pane element.
|
|
825
|
+
*
|
|
826
|
+
* This directive creates a floating Tweakpane panel that can be positioned
|
|
827
|
+
* anywhere on the screen. It serves as the root container for all
|
|
828
|
+
* Tweakpane controls (folders, bindings, buttons, etc.).
|
|
829
|
+
*
|
|
830
|
+
* @example
|
|
831
|
+
* ```html
|
|
832
|
+
* <!-- Basic usage with default positioning (top-right) -->
|
|
833
|
+
* <tweakpane-pane title="Settings">
|
|
834
|
+
* <tweakpane-number label="Speed" [(value)]="speed" />
|
|
835
|
+
* <tweakpane-checkbox label="Debug" [(value)]="debug" />
|
|
836
|
+
* </tweakpane-pane>
|
|
837
|
+
*
|
|
838
|
+
* <!-- Custom positioning -->
|
|
839
|
+
* <tweakpane-pane title="Controls" left="8px" bottom="8px" [right]="undefined">
|
|
840
|
+
* <tweakpane-folder title="Physics">
|
|
841
|
+
* <tweakpane-number label="Gravity" [(value)]="gravity" />
|
|
842
|
+
* </tweakpane-folder>
|
|
843
|
+
* </tweakpane-pane>
|
|
844
|
+
*
|
|
845
|
+
* <!-- Embedded in a container element -->
|
|
846
|
+
* <div #container></div>
|
|
847
|
+
* <tweakpane-pane [container]="container">
|
|
848
|
+
* <!-- controls -->
|
|
849
|
+
* </tweakpane-pane>
|
|
850
|
+
* ```
|
|
851
|
+
*/
|
|
405
852
|
class TweakpanePane {
|
|
406
853
|
constructor() {
|
|
854
|
+
/**
|
|
855
|
+
* CSS top position of the pane.
|
|
856
|
+
* @default '8px'
|
|
857
|
+
*/
|
|
407
858
|
this.top = input('8px', ...(ngDevMode ? [{ debugName: "top" }] : []));
|
|
859
|
+
/**
|
|
860
|
+
* CSS right position of the pane.
|
|
861
|
+
* @default '8px'
|
|
862
|
+
*/
|
|
408
863
|
this.right = input('8px', ...(ngDevMode ? [{ debugName: "right" }] : []));
|
|
864
|
+
/**
|
|
865
|
+
* CSS left position of the pane.
|
|
866
|
+
* @default undefined
|
|
867
|
+
*/
|
|
409
868
|
this.left = input(...(ngDevMode ? [undefined, { debugName: "left" }] : []));
|
|
869
|
+
/**
|
|
870
|
+
* CSS bottom position of the pane.
|
|
871
|
+
* @default undefined
|
|
872
|
+
*/
|
|
410
873
|
this.bottom = input(...(ngDevMode ? [undefined, { debugName: "bottom" }] : []));
|
|
874
|
+
/**
|
|
875
|
+
* CSS width of the pane.
|
|
876
|
+
* @default '256px'
|
|
877
|
+
*/
|
|
411
878
|
this.width = input('256px', ...(ngDevMode ? [{ debugName: "width" }] : []));
|
|
879
|
+
/**
|
|
880
|
+
* Optional container element to embed the pane into.
|
|
881
|
+
* If not provided, the pane floats freely in the document.
|
|
882
|
+
* Can be an HTMLElement or an Angular ElementRef.
|
|
883
|
+
*/
|
|
412
884
|
this.container = input(...(ngDevMode ? [undefined, { debugName: "container" }] : []));
|
|
413
885
|
this.document = inject(DOCUMENT);
|
|
414
886
|
this.title = inject(TweakpaneTitle, { host: true });
|
|
415
887
|
this.folder = inject(TweakpaneFolder, { host: true });
|
|
888
|
+
this.tweakpaneAnchor = inject(TweakpaneAnchor, { optional: true });
|
|
416
889
|
this.pane = signal(null, ...(ngDevMode ? [{ debugName: "pane" }] : []));
|
|
417
890
|
this.folder.isSelf = false;
|
|
418
891
|
afterNextRender(() => {
|
|
@@ -433,6 +906,10 @@ class TweakpanePane {
|
|
|
433
906
|
const pane = new Pane(paneOptions);
|
|
434
907
|
this.pane.set(pane);
|
|
435
908
|
this.folder.parentFolder.set(pane);
|
|
909
|
+
// Set the pane's folder for tweaks() to use
|
|
910
|
+
if (this.tweakpaneAnchor) {
|
|
911
|
+
this.tweakpaneAnchor.paneFolder.set(this.folder);
|
|
912
|
+
}
|
|
436
913
|
});
|
|
437
914
|
inject(DestroyRef).onDestroy(() => {
|
|
438
915
|
const pane = this.pane();
|
|
@@ -459,6 +936,10 @@ class TweakpanePane {
|
|
|
459
936
|
this.updateStyleEffect('width');
|
|
460
937
|
});
|
|
461
938
|
}
|
|
939
|
+
/**
|
|
940
|
+
* Updates a CSS style property on the pane's parent element.
|
|
941
|
+
* @param propertyName - The name of the style property to update
|
|
942
|
+
*/
|
|
462
943
|
updateStyleEffect(propertyName) {
|
|
463
944
|
const pane = this.pane();
|
|
464
945
|
if (!pane)
|
|
@@ -475,25 +956,61 @@ class TweakpanePane {
|
|
|
475
956
|
parentElement.style.setProperty(propertyName, typeof value === 'number' ? value + 'px' : value);
|
|
476
957
|
pane.refresh();
|
|
477
958
|
}
|
|
478
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
479
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
959
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpanePane, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
960
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TweakpanePane, isStandalone: true, selector: "tweakpane-pane", inputs: { top: { classPropertyName: "top", publicName: "top", isSignal: true, isRequired: false, transformFunction: null }, right: { classPropertyName: "right", publicName: "right", isSignal: true, isRequired: false, transformFunction: null }, left: { classPropertyName: "left", publicName: "left", isSignal: true, isRequired: false, transformFunction: null }, bottom: { classPropertyName: "bottom", publicName: "bottom", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, container: { classPropertyName: "container", publicName: "container", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: TweakpaneFolder, inputs: ["expanded", "expanded"], outputs: ["expandedChange", "expandedChange"] }], ngImport: i0 }); }
|
|
480
961
|
}
|
|
481
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
962
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpanePane, decorators: [{
|
|
482
963
|
type: Directive,
|
|
483
964
|
args: [{
|
|
484
965
|
selector: 'tweakpane-pane',
|
|
485
966
|
hostDirectives: [{ directive: TweakpaneFolder, inputs: ['expanded'], outputs: ['expandedChange'] }],
|
|
486
967
|
}]
|
|
487
|
-
}], ctorParameters: () => [] });
|
|
968
|
+
}], ctorParameters: () => [], propDecorators: { top: [{ type: i0.Input, args: [{ isSignal: true, alias: "top", required: false }] }], right: [{ type: i0.Input, args: [{ isSignal: true, alias: "right", required: false }] }], left: [{ type: i0.Input, args: [{ isSignal: true, alias: "left", required: false }] }], bottom: [{ type: i0.Input, args: [{ isSignal: true, alias: "bottom", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], container: [{ type: i0.Input, args: [{ isSignal: true, alias: "container", required: false }] }] } });
|
|
488
969
|
|
|
970
|
+
/**
|
|
971
|
+
* Directive for creating a 2D/3D/4D point input control in Tweakpane.
|
|
972
|
+
*
|
|
973
|
+
* Provides two-way binding for point values (as tuple arrays or objects).
|
|
974
|
+
* The control displays input fields for each dimension and optionally
|
|
975
|
+
* shows a 2D picker for x/y values.
|
|
976
|
+
*
|
|
977
|
+
* Values are accepted as arrays `[x, y, z?, w?]` and emitted in the same format.
|
|
978
|
+
*
|
|
979
|
+
* @example
|
|
980
|
+
* ```html
|
|
981
|
+
* <tweakpane-pane>
|
|
982
|
+
* <!-- 2D point -->
|
|
983
|
+
* <tweakpane-point label="Position" [(value)]="position2D" />
|
|
984
|
+
*
|
|
985
|
+
* <!-- 3D point with custom ranges -->
|
|
986
|
+
* <tweakpane-point
|
|
987
|
+
* label="Position"
|
|
988
|
+
* [(value)]="position3D"
|
|
989
|
+
* [params]="{
|
|
990
|
+
* x: { min: -10, max: 10 },
|
|
991
|
+
* y: { min: 0, max: 100 },
|
|
992
|
+
* z: { min: -10, max: 10 }
|
|
993
|
+
* }"
|
|
994
|
+
* />
|
|
995
|
+
*
|
|
996
|
+
* <!-- 4D point (e.g., quaternion) -->
|
|
997
|
+
* <tweakpane-point label="Rotation" [(value)]="quaternion" />
|
|
998
|
+
* </tweakpane-pane>
|
|
999
|
+
* ```
|
|
1000
|
+
*/
|
|
489
1001
|
class TweakpanePoint {
|
|
490
1002
|
constructor() {
|
|
1003
|
+
/**
|
|
1004
|
+
* Additional Tweakpane point input parameters.
|
|
1005
|
+
* Can include per-axis configuration like `{ x: { min, max }, y: { min, max } }`.
|
|
1006
|
+
* @default {}
|
|
1007
|
+
*/
|
|
491
1008
|
this.params = input({}, ...(ngDevMode ? [{ debugName: "params" }] : []));
|
|
492
1009
|
this.binding = inject(TweakpaneBinding);
|
|
493
1010
|
this.binding.syncBindingParams(this.params);
|
|
494
1011
|
}
|
|
495
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
496
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
1012
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpanePoint, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1013
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TweakpanePoint, isStandalone: true, selector: "tweakpane-point", inputs: { params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
497
1014
|
provideTweakBindingAsHost({
|
|
498
1015
|
in: (value) => {
|
|
499
1016
|
if (Array.isArray(value)) {
|
|
@@ -509,7 +1026,7 @@ class TweakpanePoint {
|
|
|
509
1026
|
}),
|
|
510
1027
|
], hostDirectives: [{ directive: TweakpaneBinding, inputs: ["value", "value"], outputs: ["valueChange", "valueChange"] }], ngImport: i0 }); }
|
|
511
1028
|
}
|
|
512
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1029
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpanePoint, decorators: [{
|
|
513
1030
|
type: Directive,
|
|
514
1031
|
args: [{
|
|
515
1032
|
selector: 'tweakpane-point',
|
|
@@ -530,29 +1047,618 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
530
1047
|
}),
|
|
531
1048
|
],
|
|
532
1049
|
}]
|
|
533
|
-
}], ctorParameters: () => [] });
|
|
1050
|
+
}], ctorParameters: () => [], propDecorators: { params: [{ type: i0.Input, args: [{ isSignal: true, alias: "params", required: false }] }] } });
|
|
534
1051
|
|
|
1052
|
+
/**
|
|
1053
|
+
* Directive for creating a text input control in Tweakpane.
|
|
1054
|
+
*
|
|
1055
|
+
* Provides two-way binding for string values with a text input UI.
|
|
1056
|
+
*
|
|
1057
|
+
* @example
|
|
1058
|
+
* ```html
|
|
1059
|
+
* <tweakpane-pane>
|
|
1060
|
+
* <tweakpane-text label="Name" [(value)]="objectName" />
|
|
1061
|
+
* <tweakpane-text label="Description" [(value)]="description" />
|
|
1062
|
+
* </tweakpane-pane>
|
|
1063
|
+
* ```
|
|
1064
|
+
*/
|
|
535
1065
|
class TweakpaneText {
|
|
536
1066
|
constructor() {
|
|
1067
|
+
/**
|
|
1068
|
+
* Additional Tweakpane string input parameters.
|
|
1069
|
+
* @default {}
|
|
1070
|
+
*/
|
|
537
1071
|
this.params = input({}, ...(ngDevMode ? [{ debugName: "params" }] : []));
|
|
538
1072
|
this.binding = inject(TweakpaneBinding);
|
|
539
1073
|
this.binding.syncBindingParams(this.params);
|
|
540
1074
|
}
|
|
541
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
542
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
1075
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneText, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1076
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TweakpaneText, isStandalone: true, selector: "tweakpane-text", inputs: { params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideTweakBindingAsHost()], hostDirectives: [{ directive: TweakpaneBinding, inputs: ["value", "value"], outputs: ["valueChange", "valueChange"] }], ngImport: i0 }); }
|
|
543
1077
|
}
|
|
544
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1078
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TweakpaneText, decorators: [{
|
|
545
1079
|
type: Directive,
|
|
546
1080
|
args: [{
|
|
547
1081
|
selector: 'tweakpane-text',
|
|
548
1082
|
hostDirectives: [{ directive: TweakpaneBinding, inputs: ['value'], outputs: ['valueChange'] }],
|
|
549
1083
|
providers: [provideTweakBindingAsHost()],
|
|
550
1084
|
}]
|
|
551
|
-
}], ctorParameters: () => [] });
|
|
1085
|
+
}], ctorParameters: () => [], propDecorators: { params: [{ type: i0.Input, args: [{ isSignal: true, alias: "params", required: false }] }] } });
|
|
1086
|
+
|
|
1087
|
+
// ============================================================================
|
|
1088
|
+
// Type Guards
|
|
1089
|
+
// ============================================================================
|
|
1090
|
+
/**
|
|
1091
|
+
* Checks if a config value is a TweakNumberConfig.
|
|
1092
|
+
* @internal
|
|
1093
|
+
*/
|
|
1094
|
+
function isNumberConfig(config) {
|
|
1095
|
+
if (typeof config !== 'object' || config === null || !('value' in config))
|
|
1096
|
+
return false;
|
|
1097
|
+
if ('color' in config || 'options' in config || 'action' in config || '__folder' in config)
|
|
1098
|
+
return false;
|
|
1099
|
+
if ('x' in config || 'y' in config)
|
|
1100
|
+
return false;
|
|
1101
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1102
|
+
const value = isSignal(config.value) ? untracked(config.value) : config.value;
|
|
1103
|
+
return typeof value === 'number';
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Checks if a config value is a TweakTextConfig.
|
|
1107
|
+
* @internal
|
|
1108
|
+
*/
|
|
1109
|
+
function isTextConfig(config) {
|
|
1110
|
+
if (typeof config !== 'object' || config === null || !('value' in config))
|
|
1111
|
+
return false;
|
|
1112
|
+
if ('color' in config || 'options' in config || 'action' in config || '__folder' in config)
|
|
1113
|
+
return false;
|
|
1114
|
+
if ('min' in config || 'max' in config || 'step' in config)
|
|
1115
|
+
return false;
|
|
1116
|
+
if ('x' in config || 'y' in config)
|
|
1117
|
+
return false;
|
|
1118
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1119
|
+
const value = isSignal(config.value) ? untracked(config.value) : config.value;
|
|
1120
|
+
return typeof value === 'string';
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Checks if a config value is a TweakColorConfig.
|
|
1124
|
+
* @internal
|
|
1125
|
+
*/
|
|
1126
|
+
function isColorConfig(config) {
|
|
1127
|
+
return typeof config === 'object' && config !== null && 'color' in config && config.color === true;
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Checks if a config value is a TweakCheckboxConfig.
|
|
1131
|
+
* @internal
|
|
1132
|
+
*/
|
|
1133
|
+
function isCheckboxConfig(config) {
|
|
1134
|
+
if (typeof config !== 'object' || config === null || !('value' in config))
|
|
1135
|
+
return false;
|
|
1136
|
+
if ('color' in config || 'options' in config || 'action' in config || '__folder' in config)
|
|
1137
|
+
return false;
|
|
1138
|
+
if ('min' in config || 'max' in config || 'step' in config)
|
|
1139
|
+
return false;
|
|
1140
|
+
if ('x' in config || 'y' in config)
|
|
1141
|
+
return false;
|
|
1142
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1143
|
+
const value = isSignal(config.value) ? untracked(config.value) : config.value;
|
|
1144
|
+
return typeof value === 'boolean';
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Checks if a config value is a TweakListConfig.
|
|
1148
|
+
* @internal
|
|
1149
|
+
*/
|
|
1150
|
+
function isListConfig(config) {
|
|
1151
|
+
return typeof config === 'object' && config !== null && 'options' in config;
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Checks if a config value is a TweakPointConfig.
|
|
1155
|
+
* @internal
|
|
1156
|
+
*/
|
|
1157
|
+
function isPointConfig(config) {
|
|
1158
|
+
return (typeof config === 'object' &&
|
|
1159
|
+
config !== null &&
|
|
1160
|
+
'value' in config &&
|
|
1161
|
+
(('x' in config && 'y' in config) ||
|
|
1162
|
+
(Array.isArray(config.value) && config.value.length >= 2) ||
|
|
1163
|
+
(isSignal(config.value) && Array.isArray(untracked(config.value)))));
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Checks if a config value is a TweakButtonConfig.
|
|
1167
|
+
* @internal
|
|
1168
|
+
*/
|
|
1169
|
+
function isButtonConfig(config) {
|
|
1170
|
+
return typeof config === 'object' && config !== null && 'action' in config && typeof config.action === 'function';
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Checks if a config value is a TweakFolderConfig.
|
|
1174
|
+
* @internal
|
|
1175
|
+
*/
|
|
1176
|
+
function isFolderConfig(config) {
|
|
1177
|
+
return typeof config === 'object' && config !== null && '__folder' in config && config.__folder === true;
|
|
1178
|
+
}
|
|
1179
|
+
// ============================================================================
|
|
1180
|
+
// Helper to create folder configs
|
|
1181
|
+
// ============================================================================
|
|
1182
|
+
/**
|
|
1183
|
+
* Creates a nested folder configuration for use with `tweaks()`.
|
|
1184
|
+
*
|
|
1185
|
+
* This helper function creates a folder configuration object that can be
|
|
1186
|
+
* used as a value in the `tweaks()` config to organize controls into
|
|
1187
|
+
* collapsible groups.
|
|
1188
|
+
*
|
|
1189
|
+
* @typeParam T - The config type for the folder's contents
|
|
1190
|
+
* @param name - The display name/title of the folder
|
|
1191
|
+
* @param config - The configuration object for controls within the folder
|
|
1192
|
+
* @param options - Optional folder settings
|
|
1193
|
+
* @param options.expanded - Whether the folder starts expanded
|
|
1194
|
+
* @returns A folder configuration object
|
|
1195
|
+
*
|
|
1196
|
+
* @example
|
|
1197
|
+
* ```typescript
|
|
1198
|
+
* const controls = tweaks('Settings', {
|
|
1199
|
+
* basic: 42,
|
|
1200
|
+
* advanced: tweaks.folder('Advanced', {
|
|
1201
|
+
* iterations: { value: 4, min: 1, max: 10 },
|
|
1202
|
+
* tolerance: { value: 0.001, min: 0, max: 1, step: 0.001 },
|
|
1203
|
+
* }),
|
|
1204
|
+
* });
|
|
1205
|
+
*
|
|
1206
|
+
* // Access nested values
|
|
1207
|
+
* controls.advanced.iterations(); // Signal<number>
|
|
1208
|
+
* ```
|
|
1209
|
+
*/
|
|
1210
|
+
function folder(name, config, options) {
|
|
1211
|
+
return {
|
|
1212
|
+
__folder: true,
|
|
1213
|
+
name,
|
|
1214
|
+
config,
|
|
1215
|
+
expanded: options?.expanded,
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
// ============================================================================
|
|
1219
|
+
// Main tweaks() function
|
|
1220
|
+
// ============================================================================
|
|
1221
|
+
/**
|
|
1222
|
+
* Creates Tweakpane controls declaratively from any component within an `ngt-canvas`.
|
|
1223
|
+
*
|
|
1224
|
+
* **Prerequisites:**
|
|
1225
|
+
* 1. Add `tweakpaneAnchor` directive to your `ngt-canvas`:
|
|
1226
|
+
* ```html
|
|
1227
|
+
* <ngt-canvas tweakpaneAnchor>
|
|
1228
|
+
* ```
|
|
1229
|
+
* 2. Add `<tweakpane-pane>` somewhere in your scene
|
|
1230
|
+
*
|
|
1231
|
+
* @param folderName - The name of the folder to create/use
|
|
1232
|
+
* @param config - Configuration object defining the controls
|
|
1233
|
+
* @param options - Optional folder options (expanded, etc.)
|
|
1234
|
+
* @returns An object with signals for each control value
|
|
1235
|
+
*
|
|
1236
|
+
* @example
|
|
1237
|
+
* ```typescript
|
|
1238
|
+
* // Basic usage with primitives (creates new signals)
|
|
1239
|
+
* const controls = tweaks('Physics', {
|
|
1240
|
+
* gravity: 9.8,
|
|
1241
|
+
* debug: false,
|
|
1242
|
+
* name: 'World',
|
|
1243
|
+
* });
|
|
1244
|
+
*
|
|
1245
|
+
* // With config objects for more control
|
|
1246
|
+
* const controls = tweaks('Physics', {
|
|
1247
|
+
* gravity: { value: 9.8, min: 0, max: 20, step: 0.1 },
|
|
1248
|
+
* color: { value: '#ff0000', color: true },
|
|
1249
|
+
* mode: { value: 'normal', options: ['normal', 'debug', 'verbose'] },
|
|
1250
|
+
* });
|
|
1251
|
+
*
|
|
1252
|
+
* // Two-way binding with existing signals
|
|
1253
|
+
* filteringEnabled = signal(true);
|
|
1254
|
+
* const controls = tweaks('Settings', {
|
|
1255
|
+
* filteringEnabled: this.filteringEnabled, // two-way binding
|
|
1256
|
+
* });
|
|
1257
|
+
*
|
|
1258
|
+
* // Buttons (actions)
|
|
1259
|
+
* const controls = tweaks('Actions', {
|
|
1260
|
+
* reset: { action: () => this.reset() },
|
|
1261
|
+
* });
|
|
1262
|
+
*
|
|
1263
|
+
* // Nested folders
|
|
1264
|
+
* const controls = tweaks('Settings', {
|
|
1265
|
+
* basic: 42,
|
|
1266
|
+
* advanced: tweaks.folder('Advanced', {
|
|
1267
|
+
* iterations: { value: 4, min: 1, max: 10 },
|
|
1268
|
+
* }),
|
|
1269
|
+
* });
|
|
1270
|
+
* ```
|
|
1271
|
+
*/
|
|
1272
|
+
function tweaks(folderName, config, { injector, ...options } = {}) {
|
|
1273
|
+
return assertInjector(tweaks, injector, () => {
|
|
1274
|
+
const maybeAnchor = inject(TweakpaneAnchor, { optional: true });
|
|
1275
|
+
const assertedInjector = inject(Injector);
|
|
1276
|
+
const destroyRef = inject(DestroyRef);
|
|
1277
|
+
if (!maybeAnchor) {
|
|
1278
|
+
throw new Error('[NGT Tweakpane] tweaks() requires TweakpaneAnchor directive. Add `tweakpaneAnchor` to your ngt-canvas element.');
|
|
1279
|
+
}
|
|
1280
|
+
const anchor = maybeAnchor;
|
|
1281
|
+
const result = {};
|
|
1282
|
+
const createdComponents = [];
|
|
1283
|
+
// Process config and create controls when pane folder is ready
|
|
1284
|
+
effect((onCleanup) => {
|
|
1285
|
+
const paneFolder = anchor.paneFolder();
|
|
1286
|
+
if (!paneFolder)
|
|
1287
|
+
return;
|
|
1288
|
+
// Get or create folder for this tweaks() call
|
|
1289
|
+
let folderRef = anchor.folders[folderName];
|
|
1290
|
+
let folderInstance;
|
|
1291
|
+
if (!folderRef) {
|
|
1292
|
+
// Create folder using the directives approach
|
|
1293
|
+
folderRef = anchor.vcr.createComponent(TweakpaneAnchorHost, {
|
|
1294
|
+
injector: Injector.create({
|
|
1295
|
+
providers: [{ provide: TweakpaneFolder, useValue: paneFolder }],
|
|
1296
|
+
parent: assertedInjector,
|
|
1297
|
+
}),
|
|
1298
|
+
directives: [
|
|
1299
|
+
{
|
|
1300
|
+
type: TweakpaneFolder,
|
|
1301
|
+
bindings: [
|
|
1302
|
+
inputBinding('title', () => folderName),
|
|
1303
|
+
inputBinding('expanded', () => options?.expanded ?? false),
|
|
1304
|
+
],
|
|
1305
|
+
},
|
|
1306
|
+
],
|
|
1307
|
+
});
|
|
1308
|
+
folderInstance = folderRef.injector.get(TweakpaneFolder);
|
|
1309
|
+
anchor.folders[folderName] = folderRef;
|
|
1310
|
+
createdComponents.push(folderRef);
|
|
1311
|
+
}
|
|
1312
|
+
else {
|
|
1313
|
+
folderInstance = folderRef.injector.get(TweakpaneFolder);
|
|
1314
|
+
}
|
|
1315
|
+
// Process each config entry
|
|
1316
|
+
processConfig(config, result, folderInstance, anchor.vcr, createdComponents, anchor, assertedInjector);
|
|
1317
|
+
onCleanup(() => {
|
|
1318
|
+
// Destroy created components
|
|
1319
|
+
for (const ref of createdComponents) {
|
|
1320
|
+
ref.destroy();
|
|
1321
|
+
}
|
|
1322
|
+
createdComponents.length = 0;
|
|
1323
|
+
});
|
|
1324
|
+
}, { injector: assertedInjector });
|
|
1325
|
+
// Cleanup on destroy
|
|
1326
|
+
destroyRef.onDestroy(() => {
|
|
1327
|
+
for (const ref of createdComponents) {
|
|
1328
|
+
ref.destroy();
|
|
1329
|
+
}
|
|
1330
|
+
});
|
|
1331
|
+
return result;
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Attach the folder helper to tweaks for convenient access.
|
|
1336
|
+
* Allows usage like `tweaks.folder('Name', config)`.
|
|
1337
|
+
*/
|
|
1338
|
+
tweaks.folder = folder;
|
|
1339
|
+
// ============================================================================
|
|
1340
|
+
// Internal: Process config and create components
|
|
1341
|
+
// ============================================================================
|
|
1342
|
+
/**
|
|
1343
|
+
* Processes a configuration object and creates the corresponding Tweakpane controls.
|
|
1344
|
+
* This is an internal function called by `tweaks()` to set up the UI.
|
|
1345
|
+
*
|
|
1346
|
+
* @param config - The configuration object to process
|
|
1347
|
+
* @param result - The result object to populate with signals
|
|
1348
|
+
* @param parentFolder - The parent folder to add controls to
|
|
1349
|
+
* @param vcr - The ViewContainerRef for creating components
|
|
1350
|
+
* @param createdComponents - Array to track created component refs for cleanup
|
|
1351
|
+
* @param anchor - The TweakpaneAnchor for folder management
|
|
1352
|
+
* @param parentInjector - The parent injector for dependency injection
|
|
1353
|
+
*
|
|
1354
|
+
* @internal
|
|
1355
|
+
*/
|
|
1356
|
+
function processConfig(config, result, parentFolder, vcr, createdComponents, anchor, parentInjector) {
|
|
1357
|
+
const keys = Object.keys(config);
|
|
1358
|
+
// Create injector with parent folder provided
|
|
1359
|
+
const folderInjector = Injector.create({
|
|
1360
|
+
providers: [{ provide: TweakpaneFolder, useValue: parentFolder }],
|
|
1361
|
+
parent: parentInjector,
|
|
1362
|
+
});
|
|
1363
|
+
for (const key of keys) {
|
|
1364
|
+
const configValue = config[key];
|
|
1365
|
+
// Handle nested folders
|
|
1366
|
+
if (isFolderConfig(configValue)) {
|
|
1367
|
+
const nestedResult = {};
|
|
1368
|
+
// Get or create nested folder
|
|
1369
|
+
const nestedFolderName = configValue.name;
|
|
1370
|
+
let nestedFolderRef = anchor.folders[nestedFolderName];
|
|
1371
|
+
let nestedFolderInstance;
|
|
1372
|
+
if (!nestedFolderRef) {
|
|
1373
|
+
nestedFolderRef = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1374
|
+
injector: folderInjector,
|
|
1375
|
+
directives: [
|
|
1376
|
+
{
|
|
1377
|
+
type: TweakpaneFolder,
|
|
1378
|
+
bindings: [
|
|
1379
|
+
inputBinding('title', () => nestedFolderName),
|
|
1380
|
+
inputBinding('expanded', () => configValue.expanded ?? false),
|
|
1381
|
+
],
|
|
1382
|
+
},
|
|
1383
|
+
],
|
|
1384
|
+
});
|
|
1385
|
+
nestedFolderInstance = nestedFolderRef.injector.get(TweakpaneFolder);
|
|
1386
|
+
anchor.folders[nestedFolderName] = nestedFolderRef;
|
|
1387
|
+
createdComponents.push(nestedFolderRef);
|
|
1388
|
+
}
|
|
1389
|
+
else {
|
|
1390
|
+
nestedFolderInstance = nestedFolderRef.injector.get(TweakpaneFolder);
|
|
1391
|
+
}
|
|
1392
|
+
processConfig(configValue.config, nestedResult, nestedFolderInstance, vcr, createdComponents, anchor, folderInjector);
|
|
1393
|
+
result[key] = nestedResult;
|
|
1394
|
+
continue;
|
|
1395
|
+
}
|
|
1396
|
+
// Handle buttons (no result signal)
|
|
1397
|
+
if (isButtonConfig(configValue)) {
|
|
1398
|
+
const buttonRef = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1399
|
+
injector: folderInjector,
|
|
1400
|
+
directives: [
|
|
1401
|
+
{
|
|
1402
|
+
type: TweakpaneButton,
|
|
1403
|
+
bindings: [
|
|
1404
|
+
inputBinding('title', () => key),
|
|
1405
|
+
...(configValue.label ? [inputBinding('label', () => configValue.label)] : []),
|
|
1406
|
+
],
|
|
1407
|
+
},
|
|
1408
|
+
],
|
|
1409
|
+
});
|
|
1410
|
+
// Subscribe to click events
|
|
1411
|
+
const buttonInstance = buttonRef.injector.get(TweakpaneButton);
|
|
1412
|
+
buttonInstance.click.subscribe(() => {
|
|
1413
|
+
configValue.action();
|
|
1414
|
+
});
|
|
1415
|
+
createdComponents.push(buttonRef);
|
|
1416
|
+
continue;
|
|
1417
|
+
}
|
|
1418
|
+
// Handle direct WritableSignal (two-way binding)
|
|
1419
|
+
if (isSignal(configValue)) {
|
|
1420
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1421
|
+
const untrackedValue = untracked(configValue);
|
|
1422
|
+
const valueType = typeof untrackedValue;
|
|
1423
|
+
if (valueType === 'number') {
|
|
1424
|
+
const ref = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1425
|
+
injector: folderInjector,
|
|
1426
|
+
directives: [
|
|
1427
|
+
{
|
|
1428
|
+
type: TweakpaneNumber,
|
|
1429
|
+
bindings: [
|
|
1430
|
+
inputBinding('label', () => key),
|
|
1431
|
+
twoWayBinding('value', configValue),
|
|
1432
|
+
],
|
|
1433
|
+
},
|
|
1434
|
+
],
|
|
1435
|
+
});
|
|
1436
|
+
createdComponents.push(ref);
|
|
1437
|
+
result[key] = configValue.asReadonly();
|
|
1438
|
+
}
|
|
1439
|
+
else if (valueType === 'string') {
|
|
1440
|
+
const ref = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1441
|
+
injector: folderInjector,
|
|
1442
|
+
directives: [
|
|
1443
|
+
{
|
|
1444
|
+
type: TweakpaneText,
|
|
1445
|
+
bindings: [
|
|
1446
|
+
inputBinding('label', () => key),
|
|
1447
|
+
twoWayBinding('value', configValue),
|
|
1448
|
+
],
|
|
1449
|
+
},
|
|
1450
|
+
],
|
|
1451
|
+
});
|
|
1452
|
+
createdComponents.push(ref);
|
|
1453
|
+
result[key] = configValue.asReadonly();
|
|
1454
|
+
}
|
|
1455
|
+
else if (valueType === 'boolean') {
|
|
1456
|
+
const ref = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1457
|
+
injector: folderInjector,
|
|
1458
|
+
directives: [
|
|
1459
|
+
{
|
|
1460
|
+
type: TweakpaneCheckbox,
|
|
1461
|
+
bindings: [
|
|
1462
|
+
inputBinding('label', () => key),
|
|
1463
|
+
twoWayBinding('value', configValue),
|
|
1464
|
+
],
|
|
1465
|
+
},
|
|
1466
|
+
],
|
|
1467
|
+
});
|
|
1468
|
+
createdComponents.push(ref);
|
|
1469
|
+
result[key] = configValue.asReadonly();
|
|
1470
|
+
}
|
|
1471
|
+
continue;
|
|
1472
|
+
}
|
|
1473
|
+
// Handle primitive values (create new signal)
|
|
1474
|
+
if (typeof configValue === 'number') {
|
|
1475
|
+
const valueSignal = signal(configValue, ...(ngDevMode ? [{ debugName: "valueSignal" }] : []));
|
|
1476
|
+
const ref = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1477
|
+
injector: folderInjector,
|
|
1478
|
+
directives: [
|
|
1479
|
+
{
|
|
1480
|
+
type: TweakpaneNumber,
|
|
1481
|
+
bindings: [inputBinding('label', () => key), twoWayBinding('value', valueSignal)],
|
|
1482
|
+
},
|
|
1483
|
+
],
|
|
1484
|
+
});
|
|
1485
|
+
createdComponents.push(ref);
|
|
1486
|
+
result[key] = valueSignal.asReadonly();
|
|
1487
|
+
continue;
|
|
1488
|
+
}
|
|
1489
|
+
if (typeof configValue === 'string') {
|
|
1490
|
+
const valueSignal = signal(configValue, ...(ngDevMode ? [{ debugName: "valueSignal" }] : []));
|
|
1491
|
+
const ref = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1492
|
+
injector: folderInjector,
|
|
1493
|
+
directives: [
|
|
1494
|
+
{
|
|
1495
|
+
type: TweakpaneText,
|
|
1496
|
+
bindings: [inputBinding('label', () => key), twoWayBinding('value', valueSignal)],
|
|
1497
|
+
},
|
|
1498
|
+
],
|
|
1499
|
+
});
|
|
1500
|
+
createdComponents.push(ref);
|
|
1501
|
+
result[key] = valueSignal.asReadonly();
|
|
1502
|
+
continue;
|
|
1503
|
+
}
|
|
1504
|
+
if (typeof configValue === 'boolean') {
|
|
1505
|
+
const valueSignal = signal(configValue, ...(ngDevMode ? [{ debugName: "valueSignal" }] : []));
|
|
1506
|
+
const ref = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1507
|
+
injector: folderInjector,
|
|
1508
|
+
directives: [
|
|
1509
|
+
{
|
|
1510
|
+
type: TweakpaneCheckbox,
|
|
1511
|
+
bindings: [inputBinding('label', () => key), twoWayBinding('value', valueSignal)],
|
|
1512
|
+
},
|
|
1513
|
+
],
|
|
1514
|
+
});
|
|
1515
|
+
createdComponents.push(ref);
|
|
1516
|
+
result[key] = valueSignal.asReadonly();
|
|
1517
|
+
continue;
|
|
1518
|
+
}
|
|
1519
|
+
// Handle config objects
|
|
1520
|
+
if (isColorConfig(configValue)) {
|
|
1521
|
+
const initialValue = isSignal(configValue.value)
|
|
1522
|
+
? untracked(configValue.value)
|
|
1523
|
+
: configValue.value;
|
|
1524
|
+
const valueSignal = isSignal(configValue.value) ? configValue.value : signal(initialValue);
|
|
1525
|
+
const ref = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1526
|
+
injector: folderInjector,
|
|
1527
|
+
directives: [
|
|
1528
|
+
{
|
|
1529
|
+
type: TweakpaneColor,
|
|
1530
|
+
bindings: [inputBinding('label', () => key), twoWayBinding('value', valueSignal)],
|
|
1531
|
+
},
|
|
1532
|
+
],
|
|
1533
|
+
});
|
|
1534
|
+
createdComponents.push(ref);
|
|
1535
|
+
result[key] = valueSignal.asReadonly();
|
|
1536
|
+
continue;
|
|
1537
|
+
}
|
|
1538
|
+
if (isListConfig(configValue)) {
|
|
1539
|
+
const initialValue = isSignal(configValue.value) ? untracked(configValue.value) : configValue.value;
|
|
1540
|
+
const valueSignal = isSignal(configValue.value)
|
|
1541
|
+
? configValue.value
|
|
1542
|
+
: signal(initialValue);
|
|
1543
|
+
const ref = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1544
|
+
injector: folderInjector,
|
|
1545
|
+
directives: [
|
|
1546
|
+
{
|
|
1547
|
+
type: TweakpaneList,
|
|
1548
|
+
bindings: [
|
|
1549
|
+
inputBinding('label', () => key),
|
|
1550
|
+
inputBinding('options', () => configValue.options),
|
|
1551
|
+
twoWayBinding('value', valueSignal),
|
|
1552
|
+
],
|
|
1553
|
+
},
|
|
1554
|
+
],
|
|
1555
|
+
});
|
|
1556
|
+
createdComponents.push(ref);
|
|
1557
|
+
result[key] = valueSignal.asReadonly();
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
if (isPointConfig(configValue)) {
|
|
1561
|
+
const initialValue = isSignal(configValue.value) ? untracked(configValue.value) : configValue.value;
|
|
1562
|
+
const valueSignal = isSignal(configValue.value) ? configValue.value : signal(initialValue);
|
|
1563
|
+
// Build params from x, y, z, w configs
|
|
1564
|
+
const params = {};
|
|
1565
|
+
if (configValue.x)
|
|
1566
|
+
params.x = configValue.x;
|
|
1567
|
+
if (configValue.y)
|
|
1568
|
+
params.y = configValue.y;
|
|
1569
|
+
if (configValue.z)
|
|
1570
|
+
params.z = configValue.z;
|
|
1571
|
+
if (configValue.w)
|
|
1572
|
+
params.w = configValue.w;
|
|
1573
|
+
const ref = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1574
|
+
injector: folderInjector,
|
|
1575
|
+
directives: [
|
|
1576
|
+
{
|
|
1577
|
+
type: TweakpanePoint,
|
|
1578
|
+
bindings: [
|
|
1579
|
+
inputBinding('label', () => key),
|
|
1580
|
+
inputBinding('params', () => params),
|
|
1581
|
+
twoWayBinding('value', valueSignal),
|
|
1582
|
+
],
|
|
1583
|
+
},
|
|
1584
|
+
],
|
|
1585
|
+
});
|
|
1586
|
+
createdComponents.push(ref);
|
|
1587
|
+
result[key] = valueSignal.asReadonly();
|
|
1588
|
+
continue;
|
|
1589
|
+
}
|
|
1590
|
+
if (isNumberConfig(configValue)) {
|
|
1591
|
+
const initialValue = isSignal(configValue.value)
|
|
1592
|
+
? untracked(configValue.value)
|
|
1593
|
+
: configValue.value;
|
|
1594
|
+
const valueSignal = isSignal(configValue.value) ? configValue.value : signal(initialValue);
|
|
1595
|
+
// Build params
|
|
1596
|
+
const params = {};
|
|
1597
|
+
if (configValue.min !== undefined)
|
|
1598
|
+
params.min = configValue.min;
|
|
1599
|
+
if (configValue.max !== undefined)
|
|
1600
|
+
params.max = configValue.max;
|
|
1601
|
+
if (configValue.step !== undefined)
|
|
1602
|
+
params.step = configValue.step;
|
|
1603
|
+
const ref = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1604
|
+
injector: folderInjector,
|
|
1605
|
+
directives: [
|
|
1606
|
+
{
|
|
1607
|
+
type: TweakpaneNumber,
|
|
1608
|
+
bindings: [
|
|
1609
|
+
inputBinding('label', () => key),
|
|
1610
|
+
inputBinding('params', () => params),
|
|
1611
|
+
twoWayBinding('value', valueSignal),
|
|
1612
|
+
],
|
|
1613
|
+
},
|
|
1614
|
+
],
|
|
1615
|
+
});
|
|
1616
|
+
createdComponents.push(ref);
|
|
1617
|
+
result[key] = valueSignal.asReadonly();
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
if (isCheckboxConfig(configValue)) {
|
|
1621
|
+
const initialValue = isSignal(configValue.value)
|
|
1622
|
+
? untracked(configValue.value)
|
|
1623
|
+
: configValue.value;
|
|
1624
|
+
const valueSignal = isSignal(configValue.value) ? configValue.value : signal(initialValue);
|
|
1625
|
+
const ref = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1626
|
+
injector: folderInjector,
|
|
1627
|
+
directives: [
|
|
1628
|
+
{
|
|
1629
|
+
type: TweakpaneCheckbox,
|
|
1630
|
+
bindings: [inputBinding('label', () => key), twoWayBinding('value', valueSignal)],
|
|
1631
|
+
},
|
|
1632
|
+
],
|
|
1633
|
+
});
|
|
1634
|
+
createdComponents.push(ref);
|
|
1635
|
+
result[key] = valueSignal.asReadonly();
|
|
1636
|
+
continue;
|
|
1637
|
+
}
|
|
1638
|
+
if (isTextConfig(configValue)) {
|
|
1639
|
+
const initialValue = isSignal(configValue.value)
|
|
1640
|
+
? untracked(configValue.value)
|
|
1641
|
+
: configValue.value;
|
|
1642
|
+
const valueSignal = isSignal(configValue.value) ? configValue.value : signal(initialValue);
|
|
1643
|
+
const ref = vcr.createComponent(TweakpaneAnchorHost, {
|
|
1644
|
+
injector: folderInjector,
|
|
1645
|
+
directives: [
|
|
1646
|
+
{
|
|
1647
|
+
type: TweakpaneText,
|
|
1648
|
+
bindings: [inputBinding('label', () => key), twoWayBinding('value', valueSignal)],
|
|
1649
|
+
},
|
|
1650
|
+
],
|
|
1651
|
+
});
|
|
1652
|
+
createdComponents.push(ref);
|
|
1653
|
+
result[key] = valueSignal.asReadonly();
|
|
1654
|
+
continue;
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
552
1658
|
|
|
553
1659
|
/**
|
|
554
1660
|
* Generated bundle index. Do not edit.
|
|
555
1661
|
*/
|
|
556
1662
|
|
|
557
|
-
export { NGT_TWEAK_BINDING_AS_HOST, TweakpaneBinding, TweakpaneBlade, TweakpaneButton, TweakpaneCheckbox, TweakpaneColor, TweakpaneDebounce, TweakpaneFolder, TweakpaneLabel, TweakpaneList, TweakpaneNumber, TweakpanePane, TweakpanePoint, TweakpaneText, TweakpaneTitle, provideTweakBindingAsHost };
|
|
1663
|
+
export { NGT_TWEAK_BINDING_AS_HOST, TweakpaneAnchor, TweakpaneAnchorHost, TweakpaneBinding, TweakpaneBlade, TweakpaneButton, TweakpaneCheckbox, TweakpaneColor, TweakpaneDebounce, TweakpaneFolder, TweakpaneLabel, TweakpaneList, TweakpaneNumber, TweakpanePane, TweakpanePoint, TweakpaneText, TweakpaneTitle, provideTweakBindingAsHost, tweaks };
|
|
558
1664
|
//# sourceMappingURL=angular-three-tweakpane.mjs.map
|