Command bar: add extrude command, nonlinear editing, etc (#1204)

* Tweak toaster look and feel

* Add icons, tweak plus icon names

* Rename commandBarMeta to commandBarConfig

* Refactor command bar, add support for icons

* Create a tailwind plugin for aria-pressed button state

* Remove overlay from behind command bar

* Clean up toolbar

* Button and other style tweaks

* Icon tweaks follow-up: make old icons work with new sizing

* Delete unused static icons

* More CSS tweaks

* Small CSS tweak to project sidebar

* Add command bar E2E test

* fumpt

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* fix typo in a comment

* Fix icon padding (built version only)

* Update onboarding and warning banner icons padding

* Misc minor style fixes

* Get Extrude opening and canceling from command bar

* Iconography tweaks

* Get extrude kind of working

* Refactor command bar config types and organization

* Move command bar configs to be co-located with each other

* Start building a state machine for the command bar

* Start converting command bar to state machine

* Add support for multiple args, confirmation step

* Submission behavior, hotkeys, code organization

* Add new test for extruding from command bar

* Polish step back and selection hotkeys, CSS tweaks

* Loading style tweaks

* Validate selection inputs, polish UX of args re-editing

* Prevent submission with multiple selection on singlular arg

* Remove stray console logs

* Tweak test, CSS nit, remove extrude "result" argument

* Fix linting warnings

* Show Ctrl+/ instead of ⌘K on all platforms but Mac

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Add "Enter sketch" to command bar

* fix command bar test

* Fix flaky cmd bar extrude test by waiting for engine select response

* Cover both button labels '⌘K' and 'Ctrl+/' in test

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Frank Noirot
2023-12-13 12:49:01 -05:00
committed by GitHub
parent b01357b49e
commit d2535bb8c2
60 changed files with 4549 additions and 1939 deletions

136
src/lib/commandTypes.ts Normal file
View File

@ -0,0 +1,136 @@
import { CustomIconName } from 'components/CustomIcon'
import { AllMachines } from 'hooks/useStateMachineCommands'
import {
AnyStateMachine,
ContextFrom,
EventFrom,
InterpreterFrom,
} from 'xstate'
import { Selection } from './selections'
type Icon = CustomIconName
const PLATFORMS = ['both', 'web', 'desktop'] as const
const INPUT_TYPES = ['options', 'string', 'number', 'selection'] as const
export type CommandInputType = (typeof INPUT_TYPES)[number]
export type CommandSetSchema<T extends AnyStateMachine> = Partial<{
[EventType in EventFrom<T>['type']]: Record<string, any>
}>
export type CommandSet<
T extends AllMachines,
Schema extends CommandSetSchema<T>
> = Partial<{
[EventType in EventFrom<T>['type']]: Command<
T,
EventFrom<T>['type'],
Schema[EventType]
>
}>
export type CommandSetConfig<
T extends AllMachines,
Schema extends CommandSetSchema<T>
> = Partial<{
[EventType in EventFrom<T>['type']]: CommandConfig<
T,
EventFrom<T>['type'],
Schema[EventType]
>
}>
export type Command<
T extends AnyStateMachine = AnyStateMachine,
CommandName extends EventFrom<T>['type'] = EventFrom<T>['type'],
CommandSchema extends CommandSetSchema<T>[CommandName] = CommandSetSchema<T>[CommandName]
> = {
name: CommandName
ownerMachine: T['id']
needsReview: boolean
onSubmit: (data?: CommandSchema) => void
onCancel?: () => void
args?: {
[ArgName in keyof CommandSchema]: CommandArgument<CommandSchema[ArgName], T>
}
description?: string
icon?: Icon
hide?: (typeof PLATFORMS)[number]
}
export type CommandConfig<
T extends AnyStateMachine = AnyStateMachine,
CommandName extends EventFrom<T>['type'] = EventFrom<T>['type'],
CommandSchema extends CommandSetSchema<T>[CommandName] = CommandSetSchema<T>[CommandName]
> = Omit<
Command<T, CommandName, CommandSchema>,
'name' | 'ownerMachine' | 'onSubmit' | 'onCancel' | 'args' | 'needsReview'
> & {
needsReview?: true
args?: {
[ArgName in keyof CommandSchema]: CommandArgumentConfig<
CommandSchema[ArgName],
T
>
}
}
export type CommandArgumentConfig<
OutputType,
T extends AnyStateMachine = AnyStateMachine
> =
| {
description?: string
required: boolean
skip?: true
defaultValue?: OutputType | ((context: ContextFrom<T>) => OutputType)
payload?: OutputType
} & (
| {
inputType: Extract<CommandInputType, 'options'>
options:
| CommandArgumentOption<OutputType>[]
| ((context: ContextFrom<T>) => CommandArgumentOption<OutputType>[])
}
| {
inputType: Extract<CommandInputType, 'selection'>
selectionTypes: Selection['type'][]
multiple: boolean
}
| { inputType: Exclude<CommandInputType, 'options' | 'selection'> }
)
export type CommandArgument<
OutputType,
T extends AnyStateMachine = AnyStateMachine
> =
| {
description?: string
required: boolean
payload?: OutputType // Payload sets the initialized value and more importantly its type
defaultValue?: OutputType // Default value is used as the starting value for the input on this argument
} & (
| {
inputType: Extract<CommandInputType, 'options'>
options: CommandArgumentOption<OutputType>[]
}
| {
inputType: Extract<CommandInputType, 'selection'>
selectionTypes: Selection['type'][]
actor: InterpreterFrom<T>
multiple: boolean
}
| { inputType: Exclude<CommandInputType, 'options' | 'selection'> }
)
export type CommandArgumentWithName<
OutputType,
T extends AnyStateMachine = AnyStateMachine
> = CommandArgument<OutputType, T> & {
name: string
}
export type CommandArgumentOption<A> = {
name: string
isCurrent?: boolean
value: A
}