diff options
Diffstat (limited to 'src/client/views')
75 files changed, 833 insertions, 1196 deletions
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 2e6ea1faa..b9772fd57 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -20,10 +20,10 @@ export interface DocComponentProps { LayoutTemplate?: () => Opt<Doc>; LayoutTemplateString?: string; } -export function DocComponent<P extends DocComponentProps>() { +export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: Doc) => T) { class Component extends Touchable<P> { //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then - @computed get Document() { return this.props.Document; } + @computed get Document(): T { return schemaCtor(this.props.Document); } // This is the "The Document" -- it encapsulates, data, layout, and any templates @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; } // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info @@ -48,7 +48,7 @@ interface ViewBoxBaseProps { renderDepth: number; rootSelected: (outsideReaction?: boolean) => boolean; } -export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() { +export function ViewBoxBaseComponent<P extends ViewBoxBaseProps, T>(schemaCtor: (doc: Doc) => T) { class Component extends Touchable<P> { //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then //@computed get Document(): T { return schemaCtor(this.props.Document); } @@ -92,17 +92,17 @@ export interface ViewBoxAnnotatableProps { renderDepth: number; isAnnotationOverlay?: boolean; } -export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() { +export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T>(schemaCtor: (doc: Doc) => T) { class Component extends Touchable<P> { @observable _annotationKeySuffix = () => "annotations"; @observable _isAnyChildContentActive = false; //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then - @computed get Document() { return this.props.Document; } + @computed get Document(): T { return schemaCtor(this.props.Document); } // This is the "The Document" -- it encapsulates, data, layout, and any templates @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; } // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info - @computed get layoutDoc() { return Doc.Layout(this.props.Document); } + @computed get layoutDoc() { return schemaCtor(Doc.Layout(this.props.Document)); } // This is the data part of a document -- ie, the data that is constant across all views of the document @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 657d92b8a..cb95a1237 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -22,13 +22,13 @@ import { CollectionDockingView } from './collections/CollectionDockingView'; import { DocumentButtonBar } from './DocumentButtonBar'; import './DocumentDecorations.scss'; import { KeyManager } from './GlobalKeyHandler'; -import { InkingStroke } from './InkingStroke'; import { InkStrokeProperties } from './InkStrokeProperties'; import { LightboxView } from './LightboxView'; import { DocumentView } from "./nodes/DocumentView"; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import React = require("react"); -import { CollectionFreeFormView } from './collections/collectionFreeForm'; +import { InkingStroke } from './InkingStroke'; +import e = require('express'); @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number, PanelHeight: number, boundsLeft: number, boundsTop: number }, { value: string }> { @@ -148,7 +148,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P pageY: e.pageY, preventDefault: emptyFunction, button: 0 - }, [SelectionManager.Views().slice(-1)[0].rootDoc]); + }, [SelectionManager.Views().lastElement().rootDoc]); return true; }, emptyFunction, this.onMaximizeClick, false, false); } @@ -233,13 +233,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P this._resizeUndo = UndoManager.StartBatch("DocDecs resize"); this._snapX = e.pageX; this._snapY = e.pageY; - const ffviewSet = new Set<CollectionFreeFormView>(); - SelectionManager.Views().forEach(docView => { - const ffview = docView.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; - ffview && ffviewSet.add(ffview); - this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) }); - }); - Array.from(ffviewSet).map(ffview => ffview.setupDragLines(false)); + SelectionManager.Views().forEach(docView => this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) })); } onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => { @@ -401,10 +395,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P this._inkDragDocs.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] })) .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => { Doc.GetProto(doc).data = new InkField(inkPts.map(ipt => // (new x — oldx) + newWidth * (oldxpoint /oldWidth) - ({ - X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width, - Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height - }))); + ({ + X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width, + Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height + }))); Doc.SetNativeWidth(doc, undefined); Doc.SetNativeHeight(doc, undefined); }); @@ -430,7 +424,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P render() { const bounds = this.Bounds; - const seldoc = SelectionManager.Views().slice(-1)[0]; + const seldoc = SelectionManager.Views().lastElement(); if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { return (null); } diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 50dca0a99..04abdbf37 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -13,7 +13,7 @@ import { CognitiveServices } from "../cognitive_services/CognitiveServices"; import { DocUtils } from "../documents/Documents"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { InteractionUtils } from "../util/InteractionUtils"; -import { ScriptingGlobals } from "../util/ScriptingGlobals"; +import { Scripting } from "../util/Scripting"; import { SelectionManager } from "../util/SelectionManager"; import { Transform } from "../util/Transform"; import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu"; @@ -943,11 +943,11 @@ export enum ToolglassTools { None = "none", } -ScriptingGlobals.add("GestureOverlay", GestureOverlay); -ScriptingGlobals.add(function setToolglass(tool: any) { +Scripting.addGlobal("GestureOverlay", GestureOverlay); +Scripting.addGlobal(function setToolglass(tool: any) { runInAction(() => GestureOverlay.Instance.Tool = tool); }); -ScriptingGlobals.add(function setPen(width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) { +Scripting.addGlobal(function setPen(width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) { runInAction(() => { GestureOverlay.Instance.SavedColor = ActiveInkColor(); SetActiveInkColor(color); @@ -959,12 +959,12 @@ ScriptingGlobals.add(function setPen(width: any, color: any, fill: any, arrowSta SetActiveDash(dash); }); }); -ScriptingGlobals.add(function resetPen() { +Scripting.addGlobal(function resetPen() { runInAction(() => { SetActiveInkColor(GestureOverlay.Instance.SavedColor ?? "rgb(0, 0, 0)"); SetActiveInkWidth(GestureOverlay.Instance.SavedWidth?.toString() ?? "2"); }); }, "resets the pen tool"); -ScriptingGlobals.add(function createText(text: any, x: any, y: any) { +Scripting.addGlobal(function createText(text: any, x: any, y: any) { GestureOverlay.Instance.dispatchGesture("text", [{ X: x, Y: y }], text); }, "creates a text document with inputted text and coordinates", "(text: any, x: any, y: any)"); diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx index a7f511ab4..24f796105 100644 --- a/src/client/views/InkControlPtHandles.tsx +++ b/src/client/views/InkControlPtHandles.tsx @@ -51,7 +51,6 @@ export class InkControlPtHandles extends React.Component<InkControlProps> { const handleIndexB = (order === 3 ? controlIndex + 2 : controlIndex + 1) % this.props.inkCtrlPoints.length; const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number")); const wasSelected = InkStrokeProperties.Instance._currentPoint === controlIndex; - if (!wasSelected) InkStrokeProperties.Instance._currentPoint = -1; const origInk = this.props.inkCtrlPoints.slice(); setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 40fe6aa68..9dbd97c16 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -24,7 +24,9 @@ import React = require("react"); import { action, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc, WidthSym } from "../../fields/Doc"; +import { documentSchema } from "../../fields/documentSchemas"; import { InkData, InkField, InkTool } from "../../fields/InkField"; +import { makeInterface } from "../../fields/Schema"; import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types"; import { TraceMobx } from "../../fields/util"; import { OmitKeys, returnFalse, setupMoveUpEvents } from "../../Utils"; @@ -44,8 +46,11 @@ import { FieldView, FieldViewProps } from "./nodes/FieldView"; import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox"; import Color = require("color"); +type InkDocument = makeInterface<[typeof documentSchema]>; +const InkDocument = makeInterface(documentSchema); + @observer -export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() { +export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocument>(InkDocument) { static readonly MaskDim = 50000; // choose a really big number to make sure mask fits over container (which in theory can be arbitrarily big) public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); } public static IsClosed(inkData: InkData) { diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 8560ccb29..7553c8118 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -1,15 +1,13 @@ -// if ((module as any).hot) { -// (module as any).hot.accept(); -// } - -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import { AssignAllExtensions } from "../../extensions/General/Extensions"; +import { MainView } from "./MainView"; import { Docs } from "../documents/Documents"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; -import { LinkManager } from "../util/LinkManager"; +import * as ReactDOM from 'react-dom'; +import * as React from 'react'; +import { DocServer } from "../DocServer"; +import { AssignAllExtensions } from "../../extensions/General/Extensions"; +import { Networking } from "../Network"; import { CollectionView } from "./collections/CollectionView"; -import { MainView } from "./MainView"; +import { LinkManager } from "../util/LinkManager"; AssignAllExtensions(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 8c0795881..fc1c694a2 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -5,6 +5,7 @@ import * as fa from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; +import "normalize.css"; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; @@ -23,7 +24,7 @@ import { DocumentManager } from '../util/DocumentManager'; import { GroupManager } from '../util/GroupManager'; import { HistoryUtil } from '../util/History'; import { Hypothesis } from '../util/HypothesisUtils'; -import { ScriptingGlobals } from '../util/ScriptingGlobals'; +import { Scripting } from '../util/Scripting'; import { SelectionManager } from '../util/SelectionManager'; import { ColorScheme, SettingsManager } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; @@ -44,6 +45,7 @@ import { GestureOverlay } from './GestureOverlay'; import { DASHBOARD_SELECTOR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss'; import { Colors } from './global/globalEnums'; import { KeyManager } from './GlobalKeyHandler'; +import { InkStrokeProperties } from './InkStrokeProperties'; import { LightboxView } from './LightboxView'; import { LinkMenu } from './linking/LinkMenu'; import "./MainView.scss"; @@ -163,28 +165,28 @@ export class MainView extends React.Component { } } - library.add(...[fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar, - fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, - fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard, - fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight, - fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow, - fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft, - fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp, - fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt, - fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer, - fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV, - fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, - fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined, - fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript, - fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any, - fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any, - fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp, - fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl, - fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo, - fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical, - fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll, - fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines, - fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt]); + library.add(fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar, + fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, + fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard, + fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight, + fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow, + fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft, + fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp, + fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt, + fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer, + fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV, + fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, + fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined, + fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript, + fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any, + fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any, + fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp, + fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl, + fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo, + fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical, + fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll, + fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines, + fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeMute, fa.faMapMarkerAlt); this.initAuthenticationRouters(); } @@ -694,4 +696,4 @@ export class MainView extends React.Component { } } -ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
\ No newline at end of file +Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
\ No newline at end of file diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 0f51cf9b2..7cf388872 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -5,17 +5,17 @@ import ReactLoading from 'react-loading'; import { Doc } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { Cast, NumCast } from "../../fields/Types"; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from "../../Utils"; -import { DocUtils } from "../documents/Documents"; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils, emptyPath } from "../../Utils"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { DragManager } from "../util/DragManager"; -import { ScriptingGlobals } from "../util/ScriptingGlobals"; +import { Scripting } from "../util/Scripting"; import { Transform } from "../util/Transform"; import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView"; import { DocumentView } from "./nodes/DocumentView"; import './OverlayView.scss'; import { ScriptingRepl } from './ScriptingRepl'; import { DefaultStyleProvider } from "./StyleProvider"; +import { DocUtils } from "../documents/Documents"; export type OverlayDisposer = () => void; @@ -226,6 +226,6 @@ export class OverlayView extends React.Component { } } // bcz: ugh ... want to be able to pass ScriptingRepl as tag argument, but that doesn't seem to work.. runtime error -ScriptingGlobals.add(function addOverlayWindow(type: string, options: OverlayElementOptions) { +Scripting.addGlobal(function addOverlayWindow(type: string, options: OverlayElementOptions) { OverlayView.Instance.addWindow(<ScriptingRepl />, options); });
\ No newline at end of file diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index dd6448654..f9dab9f82 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -18,10 +18,6 @@ import React = require("react"); import { Colors } from "./global/globalEnums"; import { CollectionFreeFormView } from "./collections/collectionFreeForm"; import { DocumentManager } from "../util/DocumentManager"; -import { pasteImageBitmap } from "./nodes/WebBoxRenderer"; -import { VideoBox } from "./nodes/VideoBox"; -import { Id } from "../../fields/FieldSymbols"; -import { ImageField } from "../../fields/URLField"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -96,20 +92,6 @@ export class PropertiesButtons extends React.Component<{}, {}> { @computed get groupButton() { return this.propertyToggleBtn("Group", "isGroup", on => `Display collection as a Group`, on => "object-group", (dv, doc) => { doc.isGroup = !doc.isGroup; doc.forceActive = doc.isGroup; }); } - @computed get freezeThumb() { - return this.propertyToggleBtn("Freeze\Thumb", "_thumb-frozen", on => `${on ? "Freeze" : "Unfreeze"} thumbnail`, on => "arrows-alt-h", (dv, doc) => { - if (doc["thumb-frozen"]) doc["thumb-frozen"] = undefined; - else { - document.body.focus(); // so that we can access the clipboard without an error - setTimeout(() => - pasteImageBitmap((data: any, error: any) => { - error && console.log(error); - data && VideoBox.convertDataUri(data, doc[Id] + "-thumb-frozen", true).then( - returnedfilename => doc["thumb-frozen"] = new ImageField(returnedfilename)); - })); - } - }); - } @computed get snapButton() { return this.propertyToggleBtn("Snap\xA0Lines", "showSnapLines", on => `Display snapping lines when objects are dragged`, on => "border-all", undefined, true); } @@ -240,7 +222,6 @@ export class PropertiesButtons extends React.Component<{}, {}> { {toggle(this.dictationButton, { display: isNovice ? "none" : "" })} {toggle(this.onClickButton)} {toggle(this.fitWidthButton)} - {toggle(this.freezeThumb)} {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? "none" : "" })} {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? "none" : "" })} {toggle(this.maskButton, { display: !isInk ? "none" : "" })} diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 47a5cd07e..f53944805 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -33,7 +33,6 @@ import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector"; import "./PropertiesView.scss"; import { DefaultStyleProvider } from "./StyleProvider"; import { PresBox } from "./nodes/trails"; -import { IconLookup } from "@fortawesome/fontawesome-svg-core"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -1302,7 +1301,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { onPointerDown={this.toggleAnchor} onClick={e => e.stopPropagation()} className="propertiesButton" > - <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" /> + <FontAwesomeIcon className="fa-icon" icon={faAnchor} size="lg" /> </button> </div> <div className="propertiesView-input inline" id="propertiesView-displayArrow"> @@ -1312,7 +1311,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { onPointerDown={this.toggleArrow} onClick={e => e.stopPropagation()} className="propertiesButton" > - <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" /> + <FontAwesomeIcon className="fa-icon" icon={faArrowRight} size="lg" /> </button> </div> </div> diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx index b7ea124b9..e2b5d8dc3 100644 --- a/src/client/views/ScriptBox.tsx +++ b/src/client/views/ScriptBox.tsx @@ -1,17 +1,17 @@ -import { action, observable } from "mobx"; -import { observer } from "mobx-react"; import * as React from "react"; -import { Doc, Opt } from "../../fields/Doc"; -import { ScriptField } from "../../fields/ScriptField"; -import { ScriptCast } from "../../fields/Types"; +import { observer } from "mobx-react"; +import { observable, action } from "mobx"; + +import "./ScriptBox.scss"; +import { OverlayView } from "./OverlayView"; +import { DocumentIconContainer } from "./nodes/DocumentIcon"; +import { Opt, Doc } from "../../fields/Doc"; import { emptyFunction } from "../../Utils"; -import { DragManager } from "../util/DragManager"; +import { ScriptCast } from "../../fields/Types"; import { CompileScript } from "../util/Scripting"; +import { ScriptField } from "../../fields/ScriptField"; +import { DragManager } from "../util/DragManager"; import { EditableView } from "./EditableView"; -import { DocumentIconContainer } from "./nodes/DocumentIcon"; -import { OverlayView } from "./OverlayView"; -import "./ScriptBox.scss"; - export interface ScriptBoxProps { onSave: (text: string, onError: (error: string) => void) => void; diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx index 4ed6da24a..c598b2861 100644 --- a/src/client/views/ScriptingRepl.tsx +++ b/src/client/views/ScriptingRepl.tsx @@ -3,8 +3,7 @@ import { action, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { DocumentManager } from '../util/DocumentManager'; -import { CompileScript, Transformer, ts } from '../util/Scripting'; -import { ScriptingGlobals } from '../util/ScriptingGlobals'; +import { CompileScript, Scripting, Transformer, ts } from '../util/Scripting'; import { DocumentIconContainer } from './nodes/DocumentIcon'; import { OverlayView } from './OverlayView'; import './ScriptingRepl.scss'; @@ -82,7 +81,7 @@ export class ScriptingRepl extends React.Component { transformer: context => { const knownVars: { [name: string]: number } = {}; const usedDocuments: number[] = []; - ScriptingGlobals.getGlobals().forEach((global: any) => knownVars[global] = 1); + Scripting.getGlobals().forEach(global => knownVars[global] = 1); return root => { function visit(node: ts.Node) { let skip = false; @@ -125,7 +124,7 @@ export class ScriptingRepl extends React.Component { case "Enter": { const docGlobals: { [name: string]: any } = {}; DocumentManager.Instance.DocumentViews.forEach((dv, i) => docGlobals[`d${i}`] = dv.props.Document); - const globals = ScriptingGlobals.makeMutableGlobalsCopy(docGlobals); + const globals = Scripting.makeMutableGlobalsCopy(docGlobals); const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: "any" }, transformer: this.getTransformer(), globals }); if (!script.compiled) { this.commands.push({ command: this.commandString, result: script.errors }); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 2782574c5..ce363cc47 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -7,7 +7,7 @@ import { Doc, Opt, StrListCast } from "../../fields/Doc"; import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types"; -import { DashColor, lightOrDark } from '../../Utils'; +import { lightOrDark, DashColor } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; import { ColorScheme } from '../util/SettingsManager'; diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index b3a24e031..ff3f92364 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -5,9 +5,9 @@ import { List } from "../../fields/List"; import { ScriptField } from "../../fields/ScriptField"; import { Cast, StrCast } from "../../fields/Types"; import { TraceMobx } from "../../fields/util"; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils"; +import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils"; import { Docs, DocUtils } from "../documents/Documents"; -import { ScriptingGlobals } from "../util/ScriptingGlobals"; +import { Scripting } from "../util/Scripting"; import { Transform } from "../util/Transform"; import { undoBatch } from "../util/UndoManager"; import { CollectionTreeView } from "./collections/CollectionTreeView"; @@ -164,7 +164,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> { } } -ScriptingGlobals.add(function switchView(doc: Doc, template: Doc | undefined) { +Scripting.addGlobal(function switchView(doc: Doc, template: Doc | undefined) { if (template?.dragFactory) { template = Cast(template.dragFactory, Doc, null); } diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 92d3e2bed..82b0218bf 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -1,17 +1,18 @@ -import { action, computed, observable, runInAction } from "mobx"; -import { observer } from "mobx-react"; import * as React from "react"; -import { Doc, DocListCast, Opt } from "../../../fields/Doc"; -import { List } from "../../../fields/List"; -import { createSchema, defaultSpec, listSpec, makeInterface } from "../../../fields/Schema"; -import { Cast, NumCast } from "../../../fields/Types"; -import { Docs } from "../../documents/Documents"; -import { Transform } from "../../util/Transform"; -import { CollectionDockingView } from "../collections/CollectionDockingView"; -import "../global/globalCssVariables.scss"; import "./Keyframe.scss"; import "./Timeline.scss"; +import "../global/globalCssVariables.scss"; +import { observer } from "mobx-react"; +import { observable, reaction, action, IReactionDisposer, observe, computed, runInAction, trace } from "mobx"; +import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc"; +import { Cast, NumCast } from "../../../fields/Types"; +import { List } from "../../../fields/List"; +import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../fields/Schema"; +import { Transform } from "../../util/Transform"; import { TimelineMenu } from "./TimelineMenu"; +import { Docs } from "../../documents/Documents"; +import { CollectionDockingView } from "../collections/CollectionDockingView"; +import { emptyPath } from "../../../Utils"; /** diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index e80ba6f36..c7e62c15d 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -13,7 +13,6 @@ import { TimelineOverview } from "./TimelineOverview"; import { Track } from "./Track"; import clamp from "../../util/clamp"; import { DocumentType } from "../../documents/DocumentTypes"; -import { IconLookup } from "@fortawesome/fontawesome-svg-core"; /** * Timeline class controls most of timeline functions besides individual keyframe and track mechanism. Main functions are @@ -347,9 +346,9 @@ export class Timeline extends React.Component<FieldViewProps> { return ( <div key="timeline_toolbox" className="timeline-toolbox" style={{ height: `${size}px` }}> <div className="playbackControls"> - <div className="timeline-icon" key="timeline_windBack" onClick={this.windBackward} title="Slow Down Animation"> <FontAwesomeIcon icon={faBackward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div> - <div className="timeline-icon" key=" timeline_play" onClick={this.onPlay} title="Play/Pause"> <FontAwesomeIcon icon={this._playButton as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div> - <div className="timeline-icon" key="timeline_windForward" onClick={this.windForward} title="Speed Up Animation"> <FontAwesomeIcon icon={faForward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div> + <div className="timeline-icon" key="timeline_windBack" onClick={this.windBackward} title="Slow Down Animation"> <FontAwesomeIcon icon={faBackward} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div> + <div className="timeline-icon" key=" timeline_play" onClick={this.onPlay} title="Play/Pause"> <FontAwesomeIcon icon={this._playButton} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div> + <div className="timeline-icon" key="timeline_windForward" onClick={this.windForward} title="Speed Up Animation"> <FontAwesomeIcon icon={faForward} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div> </div> <div className="grid-box overview-tool"> <div className="overview-box"> @@ -507,7 +506,7 @@ export class Timeline extends React.Component<FieldViewProps> { {this.children.map(doc => <div style={{ height: `${(this._titleHeight)}px` }} className="datapane" onPointerOver={() => { Doc.BrushDoc(doc); }} onPointerOut={() => { Doc.UnBrushDoc(doc); }}><p>{doc.title}</p></div>)} </div> <div key="timeline_resize" onPointerDown={this.onResizeDown}> - <FontAwesomeIcon className="resize" icon={faGripLines as IconLookup} /> + <FontAwesomeIcon className="resize" icon={faGripLines} /> </div> </div> </div> diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index aa422c092..53ca9acad 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -5,7 +5,6 @@ import "./TimelineMenu.scss"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChartLine, faRoad, faClipboard, faPen, faTrash, faTable } from "@fortawesome/free-solid-svg-icons"; import { Utils } from "../../../Utils"; -import { IconLookup } from "@fortawesome/fontawesome-svg-core"; @observer @@ -42,7 +41,7 @@ export class TimelineMenu extends React.Component { if (type === "input") { const inputRef = React.createRef<HTMLInputElement>(); let text = ""; - this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faClipboard as IconLookup} size="lg" /><input className="timeline-menu-input" ref={inputRef} placeholder={title} onChange={(e) => { + this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faClipboard} size="lg" /><input className="timeline-menu-input" ref={inputRef} placeholder={title} onChange={(e) => { e.stopPropagation(); text = e.target.value; }} onKeyDown={(e) => { @@ -53,7 +52,7 @@ export class TimelineMenu extends React.Component { } }} /></div>); } else if (type === "button") { - this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faChartLine as IconLookup} size="lg" /><p className="timeline-menu-desc" onClick={(e) => { + this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faChartLine} size="lg" /><p className="timeline-menu-desc" onClick={(e) => { e.preventDefault(); e.stopPropagation(); event(e); diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index 01f41869e..3c66faf0c 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -3,17 +3,23 @@ import { computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc } from '../../../fields/Doc'; +import { collectionSchema, documentSchema } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; +import { makeInterface } from '../../../fields/Schema'; +import { ScriptField } from '../../../fields/ScriptField'; import { NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { OmitKeys, returnFalse, Utils } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { DocumentView } from '../nodes/DocumentView'; -import { StyleProp } from '../StyleProvider'; import "./CollectionCarousel3DView.scss"; import { CollectionSubView } from './CollectionSubView'; +import { StyleProp } from '../StyleProvider'; + +type Carousel3DDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>; +const Carousel3DDocument = makeInterface(documentSchema, collectionSchema); @observer -export class CollectionCarousel3DView extends CollectionSubView() { +export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocument) { @computed get scrollSpeed() { return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; //default scroll speed } @@ -22,7 +28,7 @@ export class CollectionCarousel3DView extends CollectionSubView() { componentWillUnmount() { this._dropDisposer?.(); } - protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view + protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this._dropDisposer?.(); if (ele) { this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 467c2893f..6c2c27e8e 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -3,6 +3,8 @@ import { computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, Opt } from '../../../fields/Doc'; +import { collectionSchema, documentSchema } from '../../../fields/documentSchemas'; +import { makeInterface } from '../../../fields/Schema'; import { NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { OmitKeys, returnFalse } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; @@ -10,15 +12,18 @@ import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { StyleProp } from '../StyleProvider'; import "./CollectionCarouselView.scss"; -import { CollectionSubView } from './CollectionSubView'; +import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; + +type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>; +const CarouselDocument = makeInterface(documentSchema, collectionSchema); @observer -export class CollectionCarouselView extends CollectionSubView() { +export class CollectionCarouselView extends CollectionSubView(CarouselDocument) { private _dropDisposer?: DragManager.DragDropDisposer; componentWillUnmount() { this._dropDisposer?.(); } - protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view + protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this._dropDisposer?.(); if (ele) { this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 9e8374605..f543d924d 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -4,31 +4,31 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from "mo import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import * as GoldenLayout from "../../../client/goldenLayout"; -import { DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc"; +import { Doc, DocListCast, Opt, DocListCastAsync, DataSym } from "../../../fields/Doc"; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; -import { listSpec } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from "../../../fields/Types"; -import { inheritParentAcls } from '../../../fields/util'; import { DocServer } from "../../DocServer"; import { Docs } from '../../documents/Documents'; -import { DocumentType } from '../../documents/DocumentTypes'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DragManager } from "../../util/DragManager"; import { InteractionUtils } from '../../util/InteractionUtils'; -import { ScriptingGlobals } from '../../util/ScriptingGlobals'; +import { Scripting } from '../../util/Scripting'; import { undoBatch, UndoManager } from "../../util/UndoManager"; -import { LightboxView } from '../LightboxView'; import "./CollectionDockingView.scss"; import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import { CollectionViewType } from './CollectionView'; import { TabDocView } from './TabDocView'; import React = require("react"); +import { DocumentType } from '../../documents/DocumentTypes'; +import { listSpec } from '../../../fields/Schema'; +import { LightboxView } from '../LightboxView'; +import { inheritParentAcls } from '../../../fields/util'; const _global = (window /* browser */ || global /* node */) as any; @observer -export class CollectionDockingView extends CollectionSubView() { +export class CollectionDockingView extends CollectionSubView(doc => doc) { @observable public static Instance: CollectionDockingView; public static makeDocumentConfig(document: Doc, panelName?: string, width?: number) { return { @@ -468,8 +468,8 @@ export class CollectionDockingView extends CollectionSubView() { } } -ScriptingGlobals.add(function openInLightbox(doc: any) { LightboxView.AddDocTab(doc, "lightbox"); }, +Scripting.addGlobal(function openInLightbox(doc: any) { LightboxView.AddDocTab(doc, "lightbox"); }, "opens up document in a lightbox", "(doc: any)"); -ScriptingGlobals.add(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); }, +Scripting.addGlobal(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); }, "opens up document in tab on right side of the screen", "(doc: any)"); -ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); });
\ No newline at end of file +Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); });
\ No newline at end of file diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 364a2440e..131f5ba46 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -20,7 +20,7 @@ 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 { Scripting } from "../../util/Scripting"; import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; @@ -1285,7 +1285,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu ); } } -ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) { +Scripting.addGlobal(function gotoFrame(doc: any, newFrame: any) { const dataField = doc[Doc.LayoutFieldKey(doc)]; const childDocs = DocListCast(dataField); const currentFrame = Cast(doc._currentFrame, "number", null); diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 0a336c544..bc1407c53 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -13,7 +13,7 @@ import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); @observer -export class CollectionPileView extends CollectionSubView() { +export class CollectionPileView extends CollectionSubView(doc => doc) { _originalChrome: any = ""; _disposers: { [name: string]: IReactionDisposer } = {}; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 61ddbe70d..7d9dc39ae 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -11,7 +11,7 @@ import { computedFn } from "mobx-utils"; import { Doc, DocListCast } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { List } from "../../../fields/List"; -import { listSpec } from "../../../fields/Schema"; +import { listSpec, makeInterface } from "../../../fields/Schema"; import { ComputedField, ScriptField } from "../../../fields/ScriptField"; import { Cast, NumCast } from "../../../fields/Types"; import { @@ -25,7 +25,7 @@ import { Docs } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; import { DragManager } from "../../util/DragManager"; import { LinkManager } from "../../util/LinkManager"; -import { ScriptingGlobals } from "../../util/ScriptingGlobals"; +import { Scripting } from "../../util/Scripting"; import { SelectionManager } from "../../util/SelectionManager"; import { SnappingManager } from "../../util/SnappingManager"; import { Transform } from "../../util/Transform"; @@ -43,6 +43,8 @@ import { import { LabelBox } from "../nodes/LabelBox"; import "./CollectionStackedTimeline.scss"; +type PanZoomDocument = makeInterface<[]>; +const PanZoomDocument = makeInterface(); export type CollectionStackedTimelineProps = { Play: () => void; Pause: () => void; @@ -65,8 +67,11 @@ export enum TrimScope { } @observer -export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() { - @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined; +export class CollectionStackedTimeline extends CollectionSubView< + PanZoomDocument, + CollectionStackedTimelineProps +>(PanZoomDocument) { + @observable static SelectingRegion: CollectionStackedTimeline | undefined; @observable public static CurrentlyPlaying: Doc[]; static RangeScript: ScriptField; @@ -928,9 +933,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> <div key="right" className="collectionStackedTimeline-resizer" - onPointerDown={(e) => - this.onAnchorDown(e, this.props.mark, false) - } + onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, false)} /> </> )} @@ -938,12 +941,12 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> ); } } -ScriptingGlobals.add(function formatToTime(time: number): any { +Scripting.addGlobal(function formatToTime(time: number): any { return formatTime(time); }); -ScriptingGlobals.add(function min(num1: number, num2: number): number { +Scripting.addGlobal(function min(num1: number, num2: number): number { return Math.min(num1, num2); }); -ScriptingGlobals.add(function max(num1: number, num2: number): number { +Scripting.addGlobal(function max(num1: number, num2: number): number { return Math.max(num1, num2); });
\ No newline at end of file diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 8634ea139..cdc680a08 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -3,15 +3,16 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { CursorProperty } from "csstype"; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { DataSym, Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../fields/Doc"; +import { collectionSchema, documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { List } from "../../../fields/List"; -import { listSpec } from "../../../fields/Schema"; +import { listSpec, makeInterface } from "../../../fields/Schema"; import { SchemaHeaderField } from "../../../fields/SchemaHeaderField"; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; import { TraceMobx } from "../../../fields/util"; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils"; -import { Docs, DocUtils } from "../../documents/Documents"; +import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils, returnTrue, returnEmptyDoclist, returnEmptyFilter } from "../../../Utils"; +import { DocUtils, Docs } from "../../documents/Documents"; import { DragManager, dropActionType } from "../../util/DragManager"; import { SnappingManager } from "../../util/SnappingManager"; import { Transform } from "../../util/Transform"; @@ -22,14 +23,18 @@ import { EditableView } from "../EditableView"; import { LightboxView } from "../LightboxView"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from "../nodes/DocumentView"; -import { StyleProp } from "../StyleProvider"; +import { StyleProp, DefaultStyleProvider } from "../StyleProvider"; import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow"; import "./CollectionStackingView.scss"; import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; import { CollectionSubView } from "./CollectionSubView"; import { CollectionViewType } from "./CollectionView"; +import { FontIconBox } from "../nodes/button/FontIconBox"; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; const _global = (window /* browser */ || global /* node */) as any; +type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>; +const StackingDocument = makeInterface(collectionSchema, documentSchema); export type collectionStackingViewProps = { chromeHidden?: boolean; @@ -39,7 +44,7 @@ export type collectionStackingViewProps = { }; @observer -export class CollectionStackingView extends CollectionSubView<Partial<collectionStackingViewProps>>() { +export class CollectionStackingView extends CollectionSubView<StackingDocument, Partial<collectionStackingViewProps>>(StackingDocument) { _masonryGridRef: HTMLDivElement | null = null; _draggerRef = React.createRef<HTMLDivElement>(); _pivotFieldDisposer?: IReactionDisposer; diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx index c025e94a8..c5c3f96e8 100644 --- a/src/client/views/collections/CollectionStaffView.tsx +++ b/src/client/views/collections/CollectionStaffView.tsx @@ -6,7 +6,7 @@ import "./CollectionStaffView.scss"; import { observer } from "mobx-react"; @observer -export class CollectionStaffView extends CollectionSubView() { +export class CollectionStaffView extends CollectionSubView(doc => doc) { private _reactionDisposer: IReactionDisposer | undefined; @observable private _staves = NumCast(this.props.Document.staves); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 42e157396..34209ebc9 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -10,14 +10,14 @@ import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types"; import { GestureUtils } from "../../../pen-gestures/GestureUtils"; import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils"; import { DocServer } from "../../DocServer"; +import { Networking } from "../../Network"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; import { InteractionUtils } from "../../util/InteractionUtils"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocComponent } from "../DocComponent"; import React = require("react"); -import ReactLoading from 'react-loading'; import * as rp from 'request-promise'; -import { Networking } from "../../Network"; +import ReactLoading from 'react-loading'; export interface SubCollectionViewProps extends CollectionViewProps { @@ -25,15 +25,15 @@ export interface SubCollectionViewProps extends CollectionViewProps { isAnyChildContentActive: () => boolean; } -export function CollectionSubView<X>(moreProps?: X) { - class CollectionSubView extends DocComponent<X & SubCollectionViewProps>() { +export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: X) { + class CollectionSubView extends DocComponent<X & SubCollectionViewProps, T>(schemaCtor) { private dropDisposer?: DragManager.DragDropDisposer; private gestureDisposer?: GestureUtils.GestureEventDisposer; protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; protected _mainCont?: HTMLDivElement; @observable _focusFilters: Opt<string[]>; // docFilters that are overridden when previewing a link to an anchor which has docFilters set on it @observable _focusRangeFilters: Opt<string[]>; // docRangeFilters that are overridden when previewing a link to an anchor which has docRangeFilters set on it - protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view + protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this.dropDisposer?.(); this.gestureDisposer?.(); this._multiTouchDisposer?.(); diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index d6398fda5..292dfd77c 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -1,17 +1,17 @@ import { toUpper } from "lodash"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, Opt, StrListCast } from "../../../fields/Doc"; +import { Doc, DocCastAsync, Opt, StrListCast } from "../../../fields/Doc"; import { List } from "../../../fields/List"; import { ObjectField } from "../../../fields/ObjectField"; import { RichTextField } from "../../../fields/RichTextField"; import { listSpec } from "../../../fields/Schema"; import { ComputedField, ScriptField } from "../../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; -import { emptyFunction, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from "../../../Utils"; -import { Docs } from "../../documents/Documents"; +import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, returnEmptyString } from "../../../Utils"; +import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; -import { ScriptingGlobals } from "../../util/ScriptingGlobals"; +import { Scripting } from "../../util/Scripting"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import { EditableView } from "../EditableView"; @@ -20,10 +20,13 @@ import { ViewDefBounds } from "./collectionFreeForm/CollectionFreeFormLayoutEngi import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTimeView.scss"; +const higflyout = require("@hig/flyout"); +export const { anchorPoints } = higflyout; +export const Flyout = higflyout.default; import React = require("react"); @observer -export class CollectionTimeView extends CollectionSubView() { +export class CollectionTimeView extends CollectionSubView(doc => doc) { _changing = false; @observable _layoutEngine = "pivot"; @observable _collapsed: boolean = false; @@ -233,7 +236,7 @@ export class CollectionTimeView extends CollectionSubView() { } } -ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) { +Scripting.addGlobal(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) { let prevFilterIndex = NumCast(pivotDoc._prevFilterIndex); pivotDoc["_prevDocFilter" + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docFilters as ObjectField); pivotDoc["_prevDocRangeFilters" + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docRangeFilters as ObjectField); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index e84517f40..ea077ea40 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -3,11 +3,11 @@ import { observer } from "mobx-react"; import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; -import { listSpec } from '../../../fields/Schema'; +import { Document, listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue } from '../../../Utils'; +import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnOne } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DocumentManager } from '../../util/DocumentManager'; @@ -40,7 +40,7 @@ export type collectionTreeViewProps = { }; @observer -export class CollectionTreeView extends CollectionSubView<Partial<collectionTreeViewProps>>() { +export class CollectionTreeView extends CollectionSubView<Document, Partial<collectionTreeViewProps>>(Document) { private _treedropDisposer?: DragManager.DragDropDisposer; private _mainEle?: HTMLDivElement; private _titleRef?: HTMLDivElement | HTMLInputElement | null; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index ee2c28b5f..681a15e3d 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,14 +1,15 @@ -import { computed, observable, runInAction } from 'mobx'; +import { computed, observable, runInAction, action } from 'mobx'; import { observer } from "mobx-react"; import * as React from 'react'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app -import { Doc, DocListCast } from '../../../fields/Doc'; +import { Doc, DocListCast, StrListCast } from '../../../fields/Doc'; +import { documentSchema } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { ObjectField } from '../../../fields/ObjectField'; +import { makeInterface } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; import { Cast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { returnEmptyString } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; import { BranchCreate, BranchTask } from '../../documents/Gitlike'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; @@ -32,7 +33,10 @@ import { CollectionStackingView } from './CollectionStackingView'; import { SubCollectionViewProps } from './CollectionSubView'; import { CollectionTimeView } from './CollectionTimeView'; import { CollectionTreeView } from "./CollectionTreeView"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import './CollectionView.scss'; +import { returnEmptyString } from '../../../Utils'; +import { InkTool } from '../../../fields/InkField'; export const COLLECTION_BORDER_WIDTH = 2; const path = require('path'); @@ -80,8 +84,11 @@ export interface CollectionViewProps extends FieldViewProps { childClickScript?: ScriptField; childDoubleClickScript?: ScriptField; } + +type CollectionDocument = makeInterface<[typeof documentSchema]>; +const CollectionDocument = makeInterface(documentSchema); @observer -export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps>() { +export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps, CollectionDocument>(CollectionDocument) { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); } @observable private static _safeMode = false; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index e2ea81392..aeda71d01 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -4,12 +4,13 @@ import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; import { DateField } from "../../../../fields/DateField"; import { Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc"; +import { collectionSchema, documentSchema } from "../../../../fields/documentSchemas"; import { Id } from "../../../../fields/FieldSymbols"; import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fields/InkField"; import { List } from "../../../../fields/List"; import { ObjectField } from "../../../../fields/ObjectField"; import { RichTextField } from "../../../../fields/RichTextField"; -import { createSchema, listSpec } from "../../../../fields/Schema"; +import { createSchema, listSpec, makeInterface } from "../../../../fields/Schema"; import { ScriptField } from "../../../../fields/ScriptField"; import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types"; import { TraceMobx } from "../../../../fields/util"; @@ -40,6 +41,7 @@ import { LightboxView } from "../../LightboxView"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView"; import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox"; +import { pageSchema } from "../../nodes/ImageBox"; import { PresBox } from "../../nodes/trails/PresBox"; import { StyleLayers, StyleProp } from "../../StyleProvider"; import { CollectionDockingView } from "../CollectionDockingView"; @@ -65,6 +67,8 @@ export const panZoomSchema = createSchema({ scrollHeight: "number" // this will be set when the collection is an annotation overlay for a PDF/Webpage }); +type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof collectionSchema, typeof documentSchema, typeof pageSchema]>; +const PanZoomDocument = makeInterface(panZoomSchema, collectionSchema, documentSchema, pageSchema); export type collectionFreeformViewProps = { annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox) viewDefDivClick?: ScriptField; @@ -77,7 +81,7 @@ export type collectionFreeformViewProps = { }; @observer -export class CollectionFreeFormView extends CollectionSubView<Partial<collectionFreeformViewProps>>() { +export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, Partial<collectionFreeformViewProps>>(PanZoomDocument) { public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title?.toString() + ")"; } // this makes mobx trace() statements more descriptive private _lastNudge: any; @@ -91,7 +95,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection private _clusterDistance: number = 75; private _hitCluster: number = -1; private _disposers: { [name: string]: IReactionDisposer } = {}; - private _renderCutoffData = observable.map<string, boolean>(); private _layoutPoolData = observable.map<string, PoolData>(); private _layoutSizeData = observable.map<string, { width?: number, height?: number }>(); private _cachedPool: Map<string, PoolData> = new Map(); @@ -106,7 +109,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @observable _viewTransition: number = 0; // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan. set to 0 to use document's transition time or default of 0 @observable _hLines: number[] | undefined; @observable _vLines: number[] | undefined; - @observable _firstRender = true; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown. @observable _pullCoords: number[] = [0, 0]; @observable _pullDirection: string = ""; @observable _showAnimTimeline = false; @@ -161,7 +163,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this.layoutDoc._panY = vals.bounds.cy; this.layoutDoc._viewScale = vals.scale; } - freeformData = (force?: boolean) => !this._firstRender && (this.fitToContent || force) ? this.fitToContentVals : undefined; + freeformData = (force?: boolean) => this.fitToContent || force ? this.fitToContentVals : undefined; reverseNativeScaling = () => this.fitToContent ? true : false; panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX); panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY); @@ -203,7 +205,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } } if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) { - CollectionFreeFormDocumentView.setupKeyframes(newBoxes, NumCast(this.Document._currentFrame), true); + CollectionFreeFormDocumentView.setupKeyframes(newBoxes, this.Document._currentFrame, true); } } return retVal; @@ -233,12 +235,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const d = docDragData.droppedDocuments[i]; const layoutDoc = Doc.Layout(d); if (this.Document._currentFrame !== undefined) { - CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false); + CollectionFreeFormDocumentView.setupKeyframes([d], this.Document._currentFrame, false); const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000)); vals.x = x + (vals.x || 0) - dropPos[0]; vals.y = y + (vals.y || 0) - dropPos[1]; vals._scrollTop = this.Document.editScrollProgressivize ? vals._scrollTop : undefined; - CollectionFreeFormDocumentView.setValues(NumCast(this.Document._currentFrame), d, vals); + CollectionFreeFormDocumentView.setValues(this.Document._currentFrame, d, vals); } else { d.x = x + NumCast(d.x) - dropPos[0]; d.y = y + NumCast(d.y) - dropPos[1]; @@ -633,7 +635,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @action pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => { const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); - this.setPan(NumCast(this.Document._panX) - dx, NumCast(this.Document._panY) - dy, 0, true); + this.setPan((this.Document._panX || 0) - dx, (this.Document._panY || 0) - dy, 0, true); this._lastX = e.clientX; this._lastY = e.clientY; } @@ -965,10 +967,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection .filter(({ pos, size }) => pos && size).map(({ pos, size }) => ({ pos: pos!, size: size! })); if (measuredDocs.length) { const ranges = measuredDocs.reduce(({ xrange, yrange }, { pos, size }) => // computes range of content - ({ - xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) }, - yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) } - }) + ({ + xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) }, + yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) } + }) , { xrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }, yrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE } @@ -1043,11 +1045,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection if (state.type === "doc" && this.Document._panX !== undefined && this.Document._panY !== undefined) { const init = state.initializers![this.Document[Id]]; if (!init) { - state.initializers![this.Document[Id]] = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY) }; + state.initializers![this.Document[Id]] = { panX: this.Document._panX, panY: this.Document._panY }; HistoryUtil.pushState(state); } else if (init.panX !== this.Document._panX || init.panY !== this.Document._panY) { - init.panX = NumCast(this.Document._panX); - init.panY = NumCast(this.Document._panY); + init.panX = this.Document._panX; + init.panY = this.Document._panY; HistoryUtil.pushState(state); } } @@ -1142,7 +1144,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection isContentActive = () => this.props.isSelected() || this.props.isContentActive(); - getChildDocView(entry: PoolData, renderIndex: number) { + getChildDocView(entry: PoolData) { const childLayout = entry.pair.layout; const childData = entry.pair.data; const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine); @@ -1151,8 +1153,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection Document={childLayout} renderDepth={this.props.renderDepth + 1} replica={entry.replica} - renderIndex={renderIndex} - renderCutoffProvider={this.renderCutoffProvider} ContainingCollectionView={this.props.CollectionView} ContainingCollectionDoc={this.props.Document} CollectionFreeFormView={this} @@ -1213,7 +1213,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const { z, color, zIndex } = params.pair.layout; const { x, y, opacity } = this.Document._currentFrame === undefined ? { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) } : - CollectionFreeFormDocumentView.getValues(params.pair.layout, NumCast(this.Document._currentFrame)); + CollectionFreeFormDocumentView.getValues(params.pair.layout, this.Document._currentFrame); return { x: NumCast(x), y: NumCast(y), z: Cast(z, "number"), color: StrCast(color), zIndex: Cast(zIndex, "number"), transition: StrCast(layoutDoc.dataTransition), opacity: this._keyframeEditing ? 1 : Cast(opacity, "number", null), @@ -1256,12 +1256,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } } - - renderCutoffProvider = computedFn(function renderCutoffProvider(this: any, doc: Doc) { - return !this._renderCutoffData.get(doc[Id] + ""); - }.bind(this)); - - childPositionProviderUnmemoized = (doc: Doc, replica: string) => { return this._layoutPoolData.get(doc[Id] + (replica || "")); } @@ -1306,7 +1300,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection return { newPool, computedElementData: this.doFreeformLayout(newPool) }; } - @observable _numLoaded = 1; get doLayoutComputation() { const { newPool, computedElementData } = this.doInternalLayoutComputation; const array = Array.from(newPool.entries()); @@ -1325,9 +1318,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this._cachedPool.clear(); Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1])); const elements = computedElementData.slice(); - Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach((entry, i) => + Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach(entry => elements.push({ - ele: this.getChildDocView(entry[1], i), + ele: this.getChildDocView(entry[1]), bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica) })); @@ -1376,38 +1369,35 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection componentDidMount() { super.componentDidMount?.(); this.props.setContentView?.(this); - setTimeout(action(() => { - this._firstRender = false; - this._disposers.layoutComputation = reaction(() => this.doLayoutComputation, - (elements) => this._layoutElements = elements || [], - { fireImmediately: true, name: "doLayout" }); - - this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any); - - this._disposers.groupBounds = reaction(() => { - if (this.props.Document._isGroup && this.childDocs.length === this.childDocList?.length) { - const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() })); - return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding)); + this._disposers.layoutComputation = reaction(() => this.doLayoutComputation, + (elements) => this._layoutElements = elements || [], + { fireImmediately: true, name: "doLayout" }); + + this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any); + + this._disposers.groupBounds = reaction(() => { + if (this.props.Document._isGroup && this.childDocs.length === this.childDocList?.length) { + const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() })); + return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding)); + } + return undefined; + }, + (cbounds) => { + if (cbounds) { + const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2]; + const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)]; + const pbounds = { + x: (cbounds.x - p[0]) * this.zoomScaling() + c[0], y: (cbounds.y - p[1]) * this.zoomScaling() + c[1], + r: (cbounds.r - p[0]) * this.zoomScaling() + c[0], b: (cbounds.b - p[1]) * this.zoomScaling() + c[1] + }; + this.layoutDoc._width = (pbounds.r - pbounds.x); + this.layoutDoc._height = (pbounds.b - pbounds.y); + this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2; + this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2; + this.layoutDoc.x = pbounds.x; + this.layoutDoc.y = pbounds.y; } - return undefined; - }, - (cbounds) => { - if (cbounds) { - const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2]; - const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)]; - const pbounds = { - x: (cbounds.x - p[0]) * this.zoomScaling() + c[0], y: (cbounds.y - p[1]) * this.zoomScaling() + c[1], - r: (cbounds.r - p[0]) * this.zoomScaling() + c[0], b: (cbounds.b - p[1]) * this.zoomScaling() + c[1] - }; - this.layoutDoc._width = (pbounds.r - pbounds.x); - this.layoutDoc._height = (pbounds.b - pbounds.y); - this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2; - this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2; - this.layoutDoc.x = pbounds.x; - this.layoutDoc.y = pbounds.y; - } - }, { fireImmediately: true }); - })); + }, { fireImmediately: true }); } componentWillUnmount() { @@ -1459,8 +1449,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const height = Math.max(...docs.map(doc => NumCast(doc._height))) + 20; const dim = Math.ceil(Math.sqrt(docs.length)); docs.forEach((doc, i) => { - doc.x = NumCast(this.Document._panX) + (i % dim) * width - width * dim / 2; - doc.y = NumCast(this.Document._panY) + Math.floor(i / dim) * height - height * dim / 2; + doc.x = (this.Document._panX || 0) + (i % dim) * width - width * dim / 2; + doc.y = (this.Document._panY || 0) + Math.floor(i / dim) * height - height * dim / 2; }); } @@ -1567,35 +1557,58 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection e.stopPropagation(); } - incrementalRender = action(() => { - if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) { - const unrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id])); - const loadIncrement = 5; - for (var i = 0; i < Math.min(unrendered.length, loadIncrement); i++) { - this._renderCutoffData.set(unrendered[i][Id] + "", true); - } - } - this.childDocs.some(doc => !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1); - }); - children = () => { - this.incrementalRender(); const children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : []; - return [ - ...children, - ...this.views, - <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" /> - ]; + return [...children, ...this.views, <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />]; + } + + chooseGridSpace = (gridSpace: number): number => { + if (!this.zoomScaling()) return 50; + const divisions = this.props.PanelWidth() / this.zoomScaling() / gridSpace + 3; + return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10); + } + + @computed get backgroundGrid() { + const gridSpace = this.chooseGridSpace(NumCast(this.layoutDoc["_backgroundGrid-spacing"], 50)); + const shiftX = (this.props.isAnnotationOverlay ? 0 : -this.panX() % gridSpace - gridSpace) * this.zoomScaling(); + const shiftY = (this.props.isAnnotationOverlay ? 0 : -this.panY() % gridSpace - gridSpace) * this.zoomScaling(); + const renderGridSpace = gridSpace * this.zoomScaling(); + const w = this.props.PanelWidth() + 2 * renderGridSpace; + const h = this.props.PanelHeight() + 2 * renderGridSpace; + const strokeStyle = CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "rgba(255,255,255,0.5)" : "rgba(0, 0,0,0.5)"; + return <canvas className="collectionFreeFormView-grid" width={w} height={h} style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }} + ref={(el) => { + const ctx = el?.getContext('2d'); + if (ctx) { + const Cx = this.cachedCenteringShiftX % renderGridSpace; + const Cy = this.cachedCenteringShiftY % renderGridSpace; + ctx.lineWidth = Math.min(1, Math.max(0.5, this.zoomScaling())); + ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]); + ctx.clearRect(0, 0, w, h); + if (ctx) { + ctx.strokeStyle = strokeStyle; + ctx.beginPath(); + for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) { + ctx.moveTo(x, Cy - h); + ctx.lineTo(x, Cy + h); + } + for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) { + ctx.moveTo(Cx - w, y); + ctx.lineTo(Cx + w, y); + } + ctx.stroke(); + } + } + }} />; } @computed get placeholder() { - return <div className="collectionfreeformview-placeholder" style={{ background: StrCast(this.Document.backgroundColor) }}> + return <div className="collectionfreeformview-placeholder" style={{ background: this.Document.backgroundColor }}> <span className="collectionfreeformview-placeholderSpan">{this.props.Document.title?.toString()}</span> </div>; } @computed get marqueeView() { - TraceMobx(); return <MarqueeView {...this.props} ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined} @@ -1610,18 +1623,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}> <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}> - {this.layoutDoc._backgroundGridShow ? - <CollectionFreeFormBackgroundGrid - PanelWidth={this.props.PanelWidth} - PanelHeight={this.props.PanelHeight} - panX={this.panX} - panY={this.panY} - zoomScaling={this.zoomScaling} - layoutDoc={this.layoutDoc} - isAnnotationOverlay={this.isAnnotationOverlay} - cachedCenteringShiftX={this.cachedCenteringShiftX} - cachedCenteringShiftY={this.cachedCenteringShiftY} - /> : (null)} + {this.layoutDoc._backgroundGridShow ? this.backgroundGrid : (null)} <CollectionFreeFormViewPannableContents isAnnotationOverlay={this.isAnnotationOverlay} isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable} @@ -1640,7 +1642,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } @computed get contentScaling() { - if (this._firstRender || (this.props.isAnnotationOverlay && !this.props.annotationLayerHostsContent)) return 0; + if (this.props.isAnnotationOverlay && !this.props.annotationLayerHostsContent) return 0; const nw = this.nativeWidth; const nh = this.nativeHeight; const hscale = nh ? this.props.PanelHeight() / nh : 1; @@ -1673,9 +1675,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this.backgroundEvents ? "all" : this.props.pointerEvents as any, transform: `scale(${this.contentScaling || 1})`, width: `${100 / (this.contentScaling || 1)}%`, - height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() + height: this.isAnnotationOverlay && this.Document.scrollHeight ? this.Document.scrollHeight : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}> - {this._firstRender || (this.Document._freeformLOD && !this.props.isContentActive() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0) ? + {this.Document._freeformLOD && !this.props.isContentActive() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0 ? this.placeholder : this.marqueeView} {this.props.noOverlay ? (null) : <CollectionFreeFormOverlayView elements={this.elementFunc} />} @@ -1852,59 +1854,4 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF {this.zoomProgressivize} </div>; } -} - -interface CollectionFreeFormViewBackgroundGridProps { - panX: () => number; - panY: () => number; - PanelWidth: () => number; - PanelHeight: () => number; - isAnnotationOverlay?: boolean; - zoomScaling: () => number; - layoutDoc: Doc; - cachedCenteringShiftX: number; - cachedCenteringShiftY: number; -} -@observer -class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFormViewBackgroundGridProps> { - - - chooseGridSpace = (gridSpace: number): number => { - if (!this.props.zoomScaling()) return 50; - const divisions = this.props.PanelWidth() / this.props.zoomScaling() / gridSpace + 3; - return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10); - } - render() { - const gridSpace = this.chooseGridSpace(NumCast(this.props.layoutDoc["_backgroundGrid-spacing"], 50)); - const shiftX = (this.props.isAnnotationOverlay ? 0 : -this.props.panX() % gridSpace - gridSpace) * this.props.zoomScaling(); - const shiftY = (this.props.isAnnotationOverlay ? 0 : -this.props.panY() % gridSpace - gridSpace) * this.props.zoomScaling(); - const renderGridSpace = gridSpace * this.props.zoomScaling(); - const w = this.props.PanelWidth() + 2 * renderGridSpace; - const h = this.props.PanelHeight() + 2 * renderGridSpace; - const strokeStyle = CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "rgba(255,255,255,0.5)" : "rgba(0, 0,0,0.5)"; - return <canvas className="collectionFreeFormView-grid" width={w} height={h} style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }} - ref={(el) => { - const ctx = el?.getContext('2d'); - if (ctx) { - const Cx = this.props.cachedCenteringShiftX % renderGridSpace; - const Cy = this.props.cachedCenteringShiftY % renderGridSpace; - ctx.lineWidth = Math.min(1, Math.max(0.5, this.props.zoomScaling())); - ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]); - ctx.clearRect(0, 0, w, h); - if (ctx) { - ctx.strokeStyle = strokeStyle; - ctx.beginPath(); - for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) { - ctx.moveTo(x, Cy - h); - ctx.lineTo(x, Cy + h); - } - for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) { - ctx.moveTo(Cx - w, y); - ctx.lineTo(Cx + w, y); - } - ctx.stroke(); - } - } - }} />; - } }
\ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b10b0912f..08da682bb 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -30,9 +30,6 @@ import "./MarqueeView.scss"; import React = require("react"); import { StyleLayers } from "../../StyleProvider"; import { TreeView } from "../TreeView"; -import { VideoBox } from "../../nodes/VideoBox"; -import { ImageField, WebField } from "../../../../fields/URLField"; -import { pasteImageBitmap } from "../../nodes/WebBoxRenderer"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -137,15 +134,17 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque })(); e.stopPropagation(); } else if (e.key === "b" && e.ctrlKey) { - document.body.focus(); // so that we can access the clipboard without an error - setTimeout(() => - pasteImageBitmap((data: any, error: any) => { - error && console.log(error); - data && VideoBox.convertDataUri(data, this.props.Document[Id] + "-thumb-frozen").then(returnedfilename => { - this.props.Document["thumb-frozen"] = new ImageField(returnedfilename); - }); - })); - } else if (e.key === "s" && e.ctrlKey) { + // e.preventDefault(); + // navigator.clipboard.readText().then(text => { + // const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== ""); + // if (ns.length === 1 && text.startsWith("http")) { + // this.props.addDocument(Docs.Create.ImageDocument(text, { _nativeWidth: 300, _width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer + // } else { + // this.pasteTable(ns, x, y); + // } + // }); + // e.stopPropagation(); + e.preventDefault(); const slide = Doc.copyDragFactory(Doc.UserDoc().emptySlide as Doc)!; slide.x = x; diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index 58ea7410d..b0030471d 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -2,7 +2,9 @@ import { action, computed, Lambda, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from "react"; import { Doc, Opt } from '../../../../fields/Doc'; +import { documentSchema } from '../../../../fields/documentSchemas'; import { Id } from '../../../../fields/FieldSymbols'; +import { makeInterface } from '../../../../fields/Schema'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; @@ -18,8 +20,11 @@ import { CollectionSubView } from '../CollectionSubView'; import "./CollectionGridView.scss"; import Grid, { Layout } from "./Grid"; +type GridSchema = makeInterface<[typeof documentSchema]>; +const GridSchema = makeInterface(documentSchema); + @observer -export class CollectionGridView extends CollectionSubView() { +export class CollectionGridView extends CollectionSubView(GridSchema) { private _containerRef: React.RefObject<HTMLDivElement> = React.createRef(); private _changeListenerDisposer: Opt<Lambda>; // listens for changes in this.childLayoutPairs private _resetListenerDisposer: Opt<Lambda>; // listens for when the reset button is clicked diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 160134b60..70c8c9436 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -4,7 +4,9 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from 'mo import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, HeightSym, Opt, WidthSym } from '../../../../fields/Doc'; +import { documentSchema } from '../../../../fields/documentSchemas'; import { Id } from '../../../../fields/FieldSymbols'; +import { makeInterface } from '../../../../fields/Schema'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils'; import { DocUtils } from '../../../documents/Documents'; @@ -23,6 +25,9 @@ import { CollectionViewType } from '../CollectionView'; import "./CollectionLinearView.scss"; +type LinearDocument = makeInterface<[typeof documentSchema,]>; +const LinearDocument = makeInterface(documentSchema); + /** * CollectionLinearView is the class for rendering the horizontal collection * of documents, it useful for horizontal menus. It can either be expandable @@ -32,7 +37,7 @@ import "./CollectionLinearView.scss"; * - It is used for the context sensitive toolbar at the top (see contMenuButtons() in CollectionMenu.tsx) */ @observer -export class CollectionLinearView extends CollectionSubView() { +export class CollectionLinearView extends CollectionSubView(LinearDocument) { @observable public addMenuToggle = React.createRef<HTMLInputElement>(); @observable private _selectedIndex = -1; private _dropDisposer?: DragManager.DragDropDisposer; @@ -73,7 +78,7 @@ export class CollectionLinearView extends CollectionSubView() { { fireImmediately: true } ); } - protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view + protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this._dropDisposer && this._dropDisposer(); if (ele) { this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 2bdf92417..ec1cbadd5 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -2,9 +2,11 @@ import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from "react"; import { Doc } from '../../../../fields/Doc'; +import { documentSchema } from '../../../../fields/documentSchemas'; import { List } from '../../../../fields/List'; +import { makeInterface } from '../../../../fields/Schema'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnFalse } from '../../../../Utils'; +import { returnFalse, emptyPath, returnEmptyDoclist, emptyFunction } from '../../../../Utils'; import { DragManager, dropActionType } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; @@ -14,6 +16,8 @@ import "./CollectionMulticolumnView.scss"; import ResizeBar from './MulticolumnResizer'; import WidthLabel from './MulticolumnWidthLabel'; +type MulticolumnDocument = makeInterface<[typeof documentSchema]>; +const MulticolumnDocument = makeInterface(documentSchema); interface WidthSpecifier { magnitude: number; @@ -34,7 +38,7 @@ const resolvedUnits = Object.values(DimUnit); const resizerWidth = 8; @observer -export class CollectionMulticolumnView extends CollectionSubView() { +export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocument) { /** * @returns the list of layout documents whose width unit is @@ -47,8 +51,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { @computed private get minimumDim() { - const ratioDocs = this.ratioDefinedDocs.filter(layout => layout._dimMagnitude); - return ratioDocs.length ? Math.min(...ratioDocs.map(layout => NumCast(layout._dimMagnitude))) : 1; + return Math.min(...this.ratioDefinedDocs.filter(layout => layout._dimMagnitude).map(layout => NumCast(layout._dimMagnitude))); } /** diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 7e2b83230..a2d51e2e7 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -2,9 +2,11 @@ import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from "react"; import { Doc } from '../../../../fields/Doc'; +import { documentSchema } from '../../../../fields/documentSchemas'; import { List } from '../../../../fields/List'; +import { makeInterface } from '../../../../fields/Schema'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnFalse } from '../../../../Utils'; +import { returnFalse, emptyPath, returnEmptyDoclist, emptyFunction } from '../../../../Utils'; import { DragManager, dropActionType } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; @@ -14,6 +16,9 @@ import "./CollectionMultirowView.scss"; import HeightLabel from './MultirowHeightLabel'; import ResizeBar from './MultirowResizer'; +type MultirowDocument = makeInterface<[typeof documentSchema]>; +const MultirowDocument = makeInterface(documentSchema); + interface HeightSpecifier { magnitude: number; unit: string; @@ -33,7 +38,7 @@ const resolvedUnits = Object.values(DimUnit); const resizerHeight = 8; @observer -export class CollectionMultirowView extends CollectionSubView() { +export class CollectionMultirowView extends CollectionSubView(MultirowDocument) { /** * @returns the list of layout documents whose width unit is @@ -46,8 +51,7 @@ export class CollectionMultirowView extends CollectionSubView() { @computed private get minimumDim() { - const ratioDocs = this.ratioDefinedDocs.filter(layout => layout._dimMagnitude); - return ratioDocs.length ? Math.min(...ratioDocs.map(layout => NumCast(layout._dimMagnitude))) : 1; + return Math.min(...this.ratioDefinedDocs.filter(layout => layout._dimMagnitude).map(layout => NumCast(layout._dimMagnitude))); } /** diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx index c2bb3b3ac..a439a7998 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx @@ -2,18 +2,19 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { extname } from "path"; import DatePicker from "react-datepicker"; +import "react-datepicker/dist/react-datepicker.css"; import { CellInfo } from "react-table"; +import "react-table/react-table.css"; import { DateField } from "../../../../fields/DateField"; import { Doc, DocListCast, Field, Opt } from "../../../../fields/Doc"; import { Id } from "../../../../fields/FieldSymbols"; import { List } from "../../../../fields/List"; import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField"; import { ComputedField } from "../../../../fields/ScriptField"; -import { BoolCast, Cast, DateCast, FieldValue, StrCast } from "../../../../fields/Types"; +import { BoolCast, Cast, DateCast, FieldValue, NumCast, StrCast } from "../../../../fields/Types"; import { ImageField } from "../../../../fields/URLField"; -import { emptyFunction, Utils } from "../../../../Utils"; +import { Utils, emptyFunction } from "../../../../Utils"; import { Docs } from "../../../documents/Documents"; import { DocumentType } from "../../../documents/DocumentTypes"; import { DocumentManager } from "../../../util/DocumentManager"; @@ -28,8 +29,9 @@ import { EditableView } from "../../EditableView"; import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss'; import { DocumentIconContainer } from "../../nodes/DocumentIcon"; import { OverlayView } from "../../OverlayView"; -import { CollectionView } from "../CollectionView"; import "./CollectionSchemaView.scss"; +import { CollectionView } from "../CollectionView"; +const path = require('path'); // intialize cell properties export interface CellProps { @@ -493,8 +495,8 @@ export class CollectionSchemaImageCell extends CollectionSchemaCell { if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href;//Why is this here — good question - const ext = extname(url.href); - return url.href.replace(ext, "_o" + ext); + const ext = path.extname(url.href); // the extension of the file + return url.href.replace(ext, "_o" + path.extname(url.href)); } render() { diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 8b73351d5..b89246489 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -4,6 +4,7 @@ import { action, computed, observable, untracked, trace } from "mobx"; import { observer } from "mobx-react"; import Measure from "react-measure"; import { Resize } from "react-table"; +import "react-table/react-table.css"; import { Doc, Opt } from "../../../../fields/Doc"; import { List } from "../../../../fields/List"; import { listSpec } from "../../../../fields/Schema"; @@ -46,7 +47,7 @@ const columnTypes: Map<string, ColumnType> = new Map([ ]); @observer -export class CollectionSchemaView extends CollectionSubView() { +export class CollectionSchemaView extends CollectionSubView(doc => doc) { private _previewCont?: HTMLDivElement; @observable _previewDoc: Doc | undefined = undefined; @@ -146,43 +147,43 @@ export class CollectionSchemaView extends CollectionSubView() { const anyType = <div className={"columnMenu-option" + (type === ColumnType.Any ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Any)}> <FontAwesomeIcon icon={"align-justify"} size="sm" /> - Any - </div>; + Any + </div>; const numType = <div className={"columnMenu-option" + (type === ColumnType.Number ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Number)}> <FontAwesomeIcon icon={"hashtag"} size="sm" /> - Number - </div>; + Number + </div>; const textType = <div className={"columnMenu-option" + (type === ColumnType.String ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.String)}> <FontAwesomeIcon icon={"font"} size="sm" /> Text - </div>; + </div>; const boolType = <div className={"columnMenu-option" + (type === ColumnType.Boolean ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Boolean)}> <FontAwesomeIcon icon={"check-square"} size="sm" /> Checkbox - </div>; + </div>; const listType = <div className={"columnMenu-option" + (type === ColumnType.List ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.List)}> <FontAwesomeIcon icon={"list-ul"} size="sm" /> List - </div>; + </div>; const docType = <div className={"columnMenu-option" + (type === ColumnType.Doc ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Doc)}> <FontAwesomeIcon icon={"file"} size="sm" /> Document - </div>; + </div>; const imageType = <div className={"columnMenu-option" + (type === ColumnType.Image ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Image)}> <FontAwesomeIcon icon={"image"} size="sm" /> Image - </div>; + </div>; const dateType = <div className={"columnMenu-option" + (type === ColumnType.Date ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Date)}> <FontAwesomeIcon icon={"calendar"} size="sm" /> - Date - </div>; + Date + </div>; const allColumnTypes = <div className="columnMenu-types"> @@ -556,7 +557,7 @@ export class CollectionSchemaView extends CollectionSubView() { style={{ overflow: this.props.scrollOverflow === true ? "scroll" : undefined, backgroundColor: "white", pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined, - width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative", + width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative", }} > <div className="collectionSchemaView-tableContainer" style={{ width: `calc(100% - ${this.previewWidth()}px)` }} diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx index 605481ddf..2219345f6 100644 --- a/src/client/views/collections/collectionSchema/SchemaTable.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx @@ -4,6 +4,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, trace } from "mobx"; import { observer } from "mobx-react"; import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table"; +import "react-table/react-table.css"; import { DateField } from "../../../../fields/DateField"; import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from "../../../../fields/Doc"; import { Id } from "../../../../fields/FieldSymbols"; diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index c2b4d0eee..eb7b9a773 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -215,9 +215,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp this.dataDoc[this.fieldKey + "-recordingStart"] = new DateField(); DocUtils.ActiveRecordings.push(this); this._recorder.ondataavailable = async (e: any) => { - console.log("Data available", e); const [{ result }] = await Networking.UploadFilesToServer(e.data); - console.log("Data result", result); if (!(result instanceof Error)) { this.props.Document[this.fieldKey] = new AudioField(result.accessPaths.agnostic.client); } @@ -604,4 +602,4 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp {!this.path ? this.recordingControls : this.playbackControls} </div>; } -} +}
\ No newline at end of file diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index c2a526804..fe34d6687 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,6 +1,7 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { Doc, Opt } from "../../../fields/Doc"; +import { Document } from "../../../fields/documentSchemas"; import { List } from "../../../fields/List"; import { listSpec } from "../../../fields/Schema"; import { ComputedField } from "../../../fields/ScriptField"; @@ -22,27 +23,25 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined; sizeProvider?: (doc: Doc, replica: string) => { width: number, height: number } | undefined; layerProvider: ((doc: Doc, assign?: boolean) => boolean) | undefined; - renderCutoffProvider: (doc: Doc) => boolean; zIndex?: number; highlight?: boolean; jitterRotation: number; dataTransition?: string; replica: string; - renderIndex: number; CollectionFreeFormView: CollectionFreeFormView; } @observer -export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps>() { +export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, Document>(Document) { public static animFields = ["_height", "_width", "x", "y", "_scrollTop", "opacity"]; // fields that are configured to be animatable using animation frames @observable _animPos: number[] | undefined = undefined; @observable _contentView: DocumentView | undefined | null; get displayName() { return "CollectionFreeFormDocumentView(" + this.rootDoc.title + ")"; } // this makes mobx trace() statements more descriptive get maskCentering() { return this.props.Document.isInkMask ? InkingStroke.MaskDim / 2 : 0; } get transform() { return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${this.props.jitterRotation}deg)`; } - get X() { return this.dataProvider ? this.dataProvider.x : NumCast(this.Document.x); } - get Y() { return this.dataProvider ? this.dataProvider.y : NumCast(this.Document.y); } - get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : NumCast(this.Document.zIndex); } + get X() { return this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); } + get Y() { return this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); } + get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : (this.Document.zIndex || 0); } get Opacity() { return this.dataProvider ? this.dataProvider.opacity : undefined; } get Highlight() { return this.dataProvider?.highlight; } @computed get dataProvider() { return this.props.dataProvider?.(this.props.Document, this.props.replica); } @@ -177,11 +176,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF mixBlendMode, display: this.ZInd === -99 ? "none" : undefined }} > - {this.props.renderCutoffProvider(this.props.Document) ? - <div style={{ position: "absolute", width: this.panelWidth(), height: this.panelHeight(), background: "lightGreen" }} /> - : - <DocumentView {...divProps} ref={action((r: DocumentView | null) => this._contentView = r)} /> - } + <DocumentView {...divProps} ref={action((r: DocumentView | null) => this._contentView = r)} /> </div>; } } diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx index d975baf9b..8da5cd1b1 100644 --- a/src/client/views/nodes/ColorBox.tsx +++ b/src/client/views/nodes/ColorBox.tsx @@ -3,7 +3,9 @@ import { action } from "mobx"; import { observer } from "mobx-react"; import { ColorState, SketchPicker } from 'react-color'; import { Doc, HeightSym, WidthSym } from '../../../fields/Doc'; +import { documentSchema } from "../../../fields/documentSchemas"; import { InkTool } from "../../../fields/InkField"; +import { makeInterface } from "../../../fields/Schema"; import { StrCast } from "../../../fields/Types"; import { DocumentType } from "../../documents/DocumentTypes"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; @@ -15,8 +17,11 @@ import "./ColorBox.scss"; import { FieldView, FieldViewProps } from './FieldView'; import { RichTextMenu } from "./formattedText/RichTextMenu"; +type ColorDocument = makeInterface<[typeof documentSchema]>; +const ColorDocument = makeInterface(documentSchema); + @observer -export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() { +export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument>(ColorDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ColorBox, fieldKey); } @undoBatch diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index cbc61ffdb..d47e8340c 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -2,6 +2,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable } from 'mobx'; import { observer } from "mobx-react"; import { Doc, Opt } from '../../../fields/Doc'; +import { documentSchema } from '../../../fields/documentSchemas'; +import { createSchema, makeInterface } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; @@ -13,10 +15,15 @@ import "./ComparisonBox.scss"; import { DocumentView, DocumentViewProps } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import React = require("react"); +import { DocumentType } from '../../documents/DocumentTypes'; +export const comparisonSchema = createSchema({}); + +type ComparisonDocument = makeInterface<[typeof comparisonSchema, typeof documentSchema]>; +const ComparisonDocument = makeInterface(comparisonSchema, documentSchema); @observer -export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() { +export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ComparisonDocument>(ComparisonDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ComparisonBox, fieldKey); } protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined]; diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx index 433a0bf48..123212608 100644 --- a/src/client/views/nodes/DocumentIcon.tsx +++ b/src/client/views/nodes/DocumentIcon.tsx @@ -2,7 +2,7 @@ import { observer } from "mobx-react"; import * as React from "react"; import { DocumentView } from "./DocumentView"; import { DocumentManager } from "../../util/DocumentManager"; -import { Transformer, ts } from "../../util/Scripting"; +import { Transformer, Scripting, ts } from "../../util/Scripting"; import { Field } from "../../../fields/Doc"; @observer diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss index 9ab3171d3..228e1bdcb 100644 --- a/src/client/views/nodes/DocumentLinksButton.scss +++ b/src/client/views/nodes/DocumentLinksButton.scss @@ -1,8 +1,6 @@ @import "../global/globalCssVariables.scss"; -.documentLinksButton-wrapper { - transform-origin: top left; -} + .documentLinksButton-menu { width: 100%; height: 100%; diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 7e6ca4248..93cd02d93 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -20,7 +20,6 @@ import { DocumentView } from "./DocumentView"; import { LinkDescriptionPopup } from "./LinkDescriptionPopup"; import { TaskCompletionBox } from "./TaskCompletedBox"; import React = require("react"); -import { Transform } from "../../util/Transform"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; @@ -32,7 +31,6 @@ interface DocumentLinksButtonProps { AlwaysOn?: boolean; InMenu?: boolean; StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed) - ContentScaling?: () => number; } @observer export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> { @@ -304,16 +302,16 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp const title = this.props.InMenu ? menuTitle : buttonTitle; //render circular tooltip if it isn't set to invisible and show the number of doc links the node has, and render inner-menu link button for starting/stopping links if currently in menu - return (!Array.from(this.filteredLinks).length && !this.props.AlwaysOn) ? (null) : - <div className="documentLinksButton-wrapper" > - { - (this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink)) || - (!DocumentLinksButton.LinkEditorDocView && !this.props.InMenu) ? - <Tooltip title={<div className="dash-tooltip">{title}</div>}> - {this.linkButtonInner} - </Tooltip> - : this.linkButtonInner - } - </div>; + return !Array.from(this.filteredLinks).length && !this.props.AlwaysOn ? (null) : + this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink) ? + <Tooltip title={<div className="dash-tooltip">{title}</div>}> + {this.linkButtonInner} + </Tooltip> + : + !DocumentLinksButton.LinkEditorDocView && !this.props.InMenu ? + <Tooltip title={<div className="dash-tooltip">{title}</div>}> + {this.linkButtonInner} + </Tooltip> + : this.linkButtonInner; } } diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 4565f8504..9fcd45e72 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -58,10 +58,9 @@ .documentView-audioBackground { display: inline-block; width: 10%; - height: 25; position: absolute; - top: 10px; - left: 10px; + top: 0px; + left: 0px; border-radius: 25px; background: white; opacity: 0.3; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e7de87ea5..707c15bff 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,4 +1,3 @@ -import { IconProp } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; @@ -10,11 +9,11 @@ import { List } from "../../../fields/List"; import { ObjectField } from "../../../fields/ObjectField"; import { listSpec } from "../../../fields/Schema"; import { ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; import { AudioField } from "../../../fields/URLField"; import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; import { MobileInterface } from '../../../mobile/MobileInterface'; -import { emptyFunction, hasDescendantTarget, lightOrDark, OmitKeys, returnEmptyString, returnTrue, returnVal, simulateMouseClick, Utils } from "../../../Utils"; +import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils, lightOrDark, simulateMouseClick, returnEmptyString } from "../../../Utils"; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from '../../documents/DocumentTypes'; @@ -24,7 +23,7 @@ import { DocumentManager } from "../../util/DocumentManager"; import { DragManager, dropActionType } from "../../util/DragManager"; import { InteractionUtils } from '../../util/InteractionUtils'; import { LinkManager } from '../../util/LinkManager'; -import { ScriptingGlobals } from '../../util/ScriptingGlobals'; +import { Scripting } from '../../util/Scripting'; import { SelectionManager } from "../../util/SelectionManager"; import { SharingManager } from '../../util/SharingManager'; import { SnappingManager } from '../../util/SnappingManager'; @@ -49,6 +48,8 @@ import { RadialMenu } from './RadialMenu'; import { ScriptingBox } from "./ScriptingBox"; import { PresBox } from './trails/PresBox'; import React = require("react"); +import { IconProp } from "@fortawesome/fontawesome-svg-core"; +import { ColorScheme } from "../../util/SettingsManager"; const { Howl } = require('howler'); interface Window { @@ -109,7 +110,6 @@ export interface DocumentViewSharedProps { fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document ContainingCollectionView: Opt<CollectionView>; ContainingCollectionDoc: Opt<Doc>; - thumbShown?: () => boolean; setContentView?: (view: DocComponentView) => any; CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView; PanelWidth: () => number; @@ -179,12 +179,11 @@ export interface DocumentViewInternalProps extends DocumentViewProps { } @observer -export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps>() { +export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps, Document>(Document) { public static SelectAfterContextMenu = true; // whether a document should be selected after it's contextmenu is triggered. @observable _animateScalingTo = 0; @observable _mediaState = 0; @observable _pendingDoubleClick = false; - private _disposers: { [name: string]: IReactionDisposer } = {}; private _downX: number = 0; private _downY: number = 0; private _firstX: number = -1; @@ -205,7 +204,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps public get LayoutFieldKey() { return Doc.LayoutFieldKey(this.layoutDoc); } @computed get ShowTitle() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as (Opt<string>); } @computed get ContentScale() { return this.props.ContentScaling?.() || 1; } - @computed get thumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png"); } @computed get hidden() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Hidden); } @computed get opacity() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Opacity); } @computed get boxShadow() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow); } @@ -241,7 +239,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps this._multiTouchDisposer?.(); this._holdDisposer?.(); unbrush && Doc.UnBrushDoc(this.props.Document); - Object.values(this._disposers).forEach(disposer => disposer?.()); } handle1PointerHoldStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => { @@ -488,7 +485,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps const func = () => this.onClickHandler.script.run({ this: this.layoutDoc, self: this.rootDoc, - _readOnly_: false, scriptContext: this.props.scriptContext, thisContainer: this.props.ContainingCollectionDoc, documentView: this.props.DocumentView(), @@ -840,13 +836,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps this._componentView?.isAnyChildContentActive?.() || this.props.isContentActive()) ? true : undefined; } - @observable _retryThumb = 1; - thumbShown = () => !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb && - !this._componentView?.isAnyChildContentActive?.() ? true : false; @computed get contents() { TraceMobx(); const audioView = !this.layoutDoc._showAudio ? (null) : - <div className="documentView-audioBackground" onPointerDown={this.recordAudioAnnotation} onPointerEnter={this.onPointerEnter} > + <div className="documentView-audioBackground" + onPointerDown={this.recordAudioAnnotation} + onPointerEnter={this.onPointerEnter} + style={{ height: 25, position: "absolute", top: 10, left: 10 }} + > <FontAwesomeIcon className="documentView-audioFont" style={{ color: [DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._mediaState] }} icon={!DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "microphone" : "file-audio"} size="sm" /> @@ -856,17 +853,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps pointerEvents: this.props.pointerEvents as any ? this.props.pointerEvents as any : (this.rootDoc.type !== DocumentType.INK && ((this.props.contentPointerEvents as any) || (this.isContentActive())) ? "all" : "none"), height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined, }}> - {!this._retryThumb || !this.thumbShown() ? (null) : - <img style={{ background: "white", top: 0, position: "absolute" }} src={this.thumb} // + '?d=' + (new Date()).getTime()} - width={this.props.PanelWidth()} height={this.props.PanelHeight()} - onError={(e: any) => { - setTimeout(action(() => this._retryThumb = 0), 0); - setTimeout(action(() => this._retryThumb = 1), 150); - }} />} - <DocumentContentsView key={1} - {...this.props} + <DocumentContentsView key={1} {...this.props} docViewPath={this.props.viewPath} - thumbShown={this.thumbShown} setContentView={this.setContentView} scaling={this.contentScaling} PanelHeight={this.panelHeight} @@ -878,11 +866,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps focus={this.focus} layoutKey={this.finalLayoutKey} /> {this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints} - {this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) : - <DocumentLinksButton View={this.props.DocumentView()} - ContentScaling={this.props.ContentScaling} - Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} /> - } + {this.hideLinkButton || this.props.renderDepth === -1 ? (null) : + <div style={{ transformOrigin: "top left", transform: `scale(${Math.min(1, this.props.ScreenToLocalTransform().scale(this.props.ContentScaling?.() || 1).Scale)})` }}> + <DocumentLinksButton View={this.props.DocumentView()} Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} /> + </div>} + {audioView} </div>; } @@ -996,7 +984,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ":caption"); @computed get innards() { TraceMobx(); - const ffscale = () => (this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1); + const ffscale = (this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1); const showTitle = this.ShowTitle?.split(":")[0]; const showTitleHover = this.ShowTitle?.includes(":hover"); const showCaption = !this.props.hideCaptions && this.Document._viewType !== CollectionViewType.Carousel ? StrCast(this.layoutDoc._showCaption) : undefined; @@ -1004,14 +992,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps <div className="documentView-captionWrapper" style={{ pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : this.isContentActive() || this.props.isDocumentActive?.() ? "all" : undefined, - minWidth: 50 * ffscale(), - maxHeight: `max(100%, ${20 * ffscale()}px)` + minWidth: 50 * ffscale, + maxHeight: `max(100%, ${20 * ffscale}px)` }}> <FormattedTextBox {...OmitKeys(this.props, ['children']).omit} yPadding={10} xPadding={10} fieldKey={showCaption} - fontSize={12 * Math.max(1, 2 * ffscale() / 3)} + fontSize={12 * Math.max(1, 2 * ffscale / 3)} styleProvider={this.captionStyleProvider} dontRegisterView={true} noSidebar={true} @@ -1035,18 +1023,19 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps display={"block"} fontSize={10} GetValue={() => showTitle.split(";").length === 1 ? showTitle + "=" + Field.toString(targetDoc[showTitle.split(";")[0]] as any as Field) : "#" + showTitle} - SetValue={undoBatch((input: string) => { + SetValue={undoBatch(input => { if (input?.startsWith("#")) { if (this.props.showTitle) { this.rootDoc._showTitle = input?.substring(1) ? input.substring(1) : undefined; } else { Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : "creationDate"; } + return true; } else { - var value = input.replace(new RegExp(showTitle + "="), "") as string | number; + var value = input.replace(new RegExp(showTitle + "="), ""); if (showTitle !== "title" && Number(value).toString() === value) value = Number(value); if (showTitle.includes("Date") || showTitle === "author") return true; - Doc.SetInPlace(targetDoc, showTitle, value, true); + return Doc.SetInPlace(targetDoc, showTitle, value, true) ? true : true; } return true; })} @@ -1059,17 +1048,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps {captionView} </div>; } - @observable _: string = ""; @computed get renderDoc() { TraceMobx(); - const thumb = ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png"); - const isButton = this.props.Document.type === DocumentType.FONTICON; + const isButton: boolean = this.props.Document.type === DocumentType.FONTICON; if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || this.hidden) return null; return this.docContents ?? <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} id={this.props.Document[Id]} style={{ - background: isButton || thumb ? undefined : this.backgroundColor, + background: isButton ? undefined : this.backgroundColor, opacity: this.opacity, color: StrCast(this.layoutDoc.color, "inherit"), fontFamily: StrCast(this.Document._fontFamily, "inherit"), @@ -1078,7 +1065,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined, transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform 0.5s ease-${this._animateScalingTo < 1 ? "in" : "out"}`, }}> - {this.innards} {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : (null)} {this.widgetDecorations ?? null} @@ -1157,7 +1143,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { get LayoutFieldKey() { return this.docView?.LayoutFieldKey || "layout"; } get fitWidth() { return this.props.fitWidth?.(this.rootDoc) || this.layoutDoc.fitWidth; } - @computed get docViewPath(): DocumentView[] { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; } + @computed get docViewPath() { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; } @computed get layoutDoc() { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); } @computed get nativeWidth() { return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : @@ -1295,7 +1281,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { } } -ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) { +Scripting.addGlobal(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) { if (dv.Document.layoutKey === "layout_" + detailLayoutKeySuffix) dv.switchViews(false, "layout"); else dv.switchViews(true, detailLayoutKeySuffix); });
\ No newline at end of file diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx index c170f9867..f1f802c13 100644 --- a/src/client/views/nodes/EquationBox.tsx +++ b/src/client/views/nodes/EquationBox.tsx @@ -3,18 +3,25 @@ import { action, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { WidthSym } from '../../../fields/Doc'; +import { documentSchema } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; +import { createSchema, makeInterface } from '../../../fields/Schema'; import { NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { Docs } from '../../documents/Documents'; import { ViewBoxBaseComponent } from '../DocComponent'; import { LightboxView } from '../LightboxView'; -import './EquationBox.scss'; import { FieldView, FieldViewProps } from './FieldView'; +import './EquationBox.scss'; + + +const EquationSchema = createSchema({}); +type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSchema]>; +const EquationDocument = makeInterface(EquationSchema, documentSchema); @observer -export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() { +export class EquationBox extends ViewBoxBaseComponent<FieldViewProps, EquationDocument>(EquationDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(EquationBox, fieldKey); } public static SelectOnLoad: string = ""; _ref: React.RefObject<EquationEditor> = React.createRef(); diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index ba65acee0..fb8e89da9 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -4,17 +4,18 @@ import { action, computed, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import Select from "react-select"; import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt } from "../../../fields/Doc"; +import { documentSchema } from "../../../fields/documentSchemas"; import { List } from "../../../fields/List"; import { RichTextField } from "../../../fields/RichTextField"; -import { listSpec } from "../../../fields/Schema"; +import { listSpec, makeInterface } from "../../../fields/Schema"; import { ComputedField, ScriptField } from "../../../fields/ScriptField"; -import { Cast, NumCast, StrCast } from "../../../fields/Types"; +import { Cast, StrCast, NumCast } from "../../../fields/Types"; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { DocumentType } from "../../documents/DocumentTypes"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { UserOptions } from "../../util/GroupManager"; -import { ScriptingGlobals } from "../../util/ScriptingGlobals"; +import { Scripting } from "../../util/Scripting"; import { SelectionManager } from "../../util/SelectionManager"; import { CollectionTreeView } from "../collections/CollectionTreeView"; import { CollectionView } from "../collections/CollectionView"; @@ -29,8 +30,11 @@ const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; +type FilterBoxDocument = makeInterface<[typeof documentSchema]>; +const FilterBoxDocument = makeInterface(documentSchema); + @observer -export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() { +export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDocument>(FilterBoxDocument) { constructor(props: Readonly<FieldViewProps>) { super(props); @@ -421,6 +425,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() { treeViewHideTitle={true} focus={returnFalse} treeViewHideHeaderFields={false} + onCheckedClick={this.scriptField} dontRegisterView={true} styleProvider={this.FilterStyleProvider} layerProvider={this.props.layerProvider} @@ -470,7 +475,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() { } } -ScriptingGlobals.add(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) { +Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) { const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []); for (const filter of docFilters) { const fields = filter.split(":"); // split into key:value:modifiers @@ -480,7 +485,7 @@ ScriptingGlobals.add(function determineCheckedState(layoutDoc: Doc, facetHeader: } return undefined; }); -ScriptingGlobals.add(function readFacetData(layoutDoc: Doc, childKey: string, facetHeader: string) { +Scripting.addGlobal(function readFacetData(layoutDoc: Doc, childKey: string, facetHeader: string) { const allCollectionDocs = new Set<Doc>(); const activeTabs = DocListCast(layoutDoc[childKey]); SearchBox.foreachRecursiveDoc(activeTabs, (depth: number, doc: Doc) => allCollectionDocs.add(doc)); diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index 3ab0a3ff2..5050fc2d2 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -19,7 +19,7 @@ type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSch const EquationDocument = makeInterface(EquationSchema, documentSchema); @observer -export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps>() { +export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps, EquationDocument>(EquationDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FunctionPlotBox, fieldKey); } public static GraphCount = 0; _plot: any; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 0a4168698..89f70985c 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,12 +1,12 @@ -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx'; import { observer } from "mobx-react"; -import { extname } from 'path'; import { DataSym, Doc, DocListCast, WidthSym } from '../../../fields/Doc'; +import { documentSchema } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; -import { createSchema } from '../../../fields/Schema'; +import { createSchema, makeInterface } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; import { Cast, NumCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; @@ -29,11 +29,16 @@ import { FaceRectangles } from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; import React = require("react"); +import { SnappingManager } from '../../util/SnappingManager'; +const path = require('path'); export const pageSchema = createSchema({ googlePhotosUrl: "string", googlePhotosTags: "string" }); +type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>; +const ImageDocument = makeInterface(pageSchema, documentSchema); + const uploadIcons = { idle: "downarrow.png", loading: "loading.gif", @@ -42,7 +47,7 @@ const uploadIcons = { }; @observer -export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() { +export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ImageDocument>(ImageDocument) { protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); } private _imgRef: React.RefObject<HTMLImageElement> = React.createRef(); @@ -185,7 +190,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return url.href; //Why is this here - const ext = extname(url.href); + const ext = path.extname(url.href); return url.href.replace(ext, this._curSuffix + ext); } @@ -281,7 +286,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp } return <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}> - <div className="imageBox-fader" style={{ overflow: Array.from(this.props.docViewPath?.()).slice(-1)[0].fitWidth ? "auto" : undefined }} > + <div className="imageBox-fader" style={{ overflow: this.props.docViewPath?.().lastElement().fitWidth ? "auto" : undefined }} > <img key="paths" ref={this._imgRef} src={srcpath} style={{ transform, transformOrigin }} @@ -371,7 +376,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp scrollTop={0} down={this._marqueeing} scaling={this.props.scaling} - docView={this.props.docViewPath().slice(-1)[0]} + docView={this.props.docViewPath().lastElement()} addDocument={this.addDocument} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index c44c8c828..111509fdb 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -58,7 +58,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> { value = eq ? value.substr(1) : value; const dubEq = value.startsWith(":=") ? "computed" : value.startsWith(";=") ? "script" : false; value = dubEq ? value.substr(2) : value; - const options: ScriptOptions = { addReturn: true, params: { this: Doc.name, self: Doc.name, _last_: "any", _readOnly_: "boolean" }, editable: false }; + const options: ScriptOptions = { addReturn: true, params: { this: "Doc", _last_: "any" }, editable: false }; if (dubEq) options.typecheck = false; const script = CompileScript(value, options); return !script.compiled ? undefined : { script, type: dubEq, onDelegate: eq }; diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 0015f0b71..dae72bb78 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -2,8 +2,9 @@ import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast } from '../../../fields/Doc'; +import { documentSchema } from '../../../fields/documentSchemas'; import { List } from '../../../fields/List'; -import { listSpec } from '../../../fields/Schema'; +import { createSchema, listSpec, makeInterface } from '../../../fields/Schema'; import { Cast, StrCast } from '../../../fields/Types'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; @@ -15,13 +16,17 @@ import { FieldView, FieldViewProps } from './FieldView'; import BigText from './LabelBigText'; import './LabelBox.scss'; +const LabelSchema = createSchema({}); + +type LabelDocument = makeInterface<[typeof LabelSchema, typeof documentSchema]>; +const LabelDocument = makeInterface(LabelSchema, documentSchema); export interface LabelBoxProps { label?: string; } @observer -export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxProps)>() { +export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxProps), LabelDocument>(LabelDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); } public static LayoutStringWithTitle(fieldType: { name: string }, fieldStr: string, label: string) { return `<${fieldType.name} fieldKey={'${fieldStr}'} label={'${label}'} {...props} />`; //e.g., "<ImageBox {...props} fieldKey={"data} />" diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 437d29f39..a7bfb93eb 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -2,6 +2,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, observable } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../../fields/Doc"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { makeInterface } from "../../../fields/Schema"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { TraceMobx } from "../../../fields/util"; import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils'; @@ -21,9 +23,11 @@ const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; +type LinkAnchorSchema = makeInterface<[typeof documentSchema]>; +const LinkAnchorDocument = makeInterface(documentSchema); @observer -export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() { +export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnchorSchema>(LinkAnchorDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkAnchorBox, fieldKey); } _doubleTap = false; _lastTap: number = 0; diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 43f4b43fb..879a63248 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -1,5 +1,7 @@ import React = require("react"); import { observer } from "mobx-react"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { makeInterface } from "../../../fields/Schema"; import { emptyFunction, returnFalse } from "../../../Utils"; import { ViewBoxBaseComponent } from "../DocComponent"; import { StyleProp } from "../StyleProvider"; @@ -7,8 +9,11 @@ import { ComparisonBox } from "./ComparisonBox"; import { FieldView, FieldViewProps } from './FieldView'; import "./LinkBox.scss"; +type LinkDocument = makeInterface<[typeof documentSchema]>; +const LinkDocument = makeInterface(documentSchema); + @observer -export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { +export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(LinkDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); } isContentActiveFunc = () => this.isContentActive(); render() { diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index aa2130af5..50444c73a 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -1,11 +1,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api'; +import * as dotenv from 'dotenv'; import { action, computed, IReactionDisposer, observable, ObservableMap } from 'mobx'; import { observer } from "mobx-react"; import * as React from "react"; import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc'; +import { documentSchema } from '../../../../fields/documentSchemas'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; +import { makeInterface } from '../../../../fields/Schema'; import { NumCast, StrCast } from '../../../../fields/Types'; import { TraceMobx } from '../../../../fields/util'; import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; @@ -40,6 +43,9 @@ import { MapBoxInfoWindow } from './MapBoxInfoWindow'; // const _global = (window /* browser */ || global /* node */) as any; +type MapDocument = makeInterface<[typeof documentSchema]>; +const MapDocument = makeInterface(documentSchema); + const mapContainerStyle = { height: '100%', }; @@ -53,6 +59,7 @@ const mapOptions = { fullscreenControl: false, }; +dotenv.config({ path: __dirname + '/.env' }); const apiKey = process.env.GOOGLE_MAPS; const script = document.createElement('script'); @@ -86,7 +93,7 @@ const options = { } as google.maps.places.AutocompleteOptions; @observer -export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() { +export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>, MapDocument>(MapDocument) { private _dropDisposer?: DragManager.DragDropDisposer; private _disposers: { [name: string]: IReactionDisposer } = {}; diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 8a68f9647..f44355929 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -248,7 +248,6 @@ .pdfBox-background { width: 100%; height: 100%; - cursor: ew-resize; background: lightGray; } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 9807cee7c..d54b65d92 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -3,14 +3,17 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; -import { Doc, DocListCast, Opt, WidthSym } from "../../../fields/Doc"; -import { Cast, NumCast, StrCast, ImageCast } from '../../../fields/Types'; +import { Doc, DocListCast, Opt, WidthSym, StrListCast } from "../../../fields/Doc"; +import { documentSchema } from '../../../fields/documentSchemas'; +import { makeInterface, listSpec } from "../../../fields/Schema"; +import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { PdfField } from "../../../fields/URLField"; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { KeyCodes } from '../../util/KeyCodes'; import { undoBatch } from '../../util/UndoManager'; +import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent"; @@ -19,14 +22,18 @@ import { AnchorMenu } from '../pdf/AnchorMenu'; import { PDFViewer } from "../pdf/PDFViewer"; import { SidebarAnnos } from '../SidebarAnnos'; import { FieldView, FieldViewProps } from './FieldView'; +import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; + +type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>; +const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @observer -export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() { +export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, PdfDocument>(PdfDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); } public static openSidebarWidth = 250; - public static sidebarResizerWidth = 5; private _searchString: string = ""; private _initialScrollTarget: Opt<Doc>; private _pdfViewer: PDFViewer | undefined; @@ -39,7 +46,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @observable private _pageControls = false; @computed get pdfUrl() { return Cast(this.dataDoc[this.props.fieldKey], PdfField); } - @computed get pdfThumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url; } constructor(props: any) { super(props); @@ -48,7 +54,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw)); if (this.pdfUrl) { if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) runInAction(() => this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href)); - else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action((pdf: any) => this._pdf = pdf)); + else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action(pdf => this._pdf = pdf)); } } @@ -74,7 +80,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps const anchor = AnchorMenu.Instance?.GetAnchor() ?? Docs.Create.TextanchorDocument({ - title: StrCast(this.rootDoc.title + "@" + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)), + title: StrCast(this.rootDoc.title + "@" + this.layoutDoc._scrollTop?.toFixed(0)), y: NumCast(this.layoutDoc._scrollTop), unrendered: true }); @@ -104,8 +110,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps }); public prevAnnotation = () => this._pdfViewer?.prevAnnotation(); public nextAnnotation = () => this._pdfViewer?.nextAnnotation(); - public backPage = () => { this.Document._curPage = (NumCast(this.Document._curPage) || 1) - 1; return true; }; - public forwardPage = () => { this.Document._curPage = (NumCast(this.Document._curPage) || 1) + 1; return true; }; + public backPage = () => { this.Document._curPage = (this.Document._curPage || 1) - 1; return true; }; + public forwardPage = () => { this.Document._curPage = (this.Document._curPage || 1) + 1; return true; }; public gotoPage = (p: number) => this.Document._curPage = p; @undoBatch @@ -136,15 +142,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps if (!this.layoutDoc._showSidebar) this.toggleSidebar(); return this.addDocument(doc, sidebarKey); } - sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => { // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf + sidebarBtnDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, (e, down, delta) => { const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]); const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); - const ratio = (curNativeWidth + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth; + const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth; if (ratio >= 1) { this.layoutDoc.nativeWidth = nativeWidth * ratio; - onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]); + this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]; this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; } return false; @@ -154,17 +160,16 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @observable _previewWidth: Opt<number> = undefined; toggleSidebar = action((preview: boolean = false) => { const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); - const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth; - const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth + PDFBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth; + const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth; const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); if (preview) { - this._previewNativeWidth = nativeWidth * sideratio; - this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * sideratio / curNativeWidth; + this._previewNativeWidth = nativeWidth * ratio; + this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; this._showSidebar = true; } else { - this.layoutDoc.nativeWidth = nativeWidth * pdfratio; - this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * pdfratio / curNativeWidth; + this.layoutDoc.nativeWidth = nativeWidth * ratio; + this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; } }); @@ -180,7 +185,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps </button> </>; const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`; - const curPage = NumCast(this.Document._curPage) || 1; + const curPage = this.Document._curPage || 1; return !this.props.isContentActive() ? (null) : <div className="pdfBox-ui" onKeyDown={e => [KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true} onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? "flex" : "none" }}> @@ -218,7 +223,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5, backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK }} - onPointerDown={e => this.sidebarBtnDown(e, true)} > + onPointerDown={this.sidebarBtnDown} > <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" /> </div> </div>; @@ -248,20 +253,18 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; } contentScaling = () => 1; - isPdfContentActive = () => this.isAnyChildContentActive() || this.props.isSelected(); @computed get renderPdfView() { TraceMobx(); const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; const scale = previewScale * (this.props.scaling?.() || 1); return <div className={"pdfBox"} onContextMenu={this.specificContextMenu} style={{ - display: this.props.thumbShown?.() ? "none" : undefined, height: this.props.Document._scrollTop && !this.Document._fitWidth && (window.screen.width > 600) ? NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined }}> - <div className="pdfBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} /> + <div className="pdfBox-background" /> <div style={{ - width: `calc(${100 / scale}% - ${(this.sidebarWidth() + PDFBox.sidebarResizerWidth) / scale * (this._previewWidth ? scale : 1)}px)`, + width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`, height: `${100 / scale}%`, transform: `scale(${scale})`, position: "absolute", @@ -274,7 +277,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps dataDoc={this.dataDoc} pdf={this._pdf!} url={this.pdfUrl!.url.pathname} - isContentActive={this.isPdfContentActive} + isContentActive={this.props.isContentActive} anchorMenuClick={this.anchorMenuClick} loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined} setPdfViewer={this.setPdfViewer} @@ -304,22 +307,17 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } static pdfcache = new Map<string, Pdfjs.PDFDocumentProxy>(); - static pdfpromise = new Map<string, Promise<Pdfjs.PDFDocumentProxy>>(); + static pdfpromise = new Map<string, Pdfjs.PDFPromise<Pdfjs.PDFDocumentProxy>>(); render() { TraceMobx(); - if (this._pdf) { - if (!this.props.thumbShown?.()) { - return this.renderPdfView; - } - return null; - } + if (this._pdf) return this.renderPdfView; const href = this.pdfUrl?.url.href; if (href) { if (PDFBox.pdfcache.get(href)) setTimeout(action(() => this._pdf = PDFBox.pdfcache.get(href))); else { if (!PDFBox.pdfpromise.get(href)) PDFBox.pdfpromise.set(href, Pdfjs.getDocument(href).promise); - PDFBox.pdfpromise.get(href)?.then(action((pdf: any) => PDFBox.pdfcache.set(href, this._pdf = pdf))); + PDFBox.pdfpromise.get(href)?.then(action(pdf => PDFBox.pdfcache.set(href, this._pdf = pdf))); } } return this.renderTitleBox; diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index dbb567d3a..0c631e5f9 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -1,17 +1,20 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; // import { Canvas } from '@react-three/fiber'; -import { computed, observable, runInAction } from "mobx"; +import { action, computed, observable, reaction, trace, runInAction } from "mobx"; import { observer } from "mobx-react"; // import { BufferAttribute, Camera, Vector2, Vector3 } from 'three'; import { DateField } from "../../../fields/DateField"; -import { Doc, HeightSym, WidthSym } from "../../../fields/Doc"; +import { Doc, WidthSym, HeightSym } from "../../../fields/Doc"; +import { documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; +import { InkTool } from "../../../fields/InkField"; +import { makeInterface } from "../../../fields/Schema"; import { ComputedField } from "../../../fields/ScriptField"; import { Cast, NumCast } from "../../../fields/Types"; import { AudioField, VideoField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; -import { emptyFunction, OmitKeys, returnFalse, returnOne } from "../../../Utils"; +import { emptyFunction, numberRange, OmitKeys, returnFalse, returnOne, Utils } from "../../../Utils"; import { DocUtils } from "../../documents/Documents"; import { DocumentType } from "../../documents/DocumentTypes"; import { Networking } from "../../Network"; @@ -29,6 +32,9 @@ declare class MediaRecorder { constructor(e: any, options?: any); // whatever MediaRecorder has } +type ScreenshotDocument = makeInterface<[typeof documentSchema]>; +const ScreenshotDocument = makeInterface(documentSchema); + // interface VideoTileProps { // raised: { coord: Vector2, off: Vector3 }[]; // setRaised: (r: { coord: Vector2, off: Vector3 }[]) => void; @@ -106,7 +112,7 @@ declare class MediaRecorder { // } @observer -export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() { +export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ScreenshotDocument>(ScreenshotDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); } private _audioRec: any; private _videoRec: any; diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index 366c3fc2f..183140cd7 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -1,19 +1,18 @@ import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete"; import "@webscopeio/react-textarea-autocomplete/style.css"; -import { action, computed, observable } from "mobx"; +import { action, computed, observable, runInAction, trace } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; import { Doc } from "../../../fields/Doc"; +import { documentSchema } from "../../../fields/documentSchemas"; import { List } from "../../../fields/List"; -import { listSpec } from "../../../fields/Schema"; +import { createSchema, listSpec, makeInterface } from "../../../fields/Schema"; import { ScriptField } from "../../../fields/ScriptField"; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; -import { TraceMobx } from "../../../fields/util"; +import { Cast, NumCast, ScriptCast, StrCast, BoolCast } from "../../../fields/Types"; import { returnEmptyString } from "../../../Utils"; import { DragManager } from "../../util/DragManager"; import { InteractionUtils } from "../../util/InteractionUtils"; -import { CompileScript, ScriptParam } from "../../util/Scripting"; -import { ScriptingGlobals } from "../../util/ScriptingGlobals"; +import { CompileScript, Scripting, ScriptParam } from "../../util/Scripting"; import { ScriptManager } from "../../util/ScriptManager"; import { ContextMenu } from "../ContextMenu"; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent"; @@ -22,10 +21,15 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView"; import { OverlayView } from "../OverlayView"; import { DocumentIconContainer } from "./DocumentIcon"; import "./ScriptingBox.scss"; +import { TraceMobx } from "../../../fields/util"; const _global = (window /* browser */ || global /* node */) as any; +const ScriptingSchema = createSchema({}); +type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>; +const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema); + @observer -export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() { +export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ScriptingDocument>(ScriptingDocument) { private dropDisposer?: DragManager.DragDropDisposer; protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined; @@ -38,9 +42,9 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable @observable private _function: boolean = false; @observable private _spaced: boolean = false; - @observable private _scriptKeys: any = ScriptingGlobals.getGlobals(); - @observable private _scriptingDescriptions: any = ScriptingGlobals.getDescriptions(); - @observable private _scriptingParams: any = ScriptingGlobals.getParameters(); + @observable private _scriptKeys: any = Scripting.getGlobals(); + @observable private _scriptingDescriptions: any = Scripting.getDescriptions(); + @observable private _scriptingParams: any = Scripting.getParameters(); @observable private _currWord: string = ""; @observable private _suggestions: string[] = []; @@ -220,9 +224,9 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable ScriptManager.Instance.addScript(this.dataDoc); - this._scriptKeys = ScriptingGlobals.getGlobals(); - this._scriptingDescriptions = ScriptingGlobals.getDescriptions(); - this._scriptingParams = ScriptingGlobals.getParameters(); + this._scriptKeys = Scripting.getGlobals(); + this._scriptingDescriptions = Scripting.getDescriptions(); + this._scriptingParams = Scripting.getParameters(); } // overlays document numbers (ex. d32) over all documents when clicked on @@ -573,8 +577,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable </div>; } - renderFuncListElement(value: string | object) { - return (typeof value !== "string") ? (null) : <div> + renderFuncListElement(value: string) { + return <div> <div style={{ fontSize: "14px" }}> {value} </div> diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx index b96977f32..92d1f7446 100644 --- a/src/client/views/nodes/SliderBox.tsx +++ b/src/client/views/nodes/SliderBox.tsx @@ -2,19 +2,30 @@ import { runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider'; +import { documentSchema } from '../../../fields/documentSchemas'; +import { createSchema, makeInterface } from '../../../fields/Schema'; import { NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxBaseComponent } from '../DocComponent'; import { ScriptBox } from '../ScriptBox'; -import { StyleProp } from '../StyleProvider'; import { FieldView, FieldViewProps } from './FieldView'; import { Handle, Tick, TooltipRail, Track } from './SliderBox-components'; import './SliderBox.scss'; +import { StyleProp } from '../StyleProvider'; + +const SliderSchema = createSchema({ + _sliderMin: "number", + _sliderMax: "number", + _sliderMinThumb: "number", + _sliderMaxThumb: "number", +}); +type SliderDocument = makeInterface<[typeof SliderSchema, typeof documentSchema]>; +const SliderDocument = makeInterface(SliderSchema, documentSchema); @observer -export class SliderBox extends ViewBoxBaseComponent<FieldViewProps>() { +export class SliderBox extends ViewBoxBaseComponent<FieldViewProps, SliderDocument>(SliderDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SliderBox, fieldKey); } get minThumbKey() { return this.fieldKey + "-minThumb"; } diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index fafa39deb..56cb562bc 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -3,10 +3,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from "mobx"; import { observer } from "mobx-react"; -import { basename } from "path"; import * as rp from 'request-promise'; import { Doc, DocListCast } from "../../../fields/Doc"; +import { documentSchema } from "../../../fields/documentSchemas"; import { InkTool } from "../../../fields/InkField"; +import { makeInterface } from "../../../fields/Schema"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { AudioField, VideoField } from "../../../fields/URLField"; import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from "../../../Utils"; @@ -35,7 +36,7 @@ type VideoDocument = makeInterface<[typeof documentSchema]>; const VideoDocument = makeInterface(documentSchema); @observer -export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() { +export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, VideoDocument>(VideoDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); } static async convertDataUri(imageUri: string, returnedFilename: string) { try { @@ -188,7 +189,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp } @action public Snapshot(downX?: number, downY?: number) { - const width = NumCast(this.layoutDoc._width); + const width = (this.layoutDoc._width || 0); const canvas = document.createElement('canvas'); canvas.width = 640; canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1); @@ -202,7 +203,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp if (!this._videoRef) { const b = Docs.Create.LabelDocument({ - x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y, 1), + x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 1), _width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(), _isLinkButton: true }); @@ -223,8 +224,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png' // if you want to preview the captured image, const retitled = StrCast(this.rootDoc.title).replace(/[ -\.]/g, ""); - const encodedFilename = encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_")); - const filename = basename(encodedFilename); + const filename = path.basename(encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"))); VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && this.createRealSummaryLink(returnedFilename, downX, downY)); } @@ -232,12 +232,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => { const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath; - const width = NumCast(this.layoutDoc._width) || 1; - const height = NumCast(this.layoutDoc._height); + const width = this.layoutDoc._width || 1; + const height = this.layoutDoc._height || 0; const imageSummary = Docs.Create.ImageDocument(url, { _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc), - x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y), _isLinkButton: true, - _width: 150, _height: height / width * 150, title: "--snapshot" + NumCast(this.layoutDoc._currentTimecode) + " image-" + x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0), _isLinkButton: true, + _width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc._currentTimecode || 0) + " image-" }); Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc)); Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc)); @@ -282,7 +282,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp // @ts-ignore // vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen); this._disposers.reactionDisposer?.(); - this._disposers.reactionDisposer = reaction(() => NumCast(this.layoutDoc._currentTimecode), + this._disposers.reactionDisposer = reaction(() => (this.layoutDoc._currentTimecode || 0), time => !this._playing && (vref.currentTime = time), { fireImmediately: true }); } } @@ -373,7 +373,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp const onYoutubePlayerReady = (event: any) => { this._disposers.reactionDisposer?.(); this._disposers.youtubeReactionDisposer?.(); - this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode))); + this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek((this.layoutDoc._currentTimecode || 0))); this._disposers.youtubeReactionDisposer = reaction( () => CurrentUserUtils.SelectedTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting, (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true }); @@ -460,7 +460,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp this._youtubeIframeId = VideoBox._youtubeIframeCounter++; this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true; const classname = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : ""); - const start = untracked(() => Math.round(NumCast(this.layoutDoc._currentTimecode))); + const start = untracked(() => Math.round((this.layoutDoc._currentTimecode || 0))); return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`} onPointerLeave={this.updateTimecode} onLoad={this.youtubeIframeLoaded} className={classname} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390} @@ -719,7 +719,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp style={{ pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined, borderRadius, - overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined + overflow: this.props.docViewPath?.().lastElement().fitWidth ? "auto" : undefined }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}> <div className="videoBox-viewer" onPointerDown={this.marqueeDown} > <div style={{ @@ -755,7 +755,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp scrollTop={0} down={this._marqueeing} scaling={this.marqueeFitScaling} - docView={this.props.docViewPath().slice(-1)[0]} + docView={this.props.docViewPath().lastElement()} containerOffset={this.marqueeOffset} addDocument={this.addDocWithTimecode} finishMarquee={this.finishMarquee} diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index 7dc970496..79289abaa 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -5,12 +5,6 @@ height: 100%; position: relative; display: flex; - .webBox-background { - width: 100%; - height: 100%; - cursor: ew-resize; - background: lightGray; - } .webBox-ui { position: absolute; @@ -143,7 +137,6 @@ transform-origin: top left; width: 100%; height: 100%; - position: absolute; .webBox-htmlSpan { position: absolute; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 7ff47107e..9956cc36b 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -3,20 +3,21 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio import { observer } from "mobx-react"; import * as WebRequest from 'web-request'; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { HtmlField } from "../../../fields/HtmlField"; import { InkTool } from "../../../fields/InkField"; import { List } from "../../../fields/List"; -import { listSpec } from "../../../fields/Schema"; +import { listSpec, makeInterface } from "../../../fields/Schema"; import { ComputedField } from "../../../fields/ScriptField"; -import { Cast, ImageCast, NumCast, StrCast } from "../../../fields/Types"; -import { ImageField, WebField } from "../../../fields/URLField"; +import { Cast, NumCast, StrCast } from "../../../fields/Types"; +import { WebField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { KeyCodes } from "../../util/KeyCodes"; -import { ScriptingGlobals } from "../../util/ScriptingGlobals"; +import { Scripting } from "../../util/Scripting"; import { SnappingManager } from "../../util/SnappingManager"; import { undoBatch } from "../../util/UndoManager"; import { MarqueeOptionsMenu } from "../collections/collectionFreeForm"; @@ -35,18 +36,18 @@ import { StyleProp } from "../StyleProvider"; import { DocumentViewProps } from "./DocumentView"; import { FieldView, FieldViewProps } from './FieldView'; import { LinkDocPreview } from "./LinkDocPreview"; -import { VideoBox } from "./VideoBox"; import "./WebBox.scss"; import React = require("react"); -const { CreateImage } = require("./WebBoxRenderer"); const _global = (window /* browser */ || global /* node */) as any; const htmlToText = require("html-to-text"); +type WebDocument = makeInterface<[typeof documentSchema]>; +const WebDocument = makeInterface(documentSchema); + @observer -export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() { +export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, WebDocument>(WebDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } public static openSidebarWidth = 250; - public static sidebarResizerWidth = 5; private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void); private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); private _outerRef: React.RefObject<HTMLDivElement> = React.createRef(); @@ -62,7 +63,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @observable private _searching: boolean = false; @observable private _showSidebar = false; @observable private _scrollTimer: any; - @observable private _webPageHasBeenRendered = false; @observable private _overlayAnnoInfo: Opt<Doc>; @observable private _marqueeing: number[] | undefined; @observable private _isAnnotating = false; @@ -76,7 +76,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); } @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); } @computed get webField() { return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; } - @computed get webThumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url; } constructor(props: any) { super(props); @@ -104,7 +103,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps return true; } async componentDidMount() { - this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document. this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons) + this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. runInAction(() => { this._annotationKeySuffix = () => this._urlHash + "-annotations"; @@ -112,48 +111,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`); this.dataDoc[this.fieldKey + "-sidebar"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`); }); - reaction(() => this.props.isSelected(), - async (selected) => { - if (selected) { - this._webPageHasBeenRendered = true; - setTimeout(action(() => { - this._scrollHeight = Math.max(this.scrollHeight, this._iframe?.contentDocument?.body.scrollHeight || 0); - if (this._initialScroll !== undefined && this._outerRef.current) { - setTimeout(() => { - this._outerRef.current!.scrollTop = this._initialScroll!; - this._initialScroll = undefined; - }); - } - })); - } else if (!this.props.isContentActive() && - !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && /// don't create a thumbnail when double-clicking to enter lightbox because thumbnail will be empty - LightboxView.LightboxDoc !== this.rootDoc) { // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty. - const imageBitmap = ImageCast(this.layoutDoc["thumb-frozen"])?.url.href; - if (this._iframe && !imageBitmap) { - var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument); - if (!htmlString) { - htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text(); - } - this.layoutDoc.thumb = undefined; - const nativeWidth = NumCast(this.layoutDoc.nativeWidth); - CreateImage( - this._webUrl.endsWith("/") ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl, - this._iframe.contentDocument?.styleSheets ?? [], - htmlString, - nativeWidth, - nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(), - NumCast(this.layoutDoc._scrollTop) - ).then - ((dataUrl: any) => { - VideoBox.convertDataUri(dataUrl, this.layoutDoc[Id] + "-thumb" + (new Date()).getTime(), true).then( - returnedfilename => setTimeout(action(() => this.layoutDoc.thumb = new ImageField(returnedfilename)), 500)); - }) - .catch(function (error: any) { - console.error('oops, something went wrong!', error); - }); - } - } - }); this._disposers.autoHeight = reaction(() => this.layoutDoc._autoHeight, autoHeight => { @@ -335,6 +292,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps iframe.contentDocument.addEventListener("pointerdown", this.iframeDown); this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument.body.scrollHeight); setTimeout(action(() => this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000); + const initialScroll = this._initialScroll; + if (initialScroll !== undefined && this._outerRef.current) { + // bcz: not sure why this happens, but if the webpage isn't ready yet, it's scroll height seems to be limited. So we need to wait tp set scroll location to what we want. + setTimeout(() => this._outerRef.current!.scrollTop = initialScroll); + this._initialScroll = undefined; + } iframe.setAttribute("enable-annotation", "true"); iframe.contentDocument.addEventListener("click", undoBatch(action((e: MouseEvent) => { let href = ""; @@ -390,46 +353,42 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } } - forward = (checkAvailable?: boolean) => { + @action + forward = () => { const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), []); const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []); - if (checkAvailable) return future.length; - runInAction(() => { - if (future.length) { - const curUrl = this._url; - this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, this._url]); - this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!)); - if (this._webUrl === this._url) { - this._webUrl = curUrl; - setTimeout(action(() => this._webUrl = this._url)); - } else { - this._webUrl = this._url; - } - return true; + if (future.length) { + const curUrl = this._url; + this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, this._url]); + this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!)); + if (this._webUrl === this._url) { + this._webUrl = curUrl; + setTimeout(action(() => this._webUrl = this._url)); + } else { + this._webUrl = this._url; } - }); + return true; + } return false; } - back = (checkAvailable?: boolean) => { + @action + back = () => { const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string")); const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []); - if (checkAvailable) return history.length; - runInAction(() => { - if (history.length) { - const curUrl = this._url; - if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]); - else this.dataDoc[this.fieldKey + "-future"] = new List<string>([...future, this._url]); - this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!)); - if (this._webUrl === this._url) { - this._webUrl = curUrl; - setTimeout(action(() => this._webUrl = this._url)); - } else { - this._webUrl = this._url; - } - return true; + if (history.length) { + const curUrl = this._url; + if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]); + else this.dataDoc[this.fieldKey + "-future"] = new List<string>([...future, this._url]); + this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!)); + if (this._webUrl === this._url) { + this._webUrl = curUrl; + setTimeout(action(() => this._webUrl = this._url)); + } else { + this._webUrl = this._url; } - }); + return true; + } return false; } @@ -493,8 +452,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps <button className="submitUrl" onClick={() => this.submitURL(this._keyInput.current!.value)} onDragOver={e => e.stopPropagation()} onDrop={this.onWebUrlDrop}> GO </button> - <button className="submitUrl" onClick={() => this.back}> <FontAwesomeIcon icon="caret-left" size="lg" /> </button> - <button className="submitUrl" onClick={() => this.forward}> <FontAwesomeIcon icon="caret-right" size="lg" /> </button> + <button className="submitUrl" onClick={this.back}> <FontAwesomeIcon icon="caret-left" size="lg" /> </button> + <button className="submitUrl" onClick={this.forward}> <FontAwesomeIcon icon="caret-right" size="lg" /> </button> </div> </div> ); @@ -553,7 +512,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } @computed get urlContent() { - if (this._hackHide || (this.webThumb && (!this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc))) return (null); + if (this._hackHide) return (null); const field = this.dataDoc[this.props.fieldKey]; let view; if (field instanceof HtmlField) { @@ -571,7 +530,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps style={{ pointerEvents: this._scrollTimer ? "none" : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={"https://crossorigin.me/https://cs.brown.edu"} />; } - setTimeout(action(() => this._webPageHasBeenRendered = true)); return view; } @@ -584,37 +542,34 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps if (!this.layoutDoc._showSidebar) this.toggleSidebar(); return this.addDocumentWrapper(doc, sidebarKey); } - @observable _draggingSidebar = false; - sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => { // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf - setupMoveUpEvents(this, e, action((e, down, delta) => { - this._draggingSidebar = true; + sidebarBtnDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, (e, down, delta) => { const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]); const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); - const ratio = (curNativeWidth + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth; + const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth; if (ratio >= 1) { this.layoutDoc.nativeWidth = nativeWidth * ratio; - onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]); + this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]; this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; } return false; - }), action(() => this._draggingSidebar = false), () => this.toggleSidebar()); + }, emptyFunction, () => this.toggleSidebar()); } @observable _previewNativeWidth: Opt<number> = undefined; @observable _previewWidth: Opt<number> = undefined; toggleSidebar = action((preview: boolean = false) => { const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); - const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth; - const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth + WebBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth; + const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth; const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); if (preview) { - this._previewNativeWidth = nativeWidth * sideratio; - this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * sideratio / curNativeWidth; + this._previewNativeWidth = nativeWidth * ratio; + this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; this._showSidebar = true; } else { - this.layoutDoc.nativeWidth = nativeWidth * pdfratio; - this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * pdfratio / curNativeWidth; + this.layoutDoc.nativeWidth = nativeWidth * ratio; + this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; } }); @@ -678,7 +633,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } return this.props.styleProvider?.(doc, props, property); } - pointerEvents = () => !this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none"; + pointerEvents = () => this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none"; render() { const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : this.props.pointerEvents ? this.props.pointerEvents as any : undefined; const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; @@ -709,17 +664,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps pointerEvents={this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />; return ( <div className="webBox" ref={this._mainCont} - style={{ pointerEvents: this.pointerEvents(), display: this.props.thumbShown?.() ? "none" : undefined }} > - <div className="webBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} /> - <div className="webBox-container" style={{ - position: "absolute", - width: `calc(${100 / scale}% - ${(this.sidebarWidth() + WebBox.sidebarResizerWidth) / scale * (this._previewWidth ? scale : 1)}px)`, - transform: `scale(${scale})`, - pointerEvents - }} onContextMenu={this.specificContextMenu}> + style={{ pointerEvents: this.pointerEvents() }} > + <div className={`webBox-container`} style={{ pointerEvents }} onContextMenu={this.specificContextMenu}> <div className={"webBox-outerContent"} ref={this._outerRef} style={{ + width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`, height: `${100 / scale}%`, + transform: `scale(${scale})`, pointerEvents }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }} // block wheel events from propagating since they're handled by the iframe @@ -752,7 +703,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps </div > <SidebarAnnos ref={this._sidebarRef} {...this.props} - whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} fieldKey={this.fieldKey + "-" + this._urlHash} rootDoc={this.rootDoc} layoutDoc={this.layoutDoc} @@ -770,11 +720,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5, backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK }} - onPointerDown={e => this.sidebarBtnDown(e, true)} > + onPointerDown={this.sidebarBtnDown} > <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" /> </div> {!this.props.isContentActive() ? (null) : this.searchUI} </div>); } } -ScriptingGlobals.add(function urlHash(url: string) { return WebBox.urlHash(url); });
\ No newline at end of file +Scripting.addGlobal(function urlHash(url: string) { return WebBox.urlHash(url); });
\ No newline at end of file diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js deleted file mode 100644 index 08a5746d1..000000000 --- a/src/client/views/nodes/WebBoxRenderer.js +++ /dev/null @@ -1,395 +0,0 @@ -/** - * - * @param {StyleSheetList} styleSheets - */ -var ForeignHtmlRenderer = function (styleSheets) { - - const self = this; - - /** - * - * @param {String} binStr - */ - const binaryStringToBase64 = function (binStr) { - return new Promise(function (resolve) { - const reader = new FileReader(); - reader.readAsDataURL(binStr); - reader.onloadend = function () { - resolve(reader.result); - } - }); - }; - - function prepend(extension) { - return window.location.origin + extension; - } - function CorsProxy(url) { - return prepend("/corsProxy/") + encodeURIComponent(url); - } - /** - * - * @param {String} url - * @returns {Promise} - */ - const getResourceAsBase64 = function (webUrl, inurl) { - return new Promise(function (resolve, reject) { - const xhr = new XMLHttpRequest(); - //const url = inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl; - //const url = CorsProxy(inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl);// inurl.startsWith("http") ? CorsProxy(inurl) : inurl; - var url = inurl; - if (inurl.startsWith("/static")) { - url = (new URL(webUrl).origin + inurl); - } else - if ((inurl.startsWith("/") && !inurl.startsWith("//"))) { - url = CorsProxy(new URL(webUrl).origin + inurl); - } else if (!inurl.startsWith("http") && !inurl.startsWith("//")) { - url = CorsProxy(webUrl + "/" + inurl); - } - xhr.open("GET", url); - xhr.responseType = 'blob'; - - xhr.onreadystatechange = async function () { - if (xhr.readyState === 4 && xhr.status === 200) { - const resBase64 = await binaryStringToBase64(xhr.response); - - resolve( - { - "resourceUrl": inurl, - "resourceBase64": resBase64 - } - ); - } else if (xhr.readyState === 4) { - console.log("COULDN'T FIND: " + (inurl.startsWith("/") ? webUrl + inurl : inurl)); - resolve( - { - "resourceUrl": "", - "resourceBase64": inurl - } - ); - } - }; - - xhr.send(null); - }); - }; - - /** - * - * @param {String[]} urls - * @returns {Promise} - */ - const getMultipleResourcesAsBase64 = function (webUrl, urls) { - const promises = []; - for (let i = 0; i < urls.length; i++) { - promises.push(getResourceAsBase64(webUrl, urls[i])); - } - return Promise.all(promises); - }; - - /** - * - * @param {String} str - * @param {Number} startIndex - * @param {String} prefixToken - * @param {String[]} suffixTokens - * - * @returns {String|null} - */ - const parseValue = function (str, startIndex, prefixToken, suffixTokens) { - const idx = str.indexOf(prefixToken, startIndex); - if (idx === -1) { - return null; - } - - let val = ''; - for (let i = idx + prefixToken.length; i < str.length; i++) { - if (suffixTokens.indexOf(str[i]) !== -1) { - break; - } - - val += str[i]; - } - - return { - "foundAtIndex": idx, - "value": val - } - }; - - /** - * - * @param {String} cssRuleStr - * @returns {String[]} - */ - const getUrlsFromCssString = function (cssRuleStr, selector = "url(", delimiters = [')'], mustEndWithQuote = false) { - const urlsFound = []; - let searchStartIndex = 0; - - while (true) { - const url = parseValue(cssRuleStr, searchStartIndex, selector, delimiters); - if (url === null) { - break; - } - searchStartIndex = url.foundAtIndex + url.value.length; - if (mustEndWithQuote && url.value[url.value.length - 1] !== '"') continue; - const unquoted = removeQuotes(url.value); - if (!unquoted /* || (!unquoted.startsWith('http')&& !unquoted.startsWith("/") )*/ || unquoted === 'http://' || unquoted === 'https://') { - continue; - } - - unquoted && urlsFound.push(unquoted); - } - - return urlsFound; - }; - - /** - * - * @param {String} html - * @returns {String[]} - */ - const getImageUrlsFromFromHtml = function (html) { - return getUrlsFromCssString(html, "src=", [' ', '>', '\t'], true); - }; - const getSourceUrlsFromFromHtml = function (html) { - return getUrlsFromCssString(html, "source=", [' ', '>', '\t'], true); - }; - - /** - * - * @param {String} str - * @returns {String} - */ - const removeQuotes = function (str) { - return str.replace(/["']/g, ""); - }; - - const escapeRegExp = function (string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string - }; - - /** - * - * @param {String} contentHtml - * @param {Number} width - * @param {Number} height - * - * @returns {Promise<String>} - */ - const buildSvgDataUri = async function (webUrl, contentHtml, width, height, scroll) { - - return new Promise(async function (resolve, reject) { - - /* !! The problems !! - * 1. CORS (not really an issue, expect perhaps for images, as this is a general security consideration to begin with) - * 2. Platform won't wait for external assets to load (fonts, images, etc.) - */ - - // copy styles - let cssStyles = ""; - let urlsFoundInCss = []; - - for (let i = 0; i < styleSheets.length; i++) { - try { - const rules = styleSheets[i].cssRules - for (let j = 0; j < rules.length; j++) { - const cssRuleStr = rules[j].cssText; - urlsFoundInCss.push(...getUrlsFromCssString(cssRuleStr)); - cssStyles += cssRuleStr; - } - } catch (e) { - - } - } - - // const fetchedResourcesFromStylesheets = await getMultipleResourcesAsBase64(webUrl, urlsFoundInCss); - // for (let i = 0; i < fetchedResourcesFromStylesheets.length; i++) { - // const r = fetchedResourcesFromStylesheets[i]; - // if (r.resourceUrl) { - // cssStyles = cssStyles.replace(new RegExp(escapeRegExp(r.resourceUrl), "g"), r.resourceBase64); - // } - // } - - contentHtml = contentHtml.replace(/<source[^>]*>/g, "") // <picture> tags have a <source> which has a srcset field of image refs. instead of converting each, just use the default <img> of the picture - .replace(/noscript/g, "div").replace(/<div class="mediaset"><\/div>/g, "") // when scripting isn't available (ie, rendering web pages here), <noscript> tags should become <div>'s. But for Brown CS, there's a layout problem if you leave the empty <mediaset> tag - .replace(/<link[^>]*>/g, "") // don't need to keep any linked style sheets because we've already processed all style sheets above - .replace(/srcset="([^ "]*)[^"]*"/g, "src=\"$1\""); // instead of converting each item in the srcset to a data url, just convert the first one and use that - let urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml); - const fetchedResources = await getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml); - for (let i = 0; i < fetchedResources.length; i++) { - const r = fetchedResources[i]; - if (r.resourceUrl) { - contentHtml = contentHtml.replace(new RegExp(escapeRegExp(r.resourceUrl), "g"), r.resourceBase64); - } - } - - const styleElem = document.createElement("style"); - styleElem.innerHTML = cssStyles.replace(">", ">").replace("<", "<"); - - const styleElemString = new XMLSerializer().serializeToString(styleElem).replace(/>/g, ">").replace(/</g, "<"); - - // create DOM element string that encapsulates styles + content - const contentRootElem = document.createElement("body"); - contentRootElem.style.zIndex = "1111"; - // contentRootElem.style.transform = "scale(0.08)" - contentRootElem.innerHTML = styleElemString + contentHtml; - contentRootElem.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); - //document.body.appendChild(contentRootElem); - - const contentRootElemString = new XMLSerializer().serializeToString(contentRootElem); - - // build SVG string - const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='${width}' height='${height}'> - <foreignObject x='0' y='${-scroll}' width='${width}' height='${scroll + height}'> - ${contentRootElemString} - </foreignObject> - </svg>`; - - // convert SVG to data-uri - const dataUri = `data:image/svg+xml;base64,${window.btoa(unescape(encodeURIComponent(svg)))}`; - - resolve(dataUri); - }); - }; - - /** - * @param {String} html - * @param {Number} width - * @param {Number} height - * - * @return {Promise<Image>} - */ - this.renderToImage = async function (webUrl, html, width, height, scroll) { - return new Promise(async function (resolve, reject) { - const img = new Image(); - console.log("BUILDING SVG for:" + webUrl); - img.src = await buildSvgDataUri(webUrl, html, width, height, scroll); - - img.onload = function () { - console.log("IMAGE SVG created:" + webUrl); - resolve(img); - }; - }); - }; - - /** - * @param {String} html - * @param {Number} width - * @param {Number} height - * - * @return {Promise<Image>} - */ - this.renderToCanvas = async function (webUrl, html, width, height, scroll) { - return new Promise(async function (resolve, reject) { - const img = await self.renderToImage(webUrl, html, width, height, scroll); - - const canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - - const canvasCtx = canvas.getContext('2d'); - canvasCtx.drawImage(img, 0, 0, img.width, img.height); - - resolve(canvas); - }); - }; - - /** - * @param {String} html - * @param {Number} width - * @param {Number} height - * - * @return {Promise<String>} - */ - this.renderToBase64Png = async function (webUrl, html, width, height, scroll) { - return new Promise(async function (resolve, reject) { - const canvas = await self.renderToCanvas(webUrl, html, width, height, scroll); - resolve(canvas.toDataURL('image/png')); - }); - }; - -}; - - -export function CreateImage(webUrl, styleSheets, html, width, height, scroll) { - const val = (new ForeignHtmlRenderer(styleSheets)).renderToBase64Png(webUrl, html.replace(/\n/g, "").replace(/<script((?!\/script).)*<\/script>/g, ""), width, height, scroll); - return val; -} - - - -var ClipboardUtils = new function () { - var permissions = { - 'image/bmp': true, - 'image/gif': true, - 'image/png': true, - 'image/jpeg': true, - 'image/tiff': true - }; - - function getType(types) { - for (var j = 0; j < types.length; ++j) { - var type = types[j]; - if (permissions[type]) { - return type; - } - } - return null; - } - function getItem(items) { - for (var i = 0; i < items.length; ++i) { - var item = items[i]; - if (item) { - var type = getType(item.types); - if (type) { - return item.getType(type); - } - } - } - return null; - } - function loadFile(file, callback) { - if (window.FileReader) { - var reader = new FileReader(); - reader.onload = function () { - callback(reader.result, null); - }; - reader.onerror = function () { - callback(null, 'Incorrect file.'); - }; - reader.readAsDataURL(file); - } else { - callback(null, 'File api is not supported.'); - } - } - this.readImage = function (callback) { - if (navigator.clipboard) { - var promise = navigator.clipboard.read(); - promise - .then(function (items) { - var promise = getItem(items); - if (promise == null) { - callback(null, null); - return; - } - promise - .then(function (result) { - loadFile(result, callback); - }) - .catch(function (error) { - callback(null, 'Reading clipboard error.'); - }); - }) - .catch(function (error) { - callback(null, 'Reading clipboard error.'); - }); - } else { - callback(null, 'Clipboard is not supported.'); - } - }; -}; - - -export function pasteImageBitmap(callback) { - return ClipboardUtils.readImage(callback); -}
\ No newline at end of file diff --git a/src/client/views/nodes/button/ButtonScripts.ts b/src/client/views/nodes/button/ButtonScripts.ts index f3731b8f9..bb4dd8bc9 100644 --- a/src/client/views/nodes/button/ButtonScripts.ts +++ b/src/client/views/nodes/button/ButtonScripts.ts @@ -1,14 +1,14 @@ -import { ScriptingGlobals } from "../../../util/ScriptingGlobals"; +import { Scripting } from "../../../util/Scripting"; import { SelectionManager } from "../../../util/SelectionManager"; // toggle: Set overlay status of selected document -ScriptingGlobals.add(function changeView(view: string) { +Scripting.addGlobal(function changeView(view: string) { const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; selected ? selected.Document._viewType = view : console.log("[FontIconBox.tsx] changeView failed"); }); // toggle: Set overlay status of selected document -ScriptingGlobals.add(function toggleOverlay() { +Scripting.addGlobal(function toggleOverlay() { const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("failed"); });
\ No newline at end of file diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index ca13590de..bd103dcf7 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -5,30 +5,31 @@ import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { ColorState, SketchPicker } from 'react-color'; -import { Doc, StrListCast } from '../../../../fields/Doc'; +import { Doc, StrListCast, WidthSym, HeightSym } from '../../../../fields/Doc'; import { InkTool } from '../../../../fields/InkField'; -import { createSchema } from '../../../../fields/Schema'; +import { createSchema, makeInterface } from '../../../../fields/Schema'; import { ScriptField } from '../../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { BoolCast, Cast, NumCast, StrCast } from '../../../../fields/Types'; import { WebField } from '../../../../fields/URLField'; -import { Utils } from '../../../../Utils'; import { DocumentType } from '../../../documents/DocumentTypes'; -import { ScriptingGlobals } from "../../../util/ScriptingGlobals"; +import { Scripting } from "../../../util/Scripting"; import { SelectionManager } from '../../../util/SelectionManager'; -import { undoBatch, UndoManager } from '../../../util/UndoManager'; +import { UndoManager, undoBatch } from '../../../util/UndoManager'; import { CollectionViewType } from '../../collections/CollectionView'; import { ContextMenu } from '../../ContextMenu'; import { DocComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; import { GestureOverlay } from '../../GestureOverlay'; import { Colors } from '../../global/globalEnums'; -import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke'; +import { SetActiveInkColor, ActiveFillColor, SetActiveFillColor, ActiveInkWidth, ActiveInkColor, SetActiveInkWidth } from '../../InkingStroke'; import { StyleProp } from '../../StyleProvider'; import { FieldView, FieldViewProps } from '.././FieldView'; import { RichTextMenu } from '../formattedText/RichTextMenu'; -import { WebBox } from '../WebBox'; +import { Utils } from '../../../../Utils'; +import { IButtonProps } from './ButtonInterface'; import { FontIconBadge } from './FontIconBadge'; import './FontIconBox.scss'; +import { WebBox } from '../WebBox'; const FontIconSchema = createSchema({ icon: "string", }); @@ -56,8 +57,11 @@ export enum NumButtonType { export interface ButtonProps extends FieldViewProps { type?: ButtonType; } + +type FontIconDocument = makeInterface<[typeof FontIconSchema]>; +const FontIconDocument = makeInterface(FontIconSchema); @observer -export class FontIconBox extends DocComponent<ButtonProps>() { +export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(FontIconDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); } showTemplate = (): void => { const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null); @@ -101,11 +105,16 @@ export class FontIconBox extends DocComponent<ButtonProps>() { */ @computed get numberButton() { const numBtnType: string = StrCast(this.rootDoc.numBtnType); - const numScript = ScriptCast(this.rootDoc.script); - const setValue = (value: number) => numScript?.script.run({ value, _readOnly_: false }); + const setValue = (value: number) => { + // Script for running the toggle + const script: string = StrCast(this.rootDoc.script) + "(" + value + ")"; + ScriptField.MakeScript(script)?.script.run(); + }; // Script for checking the outcome of the toggle - const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0; + const checkScript: string = StrCast(this.rootDoc.script) + "(0, true)"; + const checkResult: number = ScriptField.MakeScript(checkScript)?.script.run().result || 0; + if (numBtnType === NumButtonType.Slider) { const dropdown = @@ -228,37 +237,34 @@ export class FontIconBox extends DocComponent<ButtonProps>() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const script = ScriptCast(this.rootDoc.script); + const script: string = StrCast(this.rootDoc.script); let noviceList: string[] = []; let text: string | undefined; let dropdown = true; let icon: IconProp = "caret-down"; - try { - if (script.script.originalScript.startsWith('setView')) { - const selected = SelectionManager.Docs().lastElement(); - if (selected) { - if (StrCast(selected.type) === DocumentType.COL) { - text = StrCast(selected._viewType); - } else { - dropdown = false; - text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type); - icon = Doc.toIcon(selected); - } + + if (script === 'setView') { + const selected = SelectionManager.Docs().lastElement(); + if (selected) { + if (StrCast(selected.type) === DocumentType.COL) { + text = StrCast(selected._viewType); } else { dropdown = false; - icon = "globe-asia"; - text = "User Default"; + text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type); + icon = Doc.toIcon(selected); } - noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking]; - } else if (script.script.originalScript.startsWith('setFont')) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); - noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia", - "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]; + } else { + dropdown = false; + icon = "globe-asia"; + text = "User Default"; } - } catch (e) { - console.log(e); + noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking]; + } else if (script === 'setFont') { + const editorView = RichTextMenu.Instance?.TextView?.EditorView; + text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); + noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia", + "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]; } // Get items to place into the list @@ -266,12 +272,18 @@ export class FontIconBox extends DocComponent<ButtonProps>() { if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) { return; } + const click = () => { + const s = ScriptField.MakeScript(script + '("' + value + '")'); + if (s) { + s.script.run().result; + } + }; return <div className="list-item" key={`${value}`} style={{ - fontFamily: script.script.originalScript.startsWith('setFont') ? value : undefined, + fontFamily: script === 'setFont' ? value : undefined, backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined }} - onClick={() => script.script.run({ value }).result}> + onClick={click}> {value[0].toUpperCase() + value.slice(1)} </div>; }); @@ -307,12 +319,15 @@ export class FontIconBox extends DocComponent<ButtonProps>() { } @observable colorPickerClosed: boolean = true; - @computed get colorScript() { return ScriptCast(this.rootDoc.script); } + @computed get colorScript() { + const script = StrCast(this.rootDoc.script); + return ScriptField.MakeScript(script + '(colValue, checkResult)', { colValue: "string", checkResult: "boolean" }); + } colorPicker = (curColor: string) => { const change = (value: ColorState) => { const s = this.colorScript; - s && undoBatch(() => s.script.run({ value: Utils.colorString(value), _readOnly_: false }).result)(); + s && undoBatch(() => s.script.run({ colValue: Utils.colorString(value), checkResult: false }).result)(); }; const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', @@ -328,7 +343,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { @computed get colorButton() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent"; + const curColor = this.colorScript?.script.run({ colValue: undefined, checkResult: true }).result ?? "transparent"; const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}> @@ -424,34 +439,52 @@ export class FontIconBox extends DocComponent<ButtonProps>() { @computed get editableText() { // Script for running the toggle - const script = ScriptCast(this.rootDoc.script); + const script: string = StrCast(this.rootDoc.script); + + // Script for checking the outcome of the toggle + const checkScript: string = StrCast(this.rootDoc.script) + "('', true)"; + // Function to run the script - const checkResult = script?.script.run({ value: "", _readOnly_: true }).result; + const checkResult = ScriptField.MakeScript(checkScript)?.script.run().result; - const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result; + const setValue = (value: string, shiftDown?: boolean): boolean => { + ScriptField.MakeScript(script + "('" + value + "')")?.script.run(); + return true; + }; return ( <div className="menuButton editableText"> <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} /> <div style={{ width: "calc(100% - .875em)", paddingLeft: "4px" }}> - <EditableView GetValue={() => script?.script.run({ value: "", _readOnly_: true }).result} SetValue={setValue} contents={checkResult} /> + <EditableView GetValue={() => checkResult} SetValue={setValue} contents={checkResult} /> </div> </div> ); } + render() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : - <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}> + <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}> {this.label} </div>; const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) : - <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}> + <div className="fontIconBox-label" style={{ color: color, backgroundColor: "transparent" }}> {this.label} </div>; + const buttonProps: IButtonProps = { + type: this.type, + rootDoc: this.rootDoc, + label: label, + backgroundColor: backgroundColor, + icon: this.icon, + color: color + }; + const buttonText = StrCast(this.rootDoc.buttonText); // TODO:glr Add label of button type @@ -460,7 +493,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { switch (this.type) { case ButtonType.TextButton: button = ( - <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}> + <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}> <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> {buttonText ? <div className="button-text"> @@ -489,7 +522,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { break; case ButtonType.ToolButton: button = ( - <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor, color }}> + <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor: backgroundColor, color: color }}> <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> {label} </div> @@ -501,7 +534,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { break; case ButtonType.ClickButton: button = ( - <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1 }}> + <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1 }}> <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> {label} </div> @@ -511,7 +544,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { const trailsIcon = <img src={`/assets/${"presTrails.png"}`} style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})` }} />; button = ( - <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}> + <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}> {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />} {menuLabel} <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} /> @@ -531,14 +564,14 @@ export class FontIconBox extends DocComponent<ButtonProps>() { // toggle: Set overlay status of selected document -ScriptingGlobals.add(function setView(view: string) { +Scripting.addGlobal(function setView(view: string) { const selected = SelectionManager.Docs().lastElement(); selected ? selected._viewType = view : console.log("[FontIconBox.tsx] changeView failed"); }); // toggle: Set overlay status of selected document -ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) { +Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); if (checkResult) { return selected?._backgroundColor ?? "transparent"; @@ -547,7 +580,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b }); // toggle: Set overlay status of selected document -ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) { +Scripting.addGlobal(function setHeaderColor(color?: string, checkResult?: boolean) { if (checkResult) { return Doc.SharingDoc().userColor; } @@ -557,7 +590,7 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole }); // toggle: Set overlay status of selected document -ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { +Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) { const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; if (checkResult && selected) { if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE; @@ -579,7 +612,7 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { **/ // toggle: Set overlay status of selected document -ScriptingGlobals.add(function setFont(font: string, checkResult?: boolean) { +Scripting.addGlobal(function setFont(font: string, checkResult?: boolean) { SelectionManager.Docs().map(doc => doc._fontFamily = font); const editorView = RichTextMenu.Instance.TextView?.EditorView; if (checkResult) { @@ -589,7 +622,7 @@ ScriptingGlobals.add(function setFont(font: string, checkResult?: boolean) { else Doc.UserDoc().fontFamily = font; }); -ScriptingGlobals.add(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") { +Scripting.addGlobal(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") { const editorView = RichTextMenu.Instance.TextView?.EditorView; const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection(); switch (info) { @@ -600,7 +633,7 @@ ScriptingGlobals.add(function getActiveTextInfo(info: "family" | "size" | "color } }); -ScriptingGlobals.add(function setAlignment(align: "left" | "right" | "center", checkResult?: boolean) { +Scripting.addGlobal(function setAlignment(align: "left" | "right" | "center", checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { return (editorView ? RichTextMenu.Instance.textAlign : Doc.UserDoc().textAlign) === align ? Colors.MEDIUM_BLUE : "transparent"; @@ -609,7 +642,7 @@ ScriptingGlobals.add(function setAlignment(align: "left" | "right" | "center", c else Doc.UserDoc().textAlign = align; }); -ScriptingGlobals.add(function setBulletList(mapStyle: "bullet" | "decimal", checkResult?: boolean) { +Scripting.addGlobal(function setBulletList(mapStyle: "bullet" | "decimal", checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle(); @@ -624,8 +657,8 @@ ScriptingGlobals.add(function setBulletList(mapStyle: "bullet" | "decimal", chec }); // toggle: Set overlay status of selected document -ScriptingGlobals.add(function setFontColor(color?: string, checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; +Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) { + const editorView = RichTextMenu.Instance.TextView?.EditorView; if (checkResult) { return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor; @@ -636,7 +669,7 @@ ScriptingGlobals.add(function setFontColor(color?: string, checkResult?: boolean }); // toggle: Set overlay status of selected document -ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boolean) { +Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); const editorView = RichTextMenu.Instance.TextView?.EditorView; @@ -653,18 +686,18 @@ ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boo }); // toggle: Set overlay status of selected document -ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; +Scripting.addGlobal(function setFontSize(size: string | number, checkResult?: boolean) { + if (typeof size === "number") size = size.toString(); + if (size && Number(size).toString() === size) size += "px"; + const editorView = RichTextMenu.Instance.TextView?.EditorView; if (checkResult) { return (editorView ? RichTextMenu.Instance.fontSize : StrCast(Doc.UserDoc().fontSize, "10px")).replace("px", ""); } - if (typeof size === "number") size = size.toString(); - if (size && Number(size).toString() === size) size += "px"; if (editorView) RichTextMenu.Instance.setFontSize(size); else Doc.UserDoc()._fontSize = size; }); -ScriptingGlobals.add(function toggleBold(checkResult?: boolean) { +Scripting.addGlobal(function toggleBold(checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { return (editorView ? RichTextMenu.Instance.bold : Doc.UserDoc().fontWeight === "bold") ? Colors.MEDIUM_BLUE : "transparent"; @@ -673,7 +706,7 @@ ScriptingGlobals.add(function toggleBold(checkResult?: boolean) { else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === "bold" ? undefined : "bold"; }); -ScriptingGlobals.add(function toggleUnderline(checkResult?: boolean) { +Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { return (editorView ? RichTextMenu.Instance.underline : Doc.UserDoc().textDecoration === "underline") ? Colors.MEDIUM_BLUE : "transparent"; @@ -682,7 +715,7 @@ ScriptingGlobals.add(function toggleUnderline(checkResult?: boolean) { else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === "underline" ? undefined : "underline"; }); -ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) { +Scripting.addGlobal(function toggleItalic(checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { return (editorView ? RichTextMenu.Instance.italics : Doc.UserDoc().fontStyle === "italics") ? Colors.MEDIUM_BLUE : "transparent"; @@ -700,7 +733,7 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) { * setStrokeColor **/ -ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) { +Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolean) { if (checkResult) { return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ? Colors.MEDIUM_BLUE : "transparent"; @@ -726,7 +759,7 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole }); // toggle: Set overlay status of selected document -ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean) { +Scripting.addGlobal(function setFillColor(color?: string, checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); if (checkResult) { if (selected?.type === DocumentType.INK) { @@ -738,7 +771,7 @@ ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.fillColor = color); }); -ScriptingGlobals.add(function setStrokeWidth(width: number, checkResult?: boolean) { +Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean) { if (checkResult) { const selected = SelectionManager.Docs().lastElement(); if (selected?.type === DocumentType.INK) { @@ -751,7 +784,7 @@ ScriptingGlobals.add(function setStrokeWidth(width: number, checkResult?: boolea }); // toggle: Set overlay status of selected document -ScriptingGlobals.add(function setStrokeColor(color?: string, checkResult?: boolean) { +Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolean) { if (checkResult) { const selected = SelectionManager.Docs().lastElement(); if (selected?.type === DocumentType.INK) { @@ -767,7 +800,7 @@ ScriptingGlobals.add(function setStrokeColor(color?: string, checkResult?: boole /** WEB * webSetURL **/ -ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) { +Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) { const selected = SelectionManager.Views().lastElement(); if (selected?.rootDoc.type === DocumentType.WEB) { if (checkResult) { @@ -777,26 +810,24 @@ ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) { //selected.rootDoc.data = new WebField(url); } }); -ScriptingGlobals.add(function webForward(checkResult?: boolean) { - const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox); - if (checkResult) { - return selected?.forward(checkResult) ? undefined : "lightGray"; +Scripting.addGlobal(function webForward() { + const selected = SelectionManager.Views().lastElement(); + if (selected?.rootDoc.type === DocumentType.WEB) { + (selected.ComponentView as WebBox).forward(); } - selected?.forward(); }); -ScriptingGlobals.add(function webBack(checkResult?: boolean) { - const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox); - if (checkResult) { - return selected?.back(checkResult) ? undefined : "lightGray"; +Scripting.addGlobal(function webBack() { + const selected = SelectionManager.Views().lastElement(); + if (selected?.rootDoc.type === DocumentType.WEB) { + (selected.ComponentView as WebBox).back(); } - selected?.back(); }); /** Schema * toggleSchemaPreview **/ -ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) { +Scripting.addGlobal(function toggleSchemaPreview(checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); if (checkResult && selected) { const result: boolean = NumCast(selected.schemaPreviewWidth) > 0; @@ -815,7 +846,7 @@ ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) { /** STACK * groupBy */ -ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) { +Scripting.addGlobal(function setGroupBy(key: string, checkResult?: boolean) { SelectionManager.Docs().map(doc => doc._fontFamily = key); const editorView = RichTextMenu.Instance.TextView?.EditorView; if (checkResult) { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 6192b6829..311d52afa 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -12,11 +12,13 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from " import { EditorView } from "prosemirror-view"; import { DateField } from '../../../../fields/DateField'; import { AclAdmin, AclEdit, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym, AclAugment } from "../../../../fields/Doc"; +import { documentSchema } from '../../../../fields/documentSchemas'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { PrefetchProxy } from '../../../../fields/Proxy'; import { RichTextField } from "../../../../fields/RichTextField"; import { RichTextUtils } from '../../../../fields/RichTextUtils'; +import { makeInterface } from "../../../../fields/Schema"; import { Cast, DateCast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types"; import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils'; @@ -75,10 +77,13 @@ export interface FormattedTextBoxProps { } export const GoogleRef = "googleDocId"; +type RichTextDocument = makeInterface<[typeof documentSchema]>; +const RichTextDocument = makeInterface(documentSchema); + type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void; @observer -export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps)>() { +export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); } public static blankState = () => EditorState.create(FormattedTextBox.Instance.config); public static Instance: FormattedTextBox; @@ -640,7 +645,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } else if (!this.rootDoc.isTemplateDoc) { const title = StrCast(this.rootDoc.title); this.rootDoc.title = "text"; - this.rootDoc.layout = this.layoutDoc.layout as string; + this.rootDoc.layout = (this.layoutDoc as Doc).layout as string; this.rootDoc.title = this.layoutDoc.isTemplateForField as string; this.rootDoc.isTemplateDoc = false; this.rootDoc.isTemplateForField = ""; @@ -1125,13 +1130,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }, dispatchTransaction: this.dispatchTransaction, nodeViews: { - dashComment(node: any, view: any, getPos: any) { return new DashDocCommentView(node, view, getPos); }, - dashDoc(node: any, view: any, getPos: any) { return new DashDocView(node, view, getPos, self); }, - dashField(node: any, view: any, getPos: any) { return new DashFieldView(node, view, getPos, self); }, - equation(node: any, view: any, getPos: any) { return new EquationView(node, view, getPos, self); }, - summary(node: any, view: any, getPos: any) { return new SummaryView(node, view, getPos); }, - ordered_list(node: any, view: any, getPos: any) { return new OrderedListView(); }, - footnote(node: any, view: any, getPos: any) { return new FootnoteView(node, view, getPos); } + dashComment(node, view, getPos) { return new DashDocCommentView(node, view, getPos); }, + dashDoc(node, view, getPos) { return new DashDocView(node, view, getPos, self); }, + dashField(node, view, getPos) { return new DashFieldView(node, view, getPos, self); }, + equation(node, view, getPos) { return new EquationView(node, view, getPos, self); }, + summary(node, view, getPos) { return new SummaryView(node, view, getPos); }, + ordered_list(node, view, getPos) { return new OrderedListView(); }, + footnote(node, view, getPos) { return new FootnoteView(node, view, getPos); } }, clipboardTextSerializer: this.clipboardTextSerializer, handlePaste: this.handlePaste, @@ -1418,7 +1423,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.dataDoc[this.fieldKey + "-translation"] = result1 + "\r\n\r\n" + result[0]; }), 1000); }); - } catch (e: any) { console.log(e.message); } + } catch (e) { console.log(e.message); } this._lastText = curText; } } diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index c76eda859..76a5675de 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -106,7 +106,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey bind("Mod-U", toggleEditableMark(schema.marks.underline)); //Commands for lists - bind("Ctrl-i", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state as any, dispatch as any)); + bind("Ctrl-i", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state, dispatch as any)); bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => { /// bcz; Argh!! replace layotuTEmpalteString with a onTab prop conditionally handles Tab); @@ -125,7 +125,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey dispatch(tx3); })) { // couldn't sink into an existing list, so wrap in a new one const newstate = state.applyTransaction(state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end))); - if (!wrapInList(schema.nodes.ordered_list)(newstate.state as any, (tx2: Transaction) => { + if (!wrapInList(schema.nodes.ordered_list)(newstate.state, (tx2: Transaction) => { const tx3 = updateBullets(tx2, schema); // when promoting to a list, assume list will format things so don't copy the stored marks. marks && tx3.ensureMarks([...marks]); @@ -160,14 +160,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey }); //Commands to modify BlockType - bind("Ctrl->", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit((state) && wrapIn(schema.nodes.blockquote)(state as any, dispatch as any))); - bind("Alt-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state as any, dispatch as any)); - bind("Shift-Ctrl-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state as any, dispatch as any)); + bind("Ctrl->", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit((state) && wrapIn(schema.nodes.blockquote)(state, dispatch as any))); + bind("Alt-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state, dispatch as any)); + bind("Shift-Ctrl-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state, dispatch as any)); bind("Ctrl-m", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: "math" + Utils.GenerateGuid() })))); for (let i = 1; i <= 6; i++) { - bind("Shift-Ctrl-" + i, (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state as any, dispatch as any)); + bind("Shift-Ctrl-" + i, (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state, dispatch as any)); } //Command to create a horizontal break line @@ -197,13 +197,13 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey bind("Backspace", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => { if (!canEdit(state)) return true; - if (!deleteSelection(state, (tx: Transaction<S>) => { + if (!deleteSelection(state, (tx: Transaction<Schema<any, any>>) => { dispatch(updateBullets(tx, schema)); })) { - if (!joinBackward(state, (tx: Transaction<S>) => { + if (!joinBackward(state, (tx: Transaction<Schema<any, any>>) => { dispatch(updateBullets(tx, schema)); })) { - if (!selectNodeBackward(state, (tx: Transaction<S>) => { + if (!selectNodeBackward(state, (tx: Transaction<Schema<any, any>>) => { dispatch(updateBullets(tx, schema)); })) { return false; @@ -225,14 +225,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey const depth = trange ? liftTarget(trange) : undefined; const split = path.length > 5 && !path[path.length - 3].textContent && path[path.length - 6].type !== schema.nodes.list_item; if (split && trange && depth !== undefined && depth !== null) { - dispatch(state.tr.lift(trange, depth) as any); + dispatch(state.tr.lift(trange, depth)); return true; } const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); const cr = state.selection.$from.node().textContent.endsWith("\n"); - if (cr || !newlineInCode(state, dispatch as any)) { - if (!splitListItem(schema.nodes.list_item)(state as any, (tx2: Transaction) => { + if (cr || !newlineInCode(state, dispatch)) { + if (!splitListItem(schema.nodes.list_item)(state, (tx2: Transaction) => { const tx3 = updateBullets(tx2, schema); marks && tx3.ensureMarks([...marks]); marks && tx3.setStoredMarks([...marks]); diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index bafae84dc..711136469 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -34,9 +34,13 @@ export class RichTextRules { wrappingInputRule( /^1\.\s$/, schema.nodes.ordered_list, - () => ({ mapStyle: "decimal", bulletStyle: 1 }), - (match: any, node: any) => node.childCount + node.attrs.order === +match[1], - ((type: any) => ({ type: type, attrs: { mapStyle: "decimal", bulletStyle: 1 } })) as any + () => { + return ({ mapStyle: "decimal", bulletStyle: 1 }); + }, + (match: any, node: any) => { + return node.childCount + node.attrs.order === +match[1]; + }, + (type: any) => ({ type: type, attrs: { mapStyle: "decimal", bulletStyle: 1 } }) ), // A. create alphabetical ordered list @@ -51,16 +55,20 @@ export class RichTextRules { (match: any, node: any) => { return node.childCount + node.attrs.order === +match[1]; }, - ((type: any) => ({ type: type, attrs: { mapStyle: "multi", bulletStyle: 1 } })) as any + (type: any) => ({ type: type, attrs: { mapStyle: "multi", bulletStyle: 1 } }) ), // * + - create bullet list wrappingInputRule(/^\s*([-+*])\s$/, schema.nodes.ordered_list, // match => { - () => ({ mapStyle: "bullet" }), // ({ order: +match[1] }) - (match: any, node: any) => node.childCount + node.attrs.order === +match[1], - ((type: any) => ({ type: type, attrs: { mapStyle: "bullet" } })) as any - ), + () => { + return ({ mapStyle: "bullet" }); + // return ({ order: +match[1] }) + }, + (match: any, node: any) => { + return node.childCount + node.attrs.order === +match[1]; + }, + (type: any) => ({ type: type, attrs: { mapStyle: "bullet" } })), // ``` create code block textblockTypeInputRule(/^```$/, schema.nodes.code_block), @@ -213,7 +221,7 @@ export class RichTextRules { tr.deleteRange(start, end).replaceSelectionWith(newNode); // replace insertion with a footnote. return tr.setSelection(new NodeSelection( // select the footnote node to open its display tr.doc.resolve( // get the location of the footnote node by subtracting the nodesize of the footnote from the current insertion point anchor (which will be immediately after the footnote node) - tr.selection.anchor - (tr.selection.$anchor.nodeBefore?.nodeSize || 0)))); + tr.selection.anchor - tr.selection.$anchor.nodeBefore!.nodeSize))); }), // activate a style by name using prefix '%<color name>' diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 2e312ee51..2370b7e16 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -6,10 +6,11 @@ import { observer } from "mobx-react"; import { ColorState, SketchPicker } from "react-color"; import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal'; import { Doc, DocListCast, DocListCastAsync, FieldResult } from "../../../../fields/Doc"; +import { documentSchema } from "../../../../fields/documentSchemas"; import { InkTool } from "../../../../fields/InkField"; import { List } from "../../../../fields/List"; import { PrefetchProxy } from "../../../../fields/Proxy"; -import { listSpec } from "../../../../fields/Schema"; +import { listSpec, makeInterface } from "../../../../fields/Schema"; import { ScriptField } from "../../../../fields/ScriptField"; import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types"; import { emptyFunction, returnFalse, returnOne, returnTrue } from '../../../../Utils'; @@ -17,7 +18,7 @@ import { Docs } from "../../../documents/Documents"; import { DocumentType } from "../../../documents/DocumentTypes"; import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; import { DocumentManager } from "../../../util/DocumentManager"; -import { ScriptingGlobals } from "../../../util/ScriptingGlobals"; +import { Scripting } from "../../../util/Scripting"; import { SelectionManager } from "../../../util/SelectionManager"; import { undoBatch, UndoManager } from "../../../util/UndoManager"; import { CollectionDockingView } from "../../collections/CollectionDockingView"; @@ -38,8 +39,11 @@ export class PinProps { hidePresBox?: boolean; } +type PresBoxSchema = makeInterface<[typeof documentSchema]>; +const PresBoxDocument = makeInterface(documentSchema); + @observer -export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { +export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); } /** @@ -2457,7 +2461,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </div>; } } -ScriptingGlobals.add(function lookupPresBoxField(container: Doc, field: string, data: Doc) { +Scripting.addGlobal(function lookupPresBoxField(container: Doc, field: string, data: Doc) { if (field === 'indexInPres') return DocListCast(container[StrCast(container.presentationFieldKey)]).indexOf(data); if (field === 'presCollapsedHeight') return container._viewType === CollectionViewType.Stacking ? 35 : 31; if (field === 'presStatus') return container.presStatus; diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index a4ec559f5..238d025dc 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -3,10 +3,11 @@ import { Tooltip } from "@material-ui/core"; import { action, computed, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; import { DataSym, Doc, Opt } from "../../../../fields/Doc"; +import { documentSchema } from '../../../../fields/documentSchemas'; import { Id } from "../../../../fields/FieldSymbols"; +import { createSchema, makeInterface } from '../../../../fields/Schema'; import { Cast, NumCast, StrCast } from "../../../../fields/Types"; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils"; -import { DocUtils } from "../../../documents/Documents"; +import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, emptyPath, returnEmptyDoclist } from "../../../../Utils"; import { DocumentType } from "../../../documents/DocumentTypes"; import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; import { DocumentManager } from "../../../util/DocumentManager"; @@ -15,20 +16,36 @@ import { Transform } from "../../../util/Transform"; import { undoBatch } from "../../../util/UndoManager"; import { ViewBoxBaseComponent } from '../../DocComponent'; import { EditableView } from "../../EditableView"; -import { Colors } from "../../global/globalEnums"; import { DocumentView, DocumentViewProps } from "../../nodes/DocumentView"; import { FieldView, FieldViewProps } from '../../nodes/FieldView'; -import { StyleProp } from "../../StyleProvider"; import { PresBox } from "./PresBox"; +import { Colors } from "../../global/globalEnums"; +import { StyleProp } from "../../StyleProvider"; import "./PresElementBox.scss"; -import { PresMovement } from "./PresEnums"; import React = require("react"); +import { DocUtils } from "../../../documents/Documents"; +import { PresMovement } from "./PresEnums"; + +export const presSchema = createSchema({ + presentationTargetDoc: Doc, + presBox: Doc, + presZoomButton: "boolean", + presNavButton: "boolean", + presHideTillShownButton: "boolean", + presFadeButton: "boolean", + presHideAfterButton: "boolean", + presGroupButton: "boolean", + presExpandInlineButton: "boolean" +}); + +type PresDocument = makeInterface<[typeof presSchema, typeof documentSchema]>; +const PresDocument = makeInterface(presSchema, documentSchema); /** * This class models the view a document added to presentation will have in the presentation. * It involves some functionality for its buttons and options. */ @observer -export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { +export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDocument>(PresDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); } _heightDisposer: IReactionDisposer | undefined; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 1856c5353..3f7f38bdf 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -26,6 +26,7 @@ import { StyleProp } from "../StyleProvider"; import { AnchorMenu } from "./AnchorMenu"; import { Annotation } from "./Annotation"; import "./PDFViewer.scss"; +const pdfjs = require('pdfjs-dist/es5/build/pdf.js'); import React = require("react"); const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const pdfjsLib = require("pdfjs-dist"); @@ -33,7 +34,7 @@ const _global = (window /* browser */ || global /* node */) as any; //pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`; // The workerSrc property shall be specified. -pdfjsLib.GlobalWorkerOptions.workerSrc = "https://unpkg.com/pdfjs-dist@2.13.216/build/pdf.worker.js"; +pdfjsLib.GlobalWorkerOptions.workerSrc = "https://unpkg.com/pdfjs-dist@2.4.456/build/pdf.worker.min.js"; interface IViewerProps extends FieldViewProps { Document: Doc; @@ -158,7 +159,7 @@ export class PDFViewer extends React.Component<IViewerProps> { initialLoad = async () => { if (this._pageSizes.length === 0) { this._pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages); - await Promise.all(this._pageSizes.map((val, i) => + await Promise.all(this._pageSizes.map<Pdfjs.PDFPromise<any>>((val, i) => this.props.pdf.getPage(i + 1).then(action((page: Pdfjs.PDFPageProxy) => { const page0or180 = page.rotate === 0 || page.rotate === 180; this._pageSizes.splice(i, 1, { @@ -218,7 +219,7 @@ export class PDFViewer extends React.Component<IViewerProps> { } pagesinit = () => { - if (this._pdfViewer._setDocumentViewerElement?.offsetParent) { + if (this._pdfViewer._setDocumentViewerElement.offsetParent) { runInAction(() => this._pdfViewer.currentScaleValue = this._zoomed = 1); this.gotoPage(NumCast(this.props.Document._curPage, 1)); } @@ -554,7 +555,7 @@ export class PDFViewer extends React.Component<IViewerProps> { </div>; } @computed get pdfViewerDiv() { - return <div className={"pdfViewerDash-text" + (this.props.pointerEvents !== "none" && this._textSelecting && this.props.isContentActive() ? "-selected" : "")} ref={this._viewer} />; + return <div className={"pdfViewerDash-text" + (this.props.pointerEvents !== "none" && this._textSelecting && (this.props.isSelected() || this.props.isContentActive()) ? "-selected" : "")} ref={this._viewer} />; } @computed get contentScaling() { return this.props.ContentScaling?.() || 1; } @computed get standinViews() { diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 5fe2a5ab1..09cfb2077 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -1,18 +1,28 @@ -import { Tooltip } from "@material-ui/core"; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field } from '../../../fields/Doc'; +import { documentSchema } from "../../../fields/documentSchemas"; import { Id } from '../../../fields/FieldSymbols'; +import { createSchema, makeInterface } from '../../../fields/Schema'; import { StrCast } from '../../../fields/Types'; -import { DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; -import { DocumentManager } from '../../util/DocumentManager'; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { ViewBoxBaseComponent } from "../DocComponent"; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import "./SearchBox.scss"; +import { DocumentManager } from '../../util/DocumentManager'; +import { DocUtils } from '../../documents/Documents'; +import { Tooltip } from "@material-ui/core"; +import { DictationOverlay } from '../DictationOverlay'; +import { CollectionSchemaBooleanCell } from '../collections/collectionSchema/CollectionSchemaCells'; + +export const searchSchema = createSchema({ + Document: Doc +}); +type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>; +const SearchBoxDocument = makeInterface(documentSchema, searchSchema); const DAMPENING_FACTOR = 0.9; const MAX_ITERATIONS = 25; @@ -28,7 +38,7 @@ export interface SearchBoxProps extends FieldViewProps { * the search panel on the left side of the screen. */ @observer -export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { +export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDocument>(SearchBoxDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); } public static Instance: SearchBox; diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx index e0d328c89..505a8da7e 100644 --- a/src/client/views/webcam/DashWebRTCVideo.tsx +++ b/src/client/views/webcam/DashWebRTCVideo.tsx @@ -12,7 +12,6 @@ import "./DashWebRTCVideo.scss"; import { hangup, initialize, refreshVideos } from "./WebCamLogic"; import React = require("react"); import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -import { IconLookup } from "@fortawesome/fontawesome-svg-core"; /** @@ -65,8 +64,8 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV }}></video> </div> <div className="buttonContainer"> - <div className="videoButtons" style={{ background: "red" }} onClick={this.onClickHangUp}><FontAwesomeIcon icon={faPhoneSlash as IconLookup} color="white" /></div> - <div className="videoButtons" style={{ background: "green" }} onClick={this.onClickRefresh}><FontAwesomeIcon icon={faSync as IconLookup} color="white" /></div> + <div className="videoButtons" style={{ background: "red" }} onClick={this.onClickHangUp}><FontAwesomeIcon icon={faPhoneSlash} color="white" /></div> + <div className="videoButtons" style={{ background: "green" }} onClick={this.onClickRefresh}><FontAwesomeIcon icon={faSync} color="white" /></div> </div> </div >; |