import React from "react"; import { computed, observable, action, reaction, IReactionDisposer, IObservableValue, makeObservable } from "mobx"; import { observer } from "mobx-react"; import { IconAction } from "eez-studio-ui/action"; import { SearchInput } from "eez-studio-ui/search-input"; import { getId, IEezObject } from "project-editor/core/object"; import { SortDirectionType, TreeAdapter, TreeObjectAdapter } from "project-editor/core/objectAdapter"; import { addItem, canAdd, getAddItemName, IPanel, isPartOfNavigation } from "project-editor/store"; import { DragAndDropManagerClass } from "project-editor/core/dd"; import { List } from "project-editor/ui-components/List"; import { ProjectContext } from "project-editor/project/context"; import classNames from "classnames"; import { ProjectEditor } from "project-editor/project-editor-interface"; //////////////////////////////////////////////////////////////////////////////// export const SortControl = observer( class SortControl extends React.Component<{ direction: SortDirectionType; onDirectionChanged: (direction: SortDirectionType) => void; }> { onClicked = () => { if (this.props.direction === "asc") { this.props.onDirectionChanged("desc"); } else if (this.props.direction === "desc") { this.props.onDirectionChanged("none"); } else { this.props.onDirectionChanged("asc"); } }; render() { const { direction } = this.props; return (
); } } ); //////////////////////////////////////////////////////////////////////////////// const AddButton = observer( class AddButton extends React.Component<{ listAdapter: TreeAdapter; navigationObject: IEezObject | undefined; onAdd: () => void; }> { static contextType = ProjectContext; declare context: React.ContextType; onAdd = async () => { if (this.props.navigationObject) { const aNewItem = await addItem(this.props.navigationObject); if (aNewItem) { this.props.listAdapter.selectItem( this.props.listAdapter.getItemFromId(getId(aNewItem))! ); const result = ProjectEditor.getEditorComponent( aNewItem, undefined ); if (result) { this.context.editorsStore.openEditor( result.object, result.subObject ); } else { this.props.onAdd(); } } } }; render() { return ( ); } } ); //////////////////////////////////////////////////////////////////////////////// const DeleteButton = observer( class DeleteButton extends React.Component<{ listAdapter: TreeAdapter; }> { onDelete = () => { this.props.listAdapter.deleteSelection(); }; render() { return ( ); } } ); //////////////////////////////////////////////////////////////////////////////// interface ListNavigationProps { id: string; title?: string; navigationObject: IEezObject; selectedObject: IObservableValue; onClickItem?: (item: IEezObject) => void; onDoubleClickItem?: (item: IEezObject) => void; additionalButtons?: JSX.Element[]; onEditItem?: (itemId: string) => void; renderItem?: (itemId: string) => React.ReactNode; dragAndDropManager?: DragAndDropManagerClass; searchInput?: boolean; editable?: boolean; onFilesDrop?: (files: File[]) => void; } export const ListNavigation = observer( class ListNavigation extends React.Component implements IPanel { static contextType = ProjectContext; declare context: React.ContextType; sortDirection: SortDirectionType = "none"; searchText: string = ""; dispose: IReactionDisposer; divRef = React.createRef(); constructor(props: ListNavigationProps) { super(props); this.readFromLocalStorage(); makeObservable(this, { sortDirection: observable, searchText: observable, editable: computed, selectedObject: computed, onSearchChange: action.bound, listAdapter: computed, readFromLocalStorage: action }); this.dispose = reaction( () => ({ sortDirection: this.sortDirection, searchText: this.searchText }), arg => { localStorage.setItem( "ListNavigationSortDirection" + this.props.id, arg.sortDirection ); localStorage.setItem( "ListNavigationSearchText" + this.props.id, arg.searchText ); } ); } readFromLocalStorage() { const sortDirectionStr = localStorage.getItem( "ListNavigationSortDirection" + this.props.id ); if (sortDirectionStr) { this.sortDirection = sortDirectionStr as SortDirectionType; } this.searchText = localStorage.getItem( "ListNavigationSearchText" + this.props.id ) || ""; } componentDidMount() { this.context.navigationStore.mountPanel(this); } componentDidUpdate() { this.readFromLocalStorage(); } componentWillUnmount() { this.dispose(); this.context.navigationStore.unmountPanel(this); } get listAdapter() { return new TreeAdapter( new TreeObjectAdapter(this.props.navigationObject), this.props.selectedObject, undefined, undefined, this.sortDirection, 0, this.onClickItem, this.onDoubleClickItem, this.searchText, this.props.editable ?? true ); } get editable() { const navigationStore = this.context.navigationStore; return this.props.editable != false && navigationStore.editable; } onClickItem = (object: IEezObject) => { if (this.props.onClickItem) { this.props.onClickItem(object); return; } const result = ProjectEditor.getEditorComponent(object, undefined); if (result) { this.context.editorsStore.openEditor( result.object, result.subObject ); } }; onDoubleClickItem = (object: IEezObject) => { if (this.props.onDoubleClickItem) { this.props.onDoubleClickItem(object); return; } const result = ProjectEditor.getEditorComponent(object, undefined); if (result) { this.context.editorsStore.openPermanentEditor( result.object, result.subObject ); } }; // interface IPanel implementation get selectedObject() { return this.props.selectedObject.get(); } get selectedObjects() { return this.listAdapter.rootItem.selectedObjects; } canCut() { return this.listAdapter.canCut(); } cutSelection() { if (this.editable) { this.listAdapter.cutSelection(); } } canCopy() { return this.listAdapter.canCopy(); } copySelection() { this.listAdapter.copySelection(); } canPaste() { return this.listAdapter.canPaste(); } pasteSelection() { if (this.editable) { this.listAdapter.pasteSelection(); } } canDelete() { return this.listAdapter.canDelete(); } deleteSelection() { if (this.editable) { this.listAdapter.deleteSelection(); } } selectAll() { this.listAdapter.selectItems( this.listAdapter.allRows.map(row => row.item) ); } onFocus = () => { const navigationStore = this.context.navigationStore; if (isPartOfNavigation(this.props.navigationObject)) { navigationStore.setSelectedPanel(this); } }; onSearchChange(event: any) { this.searchText = ($(event.target).val() as string).trim(); } render() { const { onEditItem, renderItem } = this.props; const buttons: JSX.Element[] = []; if (this.props.additionalButtons) { buttons.push(...this.props.additionalButtons); } if (this.editable) { buttons.push( { const treeElement: | HTMLDivElement | undefined | null = this.divRef.current?.querySelector( ".EezStudio_Tree" ); if (treeElement) { treeElement.focus(); } }} /> ); buttons.push( ); } return (
(this.sortDirection = direction) )} /> {(this.props.searchInput == undefined || this.props.searchInput) && ( { this.searchText = ""; })} onChange={this.onSearchChange} onKeyDown={this.onSearchChange} /> )}
{buttons}
); } } );