@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import type { AnyNodeId, CollectionId } from '@pascal-app/core'
|
|
4
|
-
import { useScene } from '@pascal-app/core'
|
|
4
|
+
import { useScene, usePascalI18n } from '@pascal-app/core'
|
|
5
5
|
import {
|
|
6
6
|
Check,
|
|
7
7
|
ChevronDown,
|
|
@@ -36,6 +36,7 @@ interface CollectionsPopoverProps {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProps) {
|
|
39
|
+
const i18n = usePascalI18n()
|
|
39
40
|
const collections = useScene((s) => s.collections)
|
|
40
41
|
const nodes = useScene((s) => s.nodes)
|
|
41
42
|
const createCollection = useScene((s) => s.createCollection)
|
|
@@ -102,7 +103,7 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
102
103
|
<div className="flex items-center gap-1.5">
|
|
103
104
|
<Layers className="h-3.5 w-3.5 text-muted-foreground" />
|
|
104
105
|
<span className="font-semibold text-foreground text-xs tracking-tight">
|
|
105
|
-
Collections
|
|
106
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.collections', message: 'Collections' })}
|
|
106
107
|
</span>
|
|
107
108
|
</div>
|
|
108
109
|
<button
|
|
@@ -114,7 +115,7 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
114
115
|
type="button"
|
|
115
116
|
>
|
|
116
117
|
<Plus className="h-3 w-3" />
|
|
117
|
-
New
|
|
118
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.new', message: 'New' })}
|
|
118
119
|
</button>
|
|
119
120
|
</div>
|
|
120
121
|
|
|
@@ -132,7 +133,7 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
132
133
|
setCreateName('')
|
|
133
134
|
}
|
|
134
135
|
}}
|
|
135
|
-
placeholder=
|
|
136
|
+
placeholder={i18n._({ id: 'editor.panels.collections.collectionsPopover.collectionName', message: 'Collection name…' })}
|
|
136
137
|
value={createName}
|
|
137
138
|
/>
|
|
138
139
|
<button
|
|
@@ -162,7 +163,7 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
162
163
|
<div className="flex flex-col items-center justify-center gap-2 px-4 py-8 text-center">
|
|
163
164
|
<Layers className="h-6 w-6 text-muted-foreground/40" />
|
|
164
165
|
<p className="text-muted-foreground text-xs">
|
|
165
|
-
No collections yet. Create one to group elements together.
|
|
166
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.noCollectionsYetCreateOneTo', message: 'No collections yet. Create one to group elements together.' })}
|
|
166
167
|
</p>
|
|
167
168
|
</div>
|
|
168
169
|
) : (
|
|
@@ -182,8 +183,7 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
182
183
|
key={collection.id}
|
|
183
184
|
>
|
|
184
185
|
<span className="truncate text-foreground/80 text-xs">
|
|
185
|
-
Delete "{collection.name}
|
|
186
|
-
</span>
|
|
186
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.deleteName', message: 'Delete "{name}"?', values: { name: collection.name } })}</span>
|
|
187
187
|
<div className="flex shrink-0 items-center gap-1">
|
|
188
188
|
<button
|
|
189
189
|
className="rounded-md bg-red-500/20 px-2 py-0.5 font-medium text-[11px] text-red-600 dark:text-red-400 transition-colors hover:bg-red-500/30"
|
|
@@ -193,14 +193,14 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
193
193
|
}}
|
|
194
194
|
type="button"
|
|
195
195
|
>
|
|
196
|
-
Delete
|
|
196
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.delete', message: 'Delete' })}
|
|
197
197
|
</button>
|
|
198
198
|
<button
|
|
199
199
|
className="rounded-md px-2 py-0.5 font-medium text-[11px] text-muted-foreground transition-colors hover:bg-black/6 dark:hover:bg-white/10"
|
|
200
200
|
onClick={() => setDeletingId(null)}
|
|
201
201
|
type="button"
|
|
202
202
|
>
|
|
203
|
-
Cancel
|
|
203
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.cancel', message: 'Cancel' })}
|
|
204
204
|
</button>
|
|
205
205
|
</div>
|
|
206
206
|
</li>
|
|
@@ -314,14 +314,14 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
314
314
|
}}
|
|
315
315
|
>
|
|
316
316
|
<Pencil className="h-3.5 w-3.5" />
|
|
317
|
-
Rename
|
|
317
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.rename', message: 'Rename' })}
|
|
318
318
|
</DropdownMenuItem>
|
|
319
319
|
<DropdownMenuItem
|
|
320
320
|
onClick={() => setDeletingId(collection.id)}
|
|
321
321
|
variant="destructive"
|
|
322
322
|
>
|
|
323
323
|
<Trash2 className="h-3.5 w-3.5" />
|
|
324
|
-
Delete
|
|
324
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.delete', message: 'Delete' })}
|
|
325
325
|
</DropdownMenuItem>
|
|
326
326
|
</DropdownMenuContent>
|
|
327
327
|
</DropdownMenu>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type AnyNodeId,
|
|
5
|
+
DECK_BOARD_GAPS,
|
|
6
|
+
DECK_BOARD_SIZES,
|
|
7
|
+
DECK_BOARD_STOCK,
|
|
8
|
+
type DeckBoardDirection,
|
|
9
|
+
type DeckBoardSize,
|
|
10
|
+
deckBoardSettings,
|
|
11
|
+
deckJoistSpacingLimit,
|
|
12
|
+
inchFractionLabel,
|
|
13
|
+
type MessageDescriptor,
|
|
14
|
+
setZoneIntent,
|
|
15
|
+
usePascalI18n,
|
|
16
|
+
useScene,
|
|
17
|
+
} from '@pascal-app/core'
|
|
18
|
+
import type { ReactNode } from 'react'
|
|
19
|
+
import { useShallow } from 'zustand/react/shallow'
|
|
20
|
+
import { applyRoomPlan } from '../../../lib/room-structure-commands'
|
|
21
|
+
import { SegmentedControl } from '../controls/segmented-control'
|
|
22
|
+
|
|
23
|
+
const IN = 0.0254
|
|
24
|
+
|
|
25
|
+
const BOARD_OPTIONS = DECK_BOARD_SIZES.map((value) => ({
|
|
26
|
+
label: DECK_BOARD_STOCK[value].label,
|
|
27
|
+
value,
|
|
28
|
+
}))
|
|
29
|
+
|
|
30
|
+
const GAP_OPTIONS = DECK_BOARD_GAPS.map((gap, index) => ({
|
|
31
|
+
label: `${inchFractionLabel(gap)} in`,
|
|
32
|
+
value: String(index),
|
|
33
|
+
}))
|
|
34
|
+
|
|
35
|
+
const RUN_OPTIONS: ReadonlyArray<{ label: MessageDescriptor; value: DeckBoardDirection }> = [
|
|
36
|
+
{
|
|
37
|
+
label: /* i18n */ {
|
|
38
|
+
id: 'editor.panels.deckBoardsFields.alongHouse',
|
|
39
|
+
message: 'Along house',
|
|
40
|
+
},
|
|
41
|
+
value: 'parallel',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: /* i18n */ { id: 'editor.panels.deckBoardsFields.diagonal', message: 'Diagonal' },
|
|
45
|
+
value: 'diagonal',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
label: /* i18n */ {
|
|
49
|
+
id: 'editor.panels.deckBoardsFields.outFromHouse',
|
|
50
|
+
message: 'Out from house',
|
|
51
|
+
},
|
|
52
|
+
value: 'perpendicular',
|
|
53
|
+
},
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
function Field({ label, children }: { label: string; children: ReactNode }) {
|
|
57
|
+
return (
|
|
58
|
+
<div className="flex min-h-7 items-center justify-between gap-3">
|
|
59
|
+
<span className="shrink-0">{label}</span>
|
|
60
|
+
{children}
|
|
61
|
+
</div>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A deck's boards (`decking`, through core's `setZoneIntent`): their stock, the gap between them
|
|
67
|
+
* and the way they run against the house. The engine lays them over the deck's frame, and the
|
|
68
|
+
* frame follows: joists 12 in on centre under diagonal 5/4 or composite boards (IRC R507.7), and
|
|
69
|
+
* along the house on beams square to it under boards run out from it.
|
|
70
|
+
*/
|
|
71
|
+
export function DeckBoardsFields({ zoneId }: { zoneId: string }) {
|
|
72
|
+
const i18n = usePascalI18n()
|
|
73
|
+
const deck = useScene(
|
|
74
|
+
useShallow((scene) => {
|
|
75
|
+
const zone = scene.nodes[zoneId as AnyNodeId]
|
|
76
|
+
if (zone?.type !== 'zone' || zone.roomType !== 'deck') return null
|
|
77
|
+
return deckBoardSettings(zone)
|
|
78
|
+
}),
|
|
79
|
+
)
|
|
80
|
+
if (!deck) return null
|
|
81
|
+
const set = (decking: NonNullable<Parameters<typeof setZoneIntent>[1]['patch']['decking']>) =>
|
|
82
|
+
applyRoomPlan(setZoneIntent(useScene.getState().nodes, { zoneId, patch: { decking } }))
|
|
83
|
+
const stock = DECK_BOARD_STOCK[deck.boardSize]
|
|
84
|
+
const gap = DECK_BOARD_GAPS.findIndex((option) => Math.abs(option - deck.gap) < 1e-6)
|
|
85
|
+
const spacing = Math.min(16 * IN, deckJoistSpacingLimit(deck))
|
|
86
|
+
return (
|
|
87
|
+
<div
|
|
88
|
+
className="mt-3 flex flex-col gap-1.5 text-muted-foreground text-xs"
|
|
89
|
+
data-deck-boards={deck.boardSize}
|
|
90
|
+
>
|
|
91
|
+
<h3 className="font-medium text-xs">
|
|
92
|
+
{i18n._({ id: 'editor.panels.deckBoardsFields.deckBoards', message: 'Deck boards' })}
|
|
93
|
+
</h3>
|
|
94
|
+
<Field label={i18n._({ id: 'editor.panels.deckBoardsFields.boards', message: 'Boards' })}>
|
|
95
|
+
<SegmentedControl
|
|
96
|
+
onChange={(boardSize: DeckBoardSize) => set({ boardSize })}
|
|
97
|
+
options={BOARD_OPTIONS}
|
|
98
|
+
value={deck.boardSize}
|
|
99
|
+
/>
|
|
100
|
+
</Field>
|
|
101
|
+
<Field label={i18n._({ id: 'editor.panels.deckBoardsFields.gap', message: 'Gap' })}>
|
|
102
|
+
<SegmentedControl
|
|
103
|
+
mixed={gap < 0}
|
|
104
|
+
onChange={(index: string) => set({ gap: DECK_BOARD_GAPS[Number(index)] })}
|
|
105
|
+
options={GAP_OPTIONS}
|
|
106
|
+
value={String(Math.max(gap, 0))}
|
|
107
|
+
/>
|
|
108
|
+
</Field>
|
|
109
|
+
<Field label={i18n._({ id: 'editor.panels.deckBoardsFields.run', message: 'Run' })}>
|
|
110
|
+
<SegmentedControl
|
|
111
|
+
onChange={(direction: DeckBoardDirection) => set({ direction })}
|
|
112
|
+
options={RUN_OPTIONS.map((option) => ({
|
|
113
|
+
label: i18n._(option.label),
|
|
114
|
+
value: option.value,
|
|
115
|
+
}))}
|
|
116
|
+
value={deck.direction}
|
|
117
|
+
/>
|
|
118
|
+
</Field>
|
|
119
|
+
<p>
|
|
120
|
+
{i18n._({
|
|
121
|
+
id: 'editor.panels.deckBoardsFields.boardsSummary',
|
|
122
|
+
message:
|
|
123
|
+
'{label} boards {thickness} × {width} in, {gap} in apart, {direction, select, parallel {along the house, on joists square to it} diagonal {at 45° to the house} other {out from the house, on joists along it, carried by beams square to the house}}; joists {spacing} in on centre.',
|
|
124
|
+
values: {
|
|
125
|
+
label: stock.label,
|
|
126
|
+
thickness: inchFractionLabel(stock.thickness),
|
|
127
|
+
width: inchFractionLabel(stock.width),
|
|
128
|
+
gap: inchFractionLabel(deck.gap),
|
|
129
|
+
direction: deck.direction,
|
|
130
|
+
spacing: Math.round(spacing / IN),
|
|
131
|
+
},
|
|
132
|
+
})}
|
|
133
|
+
</p>
|
|
134
|
+
</div>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
@@ -4,19 +4,22 @@ import {
|
|
|
4
4
|
type AnyNode,
|
|
5
5
|
type AnyNodeId,
|
|
6
6
|
type FloorFoundationPatch,
|
|
7
|
-
floorFootprintName,
|
|
8
7
|
floorFootprintSupportClass,
|
|
9
8
|
upperFloorHeightControl,
|
|
10
|
-
getLevelDisplayName,
|
|
11
9
|
getLevelElevations,
|
|
12
10
|
getStoredLevelHeight,
|
|
13
11
|
type LevelNode,
|
|
14
12
|
MIN_GROUND_FLOOR_THICKNESS,
|
|
15
13
|
MIN_SLAB_THICKNESS,
|
|
14
|
+
type PascalI18n,
|
|
16
15
|
type SlabNode,
|
|
16
|
+
useLocale,
|
|
17
|
+
usePascalI18n,
|
|
17
18
|
useScene,
|
|
18
19
|
} from '@pascal-app/core'
|
|
19
20
|
import { useViewer } from '@pascal-app/viewer'
|
|
21
|
+
import { localizedFloorFootprintName } from '../../../i18n/floor-footprint-name'
|
|
22
|
+
import { localizedLevelName } from '../../../i18n/level-name'
|
|
20
23
|
import { useRef, useState } from 'react'
|
|
21
24
|
import {
|
|
22
25
|
applyFloorFoundation,
|
|
@@ -78,6 +81,7 @@ export function FloorSection({
|
|
|
78
81
|
thickness: number
|
|
79
82
|
thicknessLabel: string
|
|
80
83
|
}) {
|
|
84
|
+
const i18n = usePascalI18n()
|
|
81
85
|
const base = 100
|
|
82
86
|
const foundation = !supported && raised ? Math.min(40, 14 + foundationHeight * 30) : 0
|
|
83
87
|
const band = Math.max(7, Math.min(16, thickness * 60))
|
|
@@ -98,32 +102,67 @@ export function FloorSection({
|
|
|
98
102
|
const left = 64
|
|
99
103
|
const right = 164
|
|
100
104
|
const labels: { y: number; text: string; key: string }[] = [
|
|
101
|
-
{
|
|
105
|
+
{
|
|
106
|
+
key: 'top',
|
|
107
|
+
y: bandTop - 9,
|
|
108
|
+
text: i18n._({ id: 'editor.panels.floorFoundationPanel.section.floorTop', message: 'Floor top' }),
|
|
109
|
+
},
|
|
102
110
|
{
|
|
103
111
|
key: 'plate',
|
|
104
112
|
y: bandTop + band / 2,
|
|
105
|
-
text: supported
|
|
113
|
+
text: supported
|
|
114
|
+
? i18n._({
|
|
115
|
+
id: 'editor.panels.floorFoundationPanel.section.floorPlate',
|
|
116
|
+
message: 'Floor plate · {thickness}',
|
|
117
|
+
values: { thickness: thicknessLabel },
|
|
118
|
+
})
|
|
119
|
+
: i18n._({
|
|
120
|
+
id: 'editor.panels.floorFoundationPanel.section.slab',
|
|
121
|
+
message: 'Slab · {thickness}',
|
|
122
|
+
values: { thickness: thicknessLabel },
|
|
123
|
+
}),
|
|
106
124
|
},
|
|
107
125
|
...(foundation
|
|
108
126
|
? [
|
|
109
127
|
{
|
|
110
128
|
key: 'foundation',
|
|
111
129
|
y: bandBottom + foundation / 2,
|
|
112
|
-
text: foundationLabel
|
|
130
|
+
text: foundationLabel
|
|
131
|
+
? i18n._({
|
|
132
|
+
id: 'editor.panels.floorFoundationPanel.section.foundationKind',
|
|
133
|
+
message: 'Foundation · {foundation}',
|
|
134
|
+
values: { foundation: foundationLabel },
|
|
135
|
+
})
|
|
136
|
+
: i18n._({ id: 'editor.panels.floorFoundationPanel.section.foundation', message: 'Foundation' }),
|
|
113
137
|
},
|
|
114
138
|
]
|
|
115
139
|
: []),
|
|
116
|
-
{
|
|
140
|
+
{
|
|
141
|
+
key: 'base',
|
|
142
|
+
y: base + 9,
|
|
143
|
+
text: supported
|
|
144
|
+
? i18n._({ id: 'editor.panels.floorFoundationPanel.section.wallsBelow', message: 'Walls below' })
|
|
145
|
+
: i18n._({ id: 'editor.panels.floorFoundationPanel.section.ground', message: 'Ground' }),
|
|
146
|
+
},
|
|
117
147
|
]
|
|
118
148
|
return (
|
|
119
149
|
<figure className="flex flex-col gap-1" data-floor-section>
|
|
120
150
|
<svg
|
|
121
151
|
aria-label={
|
|
122
152
|
supported
|
|
123
|
-
?
|
|
153
|
+
? i18n._({ id: 'editor.panels.floorFoundationPanel.floorPlateThicknesslabelThickRestingOn', message: 'Floor plate {thicknessLabel} thick, resting on the walls below', values: { thicknessLabel } })
|
|
124
154
|
: raised
|
|
125
|
-
?
|
|
126
|
-
|
|
155
|
+
? i18n._({
|
|
156
|
+
id: 'editor.panels.floorFoundationPanel.sectionRaised',
|
|
157
|
+
message:
|
|
158
|
+
'Slab {thickness} thick on a {foundation} foundation, floor top {top} above the ground',
|
|
159
|
+
values: { thickness: thicknessLabel, foundation: foundationLabel, top: topLabel ?? '' },
|
|
160
|
+
})
|
|
161
|
+
: i18n._({
|
|
162
|
+
id: 'editor.panels.floorFoundationPanel.sectionOnGround',
|
|
163
|
+
message: 'Slab {thickness} thick on the ground, floor top {top} above the ground',
|
|
164
|
+
values: { thickness: thicknessLabel, top: topLabel ?? '' },
|
|
165
|
+
})
|
|
127
166
|
}
|
|
128
167
|
className="h-auto w-full text-muted-foreground"
|
|
129
168
|
role="img"
|
|
@@ -259,7 +298,7 @@ export function FloorSection({
|
|
|
259
298
|
))}
|
|
260
299
|
</svg>
|
|
261
300
|
<figcaption className="text-right text-[10px] text-muted-foreground/70">
|
|
262
|
-
Not to scale
|
|
301
|
+
{i18n._({ id: 'editor.panels.floorFoundationPanel.notToScale', message: 'Not to scale' })}
|
|
263
302
|
</figcaption>
|
|
264
303
|
</figure>
|
|
265
304
|
)
|
|
@@ -271,15 +310,29 @@ type Notice = {
|
|
|
271
310
|
together: { ids: string[]; patch: FloorFoundationPatch; lower: boolean } | null
|
|
272
311
|
}
|
|
273
312
|
|
|
274
|
-
export function moveTogetherLabel(count: number, lower: boolean) {
|
|
275
|
-
const
|
|
276
|
-
return
|
|
313
|
+
export function moveTogetherLabel(count: number, lower: boolean, i18n: PascalI18n) {
|
|
314
|
+
const values = { count, number: String(count) }
|
|
315
|
+
return lower
|
|
316
|
+
? i18n._({
|
|
317
|
+
id: 'editor.panels.floorFoundationPanel.lowerTogether',
|
|
318
|
+
message: '{count, plural, =2 {Lower both} other {Lower all {number}}}',
|
|
319
|
+
values,
|
|
320
|
+
})
|
|
321
|
+
: i18n._({
|
|
322
|
+
id: 'editor.panels.floorFoundationPanel.raiseTogether',
|
|
323
|
+
message: '{count, plural, =2 {Raise both} other {Raise all {number}}}',
|
|
324
|
+
values,
|
|
325
|
+
})
|
|
277
326
|
}
|
|
278
327
|
|
|
279
328
|
/** "Floor plate · Ground floor", upstairs "Floor plate · Floor 1 · rests on Ground floor". */
|
|
280
|
-
export function floorPlateKicker(
|
|
329
|
+
export function floorPlateKicker(
|
|
330
|
+
nodes: Record<string, AnyNode>,
|
|
331
|
+
plate: SlabNode,
|
|
332
|
+
i18n: PascalI18n,
|
|
333
|
+
locale: string,
|
|
334
|
+
): string {
|
|
281
335
|
const level = plate.parentId ? nodes[plate.parentId] : undefined
|
|
282
|
-
const levelName = level?.type === 'level' ? ` · ${getLevelDisplayName(level)}` : ''
|
|
283
336
|
// A supported floor rests on the storey right below it.
|
|
284
337
|
let below: LevelNode | null = null
|
|
285
338
|
if (level?.type === 'level' && floorFootprintSupportClass(nodes, plate) === 'supported')
|
|
@@ -291,8 +344,20 @@ export function floorPlateKicker(nodes: Record<string, AnyNode>, plate: SlabNode
|
|
|
291
344
|
(!below || node.level > below.level)
|
|
292
345
|
)
|
|
293
346
|
below = node
|
|
294
|
-
|
|
295
|
-
|
|
347
|
+
if (level?.type !== 'level')
|
|
348
|
+
return i18n._({ id: 'editor.panels.floorFoundationPanel.kicker', message: 'Floor plate' })
|
|
349
|
+
const levelName = localizedLevelName(level, i18n, locale)
|
|
350
|
+
return below
|
|
351
|
+
? i18n._({
|
|
352
|
+
id: 'editor.panels.floorFoundationPanel.kickerRestsOn',
|
|
353
|
+
message: 'Floor plate · {level} · rests on {below}',
|
|
354
|
+
values: { level: levelName, below: localizedLevelName(below, i18n, locale) },
|
|
355
|
+
})
|
|
356
|
+
: i18n._({
|
|
357
|
+
id: 'editor.panels.floorFoundationPanel.kickerLevel',
|
|
358
|
+
message: 'Floor plate · {level}',
|
|
359
|
+
values: { level: levelName },
|
|
360
|
+
})
|
|
296
361
|
}
|
|
297
362
|
|
|
298
363
|
/**
|
|
@@ -311,7 +376,6 @@ export function floorFoundationModel(nodes: Record<string, AnyNode>, plate: Slab
|
|
|
311
376
|
: 0
|
|
312
377
|
return {
|
|
313
378
|
upper,
|
|
314
|
-
kicker: floorPlateKicker(nodes, plate),
|
|
315
379
|
preset: upper ? ('ground' as const) : footprintPreset(nodes, plate),
|
|
316
380
|
/** Upstairs: the floor top (level-local). On the ground: the foundation height. */
|
|
317
381
|
height,
|
|
@@ -332,6 +396,8 @@ export function floorFoundationModel(nodes: Record<string, AnyNode>, plate: Slab
|
|
|
332
396
|
* the building live and lands as one undo step.
|
|
333
397
|
*/
|
|
334
398
|
export function FloorFoundationPanel({ node, onClose }: { node: SlabNode; onClose: () => void }) {
|
|
399
|
+
const i18n = usePascalI18n()
|
|
400
|
+
const locale = useLocale()
|
|
335
401
|
const nodes = useScene((state) => state.nodes)
|
|
336
402
|
const unit = useViewer((s) => s.unit)
|
|
337
403
|
const metricNotation = useViewer((s) => s.metricNotation)
|
|
@@ -341,7 +407,8 @@ export function FloorFoundationPanel({ node, onClose }: { node: SlabNode; onClos
|
|
|
341
407
|
const plate = nodes[node.id as AnyNodeId]
|
|
342
408
|
if (plate?.type !== 'slab' || plate.plateRole !== 'base') return null
|
|
343
409
|
const length = (meters: number) => formatLinearMeasurement(meters, unit, metricNotation)
|
|
344
|
-
const
|
|
410
|
+
const kicker = floorPlateKicker(nodes, plate, i18n, locale)
|
|
411
|
+
const { upper, preset, height, floorTop, minHeight, minThickness, underFloor, thickFloor } =
|
|
345
412
|
floorFoundationModel(nodes, plate)
|
|
346
413
|
const level = plate.parentId ? nodes[plate.parentId as AnyNodeId] : undefined
|
|
347
414
|
const maxHeight = level?.type === 'level' ? getStoredLevelHeight(level) - 0.5 : 3
|
|
@@ -389,7 +456,7 @@ export function FloorFoundationPanel({ node, onClose }: { node: SlabNode; onClos
|
|
|
389
456
|
icon="/icons/floor.webp"
|
|
390
457
|
kicker={kicker}
|
|
391
458
|
onClose={onClose}
|
|
392
|
-
title={
|
|
459
|
+
title={localizedFloorFootprintName(nodes, plate, i18n)}
|
|
393
460
|
>
|
|
394
461
|
<div
|
|
395
462
|
className="flex flex-col gap-3 px-4 pt-3 pb-4 text-sm"
|
|
@@ -407,13 +474,13 @@ export function FloorFoundationPanel({ node, onClose }: { node: SlabNode; onClos
|
|
|
407
474
|
/>
|
|
408
475
|
<section className="flex flex-col gap-1.5" data-floor-plate-group>
|
|
409
476
|
<h3 className="font-medium text-muted-foreground text-xs">
|
|
410
|
-
{upper ? 'Floor plate' : 'Slab'}
|
|
477
|
+
{upper ? i18n._({ id: 'editor.panels.floorFoundationPanel.floorPlate', message: 'Floor plate' }) : i18n._({ id: 'editor.panels.floorFoundationPanel.slab', message: 'Slab' })}
|
|
411
478
|
</h3>
|
|
412
479
|
{upper ? (
|
|
413
|
-
<div title=
|
|
480
|
+
<div title={i18n._({ id: 'editor.panels.floorFoundationPanel.aThickerFloorGrowsUpwardThe', message: 'A thicker floor grows upward: the rooms below keep their height.' })}>
|
|
414
481
|
<MetricControl
|
|
415
482
|
className="h-9 text-xs"
|
|
416
|
-
label=
|
|
483
|
+
label={i18n._({ id: 'editor.panels.floorFoundationPanel.floorThickness', message: 'Floor thickness' })}
|
|
417
484
|
max={Math.max(MIN_SLAB_THICKNESS, maxHeight - underside)}
|
|
418
485
|
min={Math.max(MIN_SLAB_THICKNESS, minHeight - underside)}
|
|
419
486
|
onChange={(next) => previewHeight(underside + next)}
|
|
@@ -426,10 +493,10 @@ export function FloorFoundationPanel({ node, onClose }: { node: SlabNode; onClos
|
|
|
426
493
|
/>
|
|
427
494
|
</div>
|
|
428
495
|
) : (
|
|
429
|
-
<div title=
|
|
496
|
+
<div title={i18n._({ id: 'editor.panels.floorFoundationPanel.theSlabSitsOnTheFoundation', message: 'The slab sits on the foundation (or the ground): a thicker slab raises the floor top and everything on it.' })}>
|
|
430
497
|
<MetricControl
|
|
431
498
|
className="h-9 text-xs"
|
|
432
|
-
label=
|
|
499
|
+
label={i18n._({ id: 'editor.panels.floorFoundationPanel.slabThickness', message: 'Slab thickness' })}
|
|
433
500
|
max={1}
|
|
434
501
|
min={minThickness}
|
|
435
502
|
onChange={(next) => {
|
|
@@ -454,15 +521,19 @@ export function FloorFoundationPanel({ node, onClose }: { node: SlabNode; onClos
|
|
|
454
521
|
)}
|
|
455
522
|
{upper && (
|
|
456
523
|
<p className="px-0.5 text-muted-foreground text-xs" data-floor-top>
|
|
457
|
-
|
|
524
|
+
{i18n._({
|
|
525
|
+
id: 'editor.panels.floorFoundationPanel.floorTopAbove',
|
|
526
|
+
message: 'Floor top +{height}',
|
|
527
|
+
values: { height: length(floorTop) },
|
|
528
|
+
})}
|
|
458
529
|
</p>
|
|
459
530
|
)}
|
|
460
531
|
<FinishSwatch
|
|
461
532
|
fallback={EDGE_DEFAULT}
|
|
462
|
-
hint=
|
|
463
|
-
label=
|
|
533
|
+
hint={i18n._({ id: 'editor.panels.floorFoundationPanel.theFloorSOutsideEdgeAll', message: 'The floor\'s outside edge, all around the footprint.' })}
|
|
534
|
+
label={i18n._({ id: 'editor.panels.floorFoundationPanel.edgeFinish', message: 'Edge finish' })}
|
|
464
535
|
node={plate}
|
|
465
|
-
|
|
536
|
+
paintRole="edge"
|
|
466
537
|
value={plate.slots?.edge}
|
|
467
538
|
/>
|
|
468
539
|
{thickFloor && (
|
|
@@ -473,7 +544,7 @@ export function FloorFoundationPanel({ node, onClose }: { node: SlabNode; onClos
|
|
|
473
544
|
</section>
|
|
474
545
|
{underFloor && (
|
|
475
546
|
<section className="flex flex-col gap-1.5" data-under-floor-group>
|
|
476
|
-
<h3 className="font-medium text-muted-foreground text-xs">Under the floor</h3>
|
|
547
|
+
<h3 className="font-medium text-muted-foreground text-xs">{i18n._({ id: 'editor.panels.floorFoundationPanel.underTheFloor', message: 'Under the floor' })}</h3>
|
|
477
548
|
<SegmentedControl
|
|
478
549
|
onChange={(next: FootprintPreset) => {
|
|
479
550
|
if (next === preset) return
|
|
@@ -481,17 +552,17 @@ export function FloorFoundationPanel({ node, onClose }: { node: SlabNode; onClos
|
|
|
481
552
|
report(applyFloorFoundation(plate.id, patch), patch)
|
|
482
553
|
}}
|
|
483
554
|
options={[
|
|
484
|
-
{ label: 'On the ground', value: 'ground' },
|
|
485
|
-
{ label: 'Raised on a foundation', value: 'raised' },
|
|
555
|
+
{ label: i18n._({ id: 'editor.panels.floorFoundationPanel.onTheGround', message: 'On the ground' }), value: 'ground' },
|
|
556
|
+
{ label: i18n._({ id: 'editor.panels.floorFoundationPanel.raisedOnAFoundation', message: 'Raised on a foundation' }), value: 'raised' },
|
|
486
557
|
]}
|
|
487
558
|
value={preset}
|
|
488
559
|
/>
|
|
489
560
|
{preset === 'raised' && (
|
|
490
561
|
<>
|
|
491
|
-
<div title=
|
|
562
|
+
<div title={i18n._({ id: 'editor.panels.floorFoundationPanel.theFoundationStandsOnTheGround', message: 'The foundation stands on the ground; the slab sits on it.' })}>
|
|
492
563
|
<MetricControl
|
|
493
564
|
className="h-9 text-xs"
|
|
494
|
-
label=
|
|
565
|
+
label={i18n._({ id: 'editor.panels.floorFoundationPanel.foundationHeight', message: 'Foundation height' })}
|
|
495
566
|
max={Math.max(0.05, maxHeight)}
|
|
496
567
|
min={minHeight}
|
|
497
568
|
onChange={previewHeight}
|
|
@@ -505,9 +576,9 @@ export function FloorFoundationPanel({ node, onClose }: { node: SlabNode; onClos
|
|
|
505
576
|
</div>
|
|
506
577
|
<FinishSwatch
|
|
507
578
|
fallback={FOUNDATION_DEFAULT}
|
|
508
|
-
label=
|
|
579
|
+
label={i18n._({ id: 'editor.panels.floorFoundationPanel.foundationFinish', message: 'Foundation finish' })}
|
|
509
580
|
node={plate}
|
|
510
|
-
|
|
581
|
+
paintRole="foundation"
|
|
511
582
|
value={
|
|
512
583
|
typeof plate.foundation?.material === 'string'
|
|
513
584
|
? plate.foundation.material
|
|
@@ -517,7 +588,11 @@ export function FloorFoundationPanel({ node, onClose }: { node: SlabNode; onClos
|
|
|
517
588
|
</>
|
|
518
589
|
)}
|
|
519
590
|
<p className="px-0.5 text-muted-foreground text-xs" data-floor-top>
|
|
520
|
-
|
|
591
|
+
{i18n._({
|
|
592
|
+
id: 'editor.panels.floorFoundationPanel.floorTopValue',
|
|
593
|
+
message: 'Floor top {height}',
|
|
594
|
+
values: { height: length(floorTop) },
|
|
595
|
+
})}
|
|
521
596
|
</p>
|
|
522
597
|
</section>
|
|
523
598
|
)}
|
|
@@ -540,7 +615,7 @@ export function FloorFoundationPanel({ node, onClose }: { node: SlabNode; onClos
|
|
|
540
615
|
type="button"
|
|
541
616
|
variant="outline"
|
|
542
617
|
>
|
|
543
|
-
{moveTogetherLabel(notice.together.ids.length, notice.together.lower)}
|
|
618
|
+
{moveTogetherLabel(notice.together.ids.length, notice.together.lower, i18n)}
|
|
544
619
|
</Button>
|
|
545
620
|
)}
|
|
546
621
|
</div>
|