import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast } from "../../../fields/Doc";
import { BoolCast, Cast, StrCast, NumCast } from "../../../fields/Types";
import AntimodeMenu from "../AntimodeMenu";
import "./CollectionMenu.scss";
import { undoBatch } from "../../util/UndoManager";
import { CollectionViewType, CollectionView } from "./CollectionView";
import { emptyFunction, setupMoveUpEvents, Utils } from "../../../Utils";
import { CollectionGridViewChrome } from "./CollectionViewChromes";
import { DragManager } from "../../util/DragManager";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
import { List } from "../../../fields/List";
import { SelectionManager } from "../../util/SelectionManager";
@observer
export default class CollectionMenu extends AntimodeMenu {
static Instance: CollectionMenu;
@observable SelectedCollection: CollectionView | undefined;
constructor(props: Readonly<{}>) {
super(props);
CollectionMenu.Instance = this;
this._canFade = false; // don't let the inking menu fade away
this.Pinned = Cast(Doc.UserDoc()["menuCollections-pinned"], "boolean", true);
}
@action
toggleMenuPin = (e: React.MouseEvent) => {
Doc.UserDoc()["menuCollections-pinned"] = this.Pinned = !this.Pinned;
}
@computed get aButton() {
return
;
}
render() {
return this.getElement([
this.aButton,
!this.SelectedCollection ? <>> : ,
]);
}
}
interface CollectionViewChromeProps {
CollectionView: CollectionView;
type: CollectionViewType;
collapse?: (value: boolean) => any;
}
const stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
@observer
export class CollectionViewBaseChrome extends React.Component {
//(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)
get target() { return this.props.CollectionView.props.Document; }
_templateCommand = {
params: ["target", "source"], title: "=> item view",
script: "this.target.childLayout = getDocTemplate(this.source?.[0])",
immediate: undoBatch((source: Doc[]) => source.length && (this.target.childLayout = Doc.getDocTemplate(source?.[0]))),
initialize: emptyFunction,
};
_narrativeCommand = {
params: ["target", "source"], title: "=> child click view",
script: "this.target.childClickedOpenTemplateView = getDocTemplate(this.source?.[0])",
immediate: undoBatch((source: Doc[]) => source.length && (this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]))),
initialize: emptyFunction,
};
_contentCommand = {
params: ["target", "source"], title: "=> clear content",
script: "getProto(this.target).data = copyField(this.source);",
immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List(source)), // Doc.aliasDocs(source),
initialize: emptyFunction,
};
_viewCommand = {
params: ["target"], title: "=> reset view",
script: "this.target._panX = this.restoredPanX; this.target._panY = this.restoredPanY; this.target.scale = this.restoredScale;",
immediate: undoBatch((source: Doc[]) => { this.target._panX = 0; this.target._panY = 0; this.target.scale = 1; }),
initialize: (button: Doc) => { button.restoredPanX = this.target._panX; button.restoredPanY = this.target._panY; button.restoredScale = this.target.scale; },
};
_clusterCommand = {
params: ["target"], title: "=> fit content",
script: "this.target._fitToBox = !this.target._fitToBox;",
immediate: undoBatch((source: Doc[]) => this.target._fitToBox = !this.target._fitToBox),
initialize: emptyFunction
};
_fitContentCommand = {
params: ["target"], title: "=> toggle clusters",
script: "this.target.useClusters = !this.target.useClusters;",
immediate: undoBatch((source: Doc[]) => this.target.useClusters = !this.target.useClusters),
initialize: emptyFunction
};
_freeform_commands = [this._viewCommand, this._fitContentCommand, this._clusterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand];
_stacking_commands = [this._contentCommand, this._templateCommand];
_masonry_commands = [this._contentCommand, this._templateCommand];
_schema_commands = [this._templateCommand, this._narrativeCommand];
_tree_commands = [];
private get _buttonizableCommands() {
switch (this.props.type) {
case CollectionViewType.Tree: return this._tree_commands;
case CollectionViewType.Schema: return this._schema_commands;
case CollectionViewType.Stacking: return this._stacking_commands;
case CollectionViewType.Masonry: return this._stacking_commands;
case CollectionViewType.Freeform: return this._freeform_commands;
case CollectionViewType.Time: return this._freeform_commands;
case CollectionViewType.Carousel: return this._freeform_commands;
case CollectionViewType.Carousel3D: return this._freeform_commands;
}
return [];
}
private _picker: any;
private _commandRef = React.createRef();
private _viewRef = React.createRef();
@observable private _currentKey: string = "";
componentDidMount = action(() => {
this._currentKey = this._currentKey || (this._buttonizableCommands.length ? this._buttonizableCommands[0]?.title : "");
});
@undoBatch
viewChanged = (e: React.ChangeEvent) => {
//@ts-ignore
this.document._viewType = e.target.selectedOptions[0].value;
}
commandChanged = (e: React.ChangeEvent) => {
//@ts-ignore
runInAction(() => this._currentKey = e.target.selectedOptions[0].value);
}
@action
toggleViewSpecs = (e: React.SyntheticEvent) => {
this.document._facetWidth = this.document._facetWidth ? 0 : 200;
e.stopPropagation();
}
@action closeViewSpecs = () => {
this.document._facetWidth = 0;
}
@computed get subChrome() {
switch (this.props.type) {
case CollectionViewType.Freeform: return ();
// case CollectionViewType.Stacking: return ();
// case CollectionViewType.Schema: return ();
// case CollectionViewType.Tree: return ();
// case CollectionViewType.Masonry: return ();
// case CollectionViewType.Carousel3D: return ();
// case CollectionViewType.Grid: return ();
default: return null;
}
}
private get document() {
return this.props.CollectionView.props.Document;
}
private dropDisposer?: DragManager.DragDropDisposer;
protected createDropTarget = (ele: HTMLDivElement) => {
this.dropDisposer?.();
if (ele) {
this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.document);
}
}
@undoBatch
@action
protected drop(e: Event, de: DragManager.DropEvent): boolean {
const docDragData = de.complete.docDragData;
if (docDragData?.draggedDocuments.length) {
this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => c.immediate(docDragData.draggedDocuments || []));
e.stopPropagation();
}
return true;
}
dragViewDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e, (e, down, delta) => {
const vtype = this.props.CollectionView.collectionViewType;
const c = {
params: ["target"], title: vtype,
script: `this.target._viewType = '${StrCast(this.props.CollectionView.props.Document._viewType)}'`,
immediate: (source: Doc[]) => this.props.CollectionView.props.Document._viewType = Doc.getDocTemplate(source?.[0]),
initialize: emptyFunction,
};
DragManager.StartButtonDrag([this._viewRef.current!], c.script, StrCast(c.title),
{ target: this.props.CollectionView.props.Document }, c.params, c.initialize, e.clientX, e.clientY);
return true;
}, emptyFunction, emptyFunction);
}
dragCommandDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e, (e, down, delta) => {
this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c =>
DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title,
{ target: this.props.CollectionView.props.Document }, c.params, c.initialize, e.clientX, e.clientY));
return true;
}, emptyFunction, () => {
this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => c.immediate([]));
});
}
@computed get templateChrome() {
return
;
}
@computed get viewModes() {
const collapsed = this.props.CollectionView.props.Document._chromeStatus !== "enabled";
return
;
}
render() {
const scale = Math.min(1, this.props.CollectionView.props.ScreenToLocalTransform()?.Scale);
return (
{this.viewModes}
{this.templateChrome}
{this.subChrome}
);
}
}
@observer
export class CollectionFreeFormViewChrome extends React.Component {
get Document() { return this.props.CollectionView.props.Document; }
@computed get dataField() {
return this.props.CollectionView.props.Document[Doc.LayoutFieldKey(this.props.CollectionView.props.Document)];
}
@computed get childDocs() {
return DocListCast(this.dataField);
}
@undoBatch
@action
nextKeyframe = (): void => {
const currentFrame = NumCast(this.Document.currentFrame);
if (currentFrame === undefined) {
this.Document.currentFrame = 0;
CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
}
CollectionFreeFormDocumentView.updateKeyframe(this.childDocs, currentFrame || 0);
this.Document.currentFrame = Math.max(0, (currentFrame || 0) + 1);
this.Document.lastFrame = Math.max(NumCast(this.Document.currentFrame), NumCast(this.Document.lastFrame));
}
@undoBatch
@action
prevKeyframe = (): void => {
const currentFrame = NumCast(this.Document.currentFrame);
if (currentFrame === undefined) {
this.Document.currentFrame = 0;
CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
}
CollectionFreeFormDocumentView.gotoKeyframe(this.childDocs.slice());
this.Document.currentFrame = Math.max(0, (currentFrame || 0) - 1);
}
render() {
return this.Document.isAnnotationOverlay ? (null) :
this.Document.editing = !this.Document.editing)} >
{NumCast(this.Document.currentFrame)}
;
}
}