@pascal-app/editor 1.0.5 → 1.0.7
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/package.json +20 -17
- package/src/components/editor/action-menu-button.tsx +4 -1
- package/src/components/editor/alignment-3d-guide-layer.tsx +102 -11
- package/src/components/editor/bake-thumbnail.tsx +3 -2
- package/src/components/editor/custom-camera-controls.tsx +20 -8
- package/src/components/editor/elevation-3d-guide-layer.tsx +1 -1
- package/src/components/editor/first-person-controls.tsx +72 -61
- package/src/components/editor/floating-action-menu.tsx +27 -9
- package/src/components/editor/floating-building-action-menu.tsx +1 -1
- package/src/components/editor/floorplan-panel.test.ts +220 -0
- package/src/components/editor/floorplan-panel.tsx +285 -132
- package/src/components/editor/footprint-height-handle.tsx +1 -1
- package/src/components/editor/grid.tsx +24 -15
- package/src/components/editor/group-actions.ts +19 -1
- package/src/components/editor/group-floating-action-menu.tsx +1 -1
- package/src/components/editor/group-move-3d.ts +27 -2
- package/src/components/editor/group-selection-box-3d.tsx +4 -1
- package/src/components/editor/group-transform-shared.ts +20 -0
- package/src/components/editor/index.tsx +130 -20
- package/src/components/editor/inspector-extension-actions.tsx +36 -0
- package/src/components/editor/localized-html.tsx +22 -0
- package/src/components/editor/node-action-menu.tsx +46 -12
- package/src/components/editor/node-arrow-handles.tsx +1 -2
- package/src/components/editor/open-wall-ends-3d-layer.tsx +1 -1
- package/src/components/editor/opening-guides-3d-layer.tsx +1 -1
- package/src/components/editor/registry-action-contributions.test.tsx +67 -6
- package/src/components/editor/registry-action-contributions.tsx +115 -15
- package/src/components/editor/riser-diagram-panel.tsx +59 -9
- package/src/components/editor/room-controls.tsx +1 -1
- package/src/components/editor/room-handles-2d.tsx +156 -6
- package/src/components/editor/room-handles.tsx +241 -51
- package/src/components/editor/room-highlight.tsx +2 -0
- package/src/components/editor/room-labels.tsx +29 -15
- package/src/components/editor/selection-manager.tsx +15 -102
- package/src/components/editor/separator-end-grip.tsx +74 -0
- package/src/components/editor/site-edge-labels.tsx +1 -1
- package/src/components/editor/thumbnail-generator.test.ts +15 -6
- package/src/components/editor/thumbnail-generator.tsx +26 -1
- package/src/components/editor/use-floorplan-background-placement.ts +55 -52
- package/src/components/editor/views/view-bar.tsx +111 -26
- package/src/components/editor/wall-measurement-label.tsx +1 -1
- package/src/components/editor/wall-move-side-handles.tsx +5 -1
- package/src/components/editor/wall-pull-grip.tsx +193 -0
- package/src/components/editor/wall-snap-beacon-layer.tsx +81 -1
- package/src/components/editor-2d/floorplan-adjacent-walls-layer.test.ts +76 -0
- package/src/components/editor-2d/floorplan-adjacent-walls-layer.tsx +186 -0
- package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +63 -3
- package/src/components/editor-2d/floorplan-group-move.tsx +31 -3
- package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +6 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +22 -13
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +11 -3
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +7 -1
- package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +96 -4
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +52 -12
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +12 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +197 -37
- package/src/components/procedural-items/inspector.tsx +59 -12
- package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +2 -0
- package/src/components/systems/ceiling/ceiling-system.test.ts +141 -0
- package/src/components/systems/ceiling/ceiling-system.tsx +53 -18
- package/src/components/tools/distribution/distribution-run-tool.tsx +50 -26
- package/src/components/tools/item/placement-history.test.tsx +1 -1
- package/src/components/tools/item/placement-strategies.test.ts +64 -1
- package/src/components/tools/item/placement-strategies.ts +5 -9
- package/src/components/tools/item/use-placement-coordinator.tsx +30 -34
- package/src/components/tools/registry/item-surface-move.test.ts +11 -0
- package/src/components/tools/select/select-candidates.test.ts +45 -0
- package/src/components/tools/select/select-candidates.ts +13 -2
- package/src/components/tools/shared/cursor-sphere.tsx +10 -3
- package/src/components/tools/shared/draft-measurement-label.tsx +1 -1
- package/src/components/tools/shared/placement-box.tsx +1 -1
- package/src/components/tools/shared/placement-dimension-guides.tsx +1 -1
- package/src/components/tools/site/terrain-brush-cursor.tsx +1 -1
- package/src/components/tools/wall/wall-drafting.test.ts +299 -23
- package/src/components/tools/wall/wall-drafting.ts +132 -24
- package/src/components/tools/wall/wall-pull-snap.test.ts +274 -0
- package/src/components/tools/wall/wall-pull-snap.ts +563 -0
- package/src/components/tools/wall/wall-snap-geometry.test.ts +113 -1
- package/src/components/tools/wall/wall-snap-geometry.ts +137 -0
- package/src/components/tools/zone/zone-tool.tsx +3 -3
- package/src/components/ui/action-menu/action-button.tsx +2 -0
- package/src/components/ui/action-menu/camera-actions.tsx +8 -7
- package/src/components/ui/action-menu/control-modes.tsx +9 -5
- package/src/components/ui/action-menu/follow-pascal-pill.tsx +3 -1
- package/src/components/ui/action-menu/follow-pascal-toggle.tsx +7 -1
- package/src/components/ui/action-menu/furnish-tools.tsx +7 -6
- package/src/components/ui/action-menu/index.tsx +1 -0
- package/src/components/ui/action-menu/measurement-control.tsx +35 -29
- package/src/components/ui/action-menu/point-ask-button.tsx +4 -3
- package/src/components/ui/action-menu/structure-tools.tsx +25 -23
- package/src/components/ui/action-menu/view-toggles.test.ts +18 -0
- package/src/components/ui/action-menu/view-toggles.tsx +256 -51
- package/src/components/ui/build-panel/build-panel.tsx +163 -25
- package/src/components/ui/command-palette/editor-commands.tsx +38 -11
- package/src/components/ui/controls/finish-swatch.tsx +45 -19
- package/src/components/ui/controls/material-picker.tsx +24 -17
- package/src/components/ui/controls/metric-control.test.ts +52 -0
- package/src/components/ui/controls/metric-control.tsx +62 -24
- package/src/components/ui/controls/segmented-control.tsx +5 -2
- package/src/components/ui/controls/slider-control.test.tsx +23 -0
- package/src/components/ui/controls/slider-control.tsx +30 -14
- package/src/components/ui/floating-level-selector.tsx +100 -48
- package/src/components/ui/helpers/building-helper.tsx +5 -3
- package/src/components/ui/helpers/contextual-helper-panel.tsx +97 -37
- package/src/components/ui/helpers/helper-manager.test.tsx +53 -3
- package/src/components/ui/helpers/helper-manager.tsx +82 -57
- package/src/components/ui/helpers/item-helper.tsx +6 -4
- package/src/components/ui/helpers/registered-tool-helper.tsx +3 -1
- package/src/components/ui/level-duplicate-dialog.tsx +32 -23
- package/src/components/ui/level-wall-copy-prompt.tsx +124 -0
- package/src/components/ui/panels/collections/collections-popover.tsx +11 -11
- package/src/components/ui/panels/deck-boards-fields.tsx +136 -0
- package/src/components/ui/panels/floor-foundation-panel.tsx +113 -38
- package/src/components/ui/panels/floor-opening-panel.tsx +73 -25
- package/src/components/ui/panels/inspector-extensions.tsx +66 -0
- package/src/components/ui/panels/mobile-panel-sheet.tsx +3 -1
- package/src/components/ui/panels/mobile-selection-bar.tsx +8 -6
- package/src/components/ui/panels/multi-field-value.test.ts +27 -0
- package/src/components/ui/panels/multi-field-value.ts +20 -1
- package/src/components/ui/panels/multi-height-mode.tsx +8 -7
- package/src/components/ui/panels/multi-parametric-inspector.tsx +122 -34
- package/src/components/ui/panels/multi-selection-panel.tsx +75 -35
- package/src/components/ui/panels/node-display.ts +35 -24
- package/src/components/ui/panels/panel-manager.tsx +5 -3
- package/src/components/ui/panels/panel-wrapper.tsx +22 -109
- package/src/components/ui/panels/parametric-field-control.tsx +13 -10
- package/src/components/ui/panels/parametric-inspector.tsx +18 -12
- package/src/components/ui/panels/reference-panel.tsx +51 -35
- package/src/components/ui/panels/room-construction-rows.tsx +464 -65
- package/src/components/ui/panels/room-name-options.test.ts +36 -3
- package/src/components/ui/panels/room-name-options.ts +110 -11
- package/src/components/ui/panels/room-openings-field.tsx +21 -12
- package/src/components/ui/panels/room-panel.tsx +206 -99
- package/src/components/ui/panels/shape-choice.tsx +7 -5
- package/src/components/ui/panels/wall-region-list.tsx +7 -7
- package/src/components/ui/primitives/shortcut-token.tsx +10 -6
- package/src/components/ui/sidebar/icon-rail.tsx +11 -9
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +10 -10
- package/src/components/ui/sidebar/panels/items-panel/index.tsx +10 -9
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +3 -0
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +102 -32
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +4 -3
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/collection-tree-node.tsx +7 -5
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/default-names.ts +75 -0
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +117 -64
- package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +36 -3
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +7 -4
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +8 -2
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +8 -5
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +8 -3
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +8 -7
- package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +7 -6
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +16 -1
- package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +15 -8
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/zone-membership-checkbox.tsx +3 -1
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +18 -8
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +25 -14
- package/src/components/ui/sidebar/tab-bar.tsx +4 -1
- package/src/components/ui/sidebar/use-plugin-panels.tsx +19 -10
- package/src/components/ui/snap-target-badge.tsx +7 -6
- package/src/components/ui/wall-type/wall-type-editor-dialog.tsx +721 -0
- package/src/components/ui/wall-type/wall-type-menu.tsx +167 -0
- package/src/components/viewer/floorplan-compass-button.tsx +12 -3
- package/src/components/viewer/floorplan-preview.tsx +27 -19
- package/src/components/viewer/render-settings-menu.tsx +294 -0
- package/src/components/viewer/sky-control.tsx +710 -0
- package/src/components/viewer/use-viewer-ui-sounds.test.ts +15 -0
- package/src/components/viewer/use-viewer-ui-sounds.ts +46 -0
- package/src/components/viewer/viewer-controls-bar.tsx +278 -260
- package/src/components/viewer/viewer-hover-label.tsx +12 -4
- package/src/components/viewer/viewer-rooms.tsx +61 -4
- package/src/components/viewer/viewer-scene-header.tsx +28 -17
- package/src/components/viewer/viewer-stage-switcher.tsx +4 -2
- package/src/components/viewer-overlay.tsx +8 -0
- package/src/components/walkthrough-hud.tsx +75 -2
- package/src/hooks/use-keyboard.test.ts +85 -4
- package/src/hooks/use-keyboard.ts +65 -7
- package/src/hooks/use-selected-room.ts +12 -1
- package/src/i18n/floor-footprint-name.ts +30 -0
- package/src/i18n/level-name.ts +39 -0
- package/src/i18n/locales/en/messages.po +15117 -0
- package/src/i18n/locales/en/messages.ts +1 -0
- package/src/i18n/locales/en-XA/messages.ts +1 -0
- package/src/i18n/locales/es/messages.po +37501 -0
- package/src/i18n/locales/es/messages.ts +1 -0
- package/src/i18n/locales/fr/messages.po +37501 -0
- package/src/i18n/locales/fr/messages.ts +1 -0
- package/src/i18n/locales/ja/messages.po +37501 -0
- package/src/i18n/locales/ja/messages.ts +1 -0
- package/src/i18n/locales/zh-CN/messages.po +37501 -0
- package/src/i18n/locales/zh-CN/messages.ts +1 -0
- package/src/i18n/locales.test.ts +63 -0
- package/src/i18n/locales.ts +32 -0
- package/src/i18n/message-slot.tsx +25 -0
- package/src/i18n/node-messages.ts +169 -0
- package/src/i18n/pseudo-locale.ts +17 -0
- package/src/i18n/published-entry.test.ts +17 -0
- package/src/i18n/scene-theme-name.ts +16 -0
- package/src/index.tsx +156 -4
- package/src/lib/add-storey.test.ts +3 -3
- package/src/lib/contextual-help.test.ts +20 -1
- package/src/lib/contextual-help.ts +95 -19
- package/src/lib/continuation.ts +38 -11
- package/src/lib/floor-footprints.test.ts +2 -1
- package/src/lib/floor-foundation-support.test.ts +7 -3
- package/src/lib/floorplan/dimension-drive.test.ts +36 -2
- package/src/lib/floorplan/dimension-drive.ts +68 -3
- package/src/lib/floorplan/floorplan-export.test.ts +16 -5
- package/src/lib/floorplan/floorplan-export.tsx +13 -16
- package/src/lib/floorplan/floorplan-extension.test.ts +11 -12
- package/src/lib/floorplan/floorplan-extension.ts +37 -18
- package/src/lib/floorplan/floorplan-mode.test.ts +13 -6
- package/src/lib/floorplan/floorplan-mode.ts +14 -12
- package/src/lib/floorplan/floorplan-plugin-install.test.tsx +1 -1
- package/src/lib/floorplan/floorplan-readonly.ts +3 -3
- package/src/lib/floorplan/schedules.ts +10 -6
- package/src/lib/floorplan/site-plan/site-annotations.test.ts +46 -1
- package/src/lib/floorplan/site-plan/site-annotations.ts +3 -0
- package/src/lib/floorplan/site-plan/site-parts.ts +6 -3
- package/src/lib/geometry-script/author.test.ts +114 -0
- package/src/lib/geometry-script/author.ts +55 -23
- package/src/lib/geometry-script/client.ts +3 -0
- package/src/lib/geometry-script/finish-scripts.ts +7 -7
- package/src/lib/glb-export.test.ts +327 -0
- package/src/lib/glb-export.ts +70 -2
- package/src/lib/grid-frame.test.ts +56 -3
- package/src/lib/grid-frame.ts +28 -1
- package/src/lib/history.test.ts +2 -1
- package/src/lib/hud-title.ts +48 -24
- package/src/lib/ifc-export.test.ts +41 -1
- package/src/lib/inspector-card-mode.test.ts +3 -31
- package/src/lib/inspector-card-mode.ts +5 -10
- package/src/lib/inspector-expanded.test.ts +13 -0
- package/src/lib/inspector-expanded.ts +13 -1
- package/src/lib/keyboard-pan.test.ts +7 -0
- package/src/lib/keyboard-pan.ts +9 -2
- package/src/lib/layer-thickness.test.ts +22 -0
- package/src/lib/layer-thickness.ts +79 -0
- package/src/lib/lot/drop-in.test.ts +59 -1
- package/src/lib/lot/drop-in.ts +42 -50
- package/src/lib/material-swatch-style.ts +24 -0
- package/src/lib/measurement-parser.ts +7 -3
- package/src/lib/measurements.test.ts +17 -0
- package/src/lib/measurements.ts +20 -5
- package/src/lib/mezzanine-selection.test.ts +3 -1
- package/src/lib/orbit-near-plane.test.ts +12 -0
- package/src/lib/orbit-near-plane.ts +13 -0
- package/src/lib/paint-regions.test.ts +8 -5
- package/src/lib/paint-scope.test.ts +53 -0
- package/src/lib/paint-scope.ts +18 -4
- package/src/lib/panel-rows.ts +17 -8
- package/src/lib/reference-scale.ts +83 -0
- package/src/lib/room-built-on.test.ts +26 -12
- package/src/lib/room-built-on.ts +20 -2
- package/src/lib/room-construction.test.ts +28 -1
- package/src/lib/room-construction.ts +14 -0
- package/src/lib/room-controls-phase6.test.ts +140 -0
- package/src/lib/room-handle-drag.ts +557 -42
- package/src/lib/room-name-catalog.test.ts +8 -0
- package/src/lib/room-name-catalog.ts +8 -1
- package/src/lib/room-name-messages.test.ts +132 -0
- package/src/lib/room-name-messages.ts +147 -0
- package/src/lib/room-name-storage.test.ts +334 -0
- package/src/lib/room-push-dimensions.test.ts +37 -0
- package/src/lib/room-push-dimensions.ts +226 -2
- package/src/lib/room-selection.test.ts +23 -6
- package/src/lib/scene-clipboard.test.ts +70 -49
- package/src/lib/scene-clipboard.ts +9 -4
- package/src/lib/selection-cycle.ts +63 -0
- package/src/lib/separator-edit.test.ts +431 -0
- package/src/lib/separator-edit.ts +179 -0
- package/src/lib/separator-lines.ts +38 -0
- package/src/lib/sfx-bus.ts +27 -1
- package/src/lib/sfx-player.ts +16 -0
- package/src/lib/tool-cursor-icon.ts +5 -1
- package/src/lib/tool-shortcuts.ts +130 -0
- package/src/lib/viewer-selection.test.ts +28 -24
- package/src/lib/viewer-selection.ts +20 -5
- package/src/lib/wall-draw-variant.test.ts +7 -6
- package/src/lib/wall-draw-variant.ts +107 -6
- package/src/lib/wall-nudge.test.ts +44 -0
- package/src/lib/wall-nudge.ts +131 -0
- package/src/lib/wall-parallel-snap.test.ts +83 -0
- package/src/lib/wall-parallel-snap.ts +176 -0
- package/src/lib/wall-placement-pointer.test.ts +23 -0
- package/src/lib/wall-placement-pointer.ts +18 -0
- package/src/lib/wall-pull.test.ts +245 -0
- package/src/lib/wall-pull.ts +209 -0
- package/src/lib/wall-push-line-up.test.ts +132 -0
- package/src/lib/wall-region-list.ts +14 -5
- package/src/lib/wall-run-snap.test.ts +188 -0
- package/src/lib/wall-run-snap.ts +83 -0
- package/src/lib/wall-type.test.ts +125 -0
- package/src/lib/wall-type.ts +119 -0
- package/src/lib/window-interaction.test.ts +42 -0
- package/src/lib/window-interaction.ts +47 -5
- package/src/lib/world-grid-snap.test.ts +139 -2
- package/src/lib/world-grid-snap.ts +102 -105
- package/src/store/use-alignment-guides.ts +27 -1
- package/src/store/use-editor.tsx +48 -0
- package/src/store/use-floorplan-annotation-visibility.test.ts +64 -0
- package/src/store/use-floorplan-annotation-visibility.ts +47 -22
- package/src/store/use-wall-snap-indicator.ts +15 -2
|
@@ -38,15 +38,48 @@ describe('roomNameOptions', () => {
|
|
|
38
38
|
})
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
-
test('free text is
|
|
41
|
+
test('free text is offered first unless it is exactly a name, which is then active', () => {
|
|
42
42
|
const { typed, options } = roomNameOptions('Wine cellar', SECTIONS)
|
|
43
|
-
expect(typed).toEqual({ kind: 'typed', value: 'Wine cellar', match: null })
|
|
43
|
+
expect(typed).toEqual({ kind: 'typed', value: 'Wine cellar', label: 'Wine cellar', match: null })
|
|
44
44
|
expect(options).toEqual([typed!])
|
|
45
|
-
|
|
45
|
+
const exact = roomNameOptions('Kitchen', SECTIONS)
|
|
46
|
+
expect(exact.typed).toBeNull()
|
|
47
|
+
expect(exact.options[exact.active]).toMatchObject({ kind: 'suggestion', value: 'Kitchen' })
|
|
46
48
|
// A case-only difference still offers the text as typed.
|
|
47
49
|
expect(roomNameOptions('kitchen', SECTIONS).typed?.value).toBe('kitchen')
|
|
48
50
|
})
|
|
49
51
|
|
|
52
|
+
test('an exact English name wins over names that contain it', () => {
|
|
53
|
+
const storage: RoomNameSection[] = [
|
|
54
|
+
{ id: 'utility', title: 'Utility', names: ['Walk-in closet', 'Closet'] },
|
|
55
|
+
]
|
|
56
|
+
for (const typed of ['Closet', 'closet']) {
|
|
57
|
+
const { options, active } = roomNameOptions(typed, storage)
|
|
58
|
+
expect(options[active]?.value).toBe('Closet')
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('a shared label is exact only for the room’s own entry', () => {
|
|
63
|
+
const shared: RoomNameSection[] = [
|
|
64
|
+
{
|
|
65
|
+
id: 'living',
|
|
66
|
+
title: 'Séjour',
|
|
67
|
+
names: ['Living room', 'Kitchen', 'Lounge'],
|
|
68
|
+
labels: ['Salon', 'Cuisine', 'Salon'],
|
|
69
|
+
},
|
|
70
|
+
]
|
|
71
|
+
const activeValue = (current: string) => {
|
|
72
|
+
const { options, active } = roomNameOptions('Salon', shared, true, current)
|
|
73
|
+
return options[active]?.value
|
|
74
|
+
}
|
|
75
|
+
expect(activeValue('Lounge')).toBe('Lounge')
|
|
76
|
+
expect(activeValue('Lounge 2')).toBe('Lounge')
|
|
77
|
+
expect(activeValue('Living room')).toBe('Living room')
|
|
78
|
+
// Any other room falls back to the first suggestion.
|
|
79
|
+
expect(activeValue('Kitchen')).toBe('Living room')
|
|
80
|
+
expect(activeValue('')).toBe('Living room')
|
|
81
|
+
})
|
|
82
|
+
|
|
50
83
|
test('the keyboard order is the typed row, then the groups in order', () => {
|
|
51
84
|
const { groups, options } = roomNameOptions('e', SECTIONS)
|
|
52
85
|
expect(options.slice(1)).toEqual(groups.flatMap((g) => g.options))
|
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AnyNodeId, useScene } from '@pascal-app/core'
|
|
2
|
+
import {
|
|
3
|
+
baseRoomName,
|
|
4
|
+
numberedRoomName,
|
|
5
|
+
type RoomNameSection,
|
|
6
|
+
} from '../../../lib/room-name-catalog'
|
|
7
|
+
import {
|
|
8
|
+
type DescriptorTranslator,
|
|
9
|
+
displayRoomName,
|
|
10
|
+
editedRoomName,
|
|
11
|
+
} from '../../../lib/room-name-messages'
|
|
12
|
+
import { renameRoom } from '../../../lib/room-structure-commands'
|
|
2
13
|
|
|
3
14
|
export type RoomNameOption = {
|
|
4
15
|
kind: 'typed' | 'suggestion'
|
|
16
|
+
/** What a pick stores: the English catalog name, or the typed text as written. */
|
|
5
17
|
value: string
|
|
18
|
+
/** What the row shows and the search matches, in the UI language. */
|
|
19
|
+
label: string
|
|
6
20
|
match: { start: number; end: number } | null
|
|
7
21
|
}
|
|
8
22
|
|
|
@@ -11,25 +25,37 @@ export type RoomNameGroup = { id: string; title: string; options: RoomNameOption
|
|
|
11
25
|
/**
|
|
12
26
|
* The room-name combobox's rows. Until the user types, every section is listed
|
|
13
27
|
* whole — the field's current name is not a filter. Typing keeps, in every
|
|
14
|
-
* section, the names
|
|
15
|
-
* spaces), drops the sections left empty, and puts the
|
|
16
|
-
* own row unless it
|
|
17
|
-
* the keyboard walks: the typed row, then
|
|
28
|
+
* section, the names whose label or English name contains the text (ignoring
|
|
29
|
+
* case and surrounding spaces), drops the sections left empty, and puts the
|
|
30
|
+
* typed text first as its own row unless it is exactly an English catalog
|
|
31
|
+
* name. `options` is the flat order the keyboard walks: the typed row, then
|
|
32
|
+
* each group's rows. `current` is the room's stored name.
|
|
18
33
|
*/
|
|
19
34
|
export function roomNameOptions(
|
|
20
35
|
typed: string,
|
|
21
36
|
sections: readonly RoomNameSection[],
|
|
22
37
|
filtering = true,
|
|
23
|
-
|
|
38
|
+
current = '',
|
|
39
|
+
): {
|
|
40
|
+
typed: RoomNameOption | null
|
|
41
|
+
groups: RoomNameGroup[]
|
|
42
|
+
options: RoomNameOption[]
|
|
43
|
+
/** The row Enter picks after typing: an exact name, else the first suggestion. */
|
|
44
|
+
active: number
|
|
45
|
+
} {
|
|
24
46
|
const query = filtering ? typed.trim().toLowerCase() : ''
|
|
25
47
|
const groups = sections.flatMap((section) => {
|
|
26
|
-
const options = section.names.flatMap((value): RoomNameOption[] => {
|
|
27
|
-
const
|
|
28
|
-
|
|
48
|
+
const options = section.names.flatMap((value, index): RoomNameOption[] => {
|
|
49
|
+
const label = section.labels?.[index] ?? value
|
|
50
|
+
// The label is what is shown and highlighted; the English name matches too,
|
|
51
|
+
// for packs that have not translated every name yet.
|
|
52
|
+
const start = query ? label.toLowerCase().indexOf(query) : -1
|
|
53
|
+
if (query && start < 0 && !value.toLowerCase().includes(query)) return []
|
|
29
54
|
return [
|
|
30
55
|
{
|
|
31
56
|
kind: 'suggestion',
|
|
32
57
|
value,
|
|
58
|
+
label,
|
|
33
59
|
match: start >= 0 ? { start, end: start + query.length } : null,
|
|
34
60
|
},
|
|
35
61
|
]
|
|
@@ -38,10 +64,83 @@ export function roomNameOptions(
|
|
|
38
64
|
})
|
|
39
65
|
const known = sections.some((section) => section.names.includes(typed))
|
|
40
66
|
const typedRow: RoomNameOption | null =
|
|
41
|
-
query && !known ? { kind: 'typed', value: typed, match: null } : null
|
|
67
|
+
query && !known ? { kind: 'typed', value: typed, label: typed, match: null } : null
|
|
68
|
+
const options = [...(typedRow ? [typedRow] : []), ...groups.flatMap((group) => group.options)]
|
|
69
|
+
const suggestions = options.filter((option) => option.kind === 'suggestion')
|
|
70
|
+
// Labels can be shared ("Salon" for both Living room and Lounge): a shared
|
|
71
|
+
// label is only an exact match for the room's own name.
|
|
72
|
+
const own = baseRoomName(current).toLowerCase()
|
|
73
|
+
const byLabel = suggestions.filter((option) => option.label.toLowerCase() === query)
|
|
74
|
+
const exactRow =
|
|
75
|
+
suggestions.find((option) => option.value.toLowerCase() === query) ??
|
|
76
|
+
(byLabel.length === 1
|
|
77
|
+
? byLabel[0]
|
|
78
|
+
: byLabel.find((option) => option.value.toLowerCase() === own))
|
|
79
|
+
const exact = exactRow ? options.indexOf(exactRow) : -1
|
|
42
80
|
return {
|
|
43
81
|
typed: typedRow,
|
|
44
82
|
groups,
|
|
45
|
-
options
|
|
83
|
+
options,
|
|
84
|
+
active: exact >= 0 ? exact : typedRow && options.length > 1 ? 1 : 0,
|
|
46
85
|
}
|
|
47
86
|
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The commit of an edited room-name field, on blur or Enter (unless Enter
|
|
90
|
+
* picks a suggestion): the room panel's, and the layer tree's with its own
|
|
91
|
+
* `write`. `committed` is what the field last committed or showed; text still
|
|
92
|
+
* equal to it writes nothing.
|
|
93
|
+
*/
|
|
94
|
+
export function commitRoomNameEdit(
|
|
95
|
+
zoneId: string,
|
|
96
|
+
name: string,
|
|
97
|
+
committed: string,
|
|
98
|
+
text: string,
|
|
99
|
+
i18n: DescriptorTranslator,
|
|
100
|
+
write: (zoneId: string, name: string) => void = renameRoom,
|
|
101
|
+
) {
|
|
102
|
+
if (text === committed) return
|
|
103
|
+
const stored = editedRoomName(name, text, i18n)
|
|
104
|
+
if (stored !== name) write(zoneId, stored)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The layer tree's rename of a room or zone. Its field shows the name as
|
|
109
|
+
* `displayRoomName` does and passes the trimmed text, or `undefined` when
|
|
110
|
+
* cleared; it writes through `updateNode`, as main's tree does.
|
|
111
|
+
*/
|
|
112
|
+
export function commitTreeRoomRename(
|
|
113
|
+
zoneId: string,
|
|
114
|
+
text: string | undefined,
|
|
115
|
+
i18n: DescriptorTranslator,
|
|
116
|
+
) {
|
|
117
|
+
const { nodes, updateNode } = useScene.getState()
|
|
118
|
+
const id = zoneId as AnyNodeId
|
|
119
|
+
const name = nodes[id]?.name ?? ''
|
|
120
|
+
if (text === undefined) {
|
|
121
|
+
updateNode(id, { name: undefined })
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
commitRoomNameEdit(zoneId, name, displayRoomName(name, i18n), text, i18n, (_, stored) =>
|
|
125
|
+
updateNode(id, { name: stored }),
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The room panel's pick of a row: a click, or Enter on a suggestion. A suggestion stores its English catalog name,
|
|
131
|
+
* numbered against the level's other rooms, in every UI language; the typed
|
|
132
|
+
* row stores the text exactly as typed. Returns what the field shows after it.
|
|
133
|
+
*/
|
|
134
|
+
export function commitRoomNamePick(
|
|
135
|
+
zoneId: string,
|
|
136
|
+
name: string,
|
|
137
|
+
option: RoomNameOption,
|
|
138
|
+
i18n: DescriptorTranslator,
|
|
139
|
+
): string {
|
|
140
|
+
const stored =
|
|
141
|
+
option.kind === 'typed'
|
|
142
|
+
? option.value
|
|
143
|
+
: numberedRoomName(useScene.getState().nodes, zoneId, option.value)
|
|
144
|
+
if (stored !== name) renameRoom(zoneId, stored)
|
|
145
|
+
return displayRoomName(stored, i18n)
|
|
146
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { area, useScene } from '@pascal-app/core'
|
|
3
|
+
import { area, useScene, usePascalI18n } from '@pascal-app/core'
|
|
4
4
|
import { useViewer } from '@pascal-app/viewer'
|
|
5
5
|
import { useMemo } from 'react'
|
|
6
6
|
import {
|
|
@@ -15,8 +15,6 @@ import { formatAreaLabel } from '../../../lib/measurements'
|
|
|
15
15
|
import { pill } from './room-construction-rows'
|
|
16
16
|
import { ShapeChoice } from './shape-choice'
|
|
17
17
|
|
|
18
|
-
const count = (n: number) => `${n} ${n === 1 ? 'opening' : 'openings'}`
|
|
19
|
-
|
|
20
18
|
/**
|
|
21
19
|
* A room's openings through its floor (or its ceiling), inside the row's
|
|
22
20
|
* settings. The floor has "Edit floor": a session that outlines the openings
|
|
@@ -31,6 +29,7 @@ export function RoomOpeningsField({
|
|
|
31
29
|
zoneId: string
|
|
32
30
|
surface: OpeningSurface
|
|
33
31
|
}) {
|
|
32
|
+
const i18n = usePascalI18n()
|
|
34
33
|
const nodes = useScene((scene) => scene.nodes)
|
|
35
34
|
const unit = useViewer((s) => s.unit)
|
|
36
35
|
const readOnly = useScene((scene) => scene.readOnly)
|
|
@@ -38,40 +37,46 @@ export function RoomOpeningsField({
|
|
|
38
37
|
() => roomSurfaceOpenings(nodes, zoneId, surface),
|
|
39
38
|
[nodes, zoneId, surface],
|
|
40
39
|
)
|
|
40
|
+
const openingCount = i18n._({
|
|
41
|
+
id: 'editor.panels.roomOpeningsField.openings',
|
|
42
|
+
// {number} rather than #: counts are written without grouping, as before ("1000 openings").
|
|
43
|
+
message: '{count, plural, =0 {No openings} one {{number} opening} other {{number} openings}}',
|
|
44
|
+
values: { count: openings.length, number: String(openings.length) },
|
|
45
|
+
})
|
|
41
46
|
const editing = useFloorEditSession((s) => surface === 'floor' && s.session?.zoneId === zoneId)
|
|
42
47
|
const cut = (
|
|
43
48
|
<ShapeChoice
|
|
44
49
|
data={`cut-opening-${surface}`}
|
|
45
50
|
disabled={readOnly}
|
|
46
|
-
label=
|
|
51
|
+
label={i18n._({ id: 'editor.panels.roomOpeningsField.cutOpening', message: 'Cut opening' })}
|
|
47
52
|
onPick={(shape) => startOpeningDraft(zoneId, surface, shape)}
|
|
48
53
|
/>
|
|
49
54
|
)
|
|
50
55
|
if (surface === 'ceiling')
|
|
51
56
|
return (
|
|
52
57
|
<div className="flex flex-col gap-1" data-room-openings={surface}>
|
|
53
|
-
{openings.length > 0 && <span>{
|
|
58
|
+
{openings.length > 0 && <span>{openingCount}</span>}
|
|
54
59
|
{cut}
|
|
55
60
|
</div>
|
|
56
61
|
)
|
|
57
62
|
if (!editing)
|
|
58
63
|
return (
|
|
59
64
|
<div className="flex min-h-7 items-center justify-between gap-3" data-room-openings={surface}>
|
|
60
|
-
<span>{
|
|
65
|
+
<span>{openingCount}</span>
|
|
61
66
|
<button
|
|
62
67
|
className={pill}
|
|
63
68
|
data-edit-floor
|
|
64
69
|
onClick={() => startFloorEdit(zoneId)}
|
|
65
70
|
type="button"
|
|
66
71
|
>
|
|
67
|
-
Edit floor
|
|
72
|
+
{i18n._({ id: 'editor.panels.roomOpeningsField.editFloor', message: 'Edit floor' })}
|
|
68
73
|
</button>
|
|
69
74
|
</div>
|
|
70
75
|
)
|
|
71
76
|
return (
|
|
72
77
|
<div className="flex flex-col gap-1.5" data-room-openings={surface} data-editing-floor>
|
|
73
78
|
{openings.length === 0 ? (
|
|
74
|
-
<p>No openings yet. Cut one for a stair void or a hatch
|
|
79
|
+
<p>{i18n._({ id: 'editor.panels.roomOpeningsField.noOpeningsYetCutOneFor', message: 'No openings yet. Cut one for a stair void or a hatch.' })}</p>
|
|
75
80
|
) : (
|
|
76
81
|
openings.map((opening, index) => (
|
|
77
82
|
<button
|
|
@@ -83,10 +88,14 @@ export function RoomOpeningsField({
|
|
|
83
88
|
>
|
|
84
89
|
<span className="text-foreground">
|
|
85
90
|
{opening.source === 'stair'
|
|
86
|
-
? 'Stair opening'
|
|
91
|
+
? i18n._({ id: 'editor.panels.roomOpeningsField.stairOpening', message: 'Stair opening' })
|
|
87
92
|
: opening.source === 'elevator'
|
|
88
|
-
? 'Elevator opening'
|
|
89
|
-
:
|
|
93
|
+
? i18n._({ id: 'editor.panels.roomOpeningsField.elevatorOpening', message: 'Elevator opening' })
|
|
94
|
+
: i18n._({
|
|
95
|
+
id: 'editor.panels.roomOpeningsField.openingNumber',
|
|
96
|
+
message: 'Opening {number}',
|
|
97
|
+
values: { number: index + 1 },
|
|
98
|
+
})}
|
|
90
99
|
</span>
|
|
91
100
|
<span className="font-mono tabular-nums">
|
|
92
101
|
{formatAreaLabel(area([{ outer: opening.polygon, holes: [] }]), unit, 2)}
|
|
@@ -102,7 +111,7 @@ export function RoomOpeningsField({
|
|
|
102
111
|
onClick={() => (finishFloorEdit() ? undefined : endFloorEdit())}
|
|
103
112
|
type="button"
|
|
104
113
|
>
|
|
105
|
-
Done
|
|
114
|
+
{i18n._({ id: 'editor.panels.roomOpeningsField.done', message: 'Done' })}
|
|
106
115
|
</button>
|
|
107
116
|
</div>
|
|
108
117
|
</div>
|