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}
);
}
}
);