Files
modeling-app/src/components/CommandBar/CommandBarSelectionMixedInput.tsx
Pierre Jacquier 6f2d127c4f Assemblies: Set translate and rotate via point-and-click (#6167)
* WIP: Add point-and-click Import for geometry
Will eventually fix #6120
Right now the whole loop is there but the codemod doesn't work yet

* Better pathToNOde, log on non-working cm dispatch call

* Add workaround to updateModelingState not working

* Back to updateModelingState with a skip flag

* Better todo

* Change working from Import to Insert, cleanups

* Sister command in kclCommands to populate file options

* Improve path selector

* Unsure: move importAstMod to kclCommands onSubmit 😶

* Add e2e test

* Clean up for review

* Add native file menu entry and test

* No await yo lint said so

* WIP: UX improvements around foreign file imports
Fixes #6152

* WIP: Set translate and rotate via point-and-click on imports. Boilerplate code
Will eventually close #6020

* Full working loop of rotate and translate pipe mutation, including edits, only on module imports. VERY VERBOSE

* Add first e2e test for set transform. Bunch of caveats listed as TODOs

* @lrev-Dev's suggestion to remove a comment

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>

* Update to scene.settled(cmdBar)

* Add partNNN default name for alias

* Lint

* Lint

* Fix unit tests

* Add sad path insert test
Thanks @Irev-Dev for the suggestion

* Add step insert test

* Lint

* Add test for second foreign import thru file tree click

* WIP: Add point-and-click Load to copy files from outside the project into the project
Towards #6210

* Move Insert button to modeling toolbar, update menus and toolbars

* Add default value for local name alias

* Aligning tests

* Fix tests

* Add padding for filenames starting with a digit

* Lint

* Lint

* Update snapshots

* Merge branch 'main' into pierremtb/issue6210-Add-point-and-click-Load-to-copy-files-from-outside-the-project-into-the-project

* Add disabled transform subbutton

* Allow start of Transform flow from toolbar with selection

* Merge kcl-samples and local disk load into one 'Load external model' command

* Fix em tests

* Fix test

* Add test for file pick import, better input

* Fix non .kcl loading

* Lint

* Update snapshots

* Fix issue leading to test failure

* Fix clone test

* Add note

* Fix nested clone issue

* Clean up for review

* Add valueSummary for path

* Fix test after path change

* Clean up for review

* Support much wider range for transform

* Set display names

* Bug fixed itself moment...

* Add test for extrude tranform

* Oops missed a thing

* Clean up selection arg

* More tests incl for variable stuff

* Fix imports

* Add supportsTransform: true on all solids returning operations

* Fix edit flow on variables, add test

* Split transform command into translate and rotate

* Clean up and comment

* Clean up operations.ts

* Add comment

* Improve assemblies test

* Support more things

* Typo

* Fix test after unit change on import

* Last clean up for review

* Fix remaining test

---------

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2025-04-17 15:44:31 +00:00

145 lines
4.5 KiB
TypeScript

import { useSelector } from '@xstate/react'
import { useEffect, useMemo, useRef, useState } from 'react'
import type { CommandArgument } from '@src/lib/commandTypes'
import type { Selections } from '@src/lib/selections'
import {
canSubmitSelectionArg,
getSelectionCountByType,
} from '@src/lib/selections'
import { kclManager } from '@src/lib/singletons'
import {
commandBarActor,
useCommandBarState,
} from '@src/machines/commandBarMachine'
const selectionSelector = (snapshot: any) => snapshot?.context.selectionRanges
export default function CommandBarSelectionMixedInput({
arg,
stepBack,
onSubmit,
}: {
arg: CommandArgument<unknown> & { inputType: 'selectionMixed'; name: string }
stepBack: () => void
onSubmit: (data: unknown) => void
}) {
const inputRef = useRef<HTMLInputElement>(null)
const commandBarState = useCommandBarState()
const [hasSubmitted, setHasSubmitted] = useState(false)
const [hasAutoSkipped, setHasAutoSkipped] = useState(false)
const selection: Selections = useSelector(arg.machineActor, selectionSelector)
const selectionsByType = useMemo(() => {
return getSelectionCountByType(selection)
}, [selection])
const canSubmitSelection = useMemo<boolean>(() => {
if (!selection) return false
const isNonZeroRange = selection.graphSelections.some((sel) => {
const range = sel.codeRef.range
return range[1] - range[0] !== 0 // Non-zero range is always valid
})
if (isNonZeroRange) return true
return canSubmitSelectionArg(selectionsByType, arg)
}, [selectionsByType, selection])
useEffect(() => {
inputRef.current?.focus()
}, [selection, inputRef])
// Only auto-skip on initial mount if we have a valid selection
// different from the component CommandBarSelectionInput in the the dependency array
// is empty
useEffect(() => {
if (!hasAutoSkipped && canSubmitSelection && arg.skip) {
const argValue = commandBarState.context.argumentsToSubmit[arg.name]
if (argValue === undefined) {
handleSubmit()
setHasAutoSkipped(true)
}
}
}, [])
// Set selection filter if needed, and reset it when the component unmounts
useEffect(() => {
arg.selectionFilter && kclManager.setSelectionFilter(arg.selectionFilter)
return () => kclManager.defaultSelectionFilter(selection)
}, [arg.selectionFilter])
function handleChange() {
inputRef.current?.focus()
}
function handleSubmit(e?: React.FormEvent<HTMLFormElement>) {
e?.preventDefault()
if (!canSubmitSelection) {
setHasSubmitted(true)
return
}
onSubmit(selection)
}
const isMixedSelection = arg.inputType === 'selectionMixed'
const allowNoSelection = isMixedSelection && arg.allowNoSelection
const showSceneSelection =
isMixedSelection && arg.selectionSource?.allowSceneSelection
return (
<form id="arg-form" onSubmit={handleSubmit}>
<label
className={
'relative flex flex-col mx-4 my-4 ' +
(!hasSubmitted || canSubmitSelection || 'text-destroy-50')
}
>
{canSubmitSelection
? 'Select objects in the scene'
: 'Select code or objects in the scene'}
{showSceneSelection && (
<div className="scene-selection mt-2">
<p className="text-sm text-chalkboard-60">
Select objects in the scene
</p>
{/* Scene selection UI will be handled by the parent component */}
</div>
)}
{allowNoSelection && (
<button
type="button"
onClick={() => onSubmit(null)}
className="mt-2 px-4 py-2 rounded border border-chalkboard-30 text-chalkboard-90 dark:text-chalkboard-10 hover:bg-chalkboard-10 dark:hover:bg-chalkboard-90 transition-colors"
>
Continue without selection
</button>
)}
<span data-testid="cmd-bar-arg-name" className="sr-only">
{arg.name}
</span>
<input
id="selection"
name="selection"
ref={inputRef}
required
data-testid="cmd-bar-arg-value"
placeholder="Select an entity with your mouse"
className="absolute inset-0 w-full h-full opacity-0 cursor-default"
onKeyDown={(event) => {
if (event.key === 'Backspace' && event.shiftKey) {
stepBack()
} else if (event.key === 'Escape') {
commandBarActor.send({ type: 'Close' })
}
}}
onChange={handleChange}
value={JSON.stringify(selection || {})}
/>
</label>
</form>
)
}