ai-chatbot/packages/editor/src/lib/selection-routing.ts

125 lines
2.9 KiB
TypeScript

import { type AnyNode, type ItemNode, nodeRegistry } from '@pascal-app/core'
export type SelectionModifierKeys = {
meta: boolean
ctrl: boolean
shift: boolean
}
export type NodeSelectionTarget = {
phase: 'site' | 'structure' | 'furnish'
structureLayer?: 'zones' | 'elements'
}
export function isSelectionModifierActive(keys: SelectionModifierKeys): boolean {
return keys.meta || keys.ctrl || keys.shift
}
export function selectionModifiersFromEvent(
event?: {
metaKey?: boolean
ctrlKey?: boolean
shiftKey?: boolean
nativeEvent?: {
metaKey?: boolean
ctrlKey?: boolean
shiftKey?: boolean
}
} | null,
fallback?: Partial<SelectionModifierKeys>,
): SelectionModifierKeys {
const fromEvent = (
key: keyof SelectionModifierKeys,
eventKey: 'metaKey' | 'ctrlKey' | 'shiftKey',
) => {
if (typeof event?.[eventKey] === 'boolean') return event[eventKey]
if (typeof event?.nativeEvent?.[eventKey] === 'boolean') return event.nativeEvent[eventKey]
return Boolean(fallback?.[key])
}
return {
meta: fromEvent('meta', 'metaKey'),
ctrl: fromEvent('ctrl', 'ctrlKey'),
shift: fromEvent('shift', 'shiftKey'),
}
}
export function resolveSelectedIdsForNodeClick({
baseSelectedIds,
currentSelectedIds,
modifierKeys,
nodeId,
}: {
baseSelectedIds?: readonly string[]
currentSelectedIds: readonly string[]
modifierKeys: SelectionModifierKeys
nodeId: string
}): string[] {
if (isSelectionModifierActive(modifierKeys)) {
const selectedIds = baseSelectedIds ?? currentSelectedIds
if (selectedIds.includes(nodeId)) {
return selectedIds.filter((id) => id !== nodeId)
}
return [...selectedIds, nodeId]
}
return [nodeId]
}
export function resolveNodeSelectionTarget(node: AnyNode): NodeSelectionTarget | null {
if (node.type === 'building') {
return { phase: 'site' }
}
if (node.type === 'zone') {
return {
phase: 'structure',
structureLayer: 'zones',
}
}
if (node.type === 'item') {
const item = node as ItemNode
if (item.asset.category === 'door' || item.asset.category === 'window') {
return {
phase: 'structure',
structureLayer: 'elements',
}
}
return { phase: 'furnish' }
}
if (
node.type === 'wall' ||
node.type === 'fence' ||
node.type === 'column' ||
node.type === 'elevator' ||
node.type === 'slab' ||
node.type === 'ceiling' ||
node.type === 'roof' ||
node.type === 'roof-segment' ||
node.type === 'stair' ||
node.type === 'stair-segment' ||
node.type === 'spawn' ||
node.type === 'window' ||
node.type === 'door'
) {
return {
phase: 'structure',
structureLayer: 'elements',
}
}
const def = nodeRegistry.get(node.type)
if (!def) return null
if (def.category === 'furnish') {
return { phase: 'furnish' }
}
return {
phase: 'structure',
structureLayer: 'elements',
}
}