Hide inapplicable door and window panel settings
This commit is contained in:
@@ -127,8 +127,11 @@ export function DoorPanel() {
|
|||||||
const handleUpdate = useCallback(
|
const handleUpdate = useCallback(
|
||||||
(updates: Partial<DoorNode>) => {
|
(updates: Partial<DoorNode>) => {
|
||||||
if (!(selectedId && node)) return
|
if (!(selectedId && node)) return
|
||||||
|
const liveNode = useScene.getState().nodes[selectedId as AnyNodeId]
|
||||||
|
if (liveNode?.type !== 'door') return
|
||||||
|
|
||||||
const hasChange = Object.entries(updates).some(([key, value]) => {
|
const hasChange = Object.entries(updates).some(([key, value]) => {
|
||||||
const currentValue = node[key as keyof DoorNode]
|
const currentValue = liveNode[key as keyof DoorNode]
|
||||||
return !isSameDoorValue(currentValue, value)
|
return !isSameDoorValue(currentValue, value)
|
||||||
})
|
})
|
||||||
if (!hasChange) return
|
if (!hasChange) return
|
||||||
@@ -137,7 +140,9 @@ export function DoorPanel() {
|
|||||||
useInteractive.getState().removeDoorOpenState(selectedId as AnyNodeId)
|
useInteractive.getState().removeDoorOpenState(selectedId as AnyNodeId)
|
||||||
}
|
}
|
||||||
updateNode(selectedId as AnyNode['id'], updates)
|
updateNode(selectedId as AnyNode['id'], updates)
|
||||||
useScene.getState().dirtyNodes.add(selectedId as AnyNodeId)
|
const scene = useScene.getState()
|
||||||
|
scene.dirtyNodes.add(selectedId as AnyNodeId)
|
||||||
|
if (liveNode.parentId) scene.dirtyNodes.add(liveNode.parentId as AnyNodeId)
|
||||||
},
|
},
|
||||||
[selectedId, node, updateNode],
|
[selectedId, node, updateNode],
|
||||||
)
|
)
|
||||||
@@ -348,7 +353,9 @@ export function DoorPanel() {
|
|||||||
const isRollupGarageDoor = doorType === 'garage-rollup'
|
const isRollupGarageDoor = doorType === 'garage-rollup'
|
||||||
const isTiltupGarageDoor = doorType === 'garage-tiltup'
|
const isTiltupGarageDoor = doorType === 'garage-tiltup'
|
||||||
const typeMode = isOpening ? 'opening' : isGarageDoor ? 'garage' : 'door'
|
const typeMode = isOpening ? 'opening' : isGarageDoor ? 'garage' : 'door'
|
||||||
const supportsHandleSide = isSwingDoor
|
const supportsHingeSide = doorType === 'hinged'
|
||||||
|
const supportsHandleSide = doorType === 'hinged'
|
||||||
|
const supportsTopShape = !isGarageDoor
|
||||||
const maxDoorWidth = isGarageDoor ? 6 : 3
|
const maxDoorWidth = isGarageDoor ? 6 : 3
|
||||||
|
|
||||||
const setOpeningTopRadius = (index: number, value: number, commit = false) => {
|
const setOpeningTopRadius = (index: number, value: number, commit = false) => {
|
||||||
@@ -395,6 +402,7 @@ export function DoorPanel() {
|
|||||||
handleSide: 'right',
|
handleSide: 'right',
|
||||||
trackStyle: 'visible',
|
trackStyle: 'visible',
|
||||||
operationState: Math.max(node.operationState ?? 0, 0.65),
|
operationState: Math.max(node.operationState ?? 0, 0.65),
|
||||||
|
threshold: false,
|
||||||
contentPadding: [0.03, 0.04],
|
contentPadding: [0.03, 0.04],
|
||||||
segments: foldingDoorSegments,
|
segments: foldingDoorSegments,
|
||||||
}
|
}
|
||||||
@@ -411,6 +419,7 @@ export function DoorPanel() {
|
|||||||
trackStyle: 'pocket',
|
trackStyle: 'pocket',
|
||||||
slideDirection: node.slideDirection ?? 'left',
|
slideDirection: node.slideDirection ?? 'left',
|
||||||
operationState: node.operationState ?? 0,
|
operationState: node.operationState ?? 0,
|
||||||
|
threshold: false,
|
||||||
contentPadding: [0.035, 0.045],
|
contentPadding: [0.035, 0.045],
|
||||||
segments: foldingDoorSegments,
|
segments: foldingDoorSegments,
|
||||||
}
|
}
|
||||||
@@ -427,6 +436,7 @@ export function DoorPanel() {
|
|||||||
trackStyle: 'visible',
|
trackStyle: 'visible',
|
||||||
slideDirection: node.slideDirection ?? 'left',
|
slideDirection: node.slideDirection ?? 'left',
|
||||||
operationState: node.operationState ?? 0,
|
operationState: node.operationState ?? 0,
|
||||||
|
threshold: false,
|
||||||
contentPadding: [0.035, 0.045],
|
contentPadding: [0.035, 0.045],
|
||||||
segments: foldingDoorSegments,
|
segments: foldingDoorSegments,
|
||||||
}
|
}
|
||||||
@@ -443,6 +453,7 @@ export function DoorPanel() {
|
|||||||
trackStyle: 'visible',
|
trackStyle: 'visible',
|
||||||
slideDirection: node.slideDirection ?? 'left',
|
slideDirection: node.slideDirection ?? 'left',
|
||||||
operationState: node.operationState ?? 0,
|
operationState: node.operationState ?? 0,
|
||||||
|
threshold: false,
|
||||||
contentPadding: [0.03, 0.04],
|
contentPadding: [0.03, 0.04],
|
||||||
segments: frenchDoorSegments,
|
segments: frenchDoorSegments,
|
||||||
}
|
}
|
||||||
@@ -456,6 +467,7 @@ export function DoorPanel() {
|
|||||||
...dimensionUpdates,
|
...dimensionUpdates,
|
||||||
handle: false,
|
handle: false,
|
||||||
threshold: false,
|
threshold: false,
|
||||||
|
openingShape: 'rectangle',
|
||||||
trackStyle: 'overhead',
|
trackStyle: 'overhead',
|
||||||
operationState: 0,
|
operationState: 0,
|
||||||
garagePanelCount: Math.max(3, Math.min(8, node.garagePanelCount ?? 4)),
|
garagePanelCount: Math.max(3, Math.min(8, node.garagePanelCount ?? 4)),
|
||||||
@@ -472,6 +484,7 @@ export function DoorPanel() {
|
|||||||
...dimensionUpdates,
|
...dimensionUpdates,
|
||||||
handle: false,
|
handle: false,
|
||||||
threshold: false,
|
threshold: false,
|
||||||
|
openingShape: 'rectangle',
|
||||||
trackStyle: 'overhead',
|
trackStyle: 'overhead',
|
||||||
operationState: 0,
|
operationState: 0,
|
||||||
garagePanelCount: 4,
|
garagePanelCount: 4,
|
||||||
@@ -488,6 +501,7 @@ export function DoorPanel() {
|
|||||||
...dimensionUpdates,
|
...dimensionUpdates,
|
||||||
handle: false,
|
handle: false,
|
||||||
threshold: false,
|
threshold: false,
|
||||||
|
openingShape: 'rectangle',
|
||||||
trackStyle: 'overhead',
|
trackStyle: 'overhead',
|
||||||
operationState: 0,
|
operationState: 0,
|
||||||
garagePanelCount: 4,
|
garagePanelCount: 4,
|
||||||
@@ -738,7 +752,7 @@ export function DoorPanel() {
|
|||||||
/>
|
/>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
|
|
||||||
{!isOpening && (
|
{!isOpening && supportsTopShape && (
|
||||||
<PanelSection title="Top Shape">
|
<PanelSection title="Top Shape">
|
||||||
<div className="flex flex-col gap-2 px-1 pb-1">
|
<div className="flex flex-col gap-2 px-1 pb-1">
|
||||||
<SegmentedControl
|
<SegmentedControl
|
||||||
@@ -990,19 +1004,21 @@ export function DoorPanel() {
|
|||||||
{isSwingDoor && (
|
{isSwingDoor && (
|
||||||
<PanelSection title="Swing">
|
<PanelSection title="Swing">
|
||||||
<div className="flex flex-col gap-2 px-1 pb-1">
|
<div className="flex flex-col gap-2 px-1 pb-1">
|
||||||
<div className="space-y-1">
|
{supportsHingeSide && (
|
||||||
<span className="font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider">
|
<div className="space-y-1">
|
||||||
Hinges Side
|
<span className="font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider">
|
||||||
</span>
|
Hinges Side
|
||||||
<SegmentedControl
|
</span>
|
||||||
onChange={(v) => handleUpdate({ hingesSide: v })}
|
<SegmentedControl
|
||||||
options={[
|
onChange={(v) => handleUpdate({ hingesSide: v })}
|
||||||
{ label: 'Left', value: 'left' },
|
options={[
|
||||||
{ label: 'Right', value: 'right' },
|
{ label: 'Left', value: 'left' },
|
||||||
]}
|
{ label: 'Right', value: 'right' },
|
||||||
value={node.hingesSide}
|
]}
|
||||||
/>
|
value={node.hingesSide}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<span className="font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider">
|
<span className="font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider">
|
||||||
Direction
|
Direction
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import { cn } from '../../../lib/utils'
|
|||||||
import { sfxEmitter } from '../../../lib/sfx-bus'
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
||||||
import useEditor from '../../../store/use-editor'
|
import useEditor from '../../../store/use-editor'
|
||||||
import { ActionButton, ActionGroup } from '../controls/action-button'
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
||||||
import { MetricControl } from '../controls/metric-control'
|
|
||||||
import { PanelSection } from '../controls/panel-section'
|
import { PanelSection } from '../controls/panel-section'
|
||||||
import { SegmentedControl } from '../controls/segmented-control'
|
import { SegmentedControl } from '../controls/segmented-control'
|
||||||
import { SliderControl } from '../controls/slider-control'
|
import { SliderControl } from '../controls/slider-control'
|
||||||
@@ -81,14 +80,16 @@ const windowTypeOptions: Array<{ label: string; value: WindowNode['windowType']
|
|||||||
{ label: 'Louvered', value: 'louvered' },
|
{ label: 'Louvered', value: 'louvered' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const rectangleOnlyWindowTypes = new Set<WindowNode['windowType']>([
|
const shapedWindowTypes = new Set<WindowNode['windowType']>([
|
||||||
'sliding',
|
'fixed',
|
||||||
'single-hung',
|
'casement',
|
||||||
'double-hung',
|
'awning',
|
||||||
'bay',
|
'hopper',
|
||||||
'bow',
|
'louvered',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const silllessWindowTypes = new Set<WindowNode['windowType']>(['bay', 'bow'])
|
||||||
|
|
||||||
export function WindowPanel() {
|
export function WindowPanel() {
|
||||||
const selectedId = useViewer((s) => s.selection.selectedIds[0])
|
const selectedId = useViewer((s) => s.selection.selectedIds[0])
|
||||||
const setSelection = useViewer((s) => s.setSelection)
|
const setSelection = useViewer((s) => s.setSelection)
|
||||||
@@ -110,14 +111,19 @@ export function WindowPanel() {
|
|||||||
const handleUpdate = useCallback(
|
const handleUpdate = useCallback(
|
||||||
(updates: Partial<WindowNode>) => {
|
(updates: Partial<WindowNode>) => {
|
||||||
if (!(selectedId && node)) return
|
if (!(selectedId && node)) return
|
||||||
|
const liveNode = useScene.getState().nodes[selectedId as AnyNodeId]
|
||||||
|
if (liveNode?.type !== 'window') return
|
||||||
|
|
||||||
const hasChange = Object.entries(updates).some(([key, value]) => {
|
const hasChange = Object.entries(updates).some(([key, value]) => {
|
||||||
const currentValue = node[key as keyof WindowNode]
|
const currentValue = liveNode[key as keyof WindowNode]
|
||||||
return !isSameWindowValue(currentValue, value)
|
return !isSameWindowValue(currentValue, value)
|
||||||
})
|
})
|
||||||
if (!hasChange) return
|
if (!hasChange) return
|
||||||
|
|
||||||
updateNode(selectedId as AnyNode['id'], updates)
|
updateNode(selectedId as AnyNode['id'], updates)
|
||||||
useScene.getState().dirtyNodes.add(selectedId as AnyNodeId)
|
const scene = useScene.getState()
|
||||||
|
scene.dirtyNodes.add(selectedId as AnyNodeId)
|
||||||
|
if (liveNode.parentId) scene.dirtyNodes.add(liveNode.parentId as AnyNodeId)
|
||||||
},
|
},
|
||||||
[selectedId, node, updateNode],
|
[selectedId, node, updateNode],
|
||||||
)
|
)
|
||||||
@@ -316,6 +322,9 @@ export function WindowPanel() {
|
|||||||
node.windowType === 'single-hung' ||
|
node.windowType === 'single-hung' ||
|
||||||
node.windowType === 'double-hung' ||
|
node.windowType === 'double-hung' ||
|
||||||
node.windowType === 'louvered'
|
node.windowType === 'louvered'
|
||||||
|
const supportsWindowShape = shapedWindowTypes.has(node.windowType ?? 'fixed')
|
||||||
|
const supportsGrid = node.windowType === 'fixed'
|
||||||
|
const supportsSill = !silllessWindowTypes.has(node.windowType)
|
||||||
|
|
||||||
const setOperationState = (value: number) => {
|
const setOperationState = (value: number) => {
|
||||||
useInteractive.getState().cancelWindowAnimation(node.id)
|
useInteractive.getState().cancelWindowAnimation(node.id)
|
||||||
@@ -464,10 +473,10 @@ export function WindowPanel() {
|
|||||||
handleUpdate({
|
handleUpdate({
|
||||||
windowType: option.value,
|
windowType: option.value,
|
||||||
...(option.value === 'awning' ? { awningDirection } : {}),
|
...(option.value === 'awning' ? { awningDirection } : {}),
|
||||||
...(rectangleOnlyWindowTypes.has(option.value)
|
...(!shapedWindowTypes.has(option.value)
|
||||||
? { openingShape: 'rectangle' }
|
? { openingShape: 'rectangle' }
|
||||||
: {}),
|
: {}),
|
||||||
...(option.value === 'bay' || option.value === 'bow' ? { sill: false } : {}),
|
...(silllessWindowTypes.has(option.value) ? { sill: false } : {}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
type="button"
|
type="button"
|
||||||
@@ -597,7 +606,7 @@ export function WindowPanel() {
|
|||||||
/>
|
/>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
|
|
||||||
{!isOpening && !rectangleOnlyWindowTypes.has(node.windowType) && (
|
{!isOpening && supportsWindowShape && (
|
||||||
<PanelSection title="Corner Shape">
|
<PanelSection title="Corner Shape">
|
||||||
<SegmentedControl
|
<SegmentedControl
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
@@ -814,128 +823,132 @@ export function WindowPanel() {
|
|||||||
/>
|
/>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
|
|
||||||
<PanelSection title="Grid">
|
{supportsGrid && (
|
||||||
<SliderControl
|
<PanelSection title="Grid">
|
||||||
label="Columns"
|
<SliderControl
|
||||||
max={8}
|
label="Columns"
|
||||||
min={1}
|
max={8}
|
||||||
onChange={(v) => {
|
min={1}
|
||||||
const n = Math.max(1, Math.min(8, Math.round(v)))
|
onChange={(v) => {
|
||||||
handleUpdate({ columnRatios: Array(n).fill(1 / n) })
|
const n = Math.max(1, Math.min(8, Math.round(v)))
|
||||||
}}
|
handleUpdate({ columnRatios: Array(n).fill(1 / n) })
|
||||||
precision={0}
|
}}
|
||||||
step={1}
|
precision={0}
|
||||||
value={numCols}
|
step={1}
|
||||||
/>
|
value={numCols}
|
||||||
<SliderControl
|
/>
|
||||||
label="Rows"
|
<SliderControl
|
||||||
max={8}
|
label="Rows"
|
||||||
min={1}
|
max={8}
|
||||||
onChange={(v) => {
|
min={1}
|
||||||
const n = Math.max(1, Math.min(8, Math.round(v)))
|
onChange={(v) => {
|
||||||
handleUpdate({ rowRatios: Array(n).fill(1 / n) })
|
const n = Math.max(1, Math.min(8, Math.round(v)))
|
||||||
}}
|
handleUpdate({ rowRatios: Array(n).fill(1 / n) })
|
||||||
precision={0}
|
}}
|
||||||
step={1}
|
precision={0}
|
||||||
value={numRows}
|
step={1}
|
||||||
/>
|
value={numRows}
|
||||||
|
/>
|
||||||
|
|
||||||
{numCols > 1 && (
|
{numCols > 1 && (
|
||||||
<div className="mt-2 flex flex-col gap-1">
|
<div className="mt-2 flex flex-col gap-1">
|
||||||
<div className="mb-1 px-1 font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider">
|
<div className="mb-1 px-1 font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider">
|
||||||
Col Widths
|
Col Widths
|
||||||
|
</div>
|
||||||
|
{normCols.map((ratio, i) => (
|
||||||
|
<SliderControl
|
||||||
|
key={`c-${i}`}
|
||||||
|
label={`C${i + 1}`}
|
||||||
|
max={95}
|
||||||
|
min={5}
|
||||||
|
onChange={(v) => setColumnRatio(i, v / 100)}
|
||||||
|
precision={1}
|
||||||
|
step={1}
|
||||||
|
unit="%"
|
||||||
|
value={Math.round(ratio * 100 * 10) / 10}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<div className="mt-1 border-border/50 border-t pt-1">
|
||||||
|
<SliderControl
|
||||||
|
label="Divider"
|
||||||
|
max={0.1}
|
||||||
|
min={0.005}
|
||||||
|
onChange={(v) => handleUpdate({ columnDividerThickness: v })}
|
||||||
|
precision={3}
|
||||||
|
step={0.01}
|
||||||
|
unit="m"
|
||||||
|
value={Math.round((node.columnDividerThickness ?? 0.03) * 1000) / 1000}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{normCols.map((ratio, i) => (
|
)}
|
||||||
|
|
||||||
|
{numRows > 1 && (
|
||||||
|
<div className="mt-2 flex flex-col gap-1">
|
||||||
|
<div className="mb-1 px-1 font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider">
|
||||||
|
Row Heights
|
||||||
|
</div>
|
||||||
|
{normRows.map((ratio, i) => (
|
||||||
|
<SliderControl
|
||||||
|
key={`r-${i}`}
|
||||||
|
label={`R${i + 1}`}
|
||||||
|
max={95}
|
||||||
|
min={5}
|
||||||
|
onChange={(v) => setRowRatio(i, v / 100)}
|
||||||
|
precision={1}
|
||||||
|
step={1}
|
||||||
|
unit="%"
|
||||||
|
value={Math.round(ratio * 100 * 10) / 10}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<div className="mt-1 border-border/50 border-t pt-1">
|
||||||
|
<SliderControl
|
||||||
|
label="Divider"
|
||||||
|
max={0.1}
|
||||||
|
min={0.005}
|
||||||
|
onChange={(v) => handleUpdate({ rowDividerThickness: v })}
|
||||||
|
precision={3}
|
||||||
|
step={0.01}
|
||||||
|
unit="m"
|
||||||
|
value={Math.round((node.rowDividerThickness ?? 0.03) * 1000) / 1000}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</PanelSection>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{supportsSill && (
|
||||||
|
<PanelSection title="Sill">
|
||||||
|
<ToggleControl
|
||||||
|
checked={node.sill}
|
||||||
|
label="Enable Sill"
|
||||||
|
onChange={(checked) => handleUpdate({ sill: checked })}
|
||||||
|
/>
|
||||||
|
{node.sill && (
|
||||||
|
<div className="mt-1 flex flex-col gap-1">
|
||||||
<SliderControl
|
<SliderControl
|
||||||
key={`c-${i}`}
|
label="Depth"
|
||||||
label={`C${i + 1}`}
|
min={0}
|
||||||
max={95}
|
onChange={(v) => handleUpdate({ sillDepth: v })}
|
||||||
min={5}
|
|
||||||
onChange={(v) => setColumnRatio(i, v / 100)}
|
|
||||||
precision={1}
|
|
||||||
step={1}
|
|
||||||
unit="%"
|
|
||||||
value={Math.round(ratio * 100 * 10) / 10}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<div className="mt-1 border-border/50 border-t pt-1">
|
|
||||||
<SliderControl
|
|
||||||
label="Divider"
|
|
||||||
max={0.1}
|
|
||||||
min={0.005}
|
|
||||||
onChange={(v) => handleUpdate({ columnDividerThickness: v })}
|
|
||||||
precision={3}
|
precision={3}
|
||||||
step={0.01}
|
step={0.01}
|
||||||
unit="m"
|
unit="m"
|
||||||
value={Math.round((node.columnDividerThickness ?? 0.03) * 1000) / 1000}
|
value={Math.round(node.sillDepth * 1000) / 1000}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{numRows > 1 && (
|
|
||||||
<div className="mt-2 flex flex-col gap-1">
|
|
||||||
<div className="mb-1 px-1 font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider">
|
|
||||||
Row Heights
|
|
||||||
</div>
|
|
||||||
{normRows.map((ratio, i) => (
|
|
||||||
<SliderControl
|
<SliderControl
|
||||||
key={`r-${i}`}
|
label="Thickness"
|
||||||
label={`R${i + 1}`}
|
min={0}
|
||||||
max={95}
|
onChange={(v) => handleUpdate({ sillThickness: v })}
|
||||||
min={5}
|
|
||||||
onChange={(v) => setRowRatio(i, v / 100)}
|
|
||||||
precision={1}
|
|
||||||
step={1}
|
|
||||||
unit="%"
|
|
||||||
value={Math.round(ratio * 100 * 10) / 10}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<div className="mt-1 border-border/50 border-t pt-1">
|
|
||||||
<SliderControl
|
|
||||||
label="Divider"
|
|
||||||
max={0.1}
|
|
||||||
min={0.005}
|
|
||||||
onChange={(v) => handleUpdate({ rowDividerThickness: v })}
|
|
||||||
precision={3}
|
precision={3}
|
||||||
step={0.01}
|
step={0.01}
|
||||||
unit="m"
|
unit="m"
|
||||||
value={Math.round((node.rowDividerThickness ?? 0.03) * 1000) / 1000}
|
value={Math.round(node.sillThickness * 1000) / 1000}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</PanelSection>
|
||||||
</PanelSection>
|
)}
|
||||||
|
|
||||||
<PanelSection title="Sill">
|
|
||||||
<ToggleControl
|
|
||||||
checked={node.sill}
|
|
||||||
label="Enable Sill"
|
|
||||||
onChange={(checked) => handleUpdate({ sill: checked })}
|
|
||||||
/>
|
|
||||||
{node.sill && (
|
|
||||||
<div className="mt-1 flex flex-col gap-1">
|
|
||||||
<SliderControl
|
|
||||||
label="Depth"
|
|
||||||
min={0}
|
|
||||||
onChange={(v) => handleUpdate({ sillDepth: v })}
|
|
||||||
precision={3}
|
|
||||||
step={0.01}
|
|
||||||
unit="m"
|
|
||||||
value={Math.round(node.sillDepth * 1000) / 1000}
|
|
||||||
/>
|
|
||||||
<SliderControl
|
|
||||||
label="Thickness"
|
|
||||||
min={0}
|
|
||||||
onChange={(v) => handleUpdate({ sillThickness: v })}
|
|
||||||
precision={3}
|
|
||||||
step={0.01}
|
|
||||||
unit="m"
|
|
||||||
value={Math.round(node.sillThickness * 1000) / 1000}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</PanelSection>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user