babylon.quarks-editor 0.26.0 → 0.28.0
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/CHANGELOG.md +134 -0
- package/README.md +69 -9
- package/dist/core/assetDragSession.d.ts +1 -1
- package/dist/core/assetTree.d.ts +6 -1
- package/dist/core/assets.d.ts +7 -0
- package/dist/core/assets.js +11 -7
- package/dist/core/binding.d.ts +11 -2
- package/dist/core/binding.js +9 -1
- package/dist/core/collision.d.ts +4 -0
- package/dist/core/collision.js +4 -0
- package/dist/core/colors.d.ts +16 -0
- package/dist/core/colors.js +8 -0
- package/dist/core/defaultEffect.d.ts +5 -4
- package/dist/core/defaultEffect.js +4 -4
- package/dist/core/editorScene.d.ts +1 -1
- package/dist/core/editorSession.d.ts +229 -0
- package/dist/core/editorSession.js +983 -0
- package/dist/core/effectTree.d.ts +6 -1
- package/dist/core/effectTree.js +13 -88
- package/dist/core/emitterShapeWireframe.d.ts +2 -2
- package/dist/core/emitterShapeWireframe.js +45 -13
- package/dist/core/galleryDropMarker.d.ts +2 -2
- package/dist/core/galleryDropMarker.js +6 -6
- package/dist/core/galleryLayout.d.ts +3 -3
- package/dist/core/galleryLayout.js +1 -1
- package/dist/core/galleryPlayback.d.ts +1 -1
- package/dist/core/galleryPlayback.js +2 -1
- package/dist/core/galleryScene.d.ts +4 -4
- package/dist/core/galleryScene.js +3 -3
- package/dist/core/gallerySelectionMarker.d.ts +4 -4
- package/dist/core/gallerySelectionMarker.js +5 -5
- package/dist/core/groups.d.ts +1 -1
- package/dist/core/groups.js +1 -1
- package/dist/core/history.d.ts +7 -1
- package/dist/core/history.js +4 -0
- package/dist/core/indexedDbProjectStore.d.ts +14 -1
- package/dist/core/indexedDbProjectStore.js +13 -0
- package/dist/core/lineShaders.d.ts +2 -2
- package/dist/core/lineShaders.js +2 -2
- package/dist/core/loadEffect.d.ts +2 -2
- package/dist/core/loadEffect.js +1 -1
- package/dist/core/material.d.ts +12 -2
- package/dist/core/pickAssetFiles.js +1 -1
- package/dist/core/playback.d.ts +6 -0
- package/dist/core/projectStore.d.ts +26 -0
- package/dist/core/projectStore.js +1 -0
- package/dist/core/sceneEntries.d.ts +7 -4
- package/dist/core/sceneEntries.js +2 -2
- package/dist/core/scrub.d.ts +2 -2
- package/dist/core/scrub.js +2 -2
- package/dist/core/shapes.d.ts +25 -1
- package/dist/core/shapes.js +5 -0
- package/dist/core/systems.d.ts +4 -2
- package/dist/core/systems.js +3 -1
- package/dist/core/textureExport.d.ts +1 -1
- package/dist/core/timeline.d.ts +25 -5
- package/dist/core/transport.d.ts +3 -3
- package/dist/core/transport.js +2 -2
- package/dist/core/values.d.ts +21 -4
- package/dist/core/values.js +5 -0
- package/dist/core/viewportPicker.d.ts +4 -4
- package/dist/core/viewportPicker.js +8 -7
- package/dist/index.d.ts +25 -21
- package/dist/index.js +12 -12
- package/dist/react/AssetsPanel.d.ts +9 -2
- package/dist/react/AssetsPanel.js +6 -6
- package/dist/react/ChoiceDialog.d.ts +7 -0
- package/dist/react/ChoiceDialog.js +1 -1
- package/dist/react/ContextMenu.d.ts +10 -1
- package/dist/react/ContextMenu.js +1 -1
- package/dist/react/CurveEditor.d.ts +1 -1
- package/dist/react/CurveEditor.js +2 -2
- package/dist/react/EditorWorkspace.d.ts +9 -2
- package/dist/react/EditorWorkspace.js +10 -7
- package/dist/react/EffectEditor.d.ts +11 -5
- package/dist/react/EffectEditor.js +7 -7
- package/dist/react/EffectEditorHost.d.ts +33 -29
- package/dist/react/EffectEditorHost.js +247 -1674
- package/dist/react/EffectTreeView.d.ts +4 -1
- package/dist/react/EffectTreeView.js +3 -3
- package/dist/react/GradientEditor.d.ts +1 -1
- package/dist/react/GradientEditor.js +3 -3
- package/dist/react/InspectorInfoIcon.js +3 -3
- package/dist/react/InspectorLabel.js +3 -3
- package/dist/react/MessageDialog.d.ts +5 -0
- package/dist/react/MessageDialog.js +1 -1
- package/dist/react/ModuleSection.d.ts +10 -0
- package/dist/react/ModuleSection.js +8 -5
- package/dist/react/ObjectModule.d.ts +1 -1
- package/dist/react/ObjectModule.js +4 -4
- package/dist/react/ProjectsPage.d.ts +7 -1
- package/dist/react/ProjectsPage.js +33 -17
- package/dist/react/PromptDialog.d.ts +7 -0
- package/dist/react/PromptDialog.js +1 -1
- package/dist/react/ScenePanel.d.ts +9 -2
- package/dist/react/ScenePanel.js +12 -10
- package/dist/react/SubEmittersModule.d.ts +1 -1
- package/dist/react/SubEmittersModule.js +5 -5
- package/dist/react/TextureSelect.d.ts +24 -0
- package/dist/react/TextureSelect.js +74 -0
- package/dist/react/TextureUVField.d.ts +14 -0
- package/dist/react/TextureUVField.js +23 -0
- package/dist/react/TimelinePanel.d.ts +20 -7
- package/dist/react/TimelinePanel.js +21 -15
- package/dist/react/TimelineTrackRow.d.ts +13 -3
- package/dist/react/TimelineTrackRow.js +6 -6
- package/dist/react/TransportBar.d.ts +13 -4
- package/dist/react/TransportBar.js +3 -3
- package/dist/react/ValueField.d.ts +32 -5
- package/dist/react/ValueField.js +22 -11
- package/dist/react/behaviorModules.d.ts +19 -6
- package/dist/react/behaviorModules.js +68 -17
- package/dist/react/fields.d.ts +9 -0
- package/dist/react/fields.js +6 -3
- package/dist/react/host/BottomDocks.d.ts +36 -0
- package/dist/react/host/BottomDocks.js +63 -0
- package/dist/react/host/HostDialogs.d.ts +50 -0
- package/dist/react/host/HostDialogs.js +113 -0
- package/dist/react/host/InspectorSidebar.d.ts +32 -0
- package/dist/react/host/InspectorSidebar.js +59 -0
- package/dist/react/host/SceneSidebar.d.ts +53 -0
- package/dist/react/host/SceneSidebar.js +62 -0
- package/dist/react/host/ViewportPane.d.ts +56 -0
- package/dist/react/host/ViewportPane.js +91 -0
- package/dist/react/host/hostHooks.d.ts +44 -0
- package/dist/react/host/hostHooks.js +164 -0
- package/dist/react/host/hostStyles.d.ts +21 -0
- package/dist/react/host/hostStyles.js +57 -0
- package/dist/react/index.d.ts +42 -38
- package/dist/react/index.js +25 -23
- package/dist/react/inspectorHints.js +31 -0
- package/dist/react/modules.d.ts +19 -6
- package/dist/react/modules.js +134 -117
- package/dist/react/theme.d.ts +15 -0
- package/dist/react/theme.js +15 -0
- package/dist/session.d.ts +9 -0
- package/dist/session.js +8 -0
- package/package.json +18 -11
- package/src/core/assetDragSession.ts +1 -1
- package/src/core/assetTree.ts +15 -2
- package/src/core/assets.ts +25 -10
- package/src/core/binding.ts +12 -3
- package/src/core/collision.ts +4 -0
- package/src/core/colors.ts +21 -1
- package/src/core/defaultEffect.ts +8 -7
- package/src/core/editorScene.ts +1 -1
- package/src/core/editorSession.ts +1299 -0
- package/src/core/effectTree.ts +18 -101
- package/src/core/emitterShapeWireframe.ts +48 -16
- package/src/core/galleryDropMarker.ts +8 -8
- package/src/core/galleryLayout.ts +4 -4
- package/src/core/galleryPlayback.ts +3 -2
- package/src/core/galleryScene.ts +5 -5
- package/src/core/gallerySelectionMarker.ts +7 -7
- package/src/core/groups.ts +1 -1
- package/src/core/history.ts +7 -1
- package/src/core/indexedDbProjectStore.ts +14 -1
- package/src/core/lineShaders.ts +2 -2
- package/src/core/loadEffect.ts +4 -4
- package/src/core/material.ts +14 -4
- package/src/core/pickAssetFiles.ts +2 -2
- package/src/core/playback.ts +6 -0
- package/src/core/projectStore.ts +26 -0
- package/src/core/sceneEntries.ts +8 -5
- package/src/core/scrub.ts +3 -3
- package/src/core/shapes.ts +29 -2
- package/src/core/systems.ts +11 -4
- package/src/core/textureExport.ts +1 -1
- package/src/core/timeline.ts +43 -18
- package/src/core/transport.ts +4 -4
- package/src/core/values.ts +20 -4
- package/src/core/viewportPicker.ts +12 -11
- package/src/index.ts +25 -21
- package/src/react/AssetsPanel.tsx +13 -6
- package/src/react/ChoiceDialog.tsx +8 -1
- package/src/react/ContextMenu.tsx +11 -2
- package/src/react/CurveEditor.tsx +2 -2
- package/src/react/EditorWorkspace.tsx +16 -10
- package/src/react/EffectEditor.tsx +17 -9
- package/src/react/EffectEditorHost.tsx +415 -2354
- package/src/react/EffectTreeView.tsx +7 -4
- package/src/react/GradientEditor.tsx +3 -3
- package/src/react/InspectorInfoIcon.tsx +3 -3
- package/src/react/InspectorLabel.tsx +3 -3
- package/src/react/MessageDialog.tsx +6 -1
- package/src/react/ModuleSection.tsx +15 -4
- package/src/react/ObjectModule.tsx +5 -5
- package/src/react/ProjectsPage.tsx +44 -17
- package/src/react/PromptDialog.tsx +8 -1
- package/src/react/ScenePanel.tsx +21 -12
- package/src/react/SubEmittersModule.tsx +6 -6
- package/src/react/TextureSelect.tsx +118 -0
- package/src/react/TextureUVField.tsx +62 -0
- package/src/react/TimelinePanel.tsx +40 -21
- package/src/react/TimelineTrackRow.tsx +19 -9
- package/src/react/TransportBar.tsx +16 -7
- package/src/react/ValueField.tsx +60 -16
- package/src/react/behaviorModules.tsx +240 -55
- package/src/react/fields.tsx +22 -4
- package/src/react/host/BottomDocks.tsx +178 -0
- package/src/react/host/HostDialogs.tsx +228 -0
- package/src/react/host/InspectorSidebar.tsx +147 -0
- package/src/react/host/SceneSidebar.tsx +227 -0
- package/src/react/host/ViewportPane.tsx +300 -0
- package/src/react/host/hostHooks.ts +198 -0
- package/src/react/host/hostStyles.ts +65 -0
- package/src/react/index.ts +43 -38
- package/src/react/inspectorHints.ts +40 -0
- package/src/react/modules.tsx +440 -170
- package/src/react/theme.ts +15 -0
- package/src/session.ts +17 -0
package/src/core/projectStore.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/** One editor project: a library of effect assets plus the scene built from them. */
|
|
2
2
|
export interface ProjectMeta {
|
|
3
|
+
/** The project's key in its store. */
|
|
3
4
|
id: string;
|
|
5
|
+
/** The project's display name. */
|
|
4
6
|
name: string;
|
|
7
|
+
/** When the project was created, in milliseconds since the epoch. */
|
|
5
8
|
createdAt: number;
|
|
9
|
+
/** When the project last changed, in milliseconds since the epoch. */
|
|
6
10
|
updatedAt: number;
|
|
11
|
+
/** How many effect assets the project holds. */
|
|
7
12
|
assetCount: number;
|
|
8
13
|
/** Approximate bytes the project takes in storage (JSON + texture files). */
|
|
9
14
|
sizeBytes: number;
|
|
@@ -13,26 +18,35 @@ export interface ProjectMeta {
|
|
|
13
18
|
export interface StoredAsset {
|
|
14
19
|
/** Relative path inside the project (`Fire/Explosion.json`) — the asset's key. */
|
|
15
20
|
path: string;
|
|
21
|
+
/** The asset's display name, its file name without `.json`. */
|
|
16
22
|
name: string;
|
|
23
|
+
/** The effect, as quarks JSON with its textures inlined. */
|
|
17
24
|
json: unknown;
|
|
25
|
+
/** Files the effect referenced that were not imported with it. */
|
|
18
26
|
missingFiles: string[];
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
/** One effect instance in a saved scene. */
|
|
22
30
|
export interface StoredSceneEntry {
|
|
31
|
+
/** The instance's name in the Scene list. */
|
|
23
32
|
name: string;
|
|
24
33
|
/** Path of the asset it was made from, if any. */
|
|
25
34
|
assetPath: string | null;
|
|
26
35
|
/** The instance's own (possibly edited) effect, root transform included. */
|
|
27
36
|
json: unknown;
|
|
37
|
+
/** Whether the instance was shown. */
|
|
28
38
|
visible: boolean;
|
|
29
39
|
}
|
|
30
40
|
|
|
41
|
+
/** A project's saved scene: the effects placed in it, in Scene list order. */
|
|
31
42
|
export interface StoredScene {
|
|
43
|
+
/** The scene's effect instances. */
|
|
32
44
|
entries: StoredSceneEntry[];
|
|
45
|
+
/** When it was saved, in milliseconds since the epoch. */
|
|
33
46
|
savedAt: number;
|
|
34
47
|
}
|
|
35
48
|
|
|
49
|
+
/** How much storage projects use, as far as the browser tells. */
|
|
36
50
|
export interface StorageEstimate {
|
|
37
51
|
/** Bytes the whole site uses (all projects), when the browser reports it. */
|
|
38
52
|
usage: number | null;
|
|
@@ -47,26 +61,38 @@ export interface StorageEstimate {
|
|
|
47
61
|
* everything in the browser, and an embedding host can supply its own (e.g. on disk).
|
|
48
62
|
*/
|
|
49
63
|
export interface ProjectStore {
|
|
64
|
+
/** Every project, most recently changed first. */
|
|
50
65
|
listProjects(): Promise<ProjectMeta[]>;
|
|
66
|
+
/** One project, or null when there is none with that id. */
|
|
51
67
|
getProject(id: string): Promise<ProjectMeta | null>;
|
|
68
|
+
/** Creates an empty project. */
|
|
52
69
|
createProject(name: string): Promise<ProjectMeta>;
|
|
70
|
+
/** Renames a project. */
|
|
53
71
|
renameProject(id: string, name: string): Promise<void>;
|
|
72
|
+
/** Copies a project, its assets and its scene, under a new name. */
|
|
54
73
|
duplicateProject(id: string, name: string): Promise<ProjectMeta>;
|
|
74
|
+
/** Deletes a project, its assets and its scene. */
|
|
55
75
|
deleteProject(id: string): Promise<void>;
|
|
56
76
|
|
|
77
|
+
/** A project's assets. */
|
|
57
78
|
loadAssets(projectId: string): Promise<StoredAsset[]>;
|
|
58
79
|
/** Adds or replaces assets by path. */
|
|
59
80
|
putAssets(projectId: string, assets: StoredAsset[]): Promise<void>;
|
|
81
|
+
/** Deletes assets by path; paths it does not have are ignored. */
|
|
60
82
|
deleteAssets(projectId: string, paths: string[]): Promise<void>;
|
|
61
83
|
|
|
84
|
+
/** A project's saved scene, or null when it was never saved. */
|
|
62
85
|
loadScene(projectId: string): Promise<StoredScene | null>;
|
|
86
|
+
/** Replaces a project's saved scene. */
|
|
63
87
|
saveScene(projectId: string, scene: StoredScene): Promise<void>;
|
|
64
88
|
|
|
89
|
+
/** How much storage projects use. */
|
|
65
90
|
estimate(): Promise<StorageEstimate>;
|
|
66
91
|
/** Asks the browser to keep the data even under storage pressure; resolves to the result. */
|
|
67
92
|
requestPersistence(): Promise<boolean>;
|
|
68
93
|
}
|
|
69
94
|
|
|
95
|
+
/** A byte count for people: `512 B`, `1.5 KB`, `20 MB`; `—` when unknown. */
|
|
70
96
|
export function formatBytes(bytes: number | null | undefined): string {
|
|
71
97
|
if (bytes == null) return '—';
|
|
72
98
|
if (bytes < 1024) return `${bytes} B`;
|
package/src/core/sceneEntries.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {TransformNode} from '@babylonjs/core/Meshes/transformNode';
|
|
2
|
-
import type {Scene} from '@babylonjs/core/scene';
|
|
1
|
+
import type {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
|
|
2
|
+
import type {Scene} from '@babylonjs/core/scene.js';
|
|
3
3
|
import type {BatchedRenderer, ParticleEmitter, ParticleSystem} from 'babylon.quarks';
|
|
4
4
|
import {QuarksUtil} from 'babylon.quarks';
|
|
5
|
-
import type {EffectAsset} from './assets';
|
|
6
|
-
import {EffectBinding} from './binding';
|
|
7
|
-
import {disposeLoadedEffect, parseEffectFromJson} from './loadEffect';
|
|
5
|
+
import type {EffectAsset} from './assets.js';
|
|
6
|
+
import {EffectBinding} from './binding.js';
|
|
7
|
+
import {disposeLoadedEffect, parseEffectFromJson} from './loadEffect.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* One effect instance placed in the scene. Every instance is live (its systems are registered
|
|
@@ -12,12 +12,15 @@ import {disposeLoadedEffect, parseEffectFromJson} from './loadEffect';
|
|
|
12
12
|
* that are edited separately.
|
|
13
13
|
*/
|
|
14
14
|
export interface SceneEntry {
|
|
15
|
+
/** The instance's name in the Scene list, unique in the scene. */
|
|
15
16
|
name: string;
|
|
16
17
|
/** Path of the asset it came from, or a synthetic `_created/…` path for new effects. */
|
|
17
18
|
path: string;
|
|
18
19
|
/** Asset this instance was made from, if any. */
|
|
19
20
|
assetId: number | null;
|
|
21
|
+
/** The instance's root node. */
|
|
20
22
|
root: TransformNode;
|
|
23
|
+
/** Every particle system of the instance. */
|
|
21
24
|
systems: ParticleSystem[];
|
|
22
25
|
}
|
|
23
26
|
|
package/src/core/scrub.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {BatchedRenderer, ParticleSystem} from 'babylon.quarks';
|
|
2
|
-
import type {EffectBinding} from './binding';
|
|
2
|
+
import type {EffectBinding} from './binding.js';
|
|
3
3
|
|
|
4
4
|
const WIDE_STEP_THRESHOLD = 8;
|
|
5
5
|
|
|
@@ -7,10 +7,10 @@ function bindingSystems(binding: EffectBinding): ParticleSystem[] {
|
|
|
7
7
|
return [binding.system, ...binding.subSystems];
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
/** Unpauses every system in the focused effect. */
|
|
10
|
+
/** Unpauses every system in the focused effect; one that has finished stays finished. */
|
|
11
11
|
export function playFocus(binding: EffectBinding): void {
|
|
12
12
|
for (const system of bindingSystems(binding)) {
|
|
13
|
-
system.
|
|
13
|
+
system.resume();
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
package/src/core/shapes.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {Mesh} from '@babylonjs/core/Meshes/mesh';
|
|
1
|
+
import type {Mesh} from '@babylonjs/core/Meshes/mesh.js';
|
|
2
2
|
import type {EmitterShape} from 'babylon.quarks';
|
|
3
3
|
import {
|
|
4
4
|
BoxEmitFrom,
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
SphereEmitter,
|
|
18
18
|
} from 'babylon.quarks';
|
|
19
19
|
|
|
20
|
+
/** The emitter shapes the Shape module offers, by the `type` they serialise with. */
|
|
20
21
|
export type ShapeType =
|
|
21
22
|
| 'point'
|
|
22
23
|
| 'sphere'
|
|
@@ -30,7 +31,13 @@ export type ShapeType =
|
|
|
30
31
|
| 'edge'
|
|
31
32
|
| 'mesh_surface';
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
/** The Shape module's shape list, in menu order, with the label each shows. */
|
|
35
|
+
export const SHAPE_TYPES: Array<{
|
|
36
|
+
/** The shape's type. */
|
|
37
|
+
value: ShapeType;
|
|
38
|
+
/** Its label in the menu. */
|
|
39
|
+
label: string;
|
|
40
|
+
}> = [
|
|
34
41
|
{value: 'cone', label: 'Cone'},
|
|
35
42
|
{value: 'sphere', label: 'Sphere'},
|
|
36
43
|
{value: 'hemisphere', label: 'Hemisphere'},
|
|
@@ -44,17 +51,32 @@ export const SHAPE_TYPES: Array<{value: ShapeType; label: string}> = [
|
|
|
44
51
|
{value: 'point', label: 'Point'},
|
|
45
52
|
];
|
|
46
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Every shape's settings in one flat record, so switching shapes keeps what the shapes share
|
|
56
|
+
* (a cone's radius becomes the sphere's). Each shape reads the fields it has.
|
|
57
|
+
*/
|
|
47
58
|
export interface ShapeParams {
|
|
59
|
+
/** Radius of a sphere, hemisphere, cone, circle or donut. */
|
|
48
60
|
radius: number;
|
|
61
|
+
/** The part of the circle emitted from, in radians (2π: all of it). */
|
|
49
62
|
arc: number;
|
|
63
|
+
/** 0 emits from the surface only, 1 from the whole volume. */
|
|
50
64
|
thickness: number;
|
|
65
|
+
/** A cone's half angle, in radians. */
|
|
51
66
|
angle: number;
|
|
67
|
+
/** The radius of a donut's tube. */
|
|
52
68
|
donutRadius: number;
|
|
69
|
+
/** Width of a rectangle, grid or box (X). */
|
|
53
70
|
width: number;
|
|
71
|
+
/** Height of a rectangle, grid or box (Y). */
|
|
54
72
|
height: number;
|
|
73
|
+
/** How points around the arc are picked: at random, in a loop, back and forth or in bursts. */
|
|
55
74
|
mode: EmitterMode;
|
|
75
|
+
/** Snaps the arc position to steps of this fraction; 0 for none. */
|
|
56
76
|
spread: number;
|
|
77
|
+
/** A grid's column count. */
|
|
57
78
|
column: number;
|
|
79
|
+
/** A grid's row count. */
|
|
58
80
|
row: number;
|
|
59
81
|
/** Box depth (Z). */
|
|
60
82
|
depth: number;
|
|
@@ -62,9 +84,11 @@ export interface ShapeParams {
|
|
|
62
84
|
emitFrom: BoxEmitFrom;
|
|
63
85
|
/** How fast an edge emitter's loop / ping-pong point travels, in lengths per second. */
|
|
64
86
|
speed: number;
|
|
87
|
+
/** The mesh a mesh surface emitter emits from. */
|
|
65
88
|
mesh?: Mesh;
|
|
66
89
|
}
|
|
67
90
|
|
|
91
|
+
/** The settings a new shape starts from. */
|
|
68
92
|
export const DEFAULT_SHAPE_PARAMS: ShapeParams = {
|
|
69
93
|
radius: 0.3,
|
|
70
94
|
arc: Math.PI * 2,
|
|
@@ -83,11 +107,13 @@ export const DEFAULT_SHAPE_PARAMS: ShapeParams = {
|
|
|
83
107
|
mesh: undefined,
|
|
84
108
|
};
|
|
85
109
|
|
|
110
|
+
/** The type of a shape, as the Shape module lists it. */
|
|
86
111
|
export function getShapeType(shape: EmitterShape): ShapeType {
|
|
87
112
|
const type = (shape as {type?: string}).type ?? 'point';
|
|
88
113
|
return (SHAPE_TYPES.some((s) => s.value === type) ? type : 'point') as ShapeType;
|
|
89
114
|
}
|
|
90
115
|
|
|
116
|
+
/** A shape's settings, the fields it does not have taken from {@link DEFAULT_SHAPE_PARAMS}. */
|
|
91
117
|
export function readShapeParams(shape: EmitterShape): ShapeParams {
|
|
92
118
|
const anyShape = shape as unknown as {[key: string]: unknown};
|
|
93
119
|
const num = (key: string, fallback: number) =>
|
|
@@ -127,6 +153,7 @@ export const SHAPE_PARAM_KEYS: {[K in ShapeType]: Array<keyof ShapeParams>} = {
|
|
|
127
153
|
mesh_surface: [],
|
|
128
154
|
};
|
|
129
155
|
|
|
156
|
+
/** A new shape of `type`, built from the fields of `params` it has. */
|
|
130
157
|
export function createShape(type: ShapeType, params: ShapeParams): EmitterShape {
|
|
131
158
|
switch (type) {
|
|
132
159
|
case 'point':
|
package/src/core/systems.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {TransformNode} from '@babylonjs/core/Meshes/transformNode';
|
|
1
|
+
import type {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
|
|
2
2
|
import {
|
|
3
3
|
ConeEmitter,
|
|
4
4
|
ConstantColor,
|
|
@@ -7,10 +7,13 @@ import {
|
|
|
7
7
|
ParticleSystem,
|
|
8
8
|
RenderMode,
|
|
9
9
|
Vector4,
|
|
10
|
+
type ParticleEmitter,
|
|
10
11
|
} from 'babylon.quarks';
|
|
11
|
-
import type {EffectBinding} from './binding';
|
|
12
|
+
import type {EffectBinding} from './binding.js';
|
|
12
13
|
|
|
14
|
+
/** How {@link createChildSystem} builds a new system. */
|
|
13
15
|
export interface ChildSystemOptions {
|
|
16
|
+
/** The new emitter's name. */
|
|
14
17
|
name?: string;
|
|
15
18
|
/** One-shot burst (sub-emitter target) instead of a looping stream. */
|
|
16
19
|
oneShot?: boolean;
|
|
@@ -61,10 +64,14 @@ export function createChildSystem(parent: ParticleSystem, options: ChildSystemOp
|
|
|
61
64
|
export function unlinkSubEmitterReferences(systems: ParticleSystem[], removed: ParticleSystem[]): void {
|
|
62
65
|
const removedEmitters = new Set(removed.map((system) => system.emitter));
|
|
63
66
|
for (const system of systems) {
|
|
64
|
-
const behaviors = system.behaviors as Array<{type?: string; subParticleSystem?:
|
|
67
|
+
const behaviors = system.behaviors as Array<{type?: string; subParticleSystem?: ParticleEmitter}>;
|
|
65
68
|
for (let i = behaviors.length - 1; i >= 0; i--) {
|
|
66
69
|
const behavior = behaviors[i];
|
|
67
|
-
if (
|
|
70
|
+
if (
|
|
71
|
+
behavior.type === 'EmitSubParticleSystem' &&
|
|
72
|
+
behavior.subParticleSystem &&
|
|
73
|
+
removedEmitters.has(behavior.subParticleSystem)
|
|
74
|
+
) {
|
|
68
75
|
behaviors.splice(i, 1);
|
|
69
76
|
}
|
|
70
77
|
}
|
package/src/core/timeline.ts
CHANGED
|
@@ -1,27 +1,52 @@
|
|
|
1
|
-
import type {TransformNode} from '@babylonjs/core/Meshes/transformNode';
|
|
1
|
+
import type {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
|
|
2
2
|
import type {FunctionValueGenerator, ParticleSystem, ValueGenerator} from 'babylon.quarks';
|
|
3
3
|
import {ConstantValue, IntervalValue} from 'babylon.quarks';
|
|
4
|
-
import type {EffectBinding} from './binding';
|
|
5
|
-
import type {EffectTreeNode} from './effectTree';
|
|
4
|
+
import type {EffectBinding} from './binding.js';
|
|
5
|
+
import type {EffectTreeNode} from './effectTree.js';
|
|
6
6
|
|
|
7
7
|
/** Guards against a literally zero-width ruler (e.g. no tracks yet) — not a usability stretch. */
|
|
8
8
|
const MIN_TIMELINE_SPAN = 0.01;
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
/** A timeline row for a group node: a heading over the tracks of the systems under it. */
|
|
11
|
+
export interface TimelineGroupRow {
|
|
12
|
+
/** Tells group rows from track rows. */
|
|
13
|
+
kind: 'group';
|
|
14
|
+
/** The group's node. */
|
|
15
|
+
node: TransformNode;
|
|
16
|
+
/** The group's name. */
|
|
17
|
+
name: string;
|
|
18
|
+
/** How deep the group sits in the effect tree; the root's children are 0. */
|
|
19
|
+
depth: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A timeline row for one particle system: a bar from its start delay over its duration. */
|
|
23
|
+
export interface TimelineSystemRow {
|
|
24
|
+
/** Tells group rows from track rows. */
|
|
25
|
+
kind: 'track';
|
|
26
|
+
/** The system's emitter node. */
|
|
27
|
+
node: TransformNode;
|
|
28
|
+
/** The system. */
|
|
29
|
+
system: ParticleSystem;
|
|
30
|
+
/** The emitter's name. */
|
|
31
|
+
name: string;
|
|
32
|
+
/** How deep the emitter sits in the effect tree. */
|
|
33
|
+
depth: number;
|
|
34
|
+
/** True for the effect's main system. */
|
|
35
|
+
isRoot: boolean;
|
|
36
|
+
/** False for the one system an effect cannot lose. */
|
|
37
|
+
removable: boolean;
|
|
38
|
+
/** Where the bar starts: the start delay, its lower end when random, in seconds. */
|
|
39
|
+
startOffset: number;
|
|
40
|
+
/** True when the start delay is a curve and the bar's start is only indicative. */
|
|
41
|
+
startOffsetVariable: boolean;
|
|
42
|
+
/** The bar's length, the system's duration in seconds. */
|
|
43
|
+
duration: number;
|
|
44
|
+
/** True when the system loops; its bar runs to the end of the ruler. */
|
|
45
|
+
looping: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** One row of the focused effect's timeline. */
|
|
49
|
+
export type TimelineRow = TimelineGroupRow | TimelineSystemRow;
|
|
25
50
|
|
|
26
51
|
/**
|
|
27
52
|
* Reads a representative start-delay value for display without invoking the generator's
|
package/src/core/transport.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {Scene} from '@babylonjs/core/scene';
|
|
1
|
+
import type {Scene} from '@babylonjs/core/scene.js';
|
|
2
2
|
import type {BatchedRenderer} from 'babylon.quarks';
|
|
3
|
-
import type {EffectBinding} from './binding';
|
|
4
|
-
import {clearFocusFinished, isFocusFinished, setFocusElapsed, type PlaybackState} from './playback';
|
|
5
|
-
import {playFocus, scrubFocusTo} from './scrub';
|
|
3
|
+
import type {EffectBinding} from './binding.js';
|
|
4
|
+
import {clearFocusFinished, isFocusFinished, setFocusElapsed, type PlaybackState} from './playback.js';
|
|
5
|
+
import {playFocus, scrubFocusTo} from './scrub.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Stage Play/Pause shared by the viewport transport bar and the timeline. Pressing Play on a
|
package/src/core/values.ts
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
|
-
import type {FunctionValueGenerator, ValueGenerator} from 'babylon.quarks';
|
|
1
|
+
import type {FunctionValueGenerator, RotationGenerator, ValueGenerator, Vector3Generator} from 'babylon.quarks';
|
|
2
2
|
import {Bezier, ConstantValue, IntervalValue, PiecewiseBezier} from 'babylon.quarks';
|
|
3
3
|
|
|
4
|
+
/** A generator of one number: a constant or random value, or a curve over the particle's life. */
|
|
4
5
|
export type ScalarGenerator = ValueGenerator | FunctionValueGenerator;
|
|
6
|
+
/** Any generator a value field can be pointed at; ones that are not scalar read as the default. */
|
|
7
|
+
export type AnyGenerator = ScalarGenerator | Vector3Generator | RotationGenerator;
|
|
8
|
+
/** How a value field produces its number: Unity's Constant, Random Between Two Constants or Curve. */
|
|
5
9
|
export type ScalarMode = 'constant' | 'random' | 'curve';
|
|
6
10
|
|
|
7
11
|
/** Editable snapshot of a scalar value generator. */
|
|
8
12
|
export interface ScalarValueState {
|
|
13
|
+
/** Which of the fields below is the value. */
|
|
9
14
|
mode: ScalarMode;
|
|
10
15
|
/** Constant value (mode: constant). */
|
|
11
16
|
value: number;
|
|
12
|
-
/**
|
|
17
|
+
/** Lower end of the random range (mode: random). */
|
|
13
18
|
min: number;
|
|
19
|
+
/** Upper end of the random range (mode: random). */
|
|
14
20
|
max: number;
|
|
15
21
|
/** Cubic bezier control values over normalized lifetime (mode: curve). */
|
|
16
22
|
curve: [number, number, number, number];
|
|
17
23
|
}
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
/**
|
|
26
|
+
* What a value field shows for `generator`: its constant, its range or its first curve segment.
|
|
27
|
+
* Anything else, including a missing generator, reads as the constant 1.
|
|
28
|
+
*/
|
|
29
|
+
export function readScalar(generator: AnyGenerator | undefined): ScalarValueState {
|
|
20
30
|
const state: ScalarValueState = {mode: 'constant', value: 1, min: 0.5, max: 1.5, curve: [0, 1, 1, 0]};
|
|
21
31
|
if (generator instanceof ConstantValue) {
|
|
22
32
|
state.mode = 'constant';
|
|
@@ -39,6 +49,9 @@ export function readScalar(generator: ScalarGenerator | undefined): ScalarValueS
|
|
|
39
49
|
return state;
|
|
40
50
|
}
|
|
41
51
|
|
|
52
|
+
/** The generator a value field's state describes; a curve state builds a one-segment curve. */
|
|
53
|
+
export function buildScalar(state: ScalarValueState & {mode: 'curve'}): PiecewiseBezier;
|
|
54
|
+
export function buildScalar(state: ScalarValueState): ScalarGenerator;
|
|
42
55
|
export function buildScalar(state: ScalarValueState): ScalarGenerator {
|
|
43
56
|
switch (state.mode) {
|
|
44
57
|
case 'constant':
|
|
@@ -52,12 +65,14 @@ export function buildScalar(state: ScalarValueState): ScalarGenerator {
|
|
|
52
65
|
|
|
53
66
|
/** One segment of a piecewise bezier curve: start x (0..1) + four control values. */
|
|
54
67
|
export interface CurvePiece {
|
|
68
|
+
/** Where the segment starts, 0..1 over the particle's life. */
|
|
55
69
|
start: number;
|
|
70
|
+
/** The segment's four bezier control values. */
|
|
56
71
|
p: [number, number, number, number];
|
|
57
72
|
}
|
|
58
73
|
|
|
59
74
|
/** Reads pieces from a PiecewiseBezier (or synthesizes one segment for other generators). */
|
|
60
|
-
export function readPieces(generator:
|
|
75
|
+
export function readPieces(generator: AnyGenerator | undefined): CurvePiece[] {
|
|
61
76
|
if (generator instanceof PiecewiseBezier && generator.functions.length > 0) {
|
|
62
77
|
return generator.functions.map(([bezier, start]) => ({
|
|
63
78
|
start,
|
|
@@ -68,6 +83,7 @@ export function readPieces(generator: ScalarGenerator | undefined): CurvePiece[]
|
|
|
68
83
|
return [{start: 0, p: fallback.curve}];
|
|
69
84
|
}
|
|
70
85
|
|
|
86
|
+
/** A piecewise bezier from segments, sorted by their start. */
|
|
71
87
|
export function buildCurve(pieces: CurvePiece[]): PiecewiseBezier {
|
|
72
88
|
const sorted = [...pieces].sort((a, b) => a.start - b.start);
|
|
73
89
|
return new PiecewiseBezier(sorted.map((piece) => [new Bezier(...piece.p), piece.start] as [Bezier, number]));
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import '@babylonjs/core/Culling/ray';
|
|
2
|
-
import {Mesh} from '@babylonjs/core/Meshes/mesh';
|
|
3
|
-
import {MeshBuilder} from '@babylonjs/core/Meshes/meshBuilder';
|
|
4
|
-
import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
|
|
5
|
-
import type {Scene} from '@babylonjs/core/scene';
|
|
6
|
-
import {EDITOR_SCENE_NODE_METADATA} from './editorScene';
|
|
7
|
-
import {GROUND_RING_RADIUS, GalleryHoverMarker} from './gallerySelectionMarker';
|
|
8
|
-
import type {SceneEntry} from './sceneEntries';
|
|
1
|
+
import '@babylonjs/core/Culling/ray.js';
|
|
2
|
+
import {Mesh} from '@babylonjs/core/Meshes/mesh.js';
|
|
3
|
+
import {MeshBuilder} from '@babylonjs/core/Meshes/meshBuilder.js';
|
|
4
|
+
import {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
|
|
5
|
+
import type {Scene} from '@babylonjs/core/scene.js';
|
|
6
|
+
import {EDITOR_SCENE_NODE_METADATA} from './editorScene.js';
|
|
7
|
+
import {GROUND_RING_RADIUS, GalleryHoverMarker} from './gallerySelectionMarker.js';
|
|
8
|
+
import type {SceneEntry} from './sceneEntries.js';
|
|
9
9
|
|
|
10
10
|
interface PickMetadata {
|
|
11
11
|
quarksEditorPick: true;
|
|
@@ -48,9 +48,10 @@ export class GalleryViewportPicker {
|
|
|
48
48
|
} else if (soloRoot && !soloRoot.isDisposed()) {
|
|
49
49
|
this.createHandle(soloRoot);
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
if (this.hoveredRoot?.isDisposed()) {
|
|
52
|
+
this.hoveredRoot = null;
|
|
53
|
+
this.hoverMarker.hide();
|
|
54
|
+
}
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
/** Yellow focus-style ring under the hovered effect root. */
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type {
|
|
3
|
-
export {
|
|
1
|
+
export type {AssetLoadProgress, EffectAsset} from './core/assets.js';
|
|
2
|
+
export type {AssetDragPayload} from './core/assetTree.js';
|
|
3
|
+
export {EffectBinding} from './core/binding.js';
|
|
4
|
+
export type {EditorListener} from './core/binding.js';
|
|
5
|
+
export {GroundPlaneResolver, ensureGroundResolver} from './core/collision.js';
|
|
4
6
|
export {
|
|
5
7
|
DEFAULT_GRADIENT_STOPS,
|
|
6
8
|
buildColorOverLife,
|
|
@@ -11,16 +13,16 @@ export {
|
|
|
11
13
|
rgbaToVector4,
|
|
12
14
|
stopToCss,
|
|
13
15
|
stopsToCssGradient,
|
|
14
|
-
} from './core/colors';
|
|
15
|
-
export type {GradientStop} from './core/colors';
|
|
16
|
-
export {buildEffectTree, collectSystems, serializeEffectForest, serializeEffectTree} from './core/effectTree';
|
|
17
|
-
export type {EffectTreeNode} from './core/effectTree';
|
|
18
|
-
export {EffectHistory} from './core/history';
|
|
19
|
-
export type {HistoryStacks} from './core/history';
|
|
20
|
-
export {IndexedDbProjectStore} from './core/indexedDbProjectStore';
|
|
21
|
-
export {applyRendererMaterial, getMaterialLabel} from './core/material';
|
|
22
|
-
export type {RendererMaterialPatch} from './core/material';
|
|
23
|
-
export {formatBytes} from './core/projectStore';
|
|
16
|
+
} from './core/colors.js';
|
|
17
|
+
export type {GradientStop} from './core/colors.js';
|
|
18
|
+
export {buildEffectTree, collectSystems, serializeEffectForest, serializeEffectTree} from './core/effectTree.js';
|
|
19
|
+
export type {EffectTreeNode} from './core/effectTree.js';
|
|
20
|
+
export {EffectHistory} from './core/history.js';
|
|
21
|
+
export type {HistoryStacks} from './core/history.js';
|
|
22
|
+
export {IndexedDbProjectStore} from './core/indexedDbProjectStore.js';
|
|
23
|
+
export {applyRendererMaterial, getMaterialLabel} from './core/material.js';
|
|
24
|
+
export type {RendererMaterialPatch} from './core/material.js';
|
|
25
|
+
export {formatBytes} from './core/projectStore.js';
|
|
24
26
|
export type {
|
|
25
27
|
ProjectMeta,
|
|
26
28
|
ProjectStore,
|
|
@@ -28,7 +30,8 @@ export type {
|
|
|
28
30
|
StoredAsset,
|
|
29
31
|
StoredScene,
|
|
30
32
|
StoredSceneEntry,
|
|
31
|
-
} from './core/projectStore';
|
|
33
|
+
} from './core/projectStore.js';
|
|
34
|
+
export type {SceneEntry} from './core/sceneEntries.js';
|
|
32
35
|
export {
|
|
33
36
|
DEFAULT_SHAPE_PARAMS,
|
|
34
37
|
SHAPE_PARAM_KEYS,
|
|
@@ -36,10 +39,11 @@ export {
|
|
|
36
39
|
createShape,
|
|
37
40
|
getShapeType,
|
|
38
41
|
readShapeParams,
|
|
39
|
-
} from './core/shapes';
|
|
40
|
-
export type {ShapeParams, ShapeType} from './core/shapes';
|
|
41
|
-
export {createChildSystem} from './core/systems';
|
|
42
|
-
export type {ChildSystemOptions} from './core/systems';
|
|
43
|
-
export {ensurePortableTextureUrl} from './core/textureExport';
|
|
44
|
-
export {
|
|
45
|
-
export
|
|
42
|
+
} from './core/shapes.js';
|
|
43
|
+
export type {ShapeParams, ShapeType} from './core/shapes.js';
|
|
44
|
+
export {createChildSystem} from './core/systems.js';
|
|
45
|
+
export type {ChildSystemOptions} from './core/systems.js';
|
|
46
|
+
export {ensurePortableTextureUrl} from './core/textureExport.js';
|
|
47
|
+
export type {TimelineGroupRow, TimelineRow, TimelineSystemRow} from './core/timeline.js';
|
|
48
|
+
export {buildCurve, buildScalar, mergePieces, readPieces, readScalar, sampleCurve, splitPieces} from './core/values.js';
|
|
49
|
+
export type {AnyGenerator, CurvePiece, ScalarGenerator, ScalarMode, ScalarValueState} from './core/values.js';
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
SparklesIcon,
|
|
8
8
|
} from '@heroicons/react/24/solid';
|
|
9
9
|
import React, {useCallback, useMemo, useRef, useState} from 'react';
|
|
10
|
-
import {setAssetDragSession} from '../core/assetDragSession';
|
|
10
|
+
import {setAssetDragSession} from '../core/assetDragSession.js';
|
|
11
11
|
import {
|
|
12
12
|
buildAssetTree,
|
|
13
13
|
collectFolderAssets,
|
|
@@ -17,25 +17,32 @@ import {
|
|
|
17
17
|
type AssetDragPayload,
|
|
18
18
|
type AssetTreeFolder,
|
|
19
19
|
type AssetTreeNode,
|
|
20
|
-
} from '../core/assetTree';
|
|
21
|
-
import {assetIconUrl, type AssetLoadProgress, type EffectAsset} from '../core/assets';
|
|
22
|
-
import {ContextMenu, type ContextMenuItem} from './ContextMenu';
|
|
23
|
-
import {iconStyle} from './icons';
|
|
24
|
-
import {buttonStyle, hiddenFileInputStyle, inputStyle, theme} from './theme';
|
|
20
|
+
} from '../core/assetTree.js';
|
|
21
|
+
import {assetIconUrl, type AssetLoadProgress, type EffectAsset} from '../core/assets.js';
|
|
22
|
+
import {ContextMenu, type ContextMenuItem} from './ContextMenu.js';
|
|
23
|
+
import {iconStyle} from './icons.js';
|
|
24
|
+
import {buttonStyle, hiddenFileInputStyle, inputStyle, theme} from './theme.js';
|
|
25
25
|
|
|
26
26
|
const TREE_WIDTH = 200;
|
|
27
27
|
const TILE_WIDTH = 92;
|
|
28
28
|
|
|
29
|
+
/** Props of {@link AssetsPanel}. */
|
|
29
30
|
export interface AssetsPanelProps {
|
|
31
|
+
/** The assets, shown as a folder tree by their paths. */
|
|
30
32
|
assets: EffectAsset[];
|
|
31
33
|
/** Scene instance count per asset id. */
|
|
32
34
|
instanceCounts: ReadonlyMap<number, number>;
|
|
35
|
+
/** Import progress while importing; the import buttons are disabled meanwhile. */
|
|
33
36
|
loading: AssetLoadProgress | null;
|
|
37
|
+
/** The hidden folder input, clicked by hosts whose browser has no directory picker. */
|
|
34
38
|
folderInputRef: React.RefObject<HTMLInputElement | null>;
|
|
39
|
+
/** Imports picked files. */
|
|
35
40
|
onImportFiles: (files: FileList | File[]) => void;
|
|
41
|
+
/** Opens a folder to import. */
|
|
36
42
|
onImportFolder: () => void;
|
|
37
43
|
/** Adds a new instance of each asset in the payload to the scene. */
|
|
38
44
|
onAddToScene: (payload: AssetDragPayload) => void;
|
|
45
|
+
/** Deletes the assets in the payload. */
|
|
39
46
|
onDelete: (payload: AssetDragPayload) => void;
|
|
40
47
|
/** Opens the asset on its own to edit it (double-click). Without it, double-click adds to the scene. */
|
|
41
48
|
onOpenPrefab?: (asset: EffectAsset) => void;
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import React, {useEffect} from 'react';
|
|
2
|
-
import {buttonStyle, theme} from './theme';
|
|
2
|
+
import {buttonStyle, theme} from './theme.js';
|
|
3
3
|
|
|
4
|
+
/** One button of a {@link ChoiceDialog}. */
|
|
4
5
|
export interface ChoiceDialogAction {
|
|
6
|
+
/** The button's text. */
|
|
5
7
|
label: string;
|
|
8
|
+
/** Runs when the button is pressed. */
|
|
6
9
|
onSelect: () => void;
|
|
7
10
|
/** Accent button — also what Enter picks. */
|
|
8
11
|
primary?: boolean;
|
|
12
|
+
/** Shows the button in red, for a destructive choice. */
|
|
9
13
|
danger?: boolean;
|
|
10
14
|
}
|
|
11
15
|
|
|
16
|
+
/** Props of {@link ChoiceDialog}. */
|
|
12
17
|
export interface ChoiceDialogProps {
|
|
18
|
+
/** The question. */
|
|
13
19
|
title: string;
|
|
20
|
+
/** What the choice means. */
|
|
14
21
|
message: React.ReactNode;
|
|
15
22
|
/** Shown left to right; put the cancel action first. */
|
|
16
23
|
actions: ChoiceDialogAction[];
|
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import React, {useEffect, useLayoutEffect, useRef, useState} from 'react';
|
|
2
2
|
import {createPortal} from 'react-dom';
|
|
3
|
-
import {theme} from './theme';
|
|
3
|
+
import {theme} from './theme.js';
|
|
4
4
|
|
|
5
|
+
/** One entry of a {@link ContextMenu}. */
|
|
5
6
|
export interface ContextMenuItem {
|
|
7
|
+
/** The entry's text. */
|
|
6
8
|
label: string;
|
|
9
|
+
/** Runs when the entry is picked; the menu closes first. */
|
|
7
10
|
onSelect: () => void;
|
|
11
|
+
/** Shows the entry in red, for a destructive action. */
|
|
8
12
|
danger?: boolean;
|
|
13
|
+
/** Shows the entry greyed out and unpickable. */
|
|
9
14
|
disabled?: boolean;
|
|
10
15
|
}
|
|
11
16
|
|
|
17
|
+
/** Props of {@link ContextMenu}. */
|
|
12
18
|
export interface ContextMenuProps {
|
|
13
|
-
/** Client
|
|
19
|
+
/** Client X of the right-click. */
|
|
14
20
|
x: number;
|
|
21
|
+
/** Client Y of the right-click. */
|
|
15
22
|
y: number;
|
|
23
|
+
/** The entries, top to bottom. */
|
|
16
24
|
items: ContextMenuItem[];
|
|
25
|
+
/** Closes the menu: the host stops rendering it. */
|
|
17
26
|
onClose: () => void;
|
|
18
27
|
}
|
|
19
28
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {useRef, useState} from 'react';
|
|
2
|
-
import {CurvePiece, mergePieces, sampleCurve, splitPieces} from '../core/values';
|
|
3
|
-
import {inputStyle, theme} from './theme';
|
|
2
|
+
import {CurvePiece, mergePieces, sampleCurve, splitPieces} from '../core/values.js';
|
|
3
|
+
import {inputStyle, theme} from './theme.js';
|
|
4
4
|
|
|
5
5
|
const WIDTH = 218;
|
|
6
6
|
const HEIGHT = 84;
|