Files
modeling-app/src/lib/commandBarConfigs/settingsCommandConfig.ts
Frank Noirot 3fdf7bd45e Migrate to new split sidebar from accordion-like panes (#2063)
* Split ModelingSidebar out into own component

* Consolidate all ModelingPane components and config

* Make ModelingSidebar a directory of components and config

* Remove unused components

* Proper pane styling

* Make tooltip configurable to visually appear on hover only

* Remove debug panel from App

* Fix current tests

* Rename to more intuitive names

* Fix useEffect loop bug with showDebugPanel

* Fix snapshot tests

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

* Rerun CI

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

* Merge branch 'main' into franknoirot/sidebar

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

* Rerun CI

* Maybe some flakiness in the validation initScripts?

* Avoid test flakiness by waiting for more signals that loading is completed

* Don't assert, just wait for the element to be enabled

* Don't let users accidentally click the gap between the pane and the side of the window

* Firm up extrude from command bar test

* Get rid of unused imports

* Add setting to disable blinking cursor (#2065)

* Add support for "current" marker in command bar for boolean settings

* Add a cursorBlinking setting

* Rename setting to blinkingCursor, honor it in the UI

* Fix scroll layout bug in settings modal

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

* Rerun CI

* CSS tweaks

* Allow settings hotkey within KclEditorPane

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

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Rerun CI

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

* Rerun CI

* Ensure the KCL code panel is closed for camera movement test

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

* Make sure that the camera position inputs are ready to be read from

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

* Remove repeat awaits

* Make camera position fields in debug pane update when the pane is initialized

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

* Undo that CameraControls change because it made other things weird

* retry fixing camera move test

* Fix race condition where cam setting cam position parts were overwriting each other

* Rerun CI

* Rerun CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-15 12:04:17 -04:00

149 lines
4.3 KiB
TypeScript

import {
Command,
CommandArgument,
CommandArgumentConfig,
} from '../commandTypes'
import {
SettingsPaths,
SettingsLevel,
SettingProps,
} from 'lib/settings/settingsTypes'
import { settingsMachine } from 'machines/settingsMachine'
import { PathValue } from 'lib/types'
import { AnyStateMachine, ContextFrom, InterpreterFrom } from 'xstate'
import { getPropertyByPath } from 'lib/objectPropertyByPath'
import { buildCommandArgument } from 'lib/createMachineCommand'
import decamelize from 'decamelize'
import { isTauri } from 'lib/isTauri'
import { Setting } from 'lib/settings/initialSettings'
// An array of the paths to all of the settings that have commandConfigs
export const settingsWithCommandConfigs = (
s: ContextFrom<typeof settingsMachine>
) =>
Object.entries(s).flatMap(([categoryName, categorySettings]) =>
Object.entries(categorySettings)
.filter(([_, setting]) => setting.commandConfig !== undefined)
.map(([settingName]) => `${categoryName}.${settingName}`)
) as SettingsPaths[]
const levelArgConfig = <T extends AnyStateMachine = AnyStateMachine>(
actor: InterpreterFrom<T>,
isProjectAvailable: boolean,
hideOnLevel?: SettingsLevel
): CommandArgument<SettingsLevel, T> => ({
inputType: 'options' as const,
required: true,
defaultValue:
isProjectAvailable && hideOnLevel !== 'project' ? 'project' : 'user',
skip: true,
options:
isProjectAvailable && hideOnLevel !== 'project'
? [
{ name: 'User', value: 'user' as SettingsLevel },
{
name: 'Project',
value: 'project' as SettingsLevel,
isCurrent: true,
},
]
: [{ name: 'User', value: 'user' as SettingsLevel, isCurrent: true }],
machineActor: actor,
})
interface CreateSettingsArgs {
type: SettingsPaths
send: Function
context: ContextFrom<typeof settingsMachine>
actor: InterpreterFrom<typeof settingsMachine>
isProjectAvailable: boolean
}
// Takes a Setting with a commandConfig and creates a Command
// that can be used in the CommandBar component.
export function createSettingsCommand({
type,
send,
context,
actor,
isProjectAvailable,
}: CreateSettingsArgs) {
type S = PathValue<typeof context, typeof type>
const settingConfig = getPropertyByPath(context, type) as SettingProps<
S['default']
>
const valueArgPartialConfig = settingConfig['commandConfig']
const shouldHideOnThisLevel =
settingConfig?.hideOnLevel === 'user' && !isProjectAvailable
const shouldHideOnThisPlatform =
settingConfig.hideOnPlatform &&
(isTauri()
? settingConfig.hideOnPlatform === 'desktop'
: settingConfig.hideOnPlatform === 'web')
if (
!valueArgPartialConfig ||
shouldHideOnThisLevel ||
shouldHideOnThisPlatform
)
return null
let valueArgConfig = {
...valueArgPartialConfig,
required: true,
} as CommandArgumentConfig<S['default']>
// If the setting is a boolean, we coerce it into an options input type
if (valueArgConfig.inputType === 'boolean') {
valueArgConfig = {
...valueArgConfig,
inputType: 'options',
options: (cmdBarContext, machineContext) => {
const setting = getPropertyByPath(
machineContext,
type
) as Setting<boolean>
const level = cmdBarContext.argumentsToSubmit.level as SettingsLevel
const isCurrent =
setting[level] === undefined
? setting.getFallback(level) === true
: setting[level] === true
return [
{ name: 'On', value: true, isCurrent },
{ name: 'Off', value: false, isCurrent: !isCurrent },
]
},
}
}
// @ts-ignore - TODO figure out this typing for valueArgConfig
const valueArg = buildCommandArgument(valueArgConfig, context, actor)
const command: Command = {
name: type,
displayName: `Settings · ${decamelize(type.replaceAll('.', ' · '), {
separator: ' ',
})}`,
ownerMachine: 'settings',
icon: 'settings',
needsReview: false,
onSubmit: (data) => {
if (data !== undefined && data !== null) {
send({ type: `set.${type}`, data })
} else {
send(type)
}
},
args: {
level: levelArgConfig(
actor,
isProjectAvailable,
settingConfig.hideOnLevel
),
value: valueArg,
},
}
return command
}