diff options
-rw-r--r-- | src/client/util/SettingsManager.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionMenu.scss | 51 | ||||
-rw-r--r-- | src/client/views/collections/CollectionMenu.tsx | 158 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaMovableTableHOC.tsx | 26 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentLinksButton.tsx | 23 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 10 |
7 files changed, 256 insertions, 20 deletions
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index b4778d3eb..5642c5a42 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -32,7 +32,7 @@ export default class SettingsManager extends React.Component<{}> { @observable private new_password = ""; @observable private new_confirm = ""; - @computed get backgroundColor() { return Doc.UserDoc().defaultColor; } + @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; } constructor(props: {}) { super(props); diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss index 30d5103bf..d0bfd0a41 100644 --- a/src/client/views/collections/CollectionMenu.scss +++ b/src/client/views/collections/CollectionMenu.scss @@ -315,6 +315,57 @@ } } +.webBox-urlEditor { + position: relative; + opacity: 0.9; + z-index: 9001; + transition: top .5s; + + .urlEditor { + display: grid; + grid-template-columns: 1fr auto; + padding-bottom: 10px; + overflow: hidden; + margin-top: 5px; + height: 35px; + + .editorBase { + display: flex; + + .editor-collapse { + transition: all .5s, opacity 0.3s; + position: absolute; + width: 40px; + transform-origin: top left; + } + + .switchToText { + color: $main-accent; + } + + .switchToText:hover { + color: $dark-color; + } + } + + button:hover { + transform: scale(1); + } + } +} + +.webpage-urlInput { + padding: 12px 10px 11px 10px; + border: 0px; + color: grey; + letter-spacing: 2px; + outline-color: black; + background: rgb(238, 238, 238); + width: 100%; + margin-right: 10px; + height: 100%; +} + .collectionFreeFormMenu-cont { display: inline-flex; position: relative; diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 28ba6945a..cf053e1ca 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -14,10 +14,12 @@ import { ObjectField } from "../../../fields/ObjectField"; import { listSpec } from "../../../fields/Schema"; import { ScriptField } from "../../../fields/ScriptField"; import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; +import { WebField } from "../../../fields/URLField"; import { emptyFunction, setupMoveUpEvents, Utils } from "../../../Utils"; import { DocumentType } from "../../documents/DocumentTypes"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { DragManager } from "../../util/DragManager"; +import { Scripting } from "../../util/Scripting"; import { SelectionManager } from "../../util/SelectionManager"; import { undoBatch } from "../../util/UndoManager"; import AntimodeMenu, { AntimodeMenuProps } from "../AntimodeMenu"; @@ -29,7 +31,6 @@ import { DocumentView } from "../nodes/DocumentView"; import RichTextMenu from "../nodes/formattedText/RichTextMenu"; import "./CollectionMenu.scss"; import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView"; -import { Scripting } from "../../util/Scripting"; @observer export default class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> { @@ -607,6 +608,158 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu </div>; } + @action + onUrlDrop = (e: React.DragEvent) => { + const { dataTransfer } = e; + const html = dataTransfer.getData("text/html"); + const uri = dataTransfer.getData("text/uri-list"); + const url = uri || html || this._url; + this._url = url.startsWith(window.location.origin) ? + url.replace(window.location.origin, this._url.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url; + this.submitURL(); + e.stopPropagation(); + } + onUrlDragover = (e: React.DragEvent) => { + e.preventDefault(); + } + + @computed get _url() { + return this.selectedDoc ? Cast(this.selectedDoc["data"], WebField, null)?.url.toString() : "hello"; + } + + set _url(value) { this.selectedDoc && (this.selectedDoc["data"] = value); } + + @action + submitURL = () => { + if (!this._url.startsWith("http")) this._url = "http://" + this._url; + try { + const URLy = new URL(this._url); + const future = this.selectedDoc ? Cast(this.selectedDoc["data-future"], listSpec("string"), null) : null; + const history = this.selectedDoc ? Cast(this.selectedDoc["data-history"], listSpec("string"), null) : []; + const annos = this.selectedDoc ? DocListCast(this.selectedDoc["data-annotations"]) : undefined; + const url = this.selectedDoc ? Cast(this.selectedDoc["data"], WebField, null)?.url.toString() : null; + if (url) { + if (history === undefined) { + this.selectedDoc && (this.selectedDoc["data-history"] = new List<string>([url])); + + } else { + history.push(url); + } + future && (future.length = 0); + this.selectedDoc && (this.selectedDoc["data-" + this.urlHash(url)] = new List<Doc>(annos)); + } + this.selectedDoc && (this.selectedDoc["data"] = new WebField(URLy)); + this.selectedDoc && (this.selectedDoc["data-annotations"] = new List<Doc>([])); + } catch (e) { + console.log("WebBox URL error:" + this._url); + } + } + + urlHash(s: string) { + return s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0); + } + + toggleAnnotationMode = () => { + this.props.docView.layoutDoc.isAnnotating = !this.props.docView.layoutDoc.isAnnotating; + } + + @action + onURLChange = (e: React.ChangeEvent<HTMLInputElement>) => { + this._url = e.target.value; + } + + onValueKeyDown = async (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + this.submitURL(); + } + e.stopPropagation(); + } + + @action + forward = () => { + const future = this.selectedDoc && (Cast(this.selectedDoc["data-future"], listSpec("string"), null)); + const history = this.selectedDoc && Cast(this.selectedDoc["data-history"], listSpec("string"), null); + if (future?.length) { + history?.push(this._url); + this.selectedDoc && (this.selectedDoc["data-annotations-" + this.urlHash(this._url)] = new List<Doc>(DocListCast(this.selectedDoc["data-annotations"]))); + this.selectedDoc && (this.selectedDoc["data"] = new WebField(new URL(this._url = future.pop()!))); + this.selectedDoc && (this.selectedDoc["data-annotations"] = new List<Doc>(DocListCast(this.selectedDoc["data-annotations" + "-" + this.urlHash(this._url)]))); + } + } + + @action + back = () => { + const future = this.selectedDoc && (Cast(this.selectedDoc["data-future"], listSpec("string"), null)); + const history = this.selectedDoc && Cast(this.selectedDoc["data-history"], listSpec("string"), null); + if (history?.length) { + if (future === undefined) this.selectedDoc && (this.selectedDoc["data-future"] = new List<string>([this._url])); + else future.push(this._url); + this.selectedDoc && (this.selectedDoc["data-annotations" + "-" + this.urlHash(this._url)] = new List<Doc>(DocListCast(this.selectedDoc["data-annotations"]))); + this.selectedDoc && (this.selectedDoc["data"] = new WebField(new URL(this._url = history.pop()!))); + this.selectedDoc && (this.selectedDoc["data-annotations"] = new List<Doc>(DocListCast(this.selectedDoc["data-annotations" + "-" + this.urlHash(this._url)]))); + } + } + + private _keyInput = React.createRef<HTMLInputElement>(); + + @computed get urlEditor() { + return ( + <div className="webBox-urlEditor" + onDrop={this.onUrlDrop} + onDragOver={this.onUrlDragover} style={{ top: 0 }}> + <div className="urlEditor"> + <div className="editorBase"> + <div className="webBox-buttons" + onDrop={this.onUrlDrop} + onDragOver={this.onUrlDragover} style={{ display: "flex" }}> + <div className="webBox-freeze" title={"Annotate"} + style={{ background: this.props.docView.layoutDoc.isAnnotating ? "lightBlue" : "gray" }} + onClick={this.toggleAnnotationMode} > + <FontAwesomeIcon icon="pen" size={"2x"} /> + </div> + <div className="webBox-freeze" title={"Select"} + style={{ background: !this.props.docView.layoutDoc.isAnnotating ? "lightBlue" : "gray" }} + onClick={this.toggleAnnotationMode} > + <FontAwesomeIcon icon={"mouse-pointer"} size={"2x"} /> + </div> + <input className="webpage-urlInput" + placeholder="ENTER URL" + value={this._url} + onDrop={this.onUrlDrop} + onDragOver={this.onUrlDragover} + onChange={this.onURLChange} + onKeyDown={this.onValueKeyDown} + onClick={(e) => { + this._keyInput.current!.select(); + e.stopPropagation(); + }} + ref={this._keyInput} + /> + <div style={{ + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + maxWidth: "120px", + }}> + <button className="submitUrl" onClick={this.submitURL} + onDragOver={this.onUrlDragover} onDrop={this.onUrlDrop}> + GO + </button> + <button className="submitUrl" onClick={this.back}> + <FontAwesomeIcon icon="caret-left" size="lg"></FontAwesomeIcon> + </button> + <button className="submitUrl" onClick={this.forward}> + <FontAwesomeIcon icon="caret-right" size="lg"></FontAwesomeIcon> + </button> + </div> + </div> + </div> + </div> + </div> + ); + } + + @observable viewType = this.selectedDoc?._viewType; render() { @@ -648,6 +801,9 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu </button> </Tooltip> } + {/* {!this.props.isOverlay || this.document.type !== DocumentType.WEB || this.isText || this.props.isDoc ? (null) : + this.urlEditor + } */} {!this.isText ? <> {this.drawButtons} diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx index 615f9bb77..4754adc90 100644 --- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx +++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx @@ -1,19 +1,19 @@ import React = require("react"); -import { ReactTableDefaults, TableCellRenderer, RowInfo } from "react-table"; -import "./CollectionSchemaView.scss"; -import { Transform } from "../../util/Transform"; -import { Doc } from "../../../fields/Doc"; -import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager"; -import { Cast, FieldValue, StrCast } from "../../../fields/Types"; -import { ContextMenu } from "../ContextMenu"; -import { action } from "mobx"; import { library } from '@fortawesome/fontawesome-svg-core'; import { faGripVertical, faTrash } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { DocumentManager } from "../../util/DocumentManager"; +import { action } from "mobx"; +import { ReactTableDefaults, RowInfo, TableCellRenderer } from "react-table"; +import { Doc } from "../../../fields/Doc"; import { SchemaHeaderField } from "../../../fields/SchemaHeaderField"; -import { undoBatch } from "../../util/UndoManager"; +import { Cast, FieldValue, StrCast } from "../../../fields/Types"; +import { DocumentManager } from "../../util/DocumentManager"; +import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; import { SnappingManager } from "../../util/SnappingManager"; +import { Transform } from "../../util/Transform"; +import { undoBatch } from "../../util/UndoManager"; +import { ContextMenu } from "../ContextMenu"; +import "./CollectionSchemaView.scss"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; library.add(faGripVertical, faTrash); @@ -226,13 +226,15 @@ export class MovableRow extends React.Component<MovableRowProps> { render() { const { children = null, rowInfo } = this.props; + if (!rowInfo) { return <ReactTableDefaults.TrComponent>{children}</ReactTableDefaults.TrComponent>; } const { original } = rowInfo; const doc = FieldValue(Cast(original, Doc)); - if (!doc) return <></>; + + if (!doc) return (null); const reference = React.createRef<HTMLDivElement>(); const onItemDown = SetupDrag(reference, () => doc, this.move, StrCast(this.props.dropAction) as dropActionType); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b6a0504e2..e5af1b0cc 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -720,14 +720,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque </div>; } else { - //subtracted 250 for offset + //subtracted for offset var str: string = ""; for (var i = 0; i < this._pointsX.length; i++) { var x = 0; - x = this._pointsX[i] - 250; + x = this._pointsX[i] - 64; str += x.toString(); str += ","; - str += this._pointsY[i].toString(); + str += (this._pointsY[i] - 85).toString(); str += (" "); } diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index cf8645e4c..70e7d8311 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -129,9 +129,15 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp LinkDescriptionPopup.popupY = e.screenY - 100; LinkDescriptionPopup.descriptionPopup = true; - LinkDescriptionPopup.popupX = e.screenX; - LinkDescriptionPopup.popupY = e.screenY - 100; - LinkDescriptionPopup.descriptionPopup = true; + const rect = document.body.getBoundingClientRect(); + if (LinkDescriptionPopup.popupX + 200 > rect.width) { + LinkDescriptionPopup.popupX -= 190; + TaskCompletionBox.popupX -= 40; + } + if (LinkDescriptionPopup.popupY + 100 > rect.height) { + LinkDescriptionPopup.popupY -= 40; + TaskCompletionBox.popupY -= 40; + } setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500); } @@ -176,6 +182,17 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp LinkDescriptionPopup.popupY = screenY - 100; LinkDescriptionPopup.descriptionPopup = true; } + + const rect = document.body.getBoundingClientRect(); + if (LinkDescriptionPopup.popupX + 200 > rect.width) { + LinkDescriptionPopup.popupX -= 190; + TaskCompletionBox.popupX -= 40; + } + if (LinkDescriptionPopup.popupY + 100 > rect.height) { + LinkDescriptionPopup.popupY -= 40; + TaskCompletionBox.popupY -= 40; + } + setTimeout(action(() => { TaskCompletionBox.taskCompleted = false; }), 2500); } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9ae113150..7cf6b0f39 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -617,6 +617,16 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu LinkDescriptionPopup.popupY = de.y; LinkDescriptionPopup.descriptionPopup = true; + const rect = document.body.getBoundingClientRect(); + if (LinkDescriptionPopup.popupX + 200 > rect.width) { + LinkDescriptionPopup.popupX -= 190; + TaskCompletionBox.popupX -= 40; + } + if (LinkDescriptionPopup.popupY + 100 > rect.height) { + LinkDescriptionPopup.popupY -= 40; + TaskCompletionBox.popupY -= 40; + } + setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500); }); if (de.complete.annoDragData) { |