diff options
31 files changed, 356 insertions, 334 deletions
diff --git a/solr-8.3.1/server/solr/dash/conf/schema.xml b/solr-8.3.1/server/solr/dash/conf/schema.xml index 36e803d83..47aa56787 100644 --- a/solr-8.3.1/server/solr/dash/conf/schema.xml +++ b/solr-8.3.1/server/solr/dash/conf/schema.xml @@ -1,7 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <schema name="default-config" version="1.6"> <uniqueKey>id</uniqueKey> - <fieldType name="string" class="solr.StrField" sortMissingLast="true" docValues="true"/> + <fieldType name="string" class="solr.StrField" sortMissingLast="true" docValues="true"> + <tokenizer class="solr.KeywordTokenizerFactory"/> + </fieldType> <fieldType name="text" class="solr.TextField"> <analyzer type="index"> <tokenizer class="solr.StandardTokenizerFactory"/> @@ -41,23 +43,25 @@ <fieldType name="pdaterange" class="solr.DateRangeField"/> <fieldType name="pdouble" class="solr.DoublePointField"/> + <field name="string" type="string" indexed="true" stored="true" uninvertible="true"/> <field name="text" type="text" indexed="true" stored="false" uninvertible="false" multiValued="true"/> <field name="id" type="string" indexed="true" stored="true" uninvertible="false" required="true"/> <field name="_version_" type="plong" indexed="true" stored="true"/> <field name="proto" type="string" indexed="true" stored="true" uninvertible="false" /> <field name="_height" type="pdouble" indexed="true" stored="true" uninvertible="false" docValues="true"/> - + <dynamicField name="*_s" type="string" indexed="true" stored="true" uninvertible="false" docValues="true"/> <dynamicField name="*_t" type="text" indexed="true" stored="true" uninvertible="false" docValues="false"/> <dynamicField name="*_n" type="pdouble" indexed="true" stored="true" uninvertible="false" docValues="false"/> - <dynamicField name="*_d" type="pdaterange" indexed="true" stored="true" uninvertible="false" docValues="false"/> + <dynamicField name="*_d" type="pdate" indexed="true" stored="true" uninvertible="false" docValues="true"/> <dynamicField name="*_l" type="string" indexed="true" stored="true" uninvertible="false" docValues="false" multiValued="true"/> <dynamicField name="*_i" type="string" indexed="true" stored="true" uninvertible="false" docValues="false"/> <dynamicField name="*_b" type="boolean" indexed="true" stored="true" uninvertible="false" docValues="false"/> - <dynamicField name="*_a" type="text" indexed="true" stored="false" uninvertible="false" docValues="false" multiValued="true"/> - <copyField source="*_t" dest="text"/> + <copyField source="*_t" dest="*_s"/> + + <!-- <field name="_height" type="pdouble" indexed="true" stored="true" uninvertible="false" docValues="true"/> --> <copyField source="*_t" dest="*_a"/> <copyField source="*_n" dest="*_a"/> diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5e83645ec..42ba4d2c4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -112,6 +112,7 @@ export interface DocumentOptions { _columnsHideIfEmpty?: boolean; // whether stacking view column headings should be hidden isTemplateForField?: string; // the field key for which the containing document is a rendering template isTemplateDoc?: boolean; + watchedDocuments?: Doc; // list of documents to "watch" in an icon doc to display a badge targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc) templates?: List<string>; hero?: ImageField; // primary image that best represents a compound document (e.g., for a buxton device document that has multiple images) @@ -184,7 +185,7 @@ export interface DocumentOptions { targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script searchFileTypes?: List<string>; // file types allowed in a search query strokeWidth?: number; - stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere + _stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere treeViewPreventOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expand/collapse state to be independent of other views of the same document in the tree view treeViewHideTitle?: boolean; // whether to hide the title of a tree view treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items. diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 95aebbe4a..2d988d322 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -23,6 +23,7 @@ import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; import { LabelBox } from "../views/nodes/LabelBox"; import { LinkManager } from "./LinkManager"; +import { Id } from "../../fields/FieldSymbols"; export class CurrentUserUtils { private static curr_id: string; @@ -477,7 +478,7 @@ export class CurrentUserUtils { title, toolTip, ignoreClick, - dropAction: "copy", + dropAction: "alias", onDragStart: drag ? ScriptField.MakeFunction(drag) : undefined, onClick: click ? ScriptField.MakeScript(click) : undefined, ischecked: ischecked ? ComputedField.MakeFunction(ischecked) : undefined, @@ -502,19 +503,20 @@ export class CurrentUserUtils { return doc.myItemCreators as Doc; } - static menuBtnDescriptions(): { - title: string, icon: string, click: string, + static menuBtnDescriptions(doc: Doc): { + title: string, icon: string, click: string, watchedDocuments?: Doc }[] { + this.setupSharingSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing return [ - { title: "Sharing", icon: "users", click: 'scriptContext.selectMenu(self, "Sharing")' }, - { title: "Workspace", icon: "desktop", click: 'scriptContext.selectMenu(self, "Workspace")' }, - { title: "Catalog", icon: "file", click: 'scriptContext.selectMenu(self, "Catalog")' }, - { title: "Archive", icon: "archive", click: 'scriptContext.selectMenu(self, "Archive")' }, - { title: "Import", icon: "upload", click: 'scriptContext.selectMenu(self, "Import")' }, - { title: "Tools", icon: "wrench", click: 'scriptContext.selectMenu(self, "Tools")' }, - { title: "Help", icon: "question-circle", click: 'scriptContext.selectMenu(self, "Help")' }, - { title: "Settings", icon: "cog", click: 'scriptContext.selectMenu(self, "Settings")' }, - { title: "User Doc", icon: "address-card", click: 'scriptContext.selectMenu(self, "UserDoc")' }, + { title: "Sharing", icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc["sidebar-sharing"] as Doc }, + { title: "Workspace", icon: "desktop", click: 'selectMainMenu(self)' }, + { title: "Catalog", icon: "file", click: 'selectMainMenu(self)' }, + { title: "Archive", icon: "archive", click: 'selectMainMenu(self)' }, + { title: "Import", icon: "upload", click: 'selectMainMenu(self)' }, + { title: "Tools", icon: "wrench", click: 'selectMainMenu(self)' }, + { title: "Help", icon: "question-circle", click: 'selectMainMenu(self)' }, + { title: "Settings", icon: "cog", click: 'selectMainMenu(self)' }, + { title: "User Doc", icon: "address-card", click: 'selectMainMenu(self)' }, ]; } @@ -528,16 +530,17 @@ export class CurrentUserUtils { } static setupMenuPanel(doc: Doc) { if (doc.menuStack === undefined) { - const menuBtns = CurrentUserUtils.menuBtnDescriptions().map(({ title, icon, click }) => + const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, icon, click, watchedDocuments }) => Docs.Create.FontIconDocument({ icon, iconShape: "square", title, _backgroundColor: "black", - stayInCollection: true, + _stayInCollection: true, childDropAction: "same", _width: 60, _height: 60, + watchedDocuments, onClick: ScriptField.MakeScript(click, { scriptContext: "any" }), system: true })); const userDoc = menuBtns[menuBtns.length - 1]; @@ -724,7 +727,7 @@ export class CurrentUserUtils { if (doc.myCatalog === undefined) { doc.myCatalog = new PrefetchProxy(Docs.Create.SchemaDocument([], [], { title: "CATALOG", _height: 1000, _fitWidth: true, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, - childDropAction: "alias", targetDropAction: "same", stayInCollection: true, treeViewOpen: true, system: true + childDropAction: "alias", targetDropAction: "same", _stayInCollection: true, treeViewOpen: true, system: true })); } @@ -744,7 +747,7 @@ export class CurrentUserUtils { doc.myRecentlyClosed === undefined; if (doc.myRecentlyClosed === undefined) { doc.myRecentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, stayInCollection: true, system: true + title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, _stayInCollection: true, system: true })); } // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready @@ -850,12 +853,12 @@ export class CurrentUserUtils { // Import sidebar is where shared documents are contained static setupImportSidebar(doc: Doc) { if (doc["sidebar-import-documents"] === undefined) { - doc["sidebar-import-documents"] = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Imported Documents", _showTitle: "title", _height: 300, _yMargin: 30, childDropAction: "alias" })); + doc["sidebar-import-documents"] = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Imported Documents", forceActive: true, _showTitle: "title", childDropAction: "alias", _autoHeight: true, _yMargin: 30, lockedPosition: true, _chromeStatus: "disabled" })); } if (doc["sidebar-import"] === undefined) { const uploads = Cast(doc["sidebar-import-documents"], Doc, null) as Doc; - const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import external document", _backgroundColor: "black", title: "Import", icon: "upload", system: true }); - doc["sidebar-import"] = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "Imported Documents", _yMargin: 30, childDropAction: "alias" })); + const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _backgroundColor: "black", title: "Import", icon: "upload", system: true }); + doc["sidebar-import"] = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "Imported Documents", _yMargin: 20, ignoreClick: true, lockedPosition: true })); } } @@ -923,6 +926,7 @@ export class CurrentUserUtils { doc.fontFamily = StrCast(doc.fontFamily, "Arial"); doc.fontColor = StrCast(doc.fontColor, "black"); doc.fontHighlight = StrCast(doc.fontHighlight, ""); + doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, true); doc.activeCollectionBackground = StrCast(doc.activeCollectionBackground, "white"); doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null); doc.noviceMode = BoolCast(doc.noviceMode, true); @@ -931,7 +935,6 @@ export class CurrentUserUtils { Utils.DRAG_THRESHOLD = NumCast(doc["constants-dragThreshold"]); this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon this.setupDocTemplates(doc); // sets up the template menu of templates - this.setupSharingSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing this.setupImportSidebar(doc); this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile this.setupMenuPanel(doc); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 4b1860b5c..0cca61841 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -210,7 +210,7 @@ export namespace DragManager { docDragData.droppedDocuments = dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) : docDragData.dropAction === "alias" ? Doc.MakeAlias(d) : - docDragData.dropAction === "copy" ? Doc.MakeDelegate(d) : d); + docDragData.dropAction === "copy" ? Doc.MakeClone(d) : d); docDragData.dropAction !== "same" && docDragData.droppedDocuments.forEach((drop: Doc, i: number) => { const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []); const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps)); diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 3073da954..a66cce6a5 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -38,7 +38,7 @@ export namespace SearchUtil { query = query || "*"; //If we just have a filter query, search for * as the query const rpquery = Utils.prepend("/dashsearch"); const replacedQuery = query.replace(/type_t:([^ )])/, (substring, arg) => `{!join from=id to=proto_i}type_t:${arg}`); - const gotten = await rp.get(rpquery, { qs: { ...options, q: replacedQuery } }); + const gotten = await rp.get(rpquery, { qs: { ...options, /* sort: "lastModified_d desc", */ q: replacedQuery } }); const result: IdSearchResult = gotten.startsWith("<") ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten); if (!returnDocs) { return result; diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss index 560786400..ec513e5d5 100644 --- a/src/client/util/SettingsManager.scss +++ b/src/client/util/SettingsManager.scss @@ -97,6 +97,8 @@ .modes-content { display: flex; + margin-left: 10px; + font-size: 12; .modes-select { // width: 170px; @@ -112,6 +114,8 @@ .modes-playground, .default-acl { display: flex; + margin-left: 10px; + font-size: 12; .playground-check, .acl-check { @@ -125,10 +129,12 @@ .playground-text { color: black; margin-right: 10px; + margin-top: 2; } .acl-text { color: black; + margin-top: 2; } } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index fdf802c6a..c5e3e6752 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -616,7 +616,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> <div className="documentDecorations-contextMenu" onPointerDown={this.onSettingsDown}> <FontAwesomeIcon size="lg" icon="cog" /> </div></Tooltip>) : canDelete ? ( - <Tooltip title={<><div className="dash-tooltip">Delete</div></>} placement="top"> + <Tooltip title={<><div className="dash-tooltip">Close</div></>} placement="top"> <div className="documentDecorations-closeButton" onClick={this.onCloseClick}> {/* Currently, this is set to be enabled if there is no ink selected. It might be interesting to think about minimizing ink if it's useful? -syip2*/} <FontAwesomeIcon className="documentdecorations-times" icon={faTimes} size="lg" /> diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 81bb542dd..76e786257 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -678,8 +678,8 @@ export default class GestureOverlay extends Touchable { var top = Math.min(...ys); const firstx = this._points[0].X; const firsty = this._points[0].Y; - const lastx = this._points[this._points.length - 2].X; - const lasty = this._points[this._points.length - 2].Y; + var lastx = this._points[this._points.length - 2].X; + var lasty = this._points[this._points.length - 2].Y; var fourth = (lastx - firstx) / 4; if (isNaN(fourth) || fourth === 0) { fourth = 0.01; } var m = (lasty - firsty) / (lastx - firstx); @@ -771,11 +771,17 @@ export default class GestureOverlay extends Touchable { break; case "line": + if (Math.abs(firstx - lastx) < 20) { + lastx = firstx; + } + if (Math.abs(firsty - lasty) < 20) { + lasty = firsty; + } this._points.push({ X: firstx, Y: firsty }); this._points.push({ X: firstx, Y: firsty }); - this._points.push({ X: firstx + 4 * fourth, Y: m * (firstx + 4 * fourth) + b }); - this._points.push({ X: firstx + 4 * fourth, Y: m * (firstx + 4 * fourth) + b }); + this._points.push({ X: lastx, Y: lasty }); + this._points.push({ X: lastx, Y: lasty }); break; case "arrow": const x1 = left; diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index e3390426b..41311ed86 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -43,7 +43,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume this.props.Document._backgroundColor = "rgba(0,0,0,0.7)"; this.props.Document.mixBlendMode = "hard-light"; this.props.Document.color = "#9b9b9bff"; - this.props.Document.stayInCollection = true; + this.props.Document._stayInCollection = true; this.props.Document.isInkMask = true; } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index be8807336..d0f543d16 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -310,11 +310,7 @@ export class MainView extends React.Component { DocServer.Control.makeEditable(); } } - // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized) - setTimeout(async () => { - const col = this.userDoc && await Cast(this.userDoc["sidebar-sharing"], Doc); - col && Cast(col.data, listSpec(Doc)) && runInAction(() => MainViewNotifs.NotifsCol = col); - }, 100); + return true; } @@ -532,15 +528,16 @@ export class MainView extends React.Component { _lastButton: Doc | undefined; @action - selectMenu = (button: Doc, str: string) => { + selectMenu = (button: Doc) => { + const title = StrCast(Doc.GetProto(button).title); this._lastButton && (this._lastButton.color = "white"); this._lastButton && (this._lastButton._backgroundColor = ""); - if (this.panelContent === str && this.flyoutWidth !== 0) { + if (this.panelContent === title && this.flyoutWidth !== 0) { this.panelContent = "none"; this.flyoutWidth = 0; } else { let panelDoc: Doc | undefined; - switch (this.panelContent = str) { + switch (this.panelContent = title) { case "Tools": panelDoc = Doc.UserDoc()["sidebar-tools"] as Doc ?? undefined; break; case "Workspace": panelDoc = Doc.UserDoc()["sidebar-workspaces"] as Doc ?? undefined; break; case "Catalog": panelDoc = Doc.UserDoc()["sidebar-catalog"] as Doc ?? undefined; break; @@ -548,7 +545,7 @@ export class MainView extends React.Component { case "Settings": SettingsManager.Instance.open(); break; case "Import": panelDoc = Doc.UserDoc()["sidebar-import"] as Doc ?? undefined; break; case "Sharing": panelDoc = Doc.UserDoc()["sidebar-sharing"] as Doc ?? undefined; break; - case "UserDoc": panelDoc = Doc.UserDoc()["sidebar-userDoc"] as Doc ?? undefined; break; + case "User Doc": panelDoc = Doc.UserDoc()["sidebar-userDoc"] as Doc ?? undefined; break; } this.sidebarContent.proto = panelDoc; if (panelDoc) { @@ -612,7 +609,6 @@ export class MainView extends React.Component { </div> </div> {this.dockingContent} - <MainViewNotifs /> {this.showProperties ? (null) : <div className="mainView-propertiesDragger" title="Properties View Dragger" onPointerDown={this.onPropertiesPointerDown} style={{ right: rightFlyout, top: "50%" }}> @@ -903,6 +899,7 @@ export class MainView extends React.Component { const sidebarDocView = DocumentManager.Instance.getDocumentView(sidebar); const input = document.createElement("input"); input.type = "file"; + input.multiple = true; input.accept = ".zip, application/pdf, video/*, image/*, audio/*"; input.onchange = async _e => { const upload = Utils.prepend("/uploadDoc"); @@ -973,6 +970,7 @@ export class MainView extends React.Component { input.click(); } } +Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); }); Scripting.addGlobal(function freezeSidebar() { MainView.expandFlyout(); }); Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().fontFamily = "Comic Sans MS"; Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }); Scripting.addGlobal(function copyWorkspace() { diff --git a/src/client/views/MainViewNotifs.scss b/src/client/views/MainViewNotifs.scss deleted file mode 100644 index 92d7d6ee3..000000000 --- a/src/client/views/MainViewNotifs.scss +++ /dev/null @@ -1,20 +0,0 @@ -.mainNotifs-container { - position:absolute; - z-index: 1000; - top: 12px; - - .mainNotifs-badge { - position: absolute; - top: -10px; - right: -10px; - color: white; - background: #f44b42; - font-weight: 300; - border-radius: 100%; - width: 25px; - height: 25px; - text-align: center; - padding-top: 4px; - font-size: 12px; - } -}
\ No newline at end of file diff --git a/src/client/views/MainViewNotifs.tsx b/src/client/views/MainViewNotifs.tsx deleted file mode 100644 index 89006ebc8..000000000 --- a/src/client/views/MainViewNotifs.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { observable } from 'mobx'; -import { observer } from 'mobx-react'; -import "normalize.css"; -import * as React from 'react'; -import { Doc, DocListCast, Opt } from '../../fields/Doc'; -import { returnFalse, setupMoveUpEvents } from '../../Utils'; -import { DragManager } from '../util/DragManager'; -import "./MainViewNotifs.scss"; -import { MainView } from './MainView'; -import { NumCast } from '../../fields/Types'; - - -@observer -export class MainViewNotifs extends React.Component { - @observable static NotifsCol: Opt<Doc>; - _notifsRef = React.createRef<HTMLDivElement>(); - - onPointerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, - (e: PointerEvent) => { - const dragData = new DragManager.DocumentDragData([MainViewNotifs.NotifsCol!]); - DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y); - return true; - }, - returnFalse, - () => MainViewNotifs.NotifsCol && MainView.Instance.selectMenu(MainViewNotifs.NotifsCol, "Sharing")); - } - - render() { - const length = MainViewNotifs.NotifsCol ? DocListCast(MainViewNotifs.NotifsCol.data).length : 0; - return <div className="mainNotifs-container" style={{ width: 15, height: 15, top: 12 + NumCast(MainViewNotifs.NotifsCol?.position) * 60 }} ref={this._notifsRef}> - <button className="mainNotifs-badge" style={length > 0 ? { "display": "initial" } : { "display": "none" }} - onPointerDown={this.onPointerDown} > - {length} - </button> - </div>; - } -} diff --git a/src/client/views/OverlayView.scss b/src/client/views/OverlayView.scss index 09a349012..555f4298d 100644 --- a/src/client/views/OverlayView.scss +++ b/src/client/views/OverlayView.scss @@ -44,6 +44,6 @@ } .overlayView-doc { - z-index: 1; + z-index: 9002; //so that it appears above chroma position: absolute; }
\ No newline at end of file diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 5c3a8185c..49580cde4 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -182,7 +182,7 @@ export class OverlayView extends React.Component { offsetx = NumCast(d.x) - e.clientX; offsety = NumCast(d.y) - e.clientY; }; - return <div className="overlayView-doc" ref={dref} key={d[Id]} onPointerDown={onPointerDown} style={{ width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.x}px, ${d.y}px)` }}> + return <div className="overlayView-doc" ref={dref} key={d[Id]} onPointerDown={onPointerDown} style={{ top: d.type === 'presentation' ? 0 : undefined, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.x}px, ${d.y}px)` }}> <DocumentView Document={d} LibraryPath={emptyPath} diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 2451ff55a..35d4f7f6e 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -416,14 +416,14 @@ export class PropertiesButtons extends React.Component<{}, {}> { get deleteButton() { const targetDoc = this.selectedDoc; return !targetDoc ? (null) : <Tooltip - title={<><div className="dash-tooltip">{"Delete Document"}</div></>} placement="top"> + title={<><div className="dash-tooltip">Close Document</div></>} placement="top"> <div> <div className={"propertiesButtons-linkButton-empty"} onPointerDown={this.deleteDocument}> {<FontAwesomeIcon className="propertiesButtons-icon" - icon="trash-alt" size="lg" />} + icon="times" size="lg" />} </div> - <div className="propertiesButtons-title"> delete </div> + <div className="propertiesButtons-title"> close </div> </div> </Tooltip>; } @@ -639,7 +639,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { this.selectedDoc._backgroundColor = "rgba(0,0,0,0.7)"; this.selectedDoc.mixBlendMode = "hard-light"; this.selectedDoc.color = "#9b9b9bff"; - this.selectedDoc.stayInCollection = true; + this.selectedDoc._stayInCollection = true; this.selectedDoc.isInkMask = true; } } diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx index 1eb380e0b..db087fb23 100644 --- a/src/client/views/ScriptingRepl.tsx +++ b/src/client/views/ScriptingRepl.tsx @@ -104,7 +104,7 @@ export class ScriptingRepl extends React.Component { if (ts.isParameter(node.parent)) { // delete knownVars[node.text]; } else if (isntPropAccess && isntPropAssign && !(node.text in knownVars) && !(node.text in globalThis)) { - const match = node.text.match(/\d([0-9]+)/); + const match = node.text.match(/d([0-9]+)/); if (match) { const m = parseInt(match[1]); usedDocuments.push(m); diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index 4204ef5bb..6ebd5103b 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -20,82 +20,6 @@ } } -.miniPres:hover { - opacity: 1; -} - -.miniPres { - position: absolute; - overflow: hidden; - right: 10; - top: 10; - opacity: 0.1; - transition: all 0.4s; - /* border: solid 1px; */ - color: white; - /* box-shadow: black 0.4vw 0.4vw 0.8vw; */ - - .miniPresOverlay { - display: grid; - grid-template-columns: auto auto auto auto auto auto auto auto; - grid-template-rows: 100%; - height: 100%; - justify-items: center; - align-items: center; - - .miniPres-button-text { - display: flex; - height: 20; - font-weight: 400; - min-width: 100%; - border-radius: 5px; - align-items: center; - justify-content: center; - transition: all 0.3s; - } - - .miniPres-button-frame { - justify-self: center; - align-self: center; - align-items: center; - display: grid; - grid-template-columns: auto auto auto; - justify-content: space-around; - font-size: 11; - margin-left: 7; - width: 30; - height: 85%; - background-color: rgba(91, 157, 221, 0.4); - border-radius: 5px; - } - - .miniPres-divider { - width: 0.5px; - height: 80%; - border-right: solid 2px #5a5a5a; - } - - .miniPres-button { - display: flex; - height: 20; - min-width: 20; - border-radius: 100%; - align-items: center; - justify-content: center; - transition: all 0.3s; - } - - .miniPres-button:hover { - background-color: #5a5a5a; - } - - .miniPres-button-text:hover { - background-color: #5a5a5a; - } - } -} - - .lm_title { margin-top: 3px; border-radius: 5px; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 998e41bd2..43da0d3cf 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -191,31 +191,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp return retVal; } - @undoBatch - @action - public static ReplaceTab(document: Doc, stack: any): Opt<Doc> { - if (!CollectionDockingView.Instance) return undefined; - const instance = CollectionDockingView.Instance; - const replaceTab = (doc: Doc, child: any): Opt<Doc> => { - for (const contentItem of child.contentItems) { - const { config, isStack, isRow, isColumn } = contentItem; - if (isRow || isColumn || isStack) { - const val = replaceTab(doc, contentItem); - if (val) return val; - } else if (config.component === "DocumentFrameRenderer" && - config.props.documentId === doc[Id]) { - const alias = Doc.MakeAlias(doc); - config.props.documentId = alias[Id]; - config.title = alias.title; - instance.stateChanged(); - return alias; - } - } - return undefined; - }; - return replaceTab(document, instance._goldenLayout.root); - } - // // Creates a vertical split on the right side of the docking view, and then adds the Document to the right of that split // @@ -358,6 +333,32 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp return true; } + @undoBatch + @action + public ReplaceTab = (stack: any, document: Doc, libraryPath?: Doc[]) => { + Doc.GetProto(document).lastOpened = new DateField; + const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath); + if (stack === undefined) { + let stack: any = this._goldenLayout.root; + while (!stack.isStack) { + if (stack.contentItems.length) { + stack = stack.contentItems[0]; + } else { + stack.addChild({ type: 'stack', content: [docContentConfig] }); + stack = undefined; + break; + } + } + if (stack) { + stack.addChild(docContentConfig); + } + } else { + stack.addChild(docContentConfig, undefined); + } + this.layoutChanged(); + return true; + } + setupGoldenLayout() { const config = StrCast(this.props.Document.dockingConfig); if (config) { @@ -839,12 +840,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { } else if (location === "close") { return CollectionDockingView.CloseRightSplit(doc); } else if (location === "replace") { - const alias = CollectionDockingView.ReplaceTab(doc, this._stack); - if (alias) { - runInAction(() => this._document = alias); - return true; - } - return false; + CollectionDockingView.UseRightSplit(doc); + return true; } else {// if (location === "inPlace") { return CollectionDockingView.Instance.AddTab(this._stack, doc, libraryPath); } @@ -875,26 +872,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { const currentFrame = Cast(presTargetDoc.currentFrame, "number", null); return currentFrame; } - renderMiniPres() { - return ( - <div className="miniPres" - style={{ width: 250, height: 30, background: '#323232' }} - > - {<div className="miniPresOverlay"> - <div className="miniPres-button" onClick={PresBox.Instance.back}><FontAwesomeIcon icon={"arrow-left"} /></div> - <div className="miniPres-button" onClick={() => PresBox.Instance.startAutoPres(PresBox.Instance.itemIndex)}><FontAwesomeIcon icon={PresBox.Instance.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div> - <div className="miniPres-button" onClick={PresBox.Instance.next}><FontAwesomeIcon icon={"arrow-right"} /></div> - <div className="miniPres-divider"></div> - <div className="miniPres-button-text"> - Slide {PresBox.Instance.itemIndex + 1} / {PresBox.Instance.childDocs.length} - {PresBox.Instance.playButtonFrames} - </div> - <div className="miniPres-divider"></div> - <div className="miniPres-button-text" onClick={PresBox.Instance.updateMinimize}>EXIT</div> - </div>} - </div> - ); - } + renderMiniMap() { return <div className="miniMap" style={{ width: this.returnMiniSize(), height: this.returnMiniSize(), background: StrCast(this._document!._backgroundColor, @@ -977,7 +955,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} /> {document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)} - {document._viewType === CollectionViewType.Freeform && this._document?.miniPres ? this.renderMiniPres() : (null)} </>; } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index b27af66ba..2b7ae4338 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -4,7 +4,7 @@ import { CursorProperty } from "csstype"; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import Switch from 'rc-switch'; -import { DataSym, Doc, HeightSym, WidthSym, DocListCastAsync } from "../../../fields/Doc"; +import { DataSym, Doc, HeightSym, WidthSym } from "../../../fields/Doc"; import { collectionSchema, documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { List } from "../../../fields/List"; @@ -299,10 +299,6 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) const srcInd = docs.indexOf(doc); docs.splice(srcInd, 1); docs.splice((targInd > srcInd ? targInd - 1 : targInd) + plusOne, 0, doc); - DocListCastAsync(docs).then(resolvedDocs => { - const pos = resolvedDocs?.findIndex(shareDoc => shareDoc.icon === "users") || 0; // hopefully find out if the sharing doc has been moved - if (MainViewNotifs.NotifsCol && pos !== -1) MainViewNotifs.NotifsCol.position = pos; - }); } else if (i < (newDocs.length / 2)) { //glr: for some reason dragged documents are duplicated if (targInd === -1) targInd = docs.length; else targInd = docs.indexOf(newDocs[0]) + 1; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 7562d7e9c..6dd21ef7f 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -222,7 +222,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus return true; } const first = doc instanceof Doc ? doc : doc[0]; - if (!first?.stayInCollection && addDocument !== returnFalse) { + if (!first?._stayInCollection && addDocument !== returnFalse) { if (UndoManager.RunInTempBatch(() => this.removeDocument(doc))) { const added = addDocument(doc); if (!added) UndoManager.UndoTempBatch(); diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 011b6ff87..900963eb0 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -510,7 +510,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD labelScript = () => AudioBox.LabelScript; render() { - AudioBox.Instance = this; const interactive = SnappingManager.GetIsDragging() || this.active() ? "-interactive" : ""; this._first = true; // for indicating the first marker that is rendered this.path && this._buckets.length !== 100 ? this.peaks : null; // render waveform if audio is done recording diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a1c934e81..d92dc0ec2 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -6,15 +6,13 @@ import { Document } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { listSpec } from "../../../fields/Schema"; -import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { MobileInterface } from '../../../mobile/MobileInterface'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; -import { emptyFunction, emptyPath, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils"; +import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils"; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; -import { ClientRecommender } from '../../ClientRecommender'; import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from "../../util/DocumentManager"; @@ -22,7 +20,6 @@ import { DragManager, dropActionType } from "../../util/DragManager"; import { InteractionUtils } from '../../util/InteractionUtils'; import { LinkManager } from '../../util/LinkManager'; import { Scripting } from '../../util/Scripting'; -import { SearchUtil } from '../../util/SearchUtil'; import { SelectionManager } from "../../util/SelectionManager"; import SharingManager from '../../util/SharingManager'; import { SnappingManager } from '../../util/SnappingManager'; @@ -33,7 +30,6 @@ import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; -import { KeyphraseQueryView } from '../KeyphraseQueryView'; import { DocumentContentsView } from "./DocumentContentsView"; import { DocumentLinksButton } from './DocumentLinksButton'; import "./DocumentView.scss"; @@ -767,7 +763,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()) && moreItems.push({ description: "Toggle Always Show Link End", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" }); } - moreItems.push({ description: "Close", event: this.deleteClicked, icon: "trash" }); + moreItems.push({ description: "Close", event: this.deleteClicked, icon: "times" }); !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" }); cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!); @@ -819,7 +815,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu @computed get contents() { const pos = this.props.relative ? "relative " : "absolute"; TraceMobx(); - return (<div style={{ width: "100%", height: "100%" }}> + return (<div className="documentView-contentsView" style={{ borderRadius: "inherit", width: "100%", height: "100%" }}> <DocumentContentsView key={1} docFilters={this.props.docFilters} ContainingCollectionView={this.props.ContainingCollectionView} diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss index 6a540269e..75bc90d7a 100644 --- a/src/client/views/nodes/FontIconBox.scss +++ b/src/client/views/nodes/FontIconBox.scss @@ -13,6 +13,27 @@ width: 100%; } +.fontIconBadge-container { + position:absolute; + z-index: 1000; + top: 12px; + + .fontIconBadge { + position: absolute; + top: -10px; + right: -10px; + color: white; + background: #f44b42; + font-weight: 300; + border-radius: 100%; + width: 25px; + height: 25px; + text-align: center; + padding-top: 4px; + font-size: 12px; + } +} + .menuButton-round { border-radius: 100%; background-color: black; diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index 168d640e9..fa21a8594 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -5,13 +5,15 @@ import { createSchema, makeInterface } from '../../../fields/Schema'; import { DocComponent } from '../DocComponent'; import './FontIconBox.scss'; import { FieldView, FieldViewProps } from './FieldView'; -import { StrCast, Cast } from '../../../fields/Types'; -import { Utils } from "../../../Utils"; +import { StrCast, Cast, ScriptCast } from '../../../fields/Types'; +import { Utils, setupMoveUpEvents, returnFalse, emptyFunction } from "../../../Utils"; import { runInAction, observable, reaction, IReactionDisposer } from 'mobx'; -import { Doc } from '../../../fields/Doc'; +import { Doc, DocListCast } from '../../../fields/Doc'; import { ContextMenu } from '../ContextMenu'; import { ScriptField } from '../../../fields/ScriptField'; import { Tooltip } from '@material-ui/core'; +import { MainViewNotifs } from '../MainViewNotifs'; +import { DragManager } from '../../util/DragManager'; const FontIconSchema = createSchema({ icon: "string", }); @@ -73,6 +75,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>( {<FontAwesomeIcon className={`menuButton-icon-${shape}`} icon={StrCast(this.dataDoc.icon, "user") as any} color={color} size={this.layoutDoc.iconShape === "square" ? "sm" : "lg"} />} {!label ? (null) : <div className="fontIconBox-label" style={{ color, backgroundColor }}> {label} </div>} + {this.props.Document.watchedDocuments ? <FontIconBadge collection={Cast(this.props.Document.watchedDocuments, Doc, null)} /> : (null)} </div> </button>; return !this.layoutDoc.toolTip ? button : @@ -80,4 +83,34 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>( {button} </Tooltip>; } +} + +interface FontIconBadgeProps { + collection: Doc; +} + +@observer +export class FontIconBadge extends React.Component<FontIconBadgeProps> { + _notifsRef = React.createRef<HTMLDivElement>(); + + onPointerDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, + (e: PointerEvent) => { + const dragData = new DragManager.DocumentDragData([this.props.collection]); + DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y); + return true; + }, + returnFalse, emptyFunction, false); + } + + render() { + if (!(this.props.collection instanceof Doc)) return (null); + const length = DocListCast(this.props.collection.data).length; + return <div className="fontIconBadge-container" style={{ width: 15, height: 15, top: 12 }} ref={this._notifsRef}> + <div className="fontIconBadge" style={length > 0 ? { "display": "initial" } : { "display": "none" }} + onPointerDown={this.onPointerDown} > + {length} + </div> + </div>; + } }
\ No newline at end of file diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index d668d332b..216c5f39e 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -158,7 +158,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD if (field) { const funcs: ContextMenuProps[] = []; funcs.push({ description: "Rotate Clockwise 90", event: this.rotate, icon: "expand-arrows-alt" }); - funcs.push({ description: "Make Background", event: () => this.layoutDoc.isBackground = true, icon: "expand-arrows-alt" }); + funcs.push({ description: "Make Background", event: () => { this.layoutDoc.isBackground = true; this.props.bringToFront?.(this.rootDoc); }, icon: "expand-arrows-alt" }); if (!Doc.UserDoc().noviceMode) { funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" }); funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" }); @@ -466,7 +466,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD width: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`, height: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`, pointerEvents: this.layoutDoc.isBackground ? "none" : undefined, - borderRadius: `${Number(StrCast(this.layoutDoc.borderRoundisng).replace("px", "")) / this.props.ContentScaling()}px` + borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.props.ContentScaling()}px` }} > <CollectionFreeFormView {...this.props} forceScaling={true} diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss index 54accb012..c4d8f1a4f 100644 --- a/src/client/views/nodes/PresBox.scss +++ b/src/client/views/nodes/PresBox.scss @@ -13,7 +13,7 @@ $light-background: #ececec; width: 100%; min-width: 20px; height: 100%; - min-height: 41px; + min-height: 35px; letter-spacing: 2px; overflow: hidden; transition: 0.7s opacity ease; @@ -801,7 +801,7 @@ $light-background: #ececec; } - .miniPresOverlay { + .presPanelOverlay { background-color: #323232; color: white; border-radius: 5px; @@ -816,7 +816,7 @@ $light-background: #ececec; right: 10px; transition: all 0.2s; - .miniPres-button-text { + .presPanel-button-text { display: flex; height: 20; width: max-content; @@ -833,13 +833,13 @@ $light-background: #ececec; transition: all 0.3s; } - .miniPres-divider { + .presPanel-divider { width: 0.5px; height: 80%; border-right: solid 1px #5a5a5a; } - .miniPres-button-frame { + .presPanel-button-frame { justify-self: center; align-self: center; align-items: center; @@ -854,7 +854,7 @@ $light-background: #ececec; border-radius: 5px; } - .miniPres-button { + .presPanel-button { cursor: pointer; display: flex; height: 20; @@ -867,11 +867,11 @@ $light-background: #ececec; transition: all 0.3s; } - .miniPres-button:hover { + .presPanel-button:hover { background-color: #5a5a5a; } - .miniPres-button-text:hover { + .presPanel-button-text:hover { background-color: #5a5a5a; } } @@ -950,4 +950,82 @@ $light-background: #ececec; .select { font-size: 100%; } -}
\ No newline at end of file +} + +.miniPres:hover { + opacity: 1; +} + +.miniPres { + cursor: grab; + position: absolute; + overflow: hidden; + right: 10; + top: 10; + opacity: 0.1; + transition: all 0.4s; + /* border: solid 1px; */ + color: white; + /* box-shadow: black 0.4vw 0.4vw 0.8vw; */ + + .miniPresOverlay { + display: grid; + grid-template-columns: auto auto auto auto auto auto auto auto; + grid-template-rows: 100%; + height: 100%; + justify-items: center; + align-items: center; + + .miniPres-button-text { + cursor: pointer; + display: flex; + height: 20; + font-weight: 400; + min-width: 100%; + border-radius: 5px; + align-items: center; + justify-content: center; + transition: all 0.3s; + } + + .miniPres-button-frame { + justify-self: center; + align-self: center; + align-items: center; + display: grid; + grid-template-columns: auto auto auto; + justify-content: space-around; + font-size: 11; + margin-left: 7; + width: 30; + height: 85%; + background-color: rgba(91, 157, 221, 0.4); + border-radius: 5px; + } + + .miniPres-divider { + width: 0.5px; + height: 80%; + border-right: solid 2px #5a5a5a; + } + + .miniPres-button { + cursor: pointer; + display: flex; + height: 20; + min-width: 20; + border-radius: 100%; + align-items: center; + justify-content: center; + transition: all 0.3s; + } + + .miniPres-button:hover { + background-color: #5a5a5a; + } + + .miniPres-button-text:hover { + background-color: #5a5a5a; + } + } +} diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index f6f6a2333..dd4c103c4 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -147,9 +147,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> // Case 3: No more frames in current doc and next slide is defined, therefore move to next slide } else if (this.childDocs[this.itemIndex + 1] !== undefined) { const nextSelected = this.itemIndex + 1; + if (presTargetDoc.type === DocumentType.AUDIO) AudioBox.Instance.pause(); this.gotoDocument(nextSelected, this.itemIndex); const targetNext = Cast(activeNext.presentationTargetDoc, Doc, null); if (activeNext && targetNext.type === DocumentType.AUDIO && activeNext.playAuto) { + AudioBox.Instance.playFrom(0); + this._moveOnFromAudio = true; } else this._moveOnFromAudio = false; } } @@ -228,21 +231,25 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> const docToJump = curDoc; const willZoom = false; - //docToJump stayed same meaning, it was not in the group or was the last element in the group - if (activeItem.zoomProgressivize && this.rootDoc.presStatus !== 'edit') { - this.zoomProgressivizeNext(targetDoc); - } else if (docToJump === curDoc) { - //checking if curDoc has navigation open - if (curDoc.presNavButton && targetDoc) { - await DocumentManager.Instance.jumpToDocument(targetDoc, false, undefined, srcContext); - } else if (curDoc.presZoomButton && targetDoc) { + // If openDocument is selected then it should open the document for the user + if (activeItem.openDocument) { + this.props.addDocTab(activeItem, "replace"); + } else + //docToJump stayed same meaning, it was not in the group or was the last element in the group + if (activeItem.zoomProgressivize && this.rootDoc.presStatus !== 'edit') { + this.zoomProgressivizeNext(targetDoc); + } else if (docToJump === curDoc) { + //checking if curDoc has navigation open + if (curDoc.presNavButton && targetDoc) { + await DocumentManager.Instance.jumpToDocument(targetDoc, false, undefined, srcContext); + } else if (curDoc.presZoomButton && targetDoc) { + //awaiting jump so that new scale can be found, since jumping is async + await DocumentManager.Instance.jumpToDocument(targetDoc, true, undefined, srcContext); + } + } else { //awaiting jump so that new scale can be found, since jumping is async - await DocumentManager.Instance.jumpToDocument(targetDoc, true, undefined, srcContext); + targetDoc && await DocumentManager.Instance.jumpToDocument(targetDoc, willZoom, undefined, srcContext); } - } else { - //awaiting jump so that new scale can be found, since jumping is async - targetDoc && await DocumentManager.Instance.jumpToDocument(targetDoc, willZoom, undefined, srcContext); - } // After navigating to the document, if it is added as a presPinView then it will // adjust the pan and scale to that of the pinView when it was added. // TODO: Add option to remove presPinView @@ -251,10 +258,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> targetDoc._panY = activeItem.presPinViewY; targetDoc._viewScale = activeItem.presPinViewScale; } - // If openDocument is selected then it should open the document for the user - if (collectionDocView && activeItem.openDocument) { - collectionDocView.props.addDocTab(activeItem, "inPlace"); - } // If website and has presWebsite data associated then on click it should // go back to that specific website // TODO: Add progressivize for navigating web (storing websites for given frames) @@ -353,9 +356,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> for (var i = startSlide; i < this.childDocs.length; i++) { activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); - if (targetDoc.presDuration && targetDoc.presTransition) { - duration = NumCast(targetDoc.presDuration) + NumCast(targetDoc.presTransition); - } else duration = 2500; + duration = NumCast(targetDoc.presDuration) + NumCast(targetDoc.presTransition); + if (duration <= 100) { duration = 2500; } if (NumCast(targetDoc.lastFrame) > 0) { for (var f = 0; f < NumCast(targetDoc.lastFrame); f++) { await timer(duration / NumCast(targetDoc.lastFrame)); @@ -415,17 +417,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> * The method called to open the presentation as a minimized view * TODO: Look at old updateMinimize and compare... */ + @undoBatch + @action updateMinimize = () => { - const srcContext = Cast(this.rootDoc.presCollection, Doc, null); - this.turnOffEdit(); - if (srcContext) { - if (srcContext.miniPres) { - srcContext.miniPres = false; - CollectionDockingView.AddRightSplit(this.rootDoc); - } else { - srcContext.miniPres = true; - this.props.addDocTab?.(this.rootDoc, "close"); - } + if (this.layoutDoc.inOverlay) { + this.layoutDoc.presStatus = 'edit'; + Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc); + CollectionDockingView.AddRightSplit(this.rootDoc); + this.layoutDoc.inOverlay = false; + } else { + this.layoutDoc.presStatus = 'manual'; + const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); + this.rootDoc.x = pt[0] + (this.props.PanelWidth() - 250); + this.rootDoc.y = pt[1] + 10; + this.rootDoc._height = 35; + this.rootDoc._width = 250; + this.props.addDocTab?.(this.rootDoc, "close"); + Doc.AddDocToList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc); } } @@ -465,6 +473,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> break; case 'jump': //Jump Cut targetDoc.presTransition = 0; + activeItem.presZoomButton = true; activeItem.presSwitchButton = !activeItem.presSwitchButton; if (activeItem.presSwitchButton) activeItem.presMovement = 'Jump cut'; else activeItem.presMovement = 'None'; @@ -574,8 +583,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> const anchorNode = document.activeElement as HTMLDivElement; if (anchorNode && anchorNode.className?.includes("lm_title")) return; if (e.keyCode === 27) { // Escape key - if (this.layoutDoc.presStatus === "edit") { this._selectedArray = []; this._eleArray = []; this._dragArray = []; } - else this.layoutDoc.presStatus = "edit"; if (this._presTimer) clearTimeout(this._presTimer); + if (this.layoutDoc.inOverlay) { this.updateMinimize(); } + else if (this.layoutDoc.presStatus === "edit") { this._selectedArray = []; this._eleArray = []; this._dragArray = []; } + else this.layoutDoc.presStatus = "edit"; + if (this._presTimer) clearTimeout(this._presTimer); handled = true; } if ((e.metaKey || e.altKey) && e.keyCode === 65) { // Ctrl-A to select all if (this.layoutDoc.presStatus === "edit") { @@ -862,6 +873,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> return effect; } + @undoBatch + @action applyTo = (array: Doc[]) => { const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); @@ -1096,7 +1109,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> @computed get presentDropdown() { return ( <div className={`dropdown-play ${this.presentTools ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> - <div className="dropdown-play-button" onClick={() => this.updateMinimize()}> + <div className="dropdown-play-button" onClick={this.updateMinimize}> Minimize </div> <div className="dropdown-play-button" onClick={(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(); }))}> @@ -1287,9 +1300,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> doc.editZoomProgressivize = false; doc.editScrollProgressivize = false; const targetDoc = Cast(doc.presentationTargetDoc, Doc, null); - targetDoc.editSnapZoomProgressivize = false; - targetDoc.editZoomProgressivize = false; - targetDoc.editScrollProgressivize = false; + if (targetDoc) { + targetDoc.editZoomProgressivize = false; + targetDoc.editScrollProgressivize = false; + } }); } @@ -1607,18 +1621,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> @computed get playButtons() { // Case 1: There are still other frames and should go through all frames before going to next slide - return (<div className="miniPresOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}> - <div className="miniPres-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div> - <div className="miniPres-button" onClick={() => this.startAutoPres(this.itemIndex)}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div> - <div className="miniPres-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div> - <div className="miniPres-divider"></div> - <div className="miniPres-button-text" style={{ display: this.props.PanelWidth() > 250 ? "inline-flex" : "none" }}> + return (<div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}> + <div className="presPanel-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div> + <div className="presPanel-button" onClick={() => this.startAutoPres(this.itemIndex)}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div> + <div className="presPanel-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div> + <div className="presPanel-divider"></div> + <div className="presPanel-button-text" style={{ display: this.props.PanelWidth() > 250 ? "inline-flex" : "none" }}> Slide {this.itemIndex + 1} / {this.childDocs.length} {this.playButtonFrames} </div> - <div className="miniPres-divider"></div> - {this.props.PanelWidth() > 250 ? <div className="miniPres-button-text" onClick={() => this.layoutDoc.presStatus = "edit"}>EXIT</div> - : <div className="miniPres-button" onClick={() => this.layoutDoc.presStatus = "edit"}> + <div className="presPanel-divider"></div> + {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={() => this.layoutDoc.presStatus = "edit"}>EXIT</div> + : <div className="presPanel-button" onClick={() => this.layoutDoc.presStatus = "edit"}> <FontAwesomeIcon icon={"times"} /> </div>} </div>); @@ -1630,28 +1644,44 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> // needed to ensure that the childDocs are loaded for looking up fields this.childDocs.slice(); const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; - return <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} > - {this.topPanel} - {this.toolbar} - {this.newDocumentToolbarDropdown} - <div className="presBox-listCont"> - {mode !== CollectionViewType.Invalid ? - <CollectionView {...this.props} - ContainingCollectionDoc={this.props.Document} - PanelWidth={this.props.PanelWidth} - PanelHeight={this.panelHeight} - moveDocument={returnFalse} - childOpacity={returnOne} - childLayoutTemplate={this.childLayoutTemplate} - filterAddDocument={this.addDocumentFilter} - removeDocument={returnFalse} - dontRegisterView={true} - focus={this.selectElement} - ScreenToLocalTransform={this.getTransform} /> - : (null) - } + return this.layoutDoc.inOverlay ? + <div className="miniPres" style={{ width: 250, height: 35, background: '#323232', top: 0, zIndex: 3000000 }}> + {<div className="miniPresOverlay"> + <div className="miniPres-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div> + <div className="miniPres-button" onClick={() => this.startAutoPres(this.itemIndex)}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div> + <div className="miniPres-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div> + <div className="miniPres-divider"></div> + <div className="miniPres-button-text"> + Slide {this.itemIndex + 1} / {this.childDocs.length} + {this.playButtonFrames} + </div> + <div className="miniPres-divider"></div> + <div className="miniPres-button-text" onClick={this.updateMinimize}>EXIT</div> + </div>} </div> - </div>; + : + <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} > + {this.topPanel} + {this.toolbar} + {this.newDocumentToolbarDropdown} + <div className="presBox-listCont"> + {mode !== CollectionViewType.Invalid ? + <CollectionView {...this.props} + ContainingCollectionDoc={this.props.Document} + PanelWidth={this.props.PanelWidth} + PanelHeight={this.panelHeight} + moveDocument={returnFalse} + childOpacity={returnOne} + childLayoutTemplate={this.childLayoutTemplate} + filterAddDocument={this.addDocumentFilter} + removeDocument={returnFalse} + dontRegisterView={true} + focus={this.selectElement} + ScreenToLocalTransform={this.getTransform} /> + : (null) + } + </div> + </div>; } } Scripting.addGlobal(function lookupPresBoxField(container: Doc, field: string, data: Doc) { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index f1eb5ef09..8c05d3603 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -648,7 +648,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const options = cm.findByDescription("Options..."); const optionItems = options && "subitems" in options ? options.subitems : []; - !Doc.UserDoc().noviceMode && optionItems.push({ description: this.Document._singleLine ? "Make Single Line" : "Make Multi Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" }); + !Doc.UserDoc().noviceMode && optionItems.push({ description: !this.Document._singleLine ? "Make Single Line" : "Make Multi Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" }); optionItems.push({ description: `${this.Document._autoHeight ? "Lock" : "Auto"} Height`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" }); optionItems.push({ description: `${!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Lock" : "Unlock"} Aspect`, event: this.toggleNativeDimensions, icon: "snowflake" }); !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "eye" }); @@ -1580,6 +1580,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp onScroll={this.onscrolled} onDrop={this.ondrop} > <div className={`formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget} style={{ + overflow: this.layoutDoc._singleLine ? "hidden" : undefined, padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${Math.max(0, NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0) + selPad)}px ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0) + selPad}px`, pointerEvents: !this.props.active() ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : undefined) : undefined }} diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 4cc3a7cc7..9391f56ac 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -56,6 +56,9 @@ async function deserializeScript(script: ScriptField) { if (script.script.originalScript === 'self.userDoc.noviceMode') { return (script as any).script = (ScriptField.NoviceMode ?? (ScriptField.NoviceMode = ComputedField.MakeFunction('self.userDoc.noviceMode')))?.script; } + if (script.script.originalScript === `selectMainMenu(self)`) { + return (script as any).script = (ScriptField.SelectMenu ?? (ScriptField.SelectMenu = ComputedField.MakeFunction('selectMainMenu(self)')))?.script; + } const captures: ProxyField<Doc> = (script as any).captures; if (captures) { const doc = (await captures.value())!; @@ -89,6 +92,7 @@ export class ScriptField extends ObjectField { public static DeiconifyView: Opt<ScriptField>; public static ConvertToButtons: Opt<ScriptField>; public static NoviceMode: Opt<ScriptField>; + public static SelectMenu: Opt<ScriptField>; constructor(script: CompiledScript, setterscript?: CompiledScript) { super(); diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index ada13226e..848a648e1 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -102,7 +102,7 @@ export const documentSchema = createSchema({ _lockedTransform: "boolean",// whether a freeformview can pan/zoom // drag drop properties - stayInCollection: "boolean",// whether document can be dropped into a different collection + _stayInCollection: "boolean",// whether document can be dropped into a different collection dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document. dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias", "copy", "move") targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move' diff --git a/src/server/ApiManagers/SearchManager.ts b/src/server/ApiManagers/SearchManager.ts index 99f227b28..a5aaab63e 100644 --- a/src/server/ApiManagers/SearchManager.ts +++ b/src/server/ApiManagers/SearchManager.ts @@ -62,7 +62,7 @@ export class SearchManager extends ApiManager { subscription: "/dashsearch", secureHandler: async ({ req, res }) => { const solrQuery: any = {}; - ["q", "fq", "start", "rows", "hl", "hl.fl"].forEach(key => solrQuery[key] = req.query[key]); + ["q", "fq", "start", "rows", "sort", "hl", "hl.fl"].forEach(key => solrQuery[key] = req.query[key]); if (solrQuery.q === undefined) { res.send([]); return; @@ -136,6 +136,9 @@ export namespace SolrManager { const term = ToSearchTerm(value); if (term !== undefined) { const { suffix, value } = term; + if (key.endsWith('lastModified')) { + update["lastModified" + suffix] = value; + } update[key + suffix] = value; dynfield = true; } |