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 } 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 { GestureUtils } from '../../../pen-gestures/GestureUtils'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; import { SettingsManager } from '../../util/SettingsManager'; import { Transform } from '../../util/Transform'; import { undoBatch } from '../../util/UndoManager'; import { AntimodeMenu } from '../AntimodeMenu'; import { EditableView } from '../EditableView'; import { GestureOverlay } from '../GestureOverlay'; import { Colors } from '../global/globalEnums'; import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../InkingStroke'; import { LightboxView } from '../LightboxView'; import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView'; import { DocumentView, OpenWhereMod } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; import { DefaultStyleProvider } from '../StyleProvider'; import { CollectionDockingView } from './CollectionDockingView'; import { CollectionLinearView } from './collectionLinear'; import './CollectionMenu.scss'; import { COLLECTION_BORDER_WIDTH } from './CollectionView'; import { TabDocView } from './TabDocView'; import { CollectionFreeFormView } from './collectionFreeForm'; 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 (SettingsManager.headerBarHeight > 0) { SettingsManager.headerBarHeight = 0; } else { SettingsManager.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 = Doc.MyContextMenuBtns; return !(selDoc instanceof Doc) ? null : (
); } render() { const propIcon = SettingsManager.headerBarHeight > 0 ? 'angle-double-up' : 'angle-double-down'; const propTitle = SettingsManager.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, }; _viewCommand = { params: ['target'], title: 'bookmark view', script: "self.target._freeform_panX = self['target-freeform_panX']; self.target._freeform_panY = self['target-freeform_panY']; self.target._freeform_scale = self['target_freeform_scale']; gotoFrame(self.target, self['target-currentFrame']);", immediate: undoBatch((source: Doc[]) => { this.target._freeform_panX = 0; this.target._freeform_panY = 0; this.target._freeform_scale = 1; this.target._currentFrame = this.target._currentFrame === undefined ? undefined : 0; }), initialize: (button: Doc) => { button['target-panX'] = this.target._freeform_panX; button['target-panY'] = this.target._freeform_panY; button['target-_ayout_scale'] = this.target._freeform_scale; button['target-currentFrame'] = this.target._currentFrame; }, }; _clusterCommand = { params: ['target'], title: 'fit content', script: 'self.target._freeform_fitContentsToBox = !self.target._freeform_fitContentsToBox;', immediate: undoBatch((source: Doc[]) => (this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox)), initialize: emptyFunction, }; _fitContentCommand = { params: ['target'], title: 'toggle clusters', script: 'self.target._freeform_useClusters = !self.target._freeform_useClusters;', immediate: undoBatch((source: Doc[]) => (this.target._freeform_useClusters = !this.target._freeform_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 = Doc.ActiveDashboard; if (activeDash) { button['target-docFilters'] = (Doc.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.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 _notetaking_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._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.NoteTaking: return this._notetaking_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 !== Doc.MyLeftSidebarPanel ? this.document : (this.document.proto as Doc); //@ts-ignore target._type_collection = 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.NoteTaking: 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._type_collection = '${StrCast(this.props.type)}'`, immediate: (source: Doc[]) => (this.document._type_collection = 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', _layout_fitWidth: true, _width: 400, _height: 200, mediaState: 'pendingRecording' }); CollectionDockingView.AddSplit(doc, OpenWhereMod.right); }; @computed get recordButton() { const targetDoc = this.selectedDoc; return ( {'Capture screen'}} placement="top"> ); } @undoBatch onEmbed = () => { 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 embedding = Doc.MakeEmbedding(this.selectedDoc); embedding.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width); embedding.y = NumCast(this.selectedDoc.y) + 30; this.selectedDocumentView.props.addDocument?.(embedding); } }; onEmbedButtonDown = (e: React.PointerEvent): void => { setupMoveUpEvents(this, e, this.onEmbedButtonMoved, emptyFunction, emptyFunction); }; @undoBatch onEmbedButtonMoved = (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 = 'embed'; 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 embedButton() { const targetDoc = this.selectedDoc; return !targetDoc || targetDoc.type === DocumentType.PRES ? null : ( {'Tap or Drag to create an embedding'}} 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.embedButton} {/* {this.pinButton} */} {this.toggleOverlayButton}
{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; } public static gotoKeyFrame(doc: Doc, newFrame: number) { if (doc) { const childDocs = DocListCast(doc[Doc.LayoutFieldKey(doc)]); const currentFrame = Cast(doc._currentFrame, 'number', null); if (currentFrame === undefined) { doc._currentFrame = 0; CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0); } CollectionFreeFormView.updateKeyframe(undefined, [...childDocs, doc], currentFrame || 0); doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame); } } _keyTimer: NodeJS.Timeout | undefined; @undoBatch @action nextKeyframe = (): void => { const currentFrame = Cast(this.document._currentFrame, 'number', null); if (currentFrame === undefined) { this.document._currentFrame = 0; CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0); } this._keyTimer = CollectionFreeFormView.updateKeyframe(this._keyTimer, [...this.childDocs, this.document], 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); } this._keyTimer = CollectionFreeFormView.gotoKeyframe(this._keyTimer, [...this.childDocs, this.document], 1000); 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'] as GestureUtils.Gestures[]; 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 (Doc.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor); Doc.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.stroke_width = Number(value); break; case 'color': doc.color = String(value); break; case 'fill': doc.fillColor = String(value); break; case 'dash': doc.stroke_dash = 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') { Doc.ActiveTool = InkTool.Highlighter; GestureOverlay.Instance.SavedColor = ActiveInkColor(); SetActiveInkColor('rgba(245, 230, 95, 0.75)'); } else { Doc.ActiveTool = InkTool.Pen; } SetActiveArrowStart(this._head[i]); SetActiveArrowEnd(this._end[i]); SetActiveBezierApprox('300'); if (GestureOverlay.Instance) GestureOverlay.Instance.InkShape = this._shapePrims[i]; } else { this._selectedPrimitive = this._shapePrims.length; Doc.ActiveTool = InkTool.None; SetActiveArrowStart(''); SetActiveArrowEnd(''); if (GestureOverlay.Instance) GestureOverlay.Instance.InkShape = undefined; 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">
Frame number
} 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('author_date') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_')); return keys.filter(key => key.toLowerCase().indexOf(val) > -1); } 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('author_date') >= 0 || key.indexOf('modificationDate') >= 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 CollectionNoteTakingViewChrome 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.UserDoc().noviceMode) { if (docs instanceof Doc) { const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('author_date') >= 0 || key.indexOf('modificationDate') >= 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('author_date') >= 0 || key.indexOf('modificationDate') >= 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.schema_previewWidth); const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth; this.document.schema_previewWidth = 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.schema_previewWidth); 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) { CollectionFreeFormViewChrome.gotoKeyFrame(doc, newFrame); });