* 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>
145 lines
4.5 KiB
TypeScript
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>
|
|
)
|
|
}
|