@selvajs/ui 4.6.2-beta.1 → 4.7.0-beta.2

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.
@@ -10,10 +10,17 @@
10
10
 
11
11
  let { scene = $bindable(), sceneVersion = 0 }: Props = $props();
12
12
 
13
+ // Viewer aids (grid, floor, measurement overlay, CSS2D label layer) are tagged with these ids by
14
+ // @selvajs/compute. They aren't scene content, so they're hidden from the object list.
15
+ const HELPER_IDS = new Set(['grid', 'floor', 'label-layer', 'measure']);
16
+
13
17
  const getSceneObjects = () => {
14
18
  void sceneVersion;
15
19
  return scene.children.filter(
16
- (obj) => !(obj instanceof THREE.Camera) && !(obj instanceof THREE.Light)
20
+ (obj) =>
21
+ !(obj instanceof THREE.Camera) &&
22
+ !(obj instanceof THREE.Light) &&
23
+ !HELPER_IDS.has(obj.userData?.id)
17
24
  );
18
25
  };
19
26
 
@@ -8,7 +8,8 @@
8
8
  type CameraController,
9
9
  type CameraProjection,
10
10
  type MeasureTool,
11
- type ViewPreset
11
+ type ViewPreset,
12
+ type Grid
12
13
  } from '@selvajs/compute/visualization';
13
14
  import {
14
15
  Maximize,
@@ -20,6 +21,7 @@
20
21
  Square,
21
22
  Frame,
22
23
  Ruler,
24
+ Grid3x3,
23
25
  Check,
24
26
  ChevronRight
25
27
  } from '@lucide/svelte';
@@ -35,6 +37,8 @@
35
37
  showFullscreenButton?: boolean;
36
38
  showSceneManager?: boolean;
37
39
  showToolsMenu?: boolean;
40
+ /** Expose the grid show/hide toggle in the tools menu. Grid starts hidden. */
41
+ showGridToggle?: boolean;
38
42
  enableMeshClick?: boolean;
39
43
  backgroundColor?: string;
40
44
  }
@@ -53,6 +57,7 @@
53
57
  showFullscreenButton: true,
54
58
  showSceneManager: true,
55
59
  showToolsMenu: true,
60
+ showGridToggle: true,
56
61
  enableMeshClick: true,
57
62
  backgroundColor: '#E6E6E6'
58
63
  };
@@ -74,6 +79,7 @@
74
79
  let controls: OrbitControls | null = null;
75
80
  let cameraController: CameraController | null = null;
76
81
  let measureTool: MeasureTool | null = null;
82
+ let grid: Grid | null = null;
77
83
  let fitToView: (() => void) | null = null;
78
84
  let viewerInitialized = false;
79
85
  let sceneVersion = $state(0);
@@ -81,6 +87,7 @@
81
87
  let sceneManagerOpen = $state(false);
82
88
  let projection: CameraProjection = $state('perspective');
83
89
  let measureActive = $state(false);
90
+ let gridVisible = $state(false);
84
91
  let selectedMeshMetadata: Record<string, any> | null = $state(null);
85
92
  let selectedMeshName: string | null = $state(null);
86
93
 
@@ -112,7 +119,8 @@
112
119
  const opts: ThreeInitializerOptions = {
113
120
  environment: { backgroundColor: config.backgroundColor },
114
121
  controls: {},
115
- grid: { enabled: config.showToolsMenu },
122
+ // Build the grid so it can be toggled at runtime, but start hidden (off by default).
123
+ grid: { enabled: config.showToolsMenu && config.showGridToggle },
116
124
  gizmo: { enabled: false },
117
125
  measure: { enabled: config.showToolsMenu },
118
126
  events: {
@@ -133,6 +141,8 @@
133
141
  controls = init.controls;
134
142
  cameraController = init.cameraController;
135
143
  measureTool = init.measureTool;
144
+ grid = init.grid;
145
+ grid?.setVisible(gridVisible);
136
146
  fitToView = init.fitToView;
137
147
  projection = init.cameraController.getProjection();
138
148
 
@@ -156,6 +166,12 @@
156
166
  measureTool.setEnabled(measureActive);
157
167
  }
158
168
 
169
+ function toggleGrid() {
170
+ if (!grid) return;
171
+ gridVisible = !gridVisible;
172
+ grid.setVisible(gridVisible);
173
+ }
174
+
159
175
  $effect(() => {
160
176
  if (scene && camera && controls) {
161
177
  updateScene(scene, meshes, camera, controls, viewerInitialized);
@@ -309,6 +325,20 @@
309
325
  {/if}
310
326
  </DropdownMenu.Item>
311
327
 
328
+ {#if config.showGridToggle}
329
+ <DropdownMenu.Item
330
+ closeOnSelect={false}
331
+ class="{itemClass} {gridVisible ? 'text-primary' : ''}"
332
+ onSelect={toggleGrid}
333
+ >
334
+ <Grid3x3 class="h-4 w-4" />
335
+ <span class="flex-1">Grid</span>
336
+ {#if gridVisible}
337
+ <Check class="h-4 w-4" />
338
+ {/if}
339
+ </DropdownMenu.Item>
340
+ {/if}
341
+
312
342
  <!-- Scene tools -->
313
343
  {#if config.showSceneManager || config.showScreenshotButton || config.showFullscreenButton}
314
344
  <DropdownMenu.Separator class="my-1 h-px bg-border" />
@@ -3,6 +3,8 @@ export interface ViewerConfig {
3
3
  showFullscreenButton?: boolean;
4
4
  showSceneManager?: boolean;
5
5
  showToolsMenu?: boolean;
6
+ /** Expose the grid show/hide toggle in the tools menu. Grid starts hidden. */
7
+ showGridToggle?: boolean;
6
8
  enableMeshClick?: boolean;
7
9
  backgroundColor?: string;
8
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selvajs/ui",
3
- "version": "4.6.2-beta.1",
3
+ "version": "4.7.0-beta.2",
4
4
  "description": "Shared UI components and utilities for Selva applications",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -72,8 +72,8 @@
72
72
  "svelte": "5.55.5",
73
73
  "tailwind-variants": "^3.2.2",
74
74
  "vitest": "^3.2.4",
75
- "@selvajs/schemas": "4.5.0",
76
- "@selvajs/config": "0.0.0"
75
+ "@selvajs/config": "0.0.0",
76
+ "@selvajs/schemas": "4.5.0"
77
77
  },
78
78
  "scripts": {
79
79
  "dev": "vite dev",
@@ -10,10 +10,17 @@
10
10
 
11
11
  let { scene = $bindable(), sceneVersion = 0 }: Props = $props();
12
12
 
13
+ // Viewer aids (grid, floor, measurement overlay, CSS2D label layer) are tagged with these ids by
14
+ // @selvajs/compute. They aren't scene content, so they're hidden from the object list.
15
+ const HELPER_IDS = new Set(['grid', 'floor', 'label-layer', 'measure']);
16
+
13
17
  const getSceneObjects = () => {
14
18
  void sceneVersion;
15
19
  return scene.children.filter(
16
- (obj) => !(obj instanceof THREE.Camera) && !(obj instanceof THREE.Light)
20
+ (obj) =>
21
+ !(obj instanceof THREE.Camera) &&
22
+ !(obj instanceof THREE.Light) &&
23
+ !HELPER_IDS.has(obj.userData?.id)
17
24
  );
18
25
  };
19
26
 
@@ -8,7 +8,8 @@
8
8
  type CameraController,
9
9
  type CameraProjection,
10
10
  type MeasureTool,
11
- type ViewPreset
11
+ type ViewPreset,
12
+ type Grid
12
13
  } from '@selvajs/compute/visualization';
13
14
  import {
14
15
  Maximize,
@@ -20,6 +21,7 @@
20
21
  Square,
21
22
  Frame,
22
23
  Ruler,
24
+ Grid3x3,
23
25
  Check,
24
26
  ChevronRight
25
27
  } from '@lucide/svelte';
@@ -35,6 +37,8 @@
35
37
  showFullscreenButton?: boolean;
36
38
  showSceneManager?: boolean;
37
39
  showToolsMenu?: boolean;
40
+ /** Expose the grid show/hide toggle in the tools menu. Grid starts hidden. */
41
+ showGridToggle?: boolean;
38
42
  enableMeshClick?: boolean;
39
43
  backgroundColor?: string;
40
44
  }
@@ -53,6 +57,7 @@
53
57
  showFullscreenButton: true,
54
58
  showSceneManager: true,
55
59
  showToolsMenu: true,
60
+ showGridToggle: true,
56
61
  enableMeshClick: true,
57
62
  backgroundColor: '#E6E6E6'
58
63
  };
@@ -74,6 +79,7 @@
74
79
  let controls: OrbitControls | null = null;
75
80
  let cameraController: CameraController | null = null;
76
81
  let measureTool: MeasureTool | null = null;
82
+ let grid: Grid | null = null;
77
83
  let fitToView: (() => void) | null = null;
78
84
  let viewerInitialized = false;
79
85
  let sceneVersion = $state(0);
@@ -81,6 +87,7 @@
81
87
  let sceneManagerOpen = $state(false);
82
88
  let projection: CameraProjection = $state('perspective');
83
89
  let measureActive = $state(false);
90
+ let gridVisible = $state(false);
84
91
  let selectedMeshMetadata: Record<string, any> | null = $state(null);
85
92
  let selectedMeshName: string | null = $state(null);
86
93
 
@@ -112,7 +119,8 @@
112
119
  const opts: ThreeInitializerOptions = {
113
120
  environment: { backgroundColor: config.backgroundColor },
114
121
  controls: {},
115
- grid: { enabled: config.showToolsMenu },
122
+ // Build the grid so it can be toggled at runtime, but start hidden (off by default).
123
+ grid: { enabled: config.showToolsMenu && config.showGridToggle },
116
124
  gizmo: { enabled: false },
117
125
  measure: { enabled: config.showToolsMenu },
118
126
  events: {
@@ -133,6 +141,8 @@
133
141
  controls = init.controls;
134
142
  cameraController = init.cameraController;
135
143
  measureTool = init.measureTool;
144
+ grid = init.grid;
145
+ grid?.setVisible(gridVisible);
136
146
  fitToView = init.fitToView;
137
147
  projection = init.cameraController.getProjection();
138
148
 
@@ -156,6 +166,12 @@
156
166
  measureTool.setEnabled(measureActive);
157
167
  }
158
168
 
169
+ function toggleGrid() {
170
+ if (!grid) return;
171
+ gridVisible = !gridVisible;
172
+ grid.setVisible(gridVisible);
173
+ }
174
+
159
175
  $effect(() => {
160
176
  if (scene && camera && controls) {
161
177
  updateScene(scene, meshes, camera, controls, viewerInitialized);
@@ -309,6 +325,20 @@
309
325
  {/if}
310
326
  </DropdownMenu.Item>
311
327
 
328
+ {#if config.showGridToggle}
329
+ <DropdownMenu.Item
330
+ closeOnSelect={false}
331
+ class="{itemClass} {gridVisible ? 'text-primary' : ''}"
332
+ onSelect={toggleGrid}
333
+ >
334
+ <Grid3x3 class="h-4 w-4" />
335
+ <span class="flex-1">Grid</span>
336
+ {#if gridVisible}
337
+ <Check class="h-4 w-4" />
338
+ {/if}
339
+ </DropdownMenu.Item>
340
+ {/if}
341
+
312
342
  <!-- Scene tools -->
313
343
  {#if config.showSceneManager || config.showScreenshotButton || config.showFullscreenButton}
314
344
  <DropdownMenu.Separator class="my-1 h-px bg-border" />