import React = require("react"); import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon, FontAwesomeIconProps } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; import { action, computed, Lambda, observable, reaction, runInAction, trace } from "mobx"; import { observer } from "mobx-react"; import { ColorState } from "react-color"; import { Doc, DocListCast, Opt } from "../../../fields/Doc"; import { Document } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { InkTool } from "../../../fields/InkField"; import { List } from "../../../fields/List"; import { ObjectField } from "../../../fields/ObjectField"; import { RichTextField } from "../../../fields/RichTextField"; import { listSpec } from "../../../fields/Schema"; import { ScriptField } from "../../../fields/ScriptField"; import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { DocumentType } from "../../documents/DocumentTypes"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { DragManager } from "../../util/DragManager"; import { ScriptingGlobals } from "../../util/ScriptingGlobals"; import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu"; import { EditableView } from "../EditableView"; import { GestureOverlay } from "../GestureOverlay"; import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke"; import { LightboxView } from "../LightboxView"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { DocumentView } from "../nodes/DocumentView"; import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox"; import { RichTextMenu } from "../nodes/formattedText/RichTextMenu"; import { PresBox } from "../nodes/trails/PresBox"; import { DefaultStyleProvider } from "../StyleProvider"; import { CollectionDockingView } from "./CollectionDockingView"; import { CollectionLinearView } from "./collectionLinear"; import "./CollectionMenu.scss"; import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView"; import { TabDocView } from "./TabDocView"; import { Colors } from "../global/globalEnums"; interface CollectionMenuProps { panelHeight: () => number; panelWidth: () => number; } @observer export class CollectionMenu extends AntimodeMenu{ @observable static Instance: CollectionMenu; @observable SelectedCollection: DocumentView | undefined; @observable FieldKey: string; private _docBtnRef = React.createRef(); constructor(props: any) { super(props); this.FieldKey = ""; runInAction(() => CollectionMenu.Instance = this); this._canFade = false; // don't let the inking menu fade away runInAction(() => this.Pinned = Cast(Doc.UserDoc()["menuCollections-pinned"], "boolean", true)); this.jumpTo(300, 300); } componentDidMount() { reaction(() => SelectionManager.Views().length && SelectionManager.Views()[0], view => view && this.SetSelection(view)); } @action SetSelection(view: DocumentView) { this.SelectedCollection = view; } @action toggleMenuPin = (e: React.MouseEvent) => { Doc.UserDoc()["menuCollections-pinned"] = this.Pinned = !this.Pinned; if (!this.Pinned && this._left < 0) { this.jumpTo(300, 300); } } @action toggleTopBar = () => { if (CurrentUserUtils.headerBarHeight > 0) { CurrentUserUtils.headerBarHeight = 0; } else { CurrentUserUtils.headerBarHeight = 60; } } buttonBarXf = () => { if (!this._docBtnRef.current) return Transform.Identity(); const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current); return new Transform(-translateX, -translateY, 1 / scale); } @computed get contMenuButtons() { const selDoc = CurrentUserUtils.MyContextMenuBtns; return !(selDoc instanceof Doc) ? (null) :
; } render() { const propIcon = CurrentUserUtils.headerBarHeight > 0 ? "angle-double-up" : "angle-double-down"; const propTitle = CurrentUserUtils.headerBarHeight > 0 ? "Close Header Bar" : "Open Header Bar"; const prop = {propTitle}} key="topar" placement="bottom">
0 ? Colors.MEDIUM_BLUE : undefined }} onPointerDown={this.toggleTopBar}>
; // NEW BUTTONS //dash col linear view buttons const contMenuButtons =
{this.contMenuButtons} {prop}
; return contMenuButtons; // const button = Pin Menu} key="pin menu" placement="bottom"> // // ; // //OLD BUTTONS // return this.getElement(!this.SelectedCollection ? [/*button*/] : // [, // prop, // /*button*/]); } } interface CollectionViewMenuProps { type: CollectionViewType; fieldKey: string; docView: DocumentView; } const stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation(); @observer export class CollectionViewBaseChrome extends React.Component { //(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\) get document() { return this.props.docView?.props.Document; } get target() { return this.document; } _templateCommand = { params: ["target", "source"], title: "item view", script: "self.target.childLayoutTemplate = getDocTemplate(self.source?.[0])", immediate: undoBatch((source: Doc[]) => { let formatStr = source.length && Cast(source[0].text, RichTextField, null)?.Text; try { formatStr && JSON.parse(formatStr); } catch (e) { formatStr = ""; } if (source.length === 1 && formatStr) { Doc.SetInPlace(this.target, "childLayoutString", formatStr, false); } else if (source.length) { this.target.childLayoutTemplate = Doc.getDocTemplate(source?.[0]); } else { Doc.SetInPlace(this.target, "childLayoutString", undefined, true); Doc.SetInPlace(this.target, "childLayoutTemplate", undefined, true); } }), initialize: emptyFunction, }; _narrativeCommand = { params: ["target", "source"], title: "child click view", script: "self.target.childClickedOpenTemplateView = getDocTemplate(self.source?.[0])", immediate: undoBatch((source: Doc[]) => source.length && (this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]))), initialize: emptyFunction, }; _contentCommand = { params: ["target", "source"], title: "set content", script: "getProto(self.target).data = copyField(self.source);", immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List(source)), initialize: emptyFunction, }; _onClickCommand = { params: ["target", "proxy"], title: "copy onClick", script: `{ if (self.proxy?.[0]) { getProto(self.proxy[0]).onClick = copyField(self.target.onClick); getProto(self.proxy[0]).target = self.target.target; getProto(self.proxy[0]).source = copyField(self.target.source); }}`, immediate: undoBatch((source: Doc[]) => { }), initialize: emptyFunction, }; _openLinkInCommand = { params: ["target", "container"], title: "link follow target", script: `{ if (self.container?.length) { getProto(self.target).linkContainer = self.container[0]; getProto(self.target).isLinkButton = true; getProto(self.target).onClick = makeScript("getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])"); }}`, immediate: undoBatch((container: Doc[]) => { if (container.length) { Doc.GetProto(this.target).linkContainer = container[0]; Doc.GetProto(this.target).isLinkButton = true; Doc.GetProto(this.target).onClick = ScriptField.MakeScript("getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])"); } }), initialize: emptyFunction, }; _viewCommand = { params: ["target"], title: "bookmark view", script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale']; gotoFrame(self.target, self['target-currentFrame']);", immediate: undoBatch((source: Doc[]) => { this.target._panX = 0; this.target._panY = 0; this.target._viewScale = 1; this.target._currentFrame = (this.target._currentFrame === undefined ? undefined : 0); }), initialize: (button: Doc) => { button['target-panX'] = this.target._panX; button['target-panY'] = this.target._panY; button['target-viewScale'] = this.target._viewScale; button['target-currentFrame'] = this.target._currentFrame; }, }; _clusterCommand = { params: ["target"], title: "fit content", script: "self.target._fitContentsToBox = !self.target._fitContentsToBox;", immediate: undoBatch((source: Doc[]) => this.target._fitContentsToBox = !this.target._fitContentsToBox), initialize: emptyFunction }; _fitContentCommand = { params: ["target"], title: "toggle clusters", script: "self.target._useClusters = !self.target._useClusters;", immediate: undoBatch((source: Doc[]) => this.target._useClusters = !this.target._useClusters), initialize: emptyFunction }; _saveFilterCommand = { params: ["target"], title: "save filter", script: `self.target._docFilters = compareLists(self['target-docFilters'],self.target._docFilters) ? undefined : copyField(self['target-docFilters']); self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`, immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }), initialize: (button: Doc) => { const activeDash = CurrentUserUtils.ActiveDashboard; if (activeDash) { button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ? ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) as any as ObjectField) : undefined; button['target-searchFilterDocs'] = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined; } }, }; @computed get _freeform_commands() { return Doc.noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; } @computed get _stacking_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } @computed get _masonry_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } @computed get _schema_commands() { return Doc.noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; } @computed get _doc_commands() { return Doc.noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; } @computed get _tree_commands() { return undefined; } private get _buttonizableCommands() { switch (this.props.type) { default: return this._doc_commands; case CollectionViewType.Freeform: return this._freeform_commands; 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.Time: return this._freeform_commands; case CollectionViewType.Carousel: return this._freeform_commands; case CollectionViewType.Carousel3D: return this._freeform_commands; } } 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) => { const target = this.document !== CurrentUserUtils.MyLeftSidebarPanel ? this.document : this.document.proto as Doc; //@ts-ignore target._viewType = e.target.selectedOptions[0].value; } commandChanged = (e: React.ChangeEvent) => { //@ts-ignore runInAction(() => this._currentKey = e.target.selectedOptions[0].value); } @action closeViewSpecs = () => { this.document._facetWidth = 0; } @computed get subChrome() { switch (this.props.docView.props.LayoutTemplateString ? CollectionViewType.Freeform : this.props.type) { // bcz: ARgh! hack to get menu for tree view outline items default: return this.otherSubChrome; case CollectionViewType.Invalid: case CollectionViewType.Freeform: return (); case CollectionViewType.Stacking: return (); case CollectionViewType.Schema: return (); case CollectionViewType.Tree: return (); case CollectionViewType.Masonry: return (); case CollectionViewType.Carousel: case CollectionViewType.Carousel3D: return (); case CollectionViewType.Grid: return (); case CollectionViewType.Docking: return (); } } @computed get otherSubChrome() { const docType = this.props.docView.Document.type; switch (docType) { default: return (null); case DocumentType.IMG: return (); case DocumentType.PDF: return (); case DocumentType.INK: return (); case DocumentType.WEB: return (); case DocumentType.VID: return (); case DocumentType.RTF: return (); case DocumentType.MAP: return (); } } 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.type; const c = { params: ["target"], title: vtype, script: `this.target._viewType = '${StrCast(this.props.type)}'`, immediate: (source: Doc[]) => this.document._viewType = Doc.getDocTemplate(source?.[0]), initialize: emptyFunction, }; DragManager.StartButtonDrag([this._viewRef.current!], c.script, StrCast(c.title), { target: this.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.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
drop document to apply or drag to create button
} placement="bottom">
; } @computed get viewModes() { const excludedViewTypes = [CollectionViewType.Invalid, CollectionViewType.Docking, CollectionViewType.Pile, CollectionViewType.StackedTimeline, CollectionViewType.Linear]; const isPres: boolean = (this.document && this.document.type === DocumentType.PRES); return isPres ? (null) : (
drop document to apply or drag to create button
} placement="bottom">
); } @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); } @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); } @computed get notACollection() { if (this.selectedDoc) { const layoutField = Doc.LayoutField(this.selectedDoc); return this.props.type === CollectionViewType.Docking || typeof (layoutField) === "string" && !layoutField?.includes("CollectionView"); } else return false; } @computed get pinButton() { const targetDoc = this.selectedDoc; const isPinned = targetDoc && Doc.isDocPinned(targetDoc); return !targetDoc ? (null) : {Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}} placement="top"> ; } @undoBatch @action startRecording = () => { const doc = Docs.Create.ScreenshotDocument({ title: "screen recording", _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" }); //Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc); CollectionDockingView.AddSplit(doc, "right"); } @computed get recordButton() { const targetDoc = this.selectedDoc; return {"Capture screen"}} placement="top"> ; } @undoBatch @action pinWithView = (targetDoc: Opt) => { if (targetDoc) { TabDocView.PinDoc(targetDoc); const presArray: Doc[] = PresBox.Instance?.sortArray(); const size: number = PresBox.Instance?._selectedArray.size; const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined; const activeDoc = presSelected ? PresBox.Instance?.childDocs[PresBox.Instance?.childDocs.indexOf(presSelected) + 1] : PresBox.Instance?.childDocs[PresBox.Instance?.childDocs.length - 1]; if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) { const scroll = targetDoc._scrollTop; activeDoc.presPinView = true; activeDoc.presPinViewScroll = scroll; } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.MAP) { const x = targetDoc._panX; const y = targetDoc._panY; const scale = targetDoc._viewScale; activeDoc.presPinView = true; activeDoc.presPinViewX = x; activeDoc.presPinViewY = y; activeDoc.presPinViewScale = scale; } else if (targetDoc.type === DocumentType.VID) { activeDoc.presPinView = true; } else if (targetDoc.type === DocumentType.COMPARISON) { const width = targetDoc._clipWidth; activeDoc.presPinClipWidth = width; activeDoc.presPinView = true; } } } @computed get pinWithViewButton() { const presPinWithViewIcon = ; return !this.selectedDoc ? (null) : {"Pin with current view"}} placement="top"> ; } @undoBatch onAlias = () => { if (this.selectedDoc && this.selectedDocumentView) { // const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true); // copy.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width); // copy.y = NumCast(this.selectedDoc.y) + 30; // this.selectedDocumentView.props.addDocument?.(copy); const alias = Doc.MakeAlias(this.selectedDoc); alias.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width); alias.y = NumCast(this.selectedDoc.y) + 30; this.selectedDocumentView.props.addDocument?.(alias); } } onAliasButtonDown = (e: React.PointerEvent): void => { setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction); } @undoBatch onAliasButtonMoved = (e: PointerEvent) => { const contentDiv = this.selectedDocumentView?.ContentDiv; if (contentDiv && this.selectedDoc) { const dragData = new DragManager.DocumentDragData([this.selectedDoc]); const offset = [e.clientX - contentDiv.getBoundingClientRect().x, e.clientY - contentDiv.getBoundingClientRect().y]; dragData.defaultDropAction = "alias"; dragData.canEmbed = true; DragManager.StartDocumentDrag([contentDiv], dragData, e.clientX, e.clientY, { offsetX: offset[0], offsetY: offset[1], hideSource: false }); return true; } return false; } @computed get aliasButton() { const targetDoc = this.selectedDoc; return !targetDoc || targetDoc.type === DocumentType.PRES ? (null) : {"Tap or Drag to create an alias"}} placement="top"> ; } @computed get lightboxButton() { const targetDoc = this.selectedDoc; return !targetDoc ? (null) : {"View in Lightbox"}} placement="top"> ; } @computed get toggleOverlayButton() { return <> Toggle Overlay Layer} placement="bottom"> ; } render() { return (
{this.notACollection || this.props.type === CollectionViewType.Invalid ? (null) : this.viewModes}
{this.aliasButton} {/* {this.pinButton} */} {this.toggleOverlayButton} {this.pinWithViewButton}
{this.subChrome}
{this.lightboxButton} {this.recordButton} {!this._buttonizableCommands ? (null) : this.templateChrome}
); } } @observer export class CollectionDockingChrome extends React.Component { render() { return (null); } } @observer export class CollectionFreeFormViewChrome extends React.Component { public static Instance: CollectionFreeFormViewChrome; constructor(props: any) { super(props); CollectionFreeFormViewChrome.Instance = this; } get document() { return this.props.docView.props.Document; } @computed get dataField() { return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? "-annotations" : "")]; } @computed get childDocs() { return DocListCast(this.dataField); } @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); } @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); } @computed get isText() { return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false; } @undoBatch @action nextKeyframe = (): void => { const currentFrame = Cast(this.document._currentFrame, "number", null); 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 = Cast(this.document._currentFrame, "number", null); 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); } private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF", ""]; private _width = ["1", "5", "10", "100"]; private _dotsize = [10, 20, 30, 40]; private _draw = ["∿", "=", "⎯", "→", "↔︎", "ロ", "O"]; private _head = ["", "", "", "", "arrow", "", ""]; private _end = ["", "", "", "arrow", "arrow", "", ""]; private _shapePrims = ["", "", "line", "line", "line", "rectangle", "circle"]; private _title = ["pen", "highlighter", "line", "line with arrow", "line with double arrows", "square", "circle"]; private _faName = ["pen-fancy", "highlighter", "minus", "long-arrow-alt-right", "arrows-alt-h", "square", "circle"]; @observable _selectedPrimitive = this._shapePrims.length; @observable _keepPrimitiveMode = false; // for whether primitive selection enters a one-shot or persistent mode @observable _colorBtn = false; @observable _widthBtn = false; @observable _fillBtn = false; @action clearKeepPrimitiveMode() { this._selectedPrimitive = this._shapePrims.length; } @action primCreated() { if (!this._keepPrimitiveMode) { //get out of ink mode after each stroke= if (CurrentUserUtils.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor); CurrentUserUtils.ActiveTool = InkTool.None; this._selectedPrimitive = this._shapePrims.length; SetActiveArrowStart("none"); SetActiveArrowEnd("none"); } } @action changeColor = (color: string, type: string) => { const col: ColorState = { hex: color, hsl: { a: 0, h: 0, s: 0, l: 0, source: "" }, hsv: { a: 0, h: 0, s: 0, v: 0, source: "" }, rgb: { a: 0, r: 0, b: 0, g: 0, source: "" }, oldHue: 0, source: "", }; if (type === "color") { SetActiveInkColor(Utils.colorString(col)); } else if (type === "fill") { SetActiveFillColor(Utils.colorString(col)); } } @action editProperties = (value: any, field: string) => { SelectionManager.Views().forEach(action((element: DocumentView) => { const doc = Document(element.rootDoc); if (doc.type === DocumentType.INK) { switch (field) { case "width": doc.strokeWidth = Number(value); break; case "color": doc.color = String(value); break; case "fill": doc.fillColor = String(value); break; case "dash": doc.strokeDash = value; } } })); } @computed get drawButtons() { const func = action((e: React.MouseEvent | React.PointerEvent, i: number, keep: boolean) => { this._keepPrimitiveMode = keep; // these are for shapes if (this._selectedPrimitive !== i) { this._selectedPrimitive = i; if (this._title[i] === "highlighter") { CurrentUserUtils.ActiveTool = InkTool.Highlighter; GestureOverlay.Instance.SavedColor = ActiveInkColor(); SetActiveInkColor("rgba(245, 230, 95, 0.75)"); } else { CurrentUserUtils.ActiveTool = InkTool.Pen; } SetActiveArrowStart(this._head[i]); SetActiveArrowEnd(this._end[i]); SetActiveBezierApprox("300"); GestureOverlay.Instance.InkShape = this._shapePrims[i]; } else { this._selectedPrimitive = this._shapePrims.length; CurrentUserUtils.ActiveTool = InkTool.None; SetActiveArrowStart(""); SetActiveArrowEnd(""); GestureOverlay.Instance.InkShape = ""; SetActiveBezierApprox("0"); } e.stopPropagation(); }); return
{this._draw.map((icon, i) => {this._title[i]}
} placement="bottom"> )} ; } toggleButton = (key: string, value: boolean, setter: () => {}, icon: FontAwesomeIconProps["icon"], ele: JSX.Element | null) => { return {key}} placement="bottom"> ; } @computed get widthPicker() { const widthPicker = this.toggleButton("stroke width", this._widthBtn, () => this._widthBtn = !this._widthBtn, "bars", null); return !this._widthBtn ? widthPicker :
{widthPicker} {this._width.map((wid, i) => change width
} placement="bottom"> )} ; } @computed get colorPicker() { const colorPicker = this.toggleButton("stroke color", this._colorBtn, () => this._colorBtn = !this._colorBtn, "pen-nib",
); return !this._colorBtn ? colorPicker :
{colorPicker} {this._palette.map(color => )}
; } @computed get fillPicker() { const fillPicker = this.toggleButton("shape fill color", this._fillBtn, () => this._fillBtn = !this._fillBtn, "fill-drip",
); return !this._fillBtn ? fillPicker :
{fillPicker} {this._palette.map(color => )}
; } render() { return !this.props.docView.layoutDoc ? (null) :
{!this.isText ? <> {this.drawButtons} {this.widthPicker} {this.colorPicker} {this.fillPicker} {Doc.noviceMode || this.props.isDoc ? (null) : <> Back Frame
} placement="bottom">
Toggle View All
} placement="bottom">
this.props.docView.ComponentView?.setKeyFrameEditing?.(!this.props.docView.ComponentView?.getKeyFrameEditing?.()))} > {NumCast(this.document._currentFrame)}
Forward Frame
} placement="bottom">
} : (null) } {!this.selectedDocumentView?.ComponentView?.menuControls ? (null) : this.selectedDocumentView?.ComponentView?.menuControls?.()} ; } } @observer export class CollectionStackingViewChrome extends React.Component { @observable private _currentKey: string = ""; @observable private suggestions: string[] = []; get document() { return this.props.docView.props.Document; } @computed private get descending() { return StrCast(this.document._columnsSort) === "descending"; } @computed get pivotField() { return StrCast(this.document._pivotField); } getKeySuggestions = async (value: string): Promise => { const val = value.toLowerCase(); const docs = DocListCast(this.document[this.props.fieldKey]); if (Doc.noviceMode) { if (docs instanceof Doc) { const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 || key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== "_")); return keys.filter(key => key.toLowerCase().indexOf(val) > -1); } else { const keys = new Set(); docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key))); const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 || key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== "_")); return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1); } } if (docs instanceof Doc) { return Object.keys(docs).filter(key => key.toLowerCase().indexOf(val) > -1); } else { const keys = new Set(); docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key))); return Array.from(keys).filter(key => key.toLowerCase().indexOf(val) > -1); } } @action onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => { this._currentKey = newValue; } getSuggestionValue = (suggestion: string) => suggestion; renderSuggestion = (suggestion: string) => { return

{suggestion}

; } onSuggestionFetch = async ({ value }: { value: string }) => { const sugg = await this.getKeySuggestions(value); runInAction(() => { this.suggestions = sugg; }); } @action onSuggestionClear = () => { this.suggestions = []; } @action setValue = (value: string) => { this.document._pivotField = value; return true; } @action toggleSort = () => { this.document._columnsSort = this.document._columnsSort === "descending" ? "ascending" : this.document._columnsSort === "ascending" ? undefined : "descending"; } @action resetValue = () => { this._currentKey = this.pivotField; }; render() { const doctype = this.props.docView.Document.type; const isPres: boolean = (doctype === DocumentType.PRES); return ( isPres ? (null) :
GROUP BY:
this.pivotField} autosuggestProps={ { resetValue: this.resetValue, value: this._currentKey, onChange: this.onKeyChange, autosuggestProps: { inputProps: { value: this._currentKey, onChange: this.onKeyChange }, getSuggestionValue: this.getSuggestionValue, suggestions: this.suggestions, alwaysRenderSuggestions: true, renderSuggestion: this.renderSuggestion, onSuggestionsFetchRequested: this.onSuggestionFetch, onSuggestionsClearRequested: this.onSuggestionClear } }} oneLine SetValue={this.setValue} contents={this.pivotField ? this.pivotField : "N/A"} />
); } } @observer export class CollectionSchemaViewChrome extends React.Component { // private _textwrapAllRows: boolean = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []).length > 0; get document() { return this.props.docView.props.Document; } @undoBatch togglePreview = () => { const dividerWidth = 4; const borderWidth = Number(COLLECTION_BORDER_WIDTH); const panelWidth = this.props.docView.props.PanelWidth(); const previewWidth = NumCast(this.document.schemaPreviewWidth); const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth; this.document.schemaPreviewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0; } @undoBatch @action toggleTextwrap = async () => { const textwrappedRows = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []); if (textwrappedRows.length) { this.document.textwrappedSchemaRows = new List([]); } else { const docs = DocListCast(this.document[this.props.fieldKey]); const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]); this.document.textwrappedSchemaRows = new List(allRows); } } render() { const previewWidth = NumCast(this.document.schemaPreviewWidth); const textWrapped = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []).length > 0; return (
Show Preview:
{previewWidth !== 0 ? "on" : "off"}
); } } @observer export class CollectionTreeViewChrome extends React.Component { get document() { return this.props.docView.props.Document; } get sortAscending() { return this.document[this.props.fieldKey + "-sortAscending"]; } set sortAscending(value) { this.document[this.props.fieldKey + "-sortAscending"] = value; } @computed private get ascending() { return Cast(this.sortAscending, "boolean", null); } @action toggleSort = () => { if (this.sortAscending) this.sortAscending = undefined; else if (this.sortAscending === undefined) this.sortAscending = false; else this.sortAscending = true; } render() { return (
); } } // Enter scroll speed for 3D Carousel @observer export class Collection3DCarouselViewChrome extends React.Component { get document() { return this.props.docView.props.Document; } @computed get scrollSpeed() { return this.document._autoScrollSpeed; } @action setValue = (value: string) => { const numValue = Number(StrCast(value)); if (numValue > 0) { this.document._autoScrollSpeed = numValue; return true; } return false; } render() { return (
{FormattedTextBox.Focused ? : (null)}
AUTOSCROLL SPEED:
StrCast(this.scrollSpeed)} oneLine SetValue={this.setValue} contents={this.scrollSpeed ? this.scrollSpeed : 1000} />
); } } /** * Chrome for grid view. */ @observer export class CollectionGridViewChrome extends React.Component { private clicked: boolean = false; private entered: boolean = false; private decrementLimitReached: boolean = false; @observable private resize = false; private resizeListenerDisposer: Opt; get document() { return this.props.docView.props.Document; } componentDidMount() { runInAction(() => this.resize = this.props.docView.props.PanelWidth() < 700); // listener to reduce text on chrome resize (panel resize) this.resizeListenerDisposer = computed(() => this.props.docView.props.PanelWidth()).observe(({ newValue }) => { runInAction(() => this.resize = newValue < 700); }); } componentWillUnmount() { this.resizeListenerDisposer?.(); } get numCols() { return NumCast(this.document.gridNumCols, 10); } /** * Sets the value of `numCols` on the grid's Document to the value entered. */ onNumColsChange = (e: React.ChangeEvent) => { if (e.currentTarget.valueAsNumber > 0) undoBatch(() => this.document.gridNumCols = e.currentTarget.valueAsNumber)(); } /** * Sets the value of `rowHeight` on the grid's Document to the value entered. */ // @undoBatch // onRowHeightEnter = (e: React.KeyboardEvent) => { // if (e.key === "Enter" || e.key === "Tab") { // if (e.currentTarget.valueAsNumber > 0 && this.document.rowHeight as number !== e.currentTarget.valueAsNumber) { // this.document.rowHeight = e.currentTarget.valueAsNumber; // } // } // } /** * Sets whether the grid is flexible or not on the grid's Document. */ @undoBatch toggleFlex = () => { this.document.gridFlex = !BoolCast(this.document.gridFlex, true); } /** * Increments the value of numCols on button click */ onIncrementButtonClick = () => { this.clicked = true; this.entered && (this.document.gridNumCols as number)--; undoBatch(() => this.document.gridNumCols = this.numCols + 1)(); this.entered = false; } /** * Decrements the value of numCols on button click */ onDecrementButtonClick = () => { this.clicked = true; if (this.numCols > 1 && !this.decrementLimitReached) { this.entered && (this.document.gridNumCols as number)++; undoBatch(() => this.document.gridNumCols = this.numCols - 1)(); if (this.numCols === 1) this.decrementLimitReached = true; } this.entered = false; } /** * Increments the value of numCols on button hover */ incrementValue = () => { this.entered = true; if (!this.clicked && !this.decrementLimitReached) { this.document.gridNumCols = this.numCols + 1; } this.decrementLimitReached = false; this.clicked = false; } /** * Decrements the value of numCols on button hover */ decrementValue = () => { this.entered = true; if (!this.clicked) { if (this.numCols > 1) { this.document.gridNumCols = this.numCols - 1; } else { this.decrementLimitReached = true; } } this.clicked = false; } /** * Toggles the value of preventCollision */ toggleCollisions = () => { this.document.gridPreventCollision = !this.document.gridPreventCollision; } /** * Changes the value of the compactType */ changeCompactType = (e: React.ChangeEvent) => { // need to change startCompaction so that this operation will be undoable. this.document.gridStartCompaction = e.target.selectedOptions[0].value; } render() { return (
) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} /> {/* ) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} /> */}
); } } ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) { const dataField = doc[Doc.LayoutFieldKey(doc)]; const childDocs = DocListCast(dataField); const currentFrame = Cast(doc._currentFrame, "number", null); if (currentFrame === undefined) { doc._currentFrame = 0; CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0); } CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0); doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame); });