import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { Resizable } from 're-resizable' import { MouseEventHandler, useCallback, useEffect, useMemo, useContext, } from 'react' import { useHotkeys } from 'react-hotkeys-hook' import { SidebarAction, SidebarType, sidebarPanes } from './ModelingPanes' import Tooltip from 'components/Tooltip' import { ActionIcon } from 'components/ActionIcon' import { ModelingPane } from './ModelingPane' import { isDesktop } from 'lib/isDesktop' import { useModelingContext } from 'hooks/useModelingContext' import { CustomIconName } from 'components/CustomIcon' import { useCommandsContext } from 'hooks/useCommandsContext' import { IconDefinition } from '@fortawesome/free-solid-svg-icons' import { useKclContext } from 'lang/KclProvider' import { MachineManagerContext } from 'components/MachineManagerProvider' interface ModelingSidebarProps { paneOpacity: '' | 'opacity-20' | 'opacity-40' } interface BadgeInfoComputed { value: number | boolean onClick?: MouseEventHandler } function getPlatformString(): 'web' | 'desktop' { return isDesktop() ? 'desktop' : 'web' } export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) { const machineManager = useContext(MachineManagerContext) const { commandBarSend } = useCommandsContext() const kclContext = useKclContext() const { settings } = useSettingsAuthContext() const onboardingStatus = settings.context.app.onboardingStatus const { send, context } = useModelingContext() const pointerEventsCssClass = onboardingStatus.current === 'camera' || context.store?.openPanes.length === 0 ? 'pointer-events-none ' : 'pointer-events-auto ' const showDebugPanel = settings.context.modeling.showDebugPanel const paneCallbackProps = useMemo( () => ({ kclContext, settings: settings.context, platform: getPlatformString(), }), [kclContext.errors, settings.context] ) const sidebarActions: SidebarAction[] = [ { id: 'export', title: 'Export part', sidebarName: 'Export part', icon: 'floppyDiskArrow', keybinding: 'Ctrl + Shift + E', action: () => commandBarSend({ type: 'Find and select command', data: { name: 'Export', groupId: 'modeling' }, }), }, { id: 'make', title: 'Make part', sidebarName: 'Make part', icon: 'printer3d', keybinding: 'Ctrl + Shift + M', // eslint-disable-next-line @typescript-eslint/no-misused-promises action: async () => { commandBarSend({ type: 'Find and select command', data: { name: 'Make', groupId: 'modeling' }, }) }, hide: () => !isDesktop(), disable: () => { return machineManager.noMachinesReason() }, }, ] const filteredActions: SidebarAction[] = sidebarActions.filter( (action) => !action.hide || (action.hide instanceof Function && !action.hide(paneCallbackProps)) ) // // Filter out the debug panel if it's not supposed to be shown // // TODO: abstract out for allowing user to configure which panes to show const filteredPanes = useMemo( () => (showDebugPanel.current ? sidebarPanes : sidebarPanes.filter((pane) => pane.id !== 'debug') ).filter( (pane) => !pane.hide || (pane.hide instanceof Function && !pane.hide(paneCallbackProps)) ), [sidebarPanes, paneCallbackProps] ) const paneBadgeMap: Record = useMemo(() => { return filteredPanes.reduce((acc, pane) => { if (pane.showBadge) { acc[pane.id] = { value: pane.showBadge.value(paneCallbackProps), onClick: pane.showBadge.onClick, } } return acc }, {} as Record) }, [paneCallbackProps]) // Clear any hidden panes from the `openPanes` array useEffect(() => { const panesToReset: SidebarType[] = [] sidebarPanes.forEach((pane) => { if ( pane.hide === true || (pane.hide instanceof Function && pane.hide(paneCallbackProps)) ) { panesToReset.push(pane.id) } }) if (panesToReset.length > 0) { send({ type: 'Set context', data: { openPanes: context.store?.openPanes.filter( (pane) => !panesToReset.includes(pane) ), }, }) } }, [settings.context]) const togglePane = useCallback( (newPane: SidebarType) => { send({ type: 'Set context', data: { openPanes: context.store?.openPanes.includes(newPane) ? context.store?.openPanes.filter((pane) => pane !== newPane) : [...context.store?.openPanes, newPane], }, }) }, [context.store?.openPanes, send] ) return (
      = 1 ? 'pr-0.5' : '') } > {filteredPanes.map((pane) => ( togglePane(pane.id)} aria-pressed={context.store?.openPanes.includes(pane.id)} showBadge={paneBadgeMap[pane.id]} /> ))}
    {filteredActions.length > 0 && ( <>
    )}
    = 1 ? `w-full` : `hidden`) } > {filteredPanes .filter((pane) => context?.store.openPanes.includes(pane.id)) .map((pane) => ( {}} id={`${pane.id}-pane`} > {pane.Content instanceof Function ? ( togglePane(pane.id)} /> ) : ( pane.Content )} ))}
) } interface ModelingPaneButtonProps extends React.HTMLAttributes { paneConfig: { id: string sidebarName: string icon: CustomIconName | IconDefinition keybinding: string iconClassName?: string iconSize?: 'sm' | 'md' | 'lg' } onClick: () => void paneIsOpen?: boolean showBadge?: BadgeInfoComputed disabledText?: string } function ModelingPaneButton({ paneConfig, onClick, paneIsOpen, showBadge, disabledText, ...props }: ModelingPaneButtonProps) { useHotkeys(paneConfig.keybinding, onClick, { scopes: ['modeling'], }) return (
{!!showBadge?.value && (

1 ? 's' : '' }`} >  has  {typeof showBadge.value === 'number' ? ( {showBadge.value} ) : ( a )}  notification{Number(showBadge.value) > 1 ? 's' : ''}

)}
) }