aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/DocServer.ts13
-rw-r--r--src/client/Network.ts14
-rw-r--r--src/client/apis/GoogleAuthenticationManager.tsx40
-rw-r--r--src/client/apis/google_docs/GoogleApiClientUtils.ts35
-rw-r--r--src/client/apis/google_docs/GooglePhotosClientUtils.ts27
-rw-r--r--src/client/apis/youtube/YoutubeBox.tsx102
-rw-r--r--src/client/cognitive_services/CognitiveServices.ts79
-rw-r--r--src/client/documents/DocumentTypes.ts3
-rw-r--r--src/client/documents/Documents.ts115
-rw-r--r--src/client/northstar/dash-fields/HistogramField.ts8
-rw-r--r--src/client/northstar/dash-nodes/HistogramBox.tsx12
-rw-r--r--src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts16
-rw-r--r--src/client/northstar/operations/BaseOperation.ts14
-rw-r--r--src/client/northstar/utils/MathUtil.ts44
-rw-r--r--src/client/util/DictationManager.ts75
-rw-r--r--src/client/util/DocumentManager.ts74
-rw-r--r--src/client/util/DragManager.ts459
-rw-r--r--src/client/util/DropConverter.ts10
-rw-r--r--src/client/util/History.ts9
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx96
-rw-r--r--src/client/util/Import & Export/ImageUtils.ts9
-rw-r--r--src/client/util/Import & Export/ImportMetadataEntry.tsx6
-rw-r--r--src/client/util/InteractionUtils.ts51
-rw-r--r--src/client/util/LinkManager.ts68
-rw-r--r--src/client/util/ParagraphNodeSpec.ts10
-rw-r--r--src/client/util/ProsemirrorExampleTransfer.ts60
-rw-r--r--src/client/util/RichTextRules.ts228
-rw-r--r--src/client/util/RichTextSchema.tsx304
-rw-r--r--src/client/util/Scripting.ts38
-rw-r--r--src/client/util/SearchUtil.ts32
-rw-r--r--src/client/util/SelectionManager.ts5
-rw-r--r--src/client/util/SerializationHelper.ts9
-rw-r--r--src/client/util/SharingManager.tsx14
-rw-r--r--src/client/util/TooltipLinkingMenu.tsx22
-rw-r--r--src/client/util/TooltipTextMenu.scss4
-rw-r--r--src/client/util/TooltipTextMenu.tsx1335
-rw-r--r--src/client/util/TypedEvent.ts62
-rw-r--r--src/client/util/UndoManager.ts16
-rw-r--r--src/client/views/CollectionLinearView.tsx15
-rw-r--r--src/client/views/ContextMenu.tsx6
-rw-r--r--src/client/views/ContextMenuItem.tsx2
-rw-r--r--src/client/views/DictationOverlay.tsx10
-rw-r--r--src/client/views/DocComponent.tsx11
-rw-r--r--src/client/views/DocumentButtonBar.scss32
-rw-r--r--src/client/views/DocumentButtonBar.tsx102
-rw-r--r--src/client/views/DocumentDecorations.tsx161
-rw-r--r--src/client/views/EditableView.tsx12
-rw-r--r--src/client/views/GlobalKeyHandler.ts40
-rw-r--r--src/client/views/InkSelectDecorations.tsx10
-rw-r--r--src/client/views/InkingControl.tsx26
-rw-r--r--src/client/views/InkingStroke.tsx28
-rw-r--r--src/client/views/Main.scss14
-rw-r--r--src/client/views/MainView.scss23
-rw-r--r--src/client/views/MainView.tsx166
-rw-r--r--src/client/views/MainViewModal.tsx6
-rw-r--r--src/client/views/MetadataEntryMenu.tsx11
-rw-r--r--src/client/views/OverlayView.tsx11
-rw-r--r--src/client/views/PreviewCursor.tsx99
-rw-r--r--src/client/views/ScriptBox.tsx18
-rw-r--r--src/client/views/TemplateMenu.scss8
-rw-r--r--src/client/views/TemplateMenu.tsx59
-rw-r--r--src/client/views/Touchable.tsx19
-rw-r--r--src/client/views/collections/CollectionDockingView.scss2
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx188
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx207
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx62
-rw-r--r--src/client/views/collections/CollectionSchemaHeaders.tsx36
-rw-r--r--src/client/views/collections/CollectionSchemaMovableTableHOC.tsx91
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx121
-rw-r--r--src/client/views/collections/CollectionStackingView.scss5
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx174
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx97
-rw-r--r--src/client/views/collections/CollectionStaffView.tsx6
-rw-r--r--src/client/views/collections/CollectionSubView.tsx126
-rw-r--r--src/client/views/collections/CollectionTreeView.scss4
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx292
-rw-r--r--src/client/views/collections/CollectionView.scss2
-rw-r--r--src/client/views/collections/CollectionView.tsx58
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx88
-rw-r--r--src/client/views/collections/KeyRestrictionRow.tsx6
-rw-r--r--src/client/views/collections/ParentDocumentSelector.scss28
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx86
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx25
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx102
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx22
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx12
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss17
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx341
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.scss3
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx139
-rw-r--r--src/client/views/document_templates/caption_toggle/DetailedCaptionToggle.tsx6
-rw-r--r--src/client/views/linking/LinkEditor.tsx82
-rw-r--r--src/client/views/linking/LinkFollowBox.tsx48
-rw-r--r--src/client/views/linking/LinkMenu.scss85
-rw-r--r--src/client/views/linking/LinkMenu.tsx6
-rw-r--r--src/client/views/linking/LinkMenuGroup.tsx44
-rw-r--r--src/client/views/linking/LinkMenuItem.scss87
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx38
-rw-r--r--src/client/views/nodes/AudioBox.tsx27
-rw-r--r--src/client/views/nodes/ButtonBox.tsx17
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx23
-rw-r--r--src/client/views/nodes/ContentFittingDocumentView.scss5
-rw-r--r--src/client/views/nodes/ContentFittingDocumentView.tsx42
-rw-r--r--src/client/views/nodes/DocuLinkBox.tsx35
-rw-r--r--src/client/views/nodes/DocumentBox.scss15
-rw-r--r--src/client/views/nodes/DocumentBox.tsx114
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx8
-rw-r--r--src/client/views/nodes/DocumentView.scss1
-rw-r--r--src/client/views/nodes/DocumentView.tsx242
-rw-r--r--src/client/views/nodes/FaceRectangle.tsx2
-rw-r--r--src/client/views/nodes/FaceRectangles.tsx8
-rw-r--r--src/client/views/nodes/FieldView.tsx5
-rw-r--r--src/client/views/nodes/FontIconBox.tsx8
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss282
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx445
-rw-r--r--src/client/views/nodes/FormattedTextBoxComment.tsx49
-rw-r--r--src/client/views/nodes/IconBox.tsx6
-rw-r--r--src/client/views/nodes/ImageBox.scss68
-rw-r--r--src/client/views/nodes/ImageBox.tsx178
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx48
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx10
-rw-r--r--src/client/views/nodes/PDFBox.scss306
-rw-r--r--src/client/views/nodes/PDFBox.tsx85
-rw-r--r--src/client/views/nodes/PresBox.tsx38
-rw-r--r--src/client/views/nodes/VideoBox.scss5
-rw-r--r--src/client/views/nodes/VideoBox.tsx127
-rw-r--r--src/client/views/nodes/WebBox.tsx24
-rw-r--r--src/client/views/pdf/Annotation.tsx14
-rw-r--r--src/client/views/pdf/PDFMenu.tsx36
-rw-r--r--src/client/views/pdf/PDFViewer.scss4
-rw-r--r--src/client/views/pdf/PDFViewer.tsx109
-rw-r--r--src/client/views/presentationview/PresElementBox.tsx17
-rw-r--r--src/client/views/search/FilterBox.tsx53
-rw-r--r--src/client/views/search/IconButton.tsx2
-rw-r--r--src/client/views/search/NaviconButton.tsx22
-rw-r--r--src/client/views/search/SearchBox.scss7
-rw-r--r--src/client/views/search/SearchBox.tsx51
-rw-r--r--src/client/views/search/SearchItem.scss108
-rw-r--r--src/client/views/search/SearchItem.tsx154
-rw-r--r--src/client/views/search/ToggleBar.tsx3
141 files changed, 4923 insertions, 5025 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 2cec1046b..ed7fbd7ba 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,5 +1,5 @@
import * as OpenSocket from 'socket.io-client';
-import { MessageStore, Diff, YoutubeQueryTypes } from "./../server/Message";
+import { MessageStore, YoutubeQueryTypes } from "./../server/Message";
import { Opt, Doc } from '../new_fields/Doc';
import { Utils, emptyFunction } from '../Utils';
import { SerializationHelper } from './util/SerializationHelper';
@@ -82,6 +82,9 @@ export namespace DocServer {
Utils.AddServerHandler(_socket, MessageStore.UpdateField, respondToUpdate);
Utils.AddServerHandler(_socket, MessageStore.DeleteField, respondToDelete);
Utils.AddServerHandler(_socket, MessageStore.DeleteFields, respondToDelete);
+ Utils.AddServerHandler(_socket, MessageStore.ConnectionTerminated, () => {
+ alert("Your connection to the server has been terminated.");
+ });
}
function errorFunc(): never {
@@ -148,7 +151,7 @@ export namespace DocServer {
// an initial pass through the cache to determine whether the document needs to be fetched,
// is already in the process of being fetched or already exists in the
// cache
- let cached = _cache[id];
+ const cached = _cache[id];
if (cached === undefined) {
// NOT CACHED => we'll have to send a request to the server
@@ -195,7 +198,7 @@ export namespace DocServer {
}
export async function getYoutubeChannels() {
- let apiKey = await Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.Channels });
+ const apiKey = await Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.Channels });
return apiKey;
}
@@ -255,7 +258,7 @@ export namespace DocServer {
for (const field of fields) {
if (field !== undefined) {
// deserialize
- let prom = SerializationHelper.Deserialize(field).then(deserialized => {
+ const prom = SerializationHelper.Deserialize(field).then(deserialized => {
fieldMap[field.id] = deserialized;
//overwrite or delete any promises (that we inserted as flags
@@ -411,7 +414,7 @@ export namespace DocServer {
}
let _RespondToUpdate = _respondToUpdateImpl;
- let _respondToDelete = _respondToDeleteImpl;
+ const _respondToDelete = _respondToDeleteImpl;
function respondToUpdate(diff: any) {
_RespondToUpdate(diff);
diff --git a/src/client/Network.ts b/src/client/Network.ts
index 75ccb5e99..ccf60f199 100644
--- a/src/client/Network.ts
+++ b/src/client/Network.ts
@@ -1,18 +1,16 @@
import { Utils } from "../Utils";
-import { CurrentUserUtils } from "../server/authentication/models/current_user_utils";
import requestPromise = require('request-promise');
-export namespace Identified {
+export namespace Networking {
export async function FetchFromServer(relativeRoute: string) {
- return (await fetch(relativeRoute, { headers: { userId: CurrentUserUtils.id } })).text();
+ return (await fetch(relativeRoute)).text();
}
export async function PostToServer(relativeRoute: string, body?: any) {
- let options = {
+ const options = {
uri: Utils.prepend(relativeRoute),
method: "POST",
- headers: { userId: CurrentUserUtils.id },
body,
json: true
};
@@ -22,12 +20,10 @@ export namespace Identified {
export async function PostFormDataToServer(relativeRoute: string, formData: FormData) {
const parameters = {
method: 'POST',
- headers: { userId: CurrentUserUtils.id },
- body: formData,
+ body: formData
};
const response = await fetch(relativeRoute, parameters);
- const text = await response.json();
- return text;
+ return response.json();
}
} \ No newline at end of file
diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx
index 01dac3996..ce1277667 100644
--- a/src/client/apis/GoogleAuthenticationManager.tsx
+++ b/src/client/apis/GoogleAuthenticationManager.tsx
@@ -3,8 +3,7 @@ import { observer } from "mobx-react";
import * as React from "react";
import MainViewModal from "../views/MainViewModal";
import { Opt } from "../../new_fields/Doc";
-import { Identified } from "../Network";
-import { RouteStore } from "../../server/RouteStore";
+import { Networking } from "../Network";
import "./GoogleAuthenticationManager.scss";
const AuthenticationUrl = "https://accounts.google.com/o/oauth2/v2/auth";
@@ -31,7 +30,7 @@ export default class GoogleAuthenticationManager extends React.Component<{}> {
}
public fetchOrGenerateAccessToken = async () => {
- let response = await Identified.FetchFromServer(RouteStore.readGoogleAccessToken);
+ const response = await Networking.FetchFromServer("/readGoogleAccessToken");
// if this is an authentication url, activate the UI to register the new access token
if (new RegExp(AuthenticationUrl).test(response)) {
this.isOpen = true;
@@ -39,24 +38,25 @@ export default class GoogleAuthenticationManager extends React.Component<{}> {
return new Promise<string>(async resolve => {
const disposer = reaction(
() => this.authenticationCode,
- authenticationCode => {
- if (authenticationCode) {
- Identified.PostToServer(RouteStore.writeGoogleAccessToken, { authenticationCode }).then(
- ({ access_token, avatar, name }) => {
- runInAction(() => {
- this.avatar = avatar;
- this.username = name;
- });
- this.beginFadeout();
- disposer();
- resolve(access_token);
- },
- action(() => {
- this.hasBeenClicked = false;
- this.success = false;
- })
- );
+ async authenticationCode => {
+ if (!authenticationCode) {
+ return;
}
+ const { access_token, avatar, name } = await Networking.PostToServer(
+ "/writeGoogleAccessToken",
+ { authenticationCode }
+ );
+ runInAction(() => {
+ this.avatar = avatar;
+ this.username = name;
+ });
+ this.beginFadeout();
+ disposer();
+ resolve(access_token);
+ action(() => {
+ this.hasBeenClicked = false;
+ this.success = false;
+ });
}
);
});
diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts
index 1cf01fc3d..d2a79f189 100644
--- a/src/client/apis/google_docs/GoogleApiClientUtils.ts
+++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts
@@ -1,9 +1,8 @@
-import { docs_v1, slides_v1 } from "googleapis";
-import { RouteStore } from "../../../server/RouteStore";
+import { docs_v1 } from "googleapis";
import { Opt } from "../../../new_fields/Doc";
import { isArray } from "util";
import { EditorState } from "prosemirror-state";
-import { Identified } from "../../Network";
+import { Networking } from "../../Network";
export const Pulls = "googleDocsPullCount";
export const Pushes = "googleDocsPushCount";
@@ -77,14 +76,14 @@ export namespace GoogleApiClientUtils {
* @returns the documentId of the newly generated document, or undefined if the creation process fails.
*/
export const create = async (options: CreateOptions): Promise<CreationResult> => {
- const path = `${RouteStore.googleDocs}/Documents/${Actions.Create}`;
+ const path = `/googleDocs/Documents/${Actions.Create}`;
const parameters = {
requestBody: {
title: options.title || `Dash Export (${new Date().toDateString()})`
}
};
try {
- const schema: docs_v1.Schema$Document = await Identified.PostToServer(path, parameters);
+ const schema: docs_v1.Schema$Document = await Networking.PostToServer(path, parameters);
return schema.documentId;
} catch {
return undefined;
@@ -95,7 +94,7 @@ export namespace GoogleApiClientUtils {
export type ExtractResult = { text: string, paragraphs: DeconstructedParagraph[] };
export const extractText = (document: docs_v1.Schema$Document, removeNewlines = false): ExtractResult => {
- let paragraphs = extractParagraphs(document);
+ const paragraphs = extractParagraphs(document);
let text = paragraphs.map(paragraph => paragraph.contents.filter(content => !("inlineObjectId" in content)).map(run => run as docs_v1.Schema$TextRun).join("")).join("");
text = text.substring(0, text.length - 1);
removeNewlines && text.ReplaceAll("\n", "");
@@ -108,14 +107,14 @@ export namespace GoogleApiClientUtils {
const fragments: DeconstructedParagraph[] = [];
if (document.body && document.body.content) {
for (const element of document.body.content) {
- let runs: ContentArray = [];
+ const runs: ContentArray = [];
let bullet: Opt<number>;
if (element.paragraph) {
if (element.paragraph.elements) {
for (const inner of element.paragraph.elements) {
if (inner) {
if (inner.textRun) {
- let run = inner.textRun;
+ const run = inner.textRun;
(run.content || !filterEmpty) && runs.push(inner.textRun);
} else if (inner.inlineObjectElement) {
runs.push(inner.inlineObjectElement);
@@ -154,10 +153,10 @@ export namespace GoogleApiClientUtils {
}
export const retrieve = async (options: RetrieveOptions): Promise<RetrievalResult> => {
- const path = `${RouteStore.googleDocs}/Documents/${Actions.Retrieve}`;
+ const path = `/googleDocs/Documents/${Actions.Retrieve}`;
try {
const parameters = { documentId: options.documentId };
- const schema: RetrievalResult = await Identified.PostToServer(path, parameters);
+ const schema: RetrievalResult = await Networking.PostToServer(path, parameters);
return schema;
} catch {
return undefined;
@@ -165,7 +164,7 @@ export namespace GoogleApiClientUtils {
};
export const update = async (options: UpdateOptions): Promise<UpdateResult> => {
- const path = `${RouteStore.googleDocs}/Documents/${Actions.Update}`;
+ const path = `/googleDocs/Documents/${Actions.Update}`;
const parameters = {
documentId: options.documentId,
requestBody: {
@@ -173,7 +172,7 @@ export namespace GoogleApiClientUtils {
}
};
try {
- const replies: UpdateResult = await Identified.PostToServer(path, parameters);
+ const replies: UpdateResult = await Networking.PostToServer(path, parameters);
return replies;
} catch {
return undefined;
@@ -183,8 +182,8 @@ export namespace GoogleApiClientUtils {
export const read = async (options: ReadOptions): Promise<Opt<ReadResult>> => {
return retrieve({ documentId: options.documentId }).then(document => {
if (document) {
- let title = document.title!;
- let body = Utils.extractText(document, options.removeNewlines).text;
+ const title = document.title!;
+ const body = Utils.extractText(document, options.removeNewlines).text;
return { title, body };
}
});
@@ -193,7 +192,7 @@ export namespace GoogleApiClientUtils {
export const readLines = async (options: ReadOptions): Promise<Opt<ReadLinesResult>> => {
return retrieve({ documentId: options.documentId }).then(document => {
if (document) {
- let title = document.title;
+ const title = document.title;
let bodyLines = Utils.extractText(document).text.split("\n");
options.removeNewlines && (bodyLines = bodyLines.filter(line => line.length));
return { title, bodyLines };
@@ -202,7 +201,7 @@ export namespace GoogleApiClientUtils {
};
export const setStyle = async (options: UpdateOptions) => {
- let replies: any = await update({
+ const replies: any = await update({
documentId: options.documentId,
requests: options.requests
});
@@ -222,7 +221,7 @@ export namespace GoogleApiClientUtils {
let index = options.index;
const mode = options.mode;
if (!(index && mode === WriteMode.Insert)) {
- let schema = await retrieve({ documentId });
+ const schema = await retrieve({ documentId });
if (!schema || !(index = Utils.endOf(schema))) {
return undefined;
}
@@ -249,7 +248,7 @@ export namespace GoogleApiClientUtils {
return undefined;
}
requests.push(...options.content.requests);
- let replies: any = await update({ documentId: documentId, requests });
+ const replies: any = await update({ documentId: documentId, requests });
if ("errors" in replies) {
console.log("Write operation failed:");
console.log(replies.errors.map((error: any) => error.message));
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index e93fa6eb4..966d8053a 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -1,5 +1,4 @@
import { Utils } from "../../../Utils";
-import { RouteStore } from "../../../server/RouteStore";
import { ImageField } from "../../../new_fields/URLField";
import { Cast, StrCast } from "../../../new_fields/Types";
import { Doc, Opt, DocListCastAsync } from "../../../new_fields/Doc";
@@ -13,7 +12,7 @@ import { Docs, DocumentOptions } from "../../documents/Documents";
import { NewMediaItemResult, MediaItem } from "../../../server/apis/google/SharedTypes";
import { AssertionError } from "assert";
import { DocumentView } from "../../views/nodes/DocumentView";
-import { Identified } from "../../Network";
+import { Networking } from "../../Network";
import GoogleAuthenticationManager from "../GoogleAuthenticationManager";
export namespace GooglePhotos {
@@ -78,6 +77,7 @@ export namespace GooglePhotos {
}
export const CollectionToAlbum = async (options: AlbumCreationOptions): Promise<Opt<AlbumCreationResult>> => {
+ await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
const { collection, title, descriptionKey, tag } = options;
const dataDocument = Doc.GetProto(collection);
const images = ((await DocListCastAsync(dataDocument.data)) || []).filter(doc => Cast(doc.data, ImageField));
@@ -127,10 +127,11 @@ export namespace GooglePhotos {
export type CollectionConstructor = (data: Array<Doc>, options: DocumentOptions, ...args: any) => Doc;
export const CollectionFromSearch = async (constructor: CollectionConstructor, requested: Opt<Partial<Query.SearchOptions>>): Promise<Doc> => {
- let response = await Query.ContentSearch(requested);
- let uploads = await Transactions.WriteMediaItemsToServer(response);
+ await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
+ const response = await Query.ContentSearch(requested);
+ const uploads = await Transactions.WriteMediaItemsToServer(response);
const children = uploads.map((upload: Transactions.UploadInformation) => {
- let document = Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean));
+ const document = Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean));
document.fillColumn = true;
document.contentSize = upload.contentSize;
return document;
@@ -147,6 +148,7 @@ export namespace GooglePhotos {
const comparator = (a: string, b: string) => (a < b) ? -1 : (a > b ? 1 : 0);
export const TagChildImages = async (collection: Doc) => {
+ await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
const idMapping = await Cast(collection.googlePhotosIdMapping, Doc);
if (!idMapping) {
throw new Error("Appending image metadata requires that the targeted collection have already been mapped to an album!");
@@ -155,12 +157,12 @@ export namespace GooglePhotos {
const images = (await DocListCastAsync(collection.data))!.map(Doc.GetProto);
images && images.forEach(image => tagMapping.set(image[Id], ContentCategories.NONE));
const values = Object.values(ContentCategories);
- for (let value of values) {
+ for (const value of values) {
if (value !== ContentCategories.NONE) {
const results = await ContentSearch({ included: [value] });
if (results.mediaItems) {
const ids = results.mediaItems.map(item => item.id);
- for (let id of ids) {
+ for (const id of ids) {
const image = await Cast(idMapping[id], Doc);
if (image) {
const key = image[Id];
@@ -218,9 +220,9 @@ export namespace GooglePhotos {
export const AlbumSearch = async (albumId: string, pageSize = 100): Promise<MediaItem[]> => {
const photos = await endpoint();
- let mediaItems: MediaItem[] = [];
+ const mediaItems: MediaItem[] = [];
let nextPageTokenStored: Opt<string> = undefined;
- let found = 0;
+ const found = 0;
do {
const response: any = await photos.mediaItems.search(albumId, pageSize, nextPageTokenStored);
mediaItems.push(...response.mediaItems);
@@ -304,7 +306,7 @@ export namespace GooglePhotos {
};
export const WriteMediaItemsToServer = async (body: { mediaItems: any[] }): Promise<UploadInformation[]> => {
- const uploads = await Identified.PostToServer(RouteStore.googlePhotosMediaDownload, body);
+ const uploads = await Networking.PostToServer("/googlePhotosMediaDownload", body);
return uploads;
};
@@ -325,11 +327,12 @@ export namespace GooglePhotos {
}
export const UploadImages = async (sources: Doc[], album?: AlbumReference, descriptionKey = "caption"): Promise<Opt<ImageUploadResults>> => {
+ await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
if (album && "title" in album) {
album = await Create.Album(album.title);
}
const media: MediaInput[] = [];
- for (let source of sources) {
+ for (const source of sources) {
const data = Cast(Doc.GetProto(source).data, ImageField);
if (!data) {
return;
@@ -341,7 +344,7 @@ export namespace GooglePhotos {
media.push({ url, description });
}
if (media.length) {
- const results = await Identified.PostToServer(RouteStore.googlePhotosMediaUpload, { media, album });
+ const results = await Networking.PostToServer("/googlePhotosMediaUpload", { media, album });
return results;
}
};
diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index bed812852..fd3d9e2f1 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -48,44 +48,44 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
*/
async componentWillMount() {
//DocServer.getYoutubeChannels();
- let castedSearchBackUp = Cast(this.props.Document.cachedSearchResults, Doc);
- let awaitedBackUp = await castedSearchBackUp;
- let castedDetailBackUp = Cast(this.props.Document.cachedDetails, Doc);
- let awaitedDetails = await castedDetailBackUp;
+ const castedSearchBackUp = Cast(this.props.Document.cachedSearchResults, Doc);
+ const awaitedBackUp = await castedSearchBackUp;
+ const castedDetailBackUp = Cast(this.props.Document.cachedDetails, Doc);
+ const awaitedDetails = await castedDetailBackUp;
if (awaitedBackUp) {
- let jsonList = await DocListCastAsync(awaitedBackUp.json);
- let jsonDetailList = await DocListCastAsync(awaitedDetails!.json);
+ const jsonList = await DocListCastAsync(awaitedBackUp.json);
+ const jsonDetailList = await DocListCastAsync(awaitedDetails!.json);
if (jsonList!.length !== 0) {
runInAction(() => this.searchResultsFound = true);
let index = 0;
//getting the necessary information from backUps and building templates that will be used to map in render
- for (let video of jsonList!) {
-
- let videoId = await Cast(video.id, Doc);
- let id = StrCast(videoId!.videoId);
- let snippet = await Cast(video.snippet, Doc);
- let videoTitle = this.filterYoutubeTitleResult(StrCast(snippet!.title));
- let thumbnail = await Cast(snippet!.thumbnails, Doc);
- let thumbnailMedium = await Cast(thumbnail!.medium, Doc);
- let thumbnailUrl = StrCast(thumbnailMedium!.url);
- let videoDescription = StrCast(snippet!.description);
- let pusblishDate = (this.roundPublishTime(StrCast(snippet!.publishedAt)))!;
- let channelTitle = StrCast(snippet!.channelTitle);
+ for (const video of jsonList!) {
+
+ const videoId = await Cast(video.id, Doc);
+ const id = StrCast(videoId!.videoId);
+ const snippet = await Cast(video.snippet, Doc);
+ const videoTitle = this.filterYoutubeTitleResult(StrCast(snippet!.title));
+ const thumbnail = await Cast(snippet!.thumbnails, Doc);
+ const thumbnailMedium = await Cast(thumbnail!.medium, Doc);
+ const thumbnailUrl = StrCast(thumbnailMedium!.url);
+ const videoDescription = StrCast(snippet!.description);
+ const pusblishDate = (this.roundPublishTime(StrCast(snippet!.publishedAt)))!;
+ const channelTitle = StrCast(snippet!.channelTitle);
let duration: string = "";
let viewCount: string = "";
if (jsonDetailList!.length !== 0) {
- let contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc);
- let statistics = await Cast(jsonDetailList![index].statistics, Doc);
+ const contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc);
+ const statistics = await Cast(jsonDetailList![index].statistics, Doc);
duration = this.convertIsoTimeToDuration(StrCast(contentDetails!.duration));
viewCount = this.abbreviateViewCount(parseInt(StrCast(statistics!.viewCount)))!;
}
index = index + 1;
- let newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration, viewCount: viewCount };
+ const newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration, viewCount: viewCount };
runInAction(() => this.curVideoTemplates.push(newTemplate));
}
}
@@ -115,7 +115,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
*/
onEnterKeyDown = (e: React.KeyboardEvent) => {
if (e.keyCode === 13) {
- let submittedTitle = this.YoutubeSearchElement!.value;
+ const submittedTitle = this.YoutubeSearchElement!.value;
this.YoutubeSearchElement!.value = "";
this.YoutubeSearchElement!.blur();
DocServer.getYoutubeVideos(submittedTitle, this.processesVideoResults);
@@ -184,23 +184,23 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
* difference between today's date and that date, in terms of "ago" to imitate youtube.
*/
roundPublishTime = (publishTime: string) => {
- let date = new Date(publishTime).getTime();
- let curDate = new Date().getTime();
- let timeDif = curDate - date;
- let totalSeconds = timeDif / 1000;
- let totalMin = totalSeconds / 60;
- let totalHours = totalMin / 60;
- let totalDays = totalHours / 24;
- let totalMonths = totalDays / 30.417;
- let totalYears = totalMonths / 12;
-
-
- let truncYears = Math.trunc(totalYears);
- let truncMonths = Math.trunc(totalMonths);
- let truncDays = Math.trunc(totalDays);
- let truncHours = Math.trunc(totalHours);
- let truncMin = Math.trunc(totalMin);
- let truncSec = Math.trunc(totalSeconds);
+ const date = new Date(publishTime).getTime();
+ const curDate = new Date().getTime();
+ const timeDif = curDate - date;
+ const totalSeconds = timeDif / 1000;
+ const totalMin = totalSeconds / 60;
+ const totalHours = totalMin / 60;
+ const totalDays = totalHours / 24;
+ const totalMonths = totalDays / 30.417;
+ const totalYears = totalMonths / 12;
+
+
+ const truncYears = Math.trunc(totalYears);
+ const truncMonths = Math.trunc(totalMonths);
+ const truncDays = Math.trunc(totalDays);
+ const truncHours = Math.trunc(totalHours);
+ const truncMin = Math.trunc(totalMin);
+ const truncSec = Math.trunc(totalSeconds);
let pluralCase = "";
@@ -230,7 +230,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
*/
convertIsoTimeToDuration = (isoDur: string) => {
- let convertedTime = isoDur.replace(/D|H|M/g, ":").replace(/P|T|S/g, "").split(":");
+ const convertedTime = isoDur.replace(/D|H|M/g, ":").replace(/P|T|S/g, "").split(":");
if (1 === convertedTime.length) {
2 !== convertedTime[0].length && (convertedTime[0] = "0" + convertedTime[0]), convertedTime[0] = "0:" + convertedTime[0];
@@ -269,10 +269,10 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
if (this.searchResults.length !== 0) {
return <ul>
{this.searchResults.map((video, index) => {
- let filteredTitle = this.filterYoutubeTitleResult(video.snippet.title);
- let channelTitle = video.snippet.channelTitle;
- let videoDescription = video.snippet.description;
- let pusblishDate = this.roundPublishTime(video.snippet.publishedAt);
+ const filteredTitle = this.filterYoutubeTitleResult(video.snippet.title);
+ const channelTitle = video.snippet.channelTitle;
+ const videoDescription = video.snippet.description;
+ const pusblishDate = this.roundPublishTime(video.snippet.publishedAt);
let duration;
let viewCount;
if (this.videoDetails.length !== 0) {
@@ -331,26 +331,26 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
*/
@action
embedVideoOnClick = (videoId: string, filteredTitle: string) => {
- let embeddedUrl = "https://www.youtube.com/embed/" + videoId;
+ const embeddedUrl = "https://www.youtube.com/embed/" + videoId;
this.selectedVideoUrl = embeddedUrl;
- let addFunction = this.props.addDocument!;
- let newVideoX = NumCast(this.props.Document.x);
- let newVideoY = NumCast(this.props.Document.y) + NumCast(this.props.Document.height);
+ const addFunction = this.props.addDocument!;
+ const newVideoX = NumCast(this.props.Document.x);
+ const newVideoY = NumCast(this.props.Document.y) + NumCast(this.props.Document.height);
addFunction(Docs.Create.VideoDocument(embeddedUrl, { title: filteredTitle, width: 400, height: 315, x: newVideoX, y: newVideoY }));
this.videoClicked = true;
}
render() {
- let content =
+ const content =
<div className="youtubeBox-cont" style={{ width: "100%", height: "100%", position: "absolute" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
<input type="text" placeholder="Search for a video" onKeyDown={this.onEnterKeyDown} style={{ height: 40, width: "100%", border: "1px solid black", padding: 5, textAlign: "center" }} ref={(e) => this.YoutubeSearchElement = e!} />
{this.renderSearchResultsOrVideo()}
</div>;
- let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
+ const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
- let classname = "webBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+ const classname = "webBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
return (
<>
<div className={classname} >
diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts
index 08fcb4883..02eff3b25 100644
--- a/src/client/cognitive_services/CognitiveServices.ts
+++ b/src/client/cognitive_services/CognitiveServices.ts
@@ -1,8 +1,7 @@
import * as request from "request-promise";
-import { Doc, Field, Opt } from "../../new_fields/Doc";
+import { Doc, Field } from "../../new_fields/Doc";
import { Cast } from "../../new_fields/Types";
import { Docs } from "../documents/Documents";
-import { RouteStore } from "../../server/RouteStore";
import { Utils } from "../../Utils";
import { InkData } from "../../new_fields/InkField";
import { UndoManager } from "../util/UndoManager";
@@ -39,21 +38,19 @@ export enum Confidence {
export namespace CognitiveServices {
const ExecuteQuery = async <D>(service: Service, manager: APIManager<D>, data: D): Promise<any> => {
- return fetch(Utils.prepend(`${RouteStore.cognitiveServices}/${service}`)).then(async response => {
- let apiKey = await response.text();
- if (!apiKey) {
- console.log(`No API key found for ${service}: ensure index.ts has access to a .env file in your root directory`);
- return undefined;
- }
-
- let results: any;
- try {
- results = await manager.requester(apiKey, manager.converter(data), service).then(json => JSON.parse(json));
- } catch {
- results = undefined;
- }
- return results;
- });
+ const apiKey = await Utils.getApiKey(service);
+ if (!apiKey) {
+ console.log(`No API key found for ${service}: ensure index.ts has access to a .env file in your root directory.`);
+ return undefined;
+ }
+
+ let results: any;
+ try {
+ results = await manager.requester(apiKey, manager.converter(data), service).then(json => JSON.parse(json));
+ } catch {
+ results = undefined;
+ }
+ return results;
};
export namespace Image {
@@ -104,14 +101,14 @@ export namespace CognitiveServices {
export namespace Appliers {
export const ProcessImage: AnalysisApplier<string> = async (target: Doc, keys: string[], url: string, service: Service, converter: Converter) => {
- let batch = UndoManager.StartBatch("Image Analysis");
+ const batch = UndoManager.StartBatch("Image Analysis");
- let storageKey = keys[0];
+ const storageKey = keys[0];
if (!url || await Cast(target[storageKey], Doc)) {
return;
}
let toStore: any;
- let results = await ExecuteQuery(service, Manager, url);
+ const results = await ExecuteQuery(service, Manager, url);
if (!results) {
toStore = "Cognitive Services could not process the given image URL.";
} else {
@@ -134,36 +131,32 @@ export namespace CognitiveServices {
export namespace Inking {
- export const Manager: APIManager<InkData> = {
-
- converter: (inkData: InkData): string => {
- let entries = inkData.entries(), next = entries.next();
- let strokes: AzureStrokeData[] = [], id = 0;
- while (!next.done) {
- strokes.push({
- id: id++,
- points: next.value[1].pathData.map(point => `${point.x},${point.y}`).join(","),
- language: "en-US"
- });
- next = entries.next();
- }
+ export const Manager: APIManager<InkData[]> = {
+
+ converter: (inkData: InkData[]): string => {
+ let id = 0;
+ const strokes: AzureStrokeData[] = inkData.map(points => ({
+ id: id++,
+ points: points.map(({ x, y }) => `${x},${y}`).join(","),
+ language: "en-US"
+ }));
return JSON.stringify({
version: 1,
language: "en-US",
unit: "mm",
- strokes: strokes
+ strokes
});
},
requester: async (apiKey: string, body: string) => {
- let xhttp = new XMLHttpRequest();
- let serverAddress = "https://api.cognitive.microsoft.com";
- let endpoint = serverAddress + "/inkrecognizer/v1.0-preview/recognize";
+ const xhttp = new XMLHttpRequest();
+ const serverAddress = "https://api.cognitive.microsoft.com";
+ const endpoint = serverAddress + "/inkrecognizer/v1.0-preview/recognize";
- let promisified = (resolve: any, reject: any) => {
+ const promisified = (resolve: any, reject: any) => {
xhttp.onreadystatechange = function () {
if (this.readyState === 4) {
- let result = xhttp.responseText;
+ const result = xhttp.responseText;
switch (this.status) {
case 200:
return resolve(result);
@@ -187,15 +180,15 @@ export namespace CognitiveServices {
export namespace Appliers {
- export const ConcatenateHandwriting: AnalysisApplier<InkData> = async (target: Doc, keys: string[], inkData: InkData) => {
- let batch = UndoManager.StartBatch("Ink Analysis");
+ export const ConcatenateHandwriting: AnalysisApplier<InkData[]> = async (target: Doc, keys: string[], inkData: InkData[]) => {
+ const batch = UndoManager.StartBatch("Ink Analysis");
let results = await ExecuteQuery(Service.Handwriting, Manager, inkData);
if (results) {
results.recognitionUnits && (results = results.recognitionUnits);
target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Ink Analysis");
- let recognizedText = results.map((item: any) => item.recognizedText);
- let individualWords = recognizedText.filter((text: string) => text && text.split(" ").length === 1);
+ const recognizedText = results.map((item: any) => item.recognizedText);
+ const individualWords = recognizedText.filter((text: string) => text && text.split(" ").length === 1);
target[keys[1]] = individualWords.join(" ");
}
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index f6dd0c346..8f96b2fa6 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -25,5 +25,6 @@ export enum DocumentType {
COLOR = "color",
DOCULINK = "doculink",
PDFANNO = "pdfanno",
- INK = "ink"
+ INK = "ink",
+ DOCUMENT = "document"
} \ No newline at end of file
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8c6aa2006..e149963b9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -35,10 +35,10 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie
import { LinkManager } from "../util/LinkManager";
import { DocumentManager } from "../util/DocumentManager";
import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox";
-import { Scripting, CompileScript } from "../util/Scripting";
+import { Scripting } from "../util/Scripting";
import { ButtonBox } from "../views/nodes/ButtonBox";
import { FontIconBox } from "../views/nodes/FontIconBox";
-import { SchemaHeaderField, RandomPastel } from "../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
import { PresBox } from "../views/nodes/PresBox";
import { ComputedField, ScriptField } from "../../new_fields/ScriptField";
import { ProxyField } from "../../new_fields/Proxy";
@@ -48,10 +48,11 @@ import { PresElementBox } from "../views/presentationview/PresElementBox";
import { QueryBox } from "../views/nodes/QueryBox";
import { ColorBox } from "../views/nodes/ColorBox";
import { DocuLinkBox } from "../views/nodes/DocuLinkBox";
+import { DocumentBox } from "../views/nodes/DocumentBox";
import { InkingStroke } from "../views/InkingStroke";
import { InkField } from "../../new_fields/InkField";
-var requestImageSize = require('../util/request-image-size');
-var path = require('path');
+const requestImageSize = require('../util/request-image-size');
+const path = require('path');
export interface DocumentOptions {
x?: number;
@@ -96,6 +97,7 @@ export interface DocumentOptions {
schemaColumns?: List<SchemaHeaderField>;
dockingConfig?: string;
autoHeight?: boolean;
+ annotationOn?: Doc;
removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
dbDoc?: Doc;
ischecked?: ScriptField; // returns whether a font icon box is checked
@@ -112,6 +114,7 @@ export interface DocumentOptions {
dropConverter?: ScriptField; // script to run when documents are dropped on this Document.
strokeWidth?: number;
color?: string;
+ limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents
// [key: string]: Opt<Field>;
}
@@ -170,6 +173,10 @@ export namespace Docs {
layout: { view: KeyValueBox, dataField: data },
options: { height: 150 }
}],
+ [DocumentType.DOCUMENT, {
+ layout: { view: DocumentBox, dataField: data },
+ options: { height: 250 }
+ }],
[DocumentType.VID, {
layout: { view: VideoBox, dataField: data },
options: { currentTimecode: 0 },
@@ -180,7 +187,7 @@ export namespace Docs {
}],
[DocumentType.PDF, {
layout: { view: PDFBox, dataField: data },
- options: { nativeWidth: 1200, curPage: 1 }
+ options: { curPage: 1 }
}],
[DocumentType.ICON, {
layout: { view: IconBox, dataField: data },
@@ -215,7 +222,8 @@ export namespace Docs {
layout: { view: PresElementBox, dataField: data }
}],
[DocumentType.INK, {
- layout: { view: InkingStroke, dataField: data }
+ layout: { view: InkingStroke, dataField: data },
+ options: { backgroundColor: "transparent" }
}]
]);
@@ -238,16 +246,16 @@ export namespace Docs {
ProxyField.initPlugin();
ComputedField.initPlugin();
// non-guid string ids for each document prototype
- let prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix);
+ const prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix);
// fetch the actual prototype documents from the server
- let actualProtos = await DocServer.GetRefFields(prototypeIds);
+ const actualProtos = await DocServer.GetRefFields(prototypeIds);
// update this object to include any default values: DocumentOptions for all prototypes
prototypeIds.map(id => {
- let existing = actualProtos[id] as Doc;
- let type = id.replace(suffix, "") as DocumentType;
+ const existing = actualProtos[id] as Doc;
+ const type = id.replace(suffix, "") as DocumentType;
// get or create prototype of the specified type...
- let target = existing || buildPrototype(type, id);
+ const target = existing || buildPrototype(type, id);
// ...and set it if not undefined (can be undefined only if TemplateMap does not contain
// an entry dedicated to the given DocumentType)
target && PrototypeMap.set(type, target);
@@ -286,19 +294,19 @@ export namespace Docs {
*/
function buildPrototype(type: DocumentType, prototypeId: string): Opt<Doc> {
// load template from type
- let template = TemplateMap.get(type);
+ const template = TemplateMap.get(type);
if (!template) {
return undefined;
}
- let layout = template.layout;
+ const layout = template.layout;
// create title
- let upper = suffix.toUpperCase();
- let title = prototypeId.toUpperCase().replace(upper, `_${upper}`);
+ const upper = suffix.toUpperCase();
+ const title = prototypeId.toUpperCase().replace(upper, `_${upper}`);
// synthesize the default options, the type and title from computed values and
// whatever options pertain to this specific prototype
- let options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) };
+ const options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) };
options.layout = layout.view.LayoutString(layout.dataField);
- return Doc.assign(new Doc(prototypeId, true), { ...options, baseLayout: options.layout });
+ return Doc.assign(new Doc(prototypeId, true), { ...options });
}
}
@@ -309,7 +317,7 @@ export namespace Docs {
*/
export namespace Create {
- const delegateKeys = ["x", "y", "width", "height", "panX", "panY", "nativeWidth", "nativeHeight", "dropAction", "forceActive", "fitWidth"];
+ const delegateKeys = ["x", "y", "width", "height", "panX", "panY", "nativeWidth", "nativeHeight", "dropAction", "annotationOn", "forceActive", "fitWidth"];
/**
* This function receives the relevant document prototype and uses
@@ -342,8 +350,8 @@ export namespace Docs {
protoProps.isPrototype = true;
- let dataDoc = MakeDataDelegate(proto, protoProps, data);
- let viewDoc = Doc.MakeDelegate(dataDoc, delegId);
+ const dataDoc = MakeDataDelegate(proto, protoProps, data);
+ const viewDoc = Doc.MakeDelegate(dataDoc, delegId);
AudioBox.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: viewDoc }, { doc: d }, "audio link", "link to audio: " + d.title));
@@ -369,17 +377,16 @@ export namespace Docs {
}
export function ImageDocument(url: string, options: DocumentOptions = {}) {
- let imgField = new ImageField(new URL(url));
- let inst = InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: path.basename(url), ...options });
+ const imgField = new ImageField(new URL(url));
+ const inst = InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: path.basename(url), ...options });
let target = imgField.url.href;
if (new RegExp(window.location.origin).test(target)) {
- let extension = path.extname(target);
+ const extension = path.extname(target);
target = `${target.substring(0, target.length - extension.length)}_o${extension}`;
}
- // if (target !== "http://www.cs.brown.edu/") {
requestImageSize(target)
.then((size: any) => {
- let aspect = size.height / size.width;
+ const aspect = size.height / size.width;
if (!inst.nativeWidth) {
inst.nativeWidth = size.width;
}
@@ -423,7 +430,7 @@ export namespace Docs {
}
export function InkDocument(color: string, tool: number, strokeWidth: number, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
- let doc = InstanceFromProto(Prototypes.get(DocumentType.INK), new InkField(points), options);
+ const doc = InstanceFromProto(Prototypes.get(DocumentType.INK), new InkField(points), options);
doc.color = color;
doc.strokeWidth = strokeWidth;
doc.tool = tool;
@@ -439,12 +446,12 @@ export namespace Docs {
}
export async function DBDocument(url: string, options: DocumentOptions = {}, columnOptions: DocumentOptions = {}) {
- let schemaName = options.title ? options.title : "-no schema-";
- let ctlog = await Gateway.Instance.GetSchema(url, schemaName);
+ const schemaName = options.title ? options.title : "-no schema-";
+ const ctlog = await Gateway.Instance.GetSchema(url, schemaName);
if (ctlog && ctlog.schemas) {
- let schema = ctlog.schemas[0];
- let schemaDoc = Docs.Create.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! });
- let schemaDocuments = Cast(schemaDoc.data, listSpec(Doc), []);
+ const schema = ctlog.schemas[0];
+ const schemaDoc = Docs.Create.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! });
+ const schemaDocuments = Cast(schemaDoc.data, listSpec(Doc), []);
if (!schemaDocuments) {
return;
}
@@ -455,8 +462,8 @@ export namespace Docs {
if (field instanceof Doc) {
docs.push(field);
} else {
- var atmod = new ColumnAttributeModel(attr);
- let histoOp = new HistogramOperation(schema.displayName!,
+ const atmod = new ColumnAttributeModel(attr);
+ const histoOp = new HistogramOperation(schema.displayName!,
new AttributeTransformationModel(atmod, AggregateFunction.None),
new AttributeTransformationModel(atmod, AggregateFunction.Count),
new AttributeTransformationModel(atmod, AggregateFunction.Count));
@@ -481,6 +488,10 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.KVP), document, { title: document.title + ".kvp", ...options });
}
+ export function DocumentDocument(document?: Doc, options: DocumentOptions = {}) {
+ return InstanceFromProto(Prototypes.get(DocumentType.DOCUMENT), document, { title: document ? document.title + "" : "container", ...options });
+ }
+
export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Freeform }, id);
}
@@ -523,7 +534,9 @@ export namespace Docs {
}
export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id);
+ const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id);
+ Doc.GetProto(inst).data = new List<Doc>(documents);
+ return inst;
}
export function DirectoryImportDocument(options: DocumentOptions = {}) {
@@ -532,16 +545,17 @@ export namespace Docs {
export type DocConfig = {
doc: Doc,
- initialWidth?: number
+ initialWidth?: number,
+ path?: Doc[]
};
export function StandardCollectionDockingDocument(configs: Array<DocConfig>, options: DocumentOptions, id?: string, type: string = "row") {
- let layoutConfig = {
+ const layoutConfig = {
content: [
{
type: type,
content: [
- ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth))
+ ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth, config.path))
]
}
]
@@ -601,7 +615,8 @@ export namespace Docs {
* might involve arbitrary recursion (since toField might itself call convertObject)
*/
const convertObject = (object: any, title?: string): Doc => {
- let target = new Doc(), result: Opt<Field>;
+ const target = new Doc();
+ let result: Opt<Field>;
Object.keys(object).map(key => (result = toField(object[key], key)) && (target[key] = result));
title && !target.title && (target.title = title);
return target;
@@ -615,7 +630,8 @@ export namespace Docs {
* might involve arbitrary recursion (since toField might itself call convertList)
*/
const convertList = (list: Array<any>): List<Field> => {
- let target = new List(), result: Opt<Field>;
+ const target = new List();
+ let result: Opt<Field>;
list.map(item => (result = toField(item)) && target.push(result));
return target;
};
@@ -638,17 +654,20 @@ export namespace Docs {
let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise<Doc | undefined>)) | undefined = undefined;
if (type.indexOf("image") !== -1) {
ctor = Docs.Create.ImageDocument;
+ if (!options.width) options.width = 300;
}
if (type.indexOf("video") !== -1) {
ctor = Docs.Create.VideoDocument;
+ if (!options.width) options.width = 600;
+ if (!options.height) options.height = options.width * 2 / 3;
}
if (type.indexOf("audio") !== -1) {
ctor = Docs.Create.AudioDocument;
}
if (type.indexOf("pdf") !== -1) {
ctor = Docs.Create.PdfDocument;
- options.nativeWidth = 927;
- options.nativeHeight = 1200;
+ if (!options.width) options.width = 400;
+ if (!options.height) options.height = options.width * 1200 / 927;
}
if (type.indexOf("excel") !== -1) {
ctor = Docs.Create.DBDocument;
@@ -656,11 +675,11 @@ export namespace Docs {
}
if (type.indexOf("html") !== -1) {
if (path.includes(window.location.hostname)) {
- let s = path.split('/');
- let id = s[s.length - 1];
+ const s = path.split('/');
+ const id = s[s.length - 1];
return DocServer.GetRefField(id).then(field => {
if (field instanceof Doc) {
- let alias = Doc.MakeAlias(field);
+ const alias = Doc.MakeAlias(field);
alias.x = options.x || 0;
alias.y = options.y || 0;
alias.width = options.width || 300;
@@ -697,9 +716,9 @@ export namespace DocUtils {
DocListCastAsync(promoteDoc.links).then(links => {
links && links.map(async link => {
if (link) {
- let a1 = await Cast(link.anchor1, Doc);
+ const a1 = await Cast(link.anchor1, Doc);
if (a1 && Doc.AreProtosEqual(a1, promoteDoc)) link.anchor1 = copy;
- let a2 = await Cast(link.anchor2, Doc);
+ const a2 = await Cast(link.anchor2, Doc);
if (a2 && Doc.AreProtosEqual(a2, promoteDoc)) link.anchor2 = copy;
LinkManager.Instance.deleteLink(link);
LinkManager.Instance.addLink(link);
@@ -712,11 +731,11 @@ export namespace DocUtils {
}
export function MakeLink(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, title: string = "", description: string = "", id?: string) {
- let sv = DocumentManager.Instance.getDocumentView(source.doc);
+ const sv = DocumentManager.Instance.getDocumentView(source.doc);
if (sv && sv.props.ContainingCollectionDoc === target.doc) return;
if (target.doc === CurrentUserUtils.UserDocument) return undefined;
- let linkDocProto = new Doc(id, true);
+ const linkDocProto = new Doc(id, true);
UndoManager.RunInBatch(() => {
linkDocProto.type = DocumentType.LINK;
diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts
index e6f32272e..f3365e73d 100644
--- a/src/client/northstar/dash-fields/HistogramField.ts
+++ b/src/client/northstar/dash-fields/HistogramField.ts
@@ -10,7 +10,7 @@ import { Deserializable } from "../../util/SerializationHelper";
import { Copy, ToScriptString } from "../../../new_fields/FieldSymbols";
function serialize(field: HistogramField) {
- let obj = OmitKeys(field, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit;
+ const obj = OmitKeys(field, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit;
return obj;
}
@@ -19,7 +19,7 @@ function deserialize(jp: any) {
let Y: AttributeTransformationModel | undefined;
let V: AttributeTransformationModel | undefined;
- let schema = CurrentUserUtils.GetNorthstarSchema(jp.SchemaName);
+ const schema = CurrentUserUtils.GetNorthstarSchema(jp.SchemaName);
if (schema) {
CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => {
if (attr.displayName === jp.X.AttributeModel.Attribute.DisplayName) {
@@ -52,8 +52,8 @@ export class HistogramField extends ObjectField {
}
[Copy]() {
- let y = this.HistoOp;
- let z = this.HistoOp.Copy;
+ // const y = this.HistoOp;
+ // const z = this.HistoOp.Copy;
return new HistogramField(HistogramOperation.Duplicate(this.HistoOp));
}
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 854135648..8fee53fb9 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -46,8 +46,8 @@ export class HistogramBox extends React.Component<FieldViewProps> {
@action
dropX = (e: Event, de: DragManager.DropEvent) => {
- if (de.data instanceof DragManager.DocumentDragData) {
- let h = Cast(de.data.draggedDocuments[0].data, HistogramField);
+ if (de.complete.docDragData) {
+ let h = Cast(de.complete.docDragData.draggedDocuments[0].data, HistogramField);
if (h) {
this.HistoOp.X = h.HistoOp.X;
}
@@ -57,8 +57,8 @@ export class HistogramBox extends React.Component<FieldViewProps> {
}
@action
dropY = (e: Event, de: DragManager.DropEvent) => {
- if (de.data instanceof DragManager.DocumentDragData) {
- let h = Cast(de.data.draggedDocuments[0].data, HistogramField);
+ if (de.complete.docDragData) {
+ let h = Cast(de.complete.docDragData.draggedDocuments[0].data, HistogramField);
if (h) {
this.HistoOp.Y = h.HistoOp.X;
}
@@ -78,10 +78,10 @@ export class HistogramBox extends React.Component<FieldViewProps> {
componentDidMount() {
if (this._dropXRef.current) {
- this._dropXDisposer = DragManager.MakeDropTarget(this._dropXRef.current, { handlers: { drop: this.dropX.bind(this) } });
+ this._dropXDisposer = DragManager.MakeDropTarget(this._dropXRef.current, this.dropX.bind(this));
}
if (this._dropYRef.current) {
- this._dropYDisposer = DragManager.MakeDropTarget(this._dropYRef.current, { handlers: { drop: this.dropY.bind(this) } });
+ this._dropYDisposer = DragManager.MakeDropTarget(this._dropYRef.current, this.dropY.bind(this));
}
reaction(() => CurrentUserUtils.NorthstarDBCatalog, (catalog?: Catalog) => this.activateHistogramOperation(catalog), { fireImmediately: true });
reaction(() => [this.VisualBinRanges && this.VisualBinRanges.slice()], () => this.SizeConverter.SetVisualBinRanges(this.VisualBinRanges));
diff --git a/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts b/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts
index c579c8e5f..7bc097e1d 100644
--- a/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts
+++ b/src/client/northstar/model/binRanges/QuantitativeVisualBinRange.ts
@@ -37,7 +37,7 @@ export class QuantitativeVisualBinRange extends VisualBinRange {
}
public GetBins(): number[] {
- let bins = new Array<number>();
+ const bins = new Array<number>();
for (let v: number = this.DataBinRange.minValue!; v < this.DataBinRange.maxValue!; v += this.DataBinRange.step!) {
bins.push(v);
@@ -46,8 +46,8 @@ export class QuantitativeVisualBinRange extends VisualBinRange {
}
public static Initialize(dataMinValue: number, dataMaxValue: number, targetBinNumber: number, isIntegerRange: boolean): QuantitativeVisualBinRange {
- let extent = QuantitativeVisualBinRange.getExtent(dataMinValue, dataMaxValue, targetBinNumber, isIntegerRange);
- let dataBinRange = new QuantitativeBinRange();
+ const extent = QuantitativeVisualBinRange.getExtent(dataMinValue, dataMaxValue, targetBinNumber, isIntegerRange);
+ const dataBinRange = new QuantitativeBinRange();
dataBinRange.minValue = extent[0];
dataBinRange.maxValue = extent[1];
dataBinRange.step = extent[2];
@@ -60,10 +60,10 @@ export class QuantitativeVisualBinRange extends VisualBinRange {
// dataMin -= 0.1;
dataMax += 0.1;
}
- let span = dataMax - dataMin;
+ const span = dataMax - dataMin;
let step = Math.pow(10, Math.floor(Math.log10(span / m)));
- let err = m / span * step;
+ const err = m / span * step;
if (err <= .15) {
step *= 10;
@@ -78,9 +78,9 @@ export class QuantitativeVisualBinRange extends VisualBinRange {
if (isIntegerRange) {
step = Math.ceil(step);
}
- let ret: number[] = new Array<number>(3);
- let minDivStep = Math.floor(dataMin / step);
- let maxDivStep = Math.floor(dataMax / step);
+ const ret: number[] = new Array<number>(3);
+ const minDivStep = Math.floor(dataMin / step);
+ const maxDivStep = Math.floor(dataMax / step);
ret[0] = minDivStep * step; // Math.floor(Math.Round(dataMin, 8)/step)*step;
ret[1] = maxDivStep * step + step; // Math.floor(Math.Round(dataMax, 8)/step)*step + step;
ret[2] = step;
diff --git a/src/client/northstar/operations/BaseOperation.ts b/src/client/northstar/operations/BaseOperation.ts
index 0d1361ebf..013f2244e 100644
--- a/src/client/northstar/operations/BaseOperation.ts
+++ b/src/client/northstar/operations/BaseOperation.ts
@@ -44,12 +44,12 @@ export abstract class BaseOperation {
}
}
- let operationParameters = this.CreateOperationParameters();
+ const operationParameters = this.CreateOperationParameters();
if (this.Result) {
this.Result.progress = 0;
} // bcz: used to set Result to undefined, but that causes the display to blink
this.Error = "";
- let salt = Math.random().toString();
+ const salt = Math.random().toString();
this.RequestSalt = salt;
if (!operationParameters) {
@@ -59,27 +59,27 @@ export abstract class BaseOperation {
this.ComputationStarted = true;
//let start = performance.now();
- let promise = Gateway.Instance.StartOperation(operationParameters.toJSON());
+ const promise = Gateway.Instance.StartOperation(operationParameters.toJSON());
promise.catch(err => {
action(() => {
this.Error = err;
console.error(err);
});
});
- let operationReference = await promise;
+ const operationReference = await promise;
if (operationReference) {
this.OperationReference = operationReference;
- let resultParameters = new ResultParameters();
+ const resultParameters = new ResultParameters();
resultParameters.operationReference = operationReference;
- let pollPromise = new PollPromise(salt, operationReference);
+ const pollPromise = new PollPromise(salt, operationReference);
BaseOperation._currentOperations.set(this.Id, pollPromise);
pollPromise.Start(async () => {
- let result = await Gateway.Instance.GetResult(resultParameters.toJSON());
+ const result = await Gateway.Instance.GetResult(resultParameters.toJSON());
if (result instanceof ErrorResult) {
throw new Error((result).message);
}
diff --git a/src/client/northstar/utils/MathUtil.ts b/src/client/northstar/utils/MathUtil.ts
index 4b44f40c3..5def5e704 100644
--- a/src/client/northstar/utils/MathUtil.ts
+++ b/src/client/northstar/utils/MathUtil.ts
@@ -92,37 +92,37 @@ export class MathUtil {
public static DistToLineSegment(v: PIXIPoint, w: PIXIPoint, p: PIXIPoint) {
// Return minimum distance between line segment vw and point p
- var l2 = MathUtil.DistSquared(v, w); // i.e. |w-v|^2 - avoid a sqrt
+ const l2 = MathUtil.DistSquared(v, w); // i.e. |w-v|^2 - avoid a sqrt
if (l2 === 0.0) return MathUtil.Dist(p, v); // v === w case
// Consider the line extending the segment, parameterized as v + t (w - v).
// We find projection of point p onto the line.
// It falls where t = [(p-v) . (w-v)] / |w-v|^2
// We clamp t from [0,1] to handle points outside the segment vw.
- var dot = MathUtil.Dot(
+ const dot = MathUtil.Dot(
MathUtil.SubtractPoint(p, v),
MathUtil.SubtractPoint(w, v)) / l2;
- var t = Math.max(0, Math.min(1, dot));
+ const t = Math.max(0, Math.min(1, dot));
// Projection falls on the segment
- var projection = MathUtil.AddPoint(v,
+ const projection = MathUtil.AddPoint(v,
MathUtil.MultiplyConstant(
MathUtil.SubtractPoint(w, v), t));
return MathUtil.Dist(p, projection);
}
public static LineSegmentIntersection(ps1: PIXIPoint, pe1: PIXIPoint, ps2: PIXIPoint, pe2: PIXIPoint): PIXIPoint | undefined {
- var a1 = pe1.y - ps1.y;
- var b1 = ps1.x - pe1.x;
+ const a1 = pe1.y - ps1.y;
+ const b1 = ps1.x - pe1.x;
- var a2 = pe2.y - ps2.y;
- var b2 = ps2.x - pe2.x;
+ const a2 = pe2.y - ps2.y;
+ const b2 = ps2.x - pe2.x;
- var delta = a1 * b2 - a2 * b1;
+ const delta = a1 * b2 - a2 * b1;
if (delta === 0) {
return undefined;
}
- var c2 = a2 * ps2.x + b2 * ps2.y;
- var c1 = a1 * ps1.x + b1 * ps1.y;
- var invdelta = 1 / delta;
+ const c2 = a2 * ps2.x + b2 * ps2.y;
+ const c1 = a1 * ps1.x + b1 * ps1.y;
+ const invdelta = 1 / delta;
return new PIXIPoint((b2 * c1 - b1 * c2) * invdelta, (a1 * c2 - a2 * c1) * invdelta);
}
@@ -144,13 +144,13 @@ export class MathUtil {
}
public static LinePIXIRectangleIntersection(lineFrom: PIXIPoint, lineTo: PIXIPoint, rect: PIXIRectangle): Array<PIXIPoint> {
- var r1 = new PIXIPoint(rect.left, rect.top);
- var r2 = new PIXIPoint(rect.right, rect.top);
- var r3 = new PIXIPoint(rect.right, rect.bottom);
- var r4 = new PIXIPoint(rect.left, rect.bottom);
- var ret = new Array<PIXIPoint>();
- var dist = this.Dist(lineFrom, lineTo);
- var inter = this.LineSegmentIntersection(lineFrom, lineTo, r1, r2);
+ const r1 = new PIXIPoint(rect.left, rect.top);
+ const r2 = new PIXIPoint(rect.right, rect.top);
+ const r3 = new PIXIPoint(rect.right, rect.bottom);
+ const r4 = new PIXIPoint(rect.left, rect.bottom);
+ const ret = new Array<PIXIPoint>();
+ const dist = this.Dist(lineFrom, lineTo);
+ let inter = this.LineSegmentIntersection(lineFrom, lineTo, r1, r2);
if (inter && this.PointInPIXIRectangle(inter, rect) &&
this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
ret.push(inter);
@@ -190,7 +190,7 @@ export class MathUtil {
}
public static Normalize(p1: PIXIPoint) {
- var d = this.Length(p1);
+ const d = this.Length(p1);
return new PIXIPoint(p1.x / d, p1.y / d);
}
@@ -236,8 +236,8 @@ export class MathUtil {
}
public static Combinations<T>(chars: T[]) {
- let result = new Array<T>();
- let f = (prefix: any, chars: any) => {
+ const result = new Array<T>();
+ const f = (prefix: any, chars: any) => {
for (let i = 0; i < chars.length; i++) {
result.push(prefix.concat(chars[i]));
f(prefix.concat(chars[i]), chars.slice(i + 1));
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 6bbd3d0ed..3d8f2d234 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -11,7 +11,6 @@ import { Cast, CastCtor } from "../../new_fields/Types";
import { listSpec } from "../../new_fields/Schema";
import { AudioField, ImageField } from "../../new_fields/URLField";
import { HistogramField } from "../northstar/dash-fields/HistogramField";
-import { MainView } from "../views/MainView";
import { Utils } from "../../Utils";
import { RichTextField } from "../../new_fields/RichTextField";
import { DictationOverlay } from "../views/DictationOverlay";
@@ -48,7 +47,7 @@ export namespace DictationManager {
export const Infringed = "unable to process: dictation manager still involved in previous session";
const browser = (() => {
- let identifier = navigator.userAgent.toLowerCase();
+ const identifier = navigator.userAgent.toLowerCase();
if (identifier.indexOf("safari") >= 0) {
return "Safari";
}
@@ -90,7 +89,7 @@ export namespace DictationManager {
export const listen = async (options?: Partial<ListeningOptions>) => {
let results: string | undefined;
- let overlay = options !== undefined && options.useOverlay;
+ const overlay = options !== undefined && options.useOverlay;
if (overlay) {
DictationOverlay.Instance.dictationOverlayVisible = true;
DictationOverlay.Instance.isListening = { interim: false };
@@ -102,7 +101,7 @@ export namespace DictationManager {
Utils.CopyText(results);
if (overlay) {
DictationOverlay.Instance.isListening = false;
- let execute = options && options.tryExecute;
+ const execute = options && options.tryExecute;
DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results;
DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true;
}
@@ -131,12 +130,12 @@ export namespace DictationManager {
}
isListening = true;
- let handler = options ? options.interimHandler : undefined;
- let continuous = options ? options.continuous : undefined;
- let indefinite = continuous && continuous.indefinite;
- let language = options ? options.language : undefined;
- let intra = options && options.delimiters ? options.delimiters.intra : undefined;
- let inter = options && options.delimiters ? options.delimiters.inter : undefined;
+ const handler = options ? options.interimHandler : undefined;
+ const continuous = options ? options.continuous : undefined;
+ const indefinite = continuous && continuous.indefinite;
+ const language = options ? options.language : undefined;
+ const intra = options && options.delimiters ? options.delimiters.intra : undefined;
+ const inter = options && options.delimiters ? options.delimiters.inter : undefined;
recognizer.onstart = () => console.log("initiating speech recognition session...");
recognizer.interimResults = handler !== undefined;
@@ -177,7 +176,7 @@ export namespace DictationManager {
recognizer.start();
};
- let complete = () => {
+ const complete = () => {
if (indefinite) {
current && sessionResults.push(current);
sessionResults.length && resolve(sessionResults.join(inter || interSession));
@@ -213,8 +212,8 @@ export namespace DictationManager {
};
const synthesize = (e: SpeechRecognitionEvent, delimiter?: string) => {
- let results = e.results;
- let transcripts: string[] = [];
+ const results = e.results;
+ const transcripts: string[] = [];
for (let i = 0; i < results.length; i++) {
transcripts.push(results.item(i).item(0).transcript.trim());
}
@@ -238,18 +237,18 @@ export namespace DictationManager {
export const execute = async (phrase: string) => {
return UndoManager.RunInBatch(async () => {
- let targets = SelectionManager.SelectedDocuments();
+ const targets = SelectionManager.SelectedDocuments();
if (!targets || !targets.length) {
return;
}
phrase = phrase.toLowerCase();
- let entry = Independent.get(phrase);
+ const entry = Independent.get(phrase);
if (entry) {
let success = false;
- let restrictTo = entry.restrictTo;
- for (let target of targets) {
+ const restrictTo = entry.restrictTo;
+ for (const target of targets) {
if (!restrictTo || validate(target, restrictTo)) {
await entry.action(target);
success = true;
@@ -258,14 +257,14 @@ export namespace DictationManager {
return success;
}
- for (let entry of Dependent) {
- let regex = entry.expression;
- let matches = regex.exec(phrase);
+ for (const entry of Dependent) {
+ const regex = entry.expression;
+ const matches = regex.exec(phrase);
regex.lastIndex = 0;
if (matches !== null) {
let success = false;
- let restrictTo = entry.restrictTo;
- for (let target of targets) {
+ const restrictTo = entry.restrictTo;
+ for (const target of targets) {
if (!restrictTo || validate(target, restrictTo)) {
await entry.action(target, matches);
success = true;
@@ -289,7 +288,7 @@ export namespace DictationManager {
]);
const tryCast = (view: DocumentView, type: DocumentType) => {
- let ctor = ConstructorMap.get(type);
+ const ctor = ConstructorMap.get(type);
if (!ctor) {
return false;
}
@@ -297,7 +296,7 @@ export namespace DictationManager {
};
const validate = (target: DocumentView, types: DocumentType[]) => {
- for (let type of types) {
+ for (const type of types) {
if (tryCast(target, type)) {
return true;
}
@@ -306,11 +305,11 @@ export namespace DictationManager {
};
const interpretNumber = (number: string) => {
- let initial = parseInt(number);
+ const initial = parseInt(number);
if (!isNaN(initial)) {
return initial;
}
- let converted = interpreter.wordsToNumbers(number, { fuzzy: true });
+ const converted = interpreter.wordsToNumbers(number, { fuzzy: true });
if (converted === null) {
return NaN;
}
@@ -326,20 +325,20 @@ export namespace DictationManager {
["open fields", {
action: (target: DocumentView) => {
- let kvp = Docs.Create.KVPDocument(target.props.Document, { width: 300, height: 300 });
+ const kvp = Docs.Create.KVPDocument(target.props.Document, { width: 300, height: 300 });
target.props.addDocTab(kvp, target.props.DataDoc, "onRight");
}
}],
["new outline", {
action: (target: DocumentView) => {
- let newBox = Docs.Create.TextDocument({ width: 400, height: 200, title: "My Outline" });
+ const newBox = Docs.Create.TextDocument({ width: 400, height: 200, title: "My Outline" });
newBox.autoHeight = true;
- let proto = newBox.proto!;
- let prompt = "Press alt + r to start dictating here...";
- let head = 3;
- let anchor = head + prompt.length;
- let proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`;
+ const proto = newBox.proto!;
+ const prompt = "Press alt + r to start dictating here...";
+ const head = 3;
+ const anchor = head + prompt.length;
+ const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`;
proto.data = new RichTextField(proseMirrorState);
proto.backgroundColor = "#eeffff";
target.props.addDocTab(newBox, proto, "onRight");
@@ -353,10 +352,10 @@ export namespace DictationManager {
{
expression: /create (\w+) documents of type (image|nested collection)/g,
action: (target: DocumentView, matches: RegExpExecArray) => {
- let count = interpretNumber(matches[1]);
- let what = matches[2];
- let dataDoc = Doc.GetProto(target.props.Document);
- let fieldKey = "data";
+ const count = interpretNumber(matches[1]);
+ const what = matches[2];
+ const dataDoc = Doc.GetProto(target.props.Document);
+ const fieldKey = "data";
if (isNaN(count)) {
return;
}
@@ -379,7 +378,7 @@ export namespace DictationManager {
{
expression: /view as (freeform|stacking|masonry|schema|tree)/g,
action: (target: DocumentView, matches: RegExpExecArray) => {
- let mode = CollectionViewType.valueOf(matches[1]);
+ const mode = CollectionViewType.valueOf(matches[1]);
mode && (target.props.Document.viewType = mode);
},
restrictTo: [DocumentType.COL]
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 346e88f40..fb4c2155a 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -33,7 +33,7 @@ export class DocumentManager {
//gets all views
public getDocumentViewsById(id: string) {
- let toReturn: DocumentView[] = [];
+ const toReturn: DocumentView[] = [];
DocumentManager.Instance.DocumentViews.map(view => {
if (view.props.Document[Id] === id) {
toReturn.push(view);
@@ -41,7 +41,7 @@ export class DocumentManager {
});
if (toReturn.length === 0) {
DocumentManager.Instance.DocumentViews.map(view => {
- let doc = view.props.Document.proto;
+ const doc = view.props.Document.proto;
if (doc && doc[Id] && doc[Id] === id) {
toReturn.push(view);
}
@@ -57,9 +57,9 @@ export class DocumentManager {
public getDocumentViewById(id: string, preferredCollection?: CollectionView): DocumentView | undefined {
let toReturn: DocumentView | undefined;
- let passes = preferredCollection ? [preferredCollection, undefined] : [undefined];
+ const passes = preferredCollection ? [preferredCollection, undefined] : [undefined];
- for (let pass of passes) {
+ for (const pass of passes) {
DocumentManager.Instance.DocumentViews.map(view => {
if (view.props.Document[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
toReturn = view;
@@ -68,7 +68,7 @@ export class DocumentManager {
});
if (!toReturn) {
DocumentManager.Instance.DocumentViews.map(view => {
- let doc = view.props.Document.proto;
+ const doc = view.props.Document.proto;
if (doc && doc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
toReturn = view;
}
@@ -90,51 +90,57 @@ export class DocumentManager {
return views.length ? views[0] : undefined;
}
public getDocumentViews(toFind: Doc): DocumentView[] {
- let toReturn: DocumentView[] = [];
+ const toReturn: DocumentView[] = [];
DocumentManager.Instance.DocumentViews.map(view =>
- Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
+ view.props.Document === toFind && toReturn.push(view));
+ DocumentManager.Instance.DocumentViews.map(view =>
+ view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
return toReturn;
}
@computed
public get LinkedDocumentViews() {
- let pairs = DocumentManager.Instance.DocumentViews.filter(dv =>
- (dv.isSelected() || Doc.IsBrushed(dv.props.Document)) // draw links from DocumentViews that are selected or brushed OR
- || DocumentManager.Instance.DocumentViews.some(dv2 => { // Documentviews which
- let rest = DocListCast(dv2.props.Document.links).some(l => Doc.AreProtosEqual(l, dv.props.Document));// are link doc anchors
- let init = (dv2.isSelected() || Doc.IsBrushed(dv2.props.Document)) && dv2.Document.type !== DocumentType.AUDIO; // on a view that is selected or brushed
- return init && rest;
- })
- ).reduce((pairs, dv) => {
- let linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document);
- pairs.push(...linksList.reduce((pairs, link) => {
- let linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
- linkToDoc && DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
- if (dv.props.Document.type !== DocumentType.LINK || dv.props.layoutKey !== docView1.props.layoutKey) {
- pairs.push({ a: dv, b: docView1, l: link });
- }
- });
+ const pairs = DocumentManager.Instance.DocumentViews
+ //.filter(dv => (dv.isSelected() || Doc.IsBrushed(dv.props.Document))) // draw links from DocumentViews that are selected or brushed OR
+ // || DocumentManager.Instance.DocumentViews.some(dv2 => { // Documentviews which
+ // const rest = DocListCast(dv2.props.Document.links).some(l => Doc.AreProtosEqual(l, dv.props.Document));// are link doc anchors
+ // const init = (dv2.isSelected() || Doc.IsBrushed(dv2.props.Document)) && dv2.Document.type !== DocumentType.AUDIO; // on a view that is selected or brushed
+ // return init && rest;
+ // }
+ // )
+ .reduce((pairs, dv) => {
+ const linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document);
+ pairs.push(...linksList.reduce((pairs, link) => {
+ const linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
+ linkToDoc && DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
+ if (dv.props.Document.type !== DocumentType.LINK || dv.props.layoutKey !== docView1.props.layoutKey) {
+ pairs.push({ a: dv, b: docView1, l: link });
+ }
+ });
+ return pairs;
+ }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]));
return pairs;
- }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]));
- return pairs;
- }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]);
+ }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]);
return pairs;
}
public jumpToDocument = async (targetDoc: Doc, willZoom: boolean, dockFunc?: (doc: Doc) => void, docContext?: Doc, linkId?: string, closeContextIfNotFound: boolean = false): Promise<void> => {
- let highlight = () => {
+ const highlight = () => {
const finalDocView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
finalDocView && (finalDocView.Document.scrollToLinkID = linkId);
finalDocView && Doc.linkFollowHighlight(finalDocView.props.Document);
};
const docView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
- const annotatedDoc = await Cast(targetDoc.annotationOn, Doc);
+ let annotatedDoc = await Cast(docView?.props.Document.annotationOn, Doc);
+ if (annotatedDoc) {
+ const first = DocumentManager.Instance.getFirstDocumentView(annotatedDoc);
+ if (first) annotatedDoc = first.props.Document;
+ }
if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight?
- annotatedDoc && docView.props.focus(annotatedDoc, false);
- docView.props.focus(docView.props.Document, willZoom);
+ docView.props.focus(docView.props.Document, false);
highlight();
} else {
const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
@@ -176,7 +182,7 @@ export class DocumentManager {
}
public async FollowLink(link: Doc | undefined, doc: Doc, focus: (doc: Doc, maxLocation: string) => void, zoom: boolean = false, reverse: boolean = false, currentContext?: Doc) {
- const linkDocs = link ? [link] : LinkManager.Instance.getAllRelatedLinks(doc);
+ const linkDocs = link ? [link] : DocListCast(doc.links);
SelectionManager.DeselectAll();
const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc));
const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc));
@@ -194,17 +200,19 @@ export class DocumentManager {
const target = linkFollowDocs[reverse ? 1 : 0];
target.currentTimecode !== undefined && (target.currentTimecode = linkFollowTimecodes[reverse ? 1 : 0]);
DocumentManager.Instance.jumpToDocument(linkFollowDocs[reverse ? 1 : 0], zoom, (doc: Doc) => focus(doc, maxLocation), targetContext, linkDoc[Id]);
+ } else if (link) {
+ DocumentManager.Instance.jumpToDocument(link, zoom, (doc: Doc) => focus(doc, "onRight"), undefined, undefined);
}
}
@action
zoomIntoScale = (docDelegate: Doc, scale: number) => {
- let docView = DocumentManager.Instance.getDocumentView(Doc.GetProto(docDelegate));
+ const docView = DocumentManager.Instance.getDocumentView(Doc.GetProto(docDelegate));
docView && docView.props.zoomToScale(scale);
}
getScaleOfDocView = (docDelegate: Doc) => {
- let doc = Doc.GetProto(docDelegate);
+ const doc = Doc.GetProto(docDelegate);
const docView = DocumentManager.Instance.getDocumentView(doc);
if (docView) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index bbc29585c..df2f5fe3c 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,7 +1,5 @@
-import { action, runInAction } from "mobx";
-import { Doc, Field } from "../../new_fields/Doc";
-import { Cast, StrCast, ScriptCast } from "../../new_fields/Types";
-import { URLField } from "../../new_fields/URLField";
+import { Doc, Field, DocListCast } from "../../new_fields/Doc";
+import { Cast, ScriptCast } from "../../new_fields/Types";
import { emptyFunction } from "../../Utils";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import * as globalCssVariables from "../views/globalCssVariables.scss";
@@ -20,43 +18,46 @@ import { convertDropDataToButtons } from "./DropConverter";
export type dropActionType = "alias" | "copy" | undefined;
export function SetupDrag(
_reference: React.RefObject<HTMLElement>,
- docFunc: () => Doc | Promise<Doc>,
+ docFunc: () => Doc | Promise<Doc> | undefined,
moveFunc?: DragManager.MoveFunction,
dropAction?: dropActionType,
- options?: any,
+ treeViewId?: string,
dontHideOnDrop?: boolean,
dragStarted?: () => void
) {
- let onRowMove = async (e: PointerEvent) => {
+ const onRowMove = async (e: PointerEvent) => {
e.stopPropagation();
e.preventDefault();
document.removeEventListener("pointermove", onRowMove);
document.removeEventListener('pointerup', onRowUp);
- let doc = await docFunc();
- var dragData = new DragManager.DocumentDragData([doc]);
- dragData.dropAction = dropAction;
- dragData.moveDocument = moveFunc;
- dragData.options = options;
- dragData.dontHideOnDrop = dontHideOnDrop;
- DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y);
- dragStarted && dragStarted();
+ const doc = await docFunc();
+ if (doc) {
+ const dragData = new DragManager.DocumentDragData([doc]);
+ dragData.dropAction = dropAction;
+ dragData.moveDocument = moveFunc;
+ dragData.treeViewId = treeViewId;
+ dragData.dontHideOnDrop = dontHideOnDrop;
+ DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y);
+ dragStarted && dragStarted();
+ }
};
- let onRowUp = (): void => {
+ const onRowUp = (): void => {
document.removeEventListener("pointermove", onRowMove);
document.removeEventListener('pointerup', onRowUp);
};
- let onItemDown = async (e: React.PointerEvent) => {
+ const onItemDown = async (e: React.PointerEvent) => {
if (e.button === 0) {
e.stopPropagation();
if (e.shiftKey && CollectionDockingView.Instance) {
e.persist();
- CollectionDockingView.Instance.StartOtherDrag({
+ const dragDoc = await docFunc();
+ dragDoc && CollectionDockingView.Instance.StartOtherDrag({
pageX: e.pageX,
pageY: e.pageY,
preventDefault: emptyFunction,
button: 0
- }, [await docFunc()]);
+ }, [dragDoc]);
} else {
document.addEventListener("pointermove", onRowMove);
document.addEventListener("pointerup", onRowUp);
@@ -66,62 +67,9 @@ export function SetupDrag(
return onItemDown;
}
-function moveLinkedDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
- const document = SelectionManager.SelectedDocuments()[0];
- document && document.props.removeDocument && document.props.removeDocument(doc);
- addDocument(doc);
- return true;
-}
-
-export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: number, linkDoc: Doc, sourceDoc: Doc) {
- let draggeddoc = LinkManager.Instance.getOppositeAnchor(linkDoc, sourceDoc);
- if (draggeddoc) {
- let moddrag = await Cast(draggeddoc.annotationOn, Doc);
- let dragdocs = moddrag ? [moddrag] : [draggeddoc];
- let dragData = new DragManager.DocumentDragData(dragdocs);
- dragData.moveDocument = moveLinkedDocument;
- DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, {
- handlers: {
- dragComplete: action(emptyFunction),
- },
- hideSource: false
- });
- }
-}
-
-export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc, singleLink?: Doc) {
- let srcTarg = sourceDoc.proto;
- let draggedDocs: Doc[] = [];
-
- if (srcTarg) {
- let linkDocs = singleLink ? [singleLink] : LinkManager.Instance.getAllRelatedLinks(srcTarg);
- if (linkDocs) {
- draggedDocs = linkDocs.map(link => {
- let opp = LinkManager.Instance.getOppositeAnchor(link, sourceDoc);
- if (opp) return opp;
- }) as Doc[];
- }
- }
- if (draggedDocs.length) {
- let moddrag: Doc[] = [];
- for (const draggedDoc of draggedDocs) {
- let doc = await Cast(draggedDoc.annotationOn, Doc);
- if (doc) moddrag.push(doc);
- }
- let dragdocs = moddrag.length ? moddrag : draggedDocs;
- let dragData = new DragManager.DocumentDragData(dragdocs);
- dragData.moveDocument = moveLinkedDocument;
- DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, {
- handlers: {
- dragComplete: action(emptyFunction),
- },
- hideSource: false
- });
- }
-}
-
-
export namespace DragManager {
+ let dragDiv: HTMLDivElement;
+
export function Root() {
const root = document.getElementById("root");
if (!root) {
@@ -129,79 +77,45 @@ export namespace DragManager {
}
return root;
}
+ export let AbortDrag: () => void = emptyFunction;
+ export type MoveFunction = (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
- let dragDiv: HTMLDivElement;
-
- export enum DragButtons {
- Left = 1,
- Right = 2,
- Both = Left | Right
- }
-
- interface DragOptions {
- handlers: DragHandlers;
-
- hideSource: boolean | (() => boolean);
-
- dragHasStarted?: () => void;
-
- withoutShiftDrag?: boolean;
-
- finishDrag?: (dropData: { [id: string]: any }) => void;
-
- offsetX?: number;
-
+ export interface DragDropDisposer { (): void; }
+ export interface DragOptions {
+ dragComplete?: (e: DragCompleteEvent) => void; // function to invoke when drag has completed
+ hideSource?: boolean; // hide source document during drag
+ offsetX?: number; // offset of top left of source drag visual from cursor
offsetY?: number;
}
- export interface DragDropDisposer {
- (): void;
- }
-
- export class DragCompleteEvent { }
-
- export interface DragHandlers {
- dragComplete: (e: DragCompleteEvent) => void;
- }
-
- export interface DropOptions {
- handlers: DropHandlers;
- }
+ // event called when the drag operation results in a drop action
export class DropEvent {
constructor(
readonly x: number,
readonly y: number,
- readonly data: { [id: string]: any },
- readonly mods: string
+ readonly complete: DragCompleteEvent,
+ readonly altKey: boolean,
+ readonly metaKey: boolean,
+ readonly ctrlKey: boolean
) { }
}
- export interface DropHandlers {
- drop: (e: Event, de: DropEvent) => void;
- }
-
- export function MakeDropTarget(
- element: HTMLElement,
- options: DropOptions
- ): DragDropDisposer {
- if ("canDrop" in element.dataset) {
- throw new Error(
- "Element is already droppable, can't make it droppable again"
- );
+ // event called when the drag operation has completed (aborted or completed a drop) -- this will be after any drop event has been generated
+ export class DragCompleteEvent {
+ constructor(aborted: boolean, dragData: { [id: string]: any }) {
+ this.aborted = aborted;
+ this.docDragData = dragData instanceof DocumentDragData ? dragData : undefined;
+ this.annoDragData = dragData instanceof PdfAnnoDragData ? dragData : undefined;
+ this.linkDragData = dragData instanceof LinkDragData ? dragData : undefined;
+ this.columnDragData = dragData instanceof ColumnDragData ? dragData : undefined;
}
- element.dataset.canDrop = "true";
- const handler = (e: Event) => {
- const ce = e as CustomEvent<DropEvent>;
- options.handlers.drop(e, ce.detail);
- };
- element.addEventListener("dashOnDrop", handler);
- return () => {
- element.removeEventListener("dashOnDrop", handler);
- delete element.dataset.canDrop;
- };
+ aborted: boolean;
+ docDragData?: DocumentDragData;
+ annoDragData?: PdfAnnoDragData;
+ linkDragData?: LinkDragData;
+ columnDragData?: ColumnDragData;
}
- export type MoveFunction = (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
export class DocumentDragData {
constructor(dragDoc: Doc[]) {
this.draggedDocuments = dragDoc;
@@ -210,6 +124,9 @@ export namespace DragManager {
}
draggedDocuments: Doc[];
droppedDocuments: Doc[];
+ dragDivName?: string;
+ treeViewId?: string;
+ dontHideOnDrop?: boolean;
offset: number[];
dropAction: dropActionType;
userDropAction: dropActionType;
@@ -217,16 +134,32 @@ export namespace DragManager {
moveDocument?: MoveFunction;
isSelectionMove?: boolean; // indicates that an explicitly selected Document is being dragged. this will suppress onDragStart scripts
applyAsTemplate?: boolean;
- [id: string]: any;
}
-
- export class AnnotationDragData {
+ export class LinkDragData {
+ constructor(linkSourceDoc: Doc) {
+ this.linkSourceDocument = linkSourceDoc;
+ }
+ droppedDocuments: Doc[] = [];
+ linkSourceDocument: Doc;
+ dontClearTextBox?: boolean;
+ linkDocument?: Doc;
+ }
+ export class ColumnDragData {
+ constructor(colKey: SchemaHeaderField) {
+ this.colKey = colKey;
+ }
+ colKey: SchemaHeaderField;
+ }
+ // used by PDFs to conditionally (if the drop completes) create a text annotation when dragging from the PDF toolbar when a text region has been selected.
+ // this is pretty clunky and should be rethought out using linkDrag or DocumentDrag
+ export class PdfAnnoDragData {
constructor(dragDoc: Doc, annotationDoc: Doc, dropDoc: Doc) {
this.dragDocument = dragDoc;
this.dropDocument = dropDoc;
this.annotationDocument = annotationDoc;
this.offset = [0, 0];
}
+ linkedToDoc?: boolean;
targetContext: Doc | undefined;
dragDocument: Doc;
annotationDocument: Doc;
@@ -236,98 +169,103 @@ export namespace DragManager {
userDropAction: dropActionType;
}
- export let StartDragFunctions: (() => void)[] = [];
+ export function MakeDropTarget(
+ element: HTMLElement,
+ dropFunc: (e: Event, de: DropEvent) => void
+ ): DragDropDisposer {
+ if ("canDrop" in element.dataset) {
+ throw new Error(
+ "Element is already droppable, can't make it droppable again"
+ );
+ }
+ element.dataset.canDrop = "true";
+ const handler = (e: Event) => dropFunc(e, (e as CustomEvent<DropEvent>).detail);
+ element.addEventListener("dashOnDrop", handler);
+ return () => {
+ element.removeEventListener("dashOnDrop", handler);
+ delete element.dataset.canDrop;
+ };
+ }
+ // drag a document and drop it (or make an alias/copy on drop)
export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
- runInAction(() => StartDragFunctions.map(func => func()));
+ const finishDrag = (e: DragCompleteEvent) => {
+ e.docDragData && (e.docDragData.droppedDocuments =
+ dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? ScriptCast(d.onDragStart).script.run({ this: d }).result :
+ dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? Doc.MakeAlias(d) :
+ dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? Doc.MakeCopy(d, true) : d)
+ );
+ e.docDragData?.droppedDocuments.forEach((drop: Doc, i: number) =>
+ Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []).map(prop => drop[prop] = undefined));
+ };
dragData.draggedDocuments.map(d => d.dragFactory); // does this help? trying to make sure the dragFactory Doc is loaded
- StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag :
- (dropData: { [id: string]: any }) => {
- (dropData.droppedDocuments =
- dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? ScriptCast(d.onDragStart).script.run({ this: d }).result :
- dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? Doc.MakeAlias(d) :
- dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? Doc.MakeCopy(d, true) : d)
- );
- dropData.droppedDocuments.forEach((drop: Doc, i: number) =>
- Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []).map(prop => drop[prop] = undefined));
- });
+ StartDrag(eles, dragData, downX, downY, options, finishDrag);
}
+ // drag a button template and drop a new button
export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
- let dragData = new DragManager.DocumentDragData([]);
- runInAction(() => StartDragFunctions.map(func => func()));
- StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag :
- (dropData: { [id: string]: any }) => {
- let bd = Docs.Create.ButtonDocument({ width: 150, height: 50, title: title });
- bd.onClick = ScriptField.MakeScript(script);
- params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc)));
- initialize && initialize(bd);
- bd.buttonParams = new List<string>(params);
- dropData.droppedDocuments = [bd];
- });
+ const finishDrag = (e: DragCompleteEvent) => {
+ const bd = Docs.Create.ButtonDocument({ width: 150, height: 50, title: title });
+ bd.onClick = ScriptField.MakeScript(script);
+ params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc)));
+ initialize && initialize(bd);
+ bd.buttonParams = new List<string>(params);
+ e.docDragData && (e.docDragData.droppedDocuments = [bd]);
+ };
+ StartDrag(eles, new DragManager.DocumentDragData([]), downX, downY, options, finishDrag);
}
- export function StartLinkedDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
- dragData.moveDocument = moveLinkedDocument;
+ // drag links and drop link targets (aliasing them if needed)
+ export async function StartLinkTargetsDrag(dragEle: HTMLElement, downX: number, downY: number, sourceDoc: Doc, specificLinks?: Doc[]) {
+ const draggedDocs = (specificLinks ? specificLinks : DocListCast(sourceDoc.links)).map(link => LinkManager.Instance.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[];
- runInAction(() => StartDragFunctions.map(func => func()));
- StartDrag(eles, dragData, downX, downY, options,
- (dropData: { [id: string]: any }) => {
- let droppedDocuments: Doc[] = dragData.draggedDocuments.reduce((droppedDocs: Doc[], d) => {
- let dvs = DocumentManager.Instance.getDocumentViews(d);
- if (dvs.length) {
- let containingView = SelectionManager.SelectedDocuments()[0] ? SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView : undefined;
- let inContext = dvs.filter(dv => dv.props.ContainingCollectionView === containingView);
- if (inContext.length) {
- inContext.forEach(dv => droppedDocs.push(dv.props.Document));
+ if (draggedDocs.length) {
+ const moddrag: Doc[] = [];
+ for (const draggedDoc of draggedDocs) {
+ const doc = await Cast(draggedDoc.annotationOn, Doc);
+ if (doc) moddrag.push(doc);
+ }
+
+ const dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
+ dragData.moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean => {
+ const document = SelectionManager.SelectedDocuments()[0];
+ document && document.props.removeDocument && document.props.removeDocument(doc);
+ addDocument(doc);
+ return true;
+ };
+ const containingView = SelectionManager.SelectedDocuments()[0] ? SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView : undefined;
+ const finishDrag = (e: DragCompleteEvent) =>
+ e.docDragData && (e.docDragData.droppedDocuments =
+ dragData.draggedDocuments.reduce((droppedDocs, d) => {
+ const dvs = DocumentManager.Instance.getDocumentViews(d).filter(dv => dv.props.ContainingCollectionView === containingView);
+ if (dvs.length) {
+ dvs.forEach(dv => droppedDocs.push(dv.props.Document));
} else {
droppedDocs.push(Doc.MakeAlias(d));
}
- } else {
- droppedDocs.push(Doc.MakeAlias(d));
- }
- return droppedDocs;
- }, []);
- dropData.droppedDocuments = droppedDocuments;
- });
- }
+ return droppedDocs;
+ }, [] as Doc[]));
- export function StartAnnotationDrag(eles: HTMLElement[], dragData: AnnotationDragData, downX: number, downY: number, options?: DragOptions) {
- StartDrag(eles, dragData, downX, downY, options);
- }
-
- export class LinkDragData {
- constructor(linkSourceDoc: Doc, blacklist: Doc[] = []) {
- this.linkSourceDocument = linkSourceDoc;
- this.blacklist = blacklist;
+ StartDrag([dragEle], dragData, downX, downY, undefined, finishDrag);
}
- droppedDocuments: Doc[] = [];
- linkSourceDocument: Doc;
- blacklist: Doc[];
- dontClearTextBox?: boolean;
- [id: string]: any;
}
- // for column dragging in schema view
- export class ColumnDragData {
- constructor(colKey: SchemaHeaderField) {
- this.colKey = colKey;
- }
- colKey: SchemaHeaderField;
- [id: string]: any;
+ // drag&drop the pdf annotation anchor which will create a text note on drop via a dropCompleted() DragOption
+ export function StartPdfAnnoDrag(eles: HTMLElement[], dragData: PdfAnnoDragData, downX: number, downY: number, options?: DragOptions) {
+ StartDrag(eles, dragData, downX, downY, options);
}
- export function StartLinkDrag(ele: HTMLElement, dragData: LinkDragData, downX: number, downY: number, options?: DragOptions) {
- StartDrag([ele], dragData, downX, downY, options);
+ // drags a linker button and creates a link on drop
+ export function StartLinkDrag(ele: HTMLElement, sourceDoc: Doc, downX: number, downY: number, options?: DragOptions) {
+ StartDrag([ele], new DragManager.LinkDragData(sourceDoc), downX, downY, options);
}
+ // drags a column from a schema view
export function StartColumnDrag(ele: HTMLElement, dragData: ColumnDragData, downX: number, downY: number, options?: DragOptions) {
StartDrag([ele], dragData, downX, downY, options);
}
- export let AbortDrag: () => void = emptyFunction;
-
- function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) {
+ function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
eles = eles.filter(e => e);
if (!dragDiv) {
dragDiv = document.createElement("div");
@@ -336,80 +274,64 @@ export namespace DragManager {
DragManager.Root().appendChild(dragDiv);
}
SelectionManager.SetIsDragging(true);
- let scaleXs: number[] = [];
- let scaleYs: number[] = [];
- let xs: number[] = [];
- let ys: number[] = [];
-
- const docs = dragData instanceof DocumentDragData ? dragData.draggedDocuments :
- dragData instanceof AnnotationDragData ? [dragData.dragDocument] : [];
- let dragElements = eles.map(ele => {
- const w = ele.offsetWidth,
- h = ele.offsetHeight;
+ const scaleXs: number[] = [];
+ const scaleYs: number[] = [];
+ const xs: number[] = [];
+ const ys: number[] = [];
+
+ const docs = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof PdfAnnoDragData ? [dragData.dragDocument] : [];
+ const dragElements = eles.map(ele => {
+ if (!ele.parentNode) dragDiv.appendChild(ele);
+ const dragElement = ele.parentNode === dragDiv ? ele : ele.cloneNode(true) as HTMLElement;
const rect = ele.getBoundingClientRect();
- const scaleX = rect.width / w,
- scaleY = rect.height / h;
- let x = rect.left,
- y = rect.top;
- xs.push(x);
- ys.push(y);
+ const scaleX = rect.width / ele.offsetWidth,
+ scaleY = rect.height / ele.offsetHeight;
+ xs.push(rect.left);
+ ys.push(rect.top);
scaleXs.push(scaleX);
scaleYs.push(scaleY);
- let dragElement = ele.cloneNode(true) as HTMLElement;
dragElement.style.opacity = "0.7";
- dragElement.style.borderRadius = getComputedStyle(ele).borderRadius;
dragElement.style.position = "absolute";
dragElement.style.margin = "0";
dragElement.style.top = "0";
dragElement.style.bottom = "";
dragElement.style.left = "0";
- dragElement.style.transition = "none";
dragElement.style.color = "black";
+ dragElement.style.transition = "none";
dragElement.style.transformOrigin = "0 0";
+ dragElement.style.borderRadius = getComputedStyle(ele).borderRadius;
dragElement.style.zIndex = globalCssVariables.contextMenuZindex;// "1000";
- dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
+ dragElement.style.transform = `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0)}px) scale(${scaleX}, ${scaleY})`;
dragElement.style.width = `${rect.width / scaleX}px`;
dragElement.style.height = `${rect.height / scaleY}px`;
if (docs.length) {
- var pdfBox = dragElement.getElementsByTagName("canvas");
- var pdfBoxSrc = ele.getElementsByTagName("canvas");
+ const pdfBox = dragElement.getElementsByTagName("canvas");
+ const pdfBoxSrc = ele.getElementsByTagName("canvas");
Array.from(pdfBox).map((pb, i) => pb.getContext('2d')!.drawImage(pdfBoxSrc[i], 0, 0));
- var pdfView = dragElement.getElementsByClassName("pdfViewer-viewer");
- var pdfViewSrc = ele.getElementsByClassName("pdfViewer-viewer");
- let tops = Array.from(pdfViewSrc).map(p => p.scrollTop);
- let oldopacity = dragElement.style.opacity;
+ const pdfView = dragElement.getElementsByClassName("pdfViewer-viewer");
+ const pdfViewSrc = ele.getElementsByClassName("pdfViewer-viewer");
+ const tops = Array.from(pdfViewSrc).map(p => p.scrollTop);
+ const oldopacity = dragElement.style.opacity;
dragElement.style.opacity = "0";
setTimeout(() => {
dragElement.style.opacity = oldopacity;
Array.from(pdfView).map((v, i) => v.scrollTo({ top: tops[i] }));
}, 0);
}
- let set = dragElement.getElementsByTagName('*');
if (dragElement.hasAttribute("style")) (dragElement as any).style.pointerEvents = "none";
+ const set = dragElement.getElementsByTagName('*');
// tslint:disable-next-line: prefer-for-of
for (let i = 0; i < set.length; i++) {
- if (set[i].hasAttribute("style")) {
- let s = set[i];
- (s as any).style.pointerEvents = "none";
- }
+ set[i].hasAttribute("style") && ((set[i] as any).style.pointerEvents = "none");
}
-
dragDiv.appendChild(dragElement);
return dragElement;
});
- let hideSource = false;
- if (options) {
- if (typeof options.hideSource === "boolean") {
- hideSource = options.hideSource;
- } else {
- hideSource = options.hideSource();
- }
- }
-
- eles.map(ele => ele.hidden = hideSource);
+ const hideSource = options?.hideSource ? true : false;
+ eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.parentElement.hidden = hideSource) : (ele.hidden = hideSource));
let lastX = downX;
let lastY = downY;
@@ -418,9 +340,9 @@ export namespace DragManager {
if (dragData instanceof DocumentDragData) {
dragData.userDropAction = e.ctrlKey ? "alias" : undefined;
}
- if (((options && !options.withoutShiftDrag) || !options) && e.shiftKey && CollectionDockingView.Instance) {
+ if (e.shiftKey && CollectionDockingView.Instance) {
AbortDrag();
- finishDrag && finishDrag(dragData);
+ finishDrag?.(new DragCompleteEvent(true, dragData));
CollectionDockingView.Instance.StartOtherDrag({
pageX: e.pageX,
pageY: e.pageY,
@@ -429,61 +351,56 @@ export namespace DragManager {
}, dragData.droppedDocuments);
}
//TODO: Why can't we use e.movementX and e.movementY?
- let moveX = e.pageX - lastX;
- let moveY = e.pageY - lastY;
+ const moveX = e.pageX - lastX;
+ const moveY = e.pageY - lastY;
lastX = e.pageX;
lastY = e.pageY;
dragElements.map((dragElement, i) => (dragElement.style.transform =
- `translate(${(xs[i] += moveX) + (options ? (options.offsetX || 0) : 0)}px, ${(ys[i] += moveY) + (options ? (options.offsetY || 0) : 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`)
+ `translate(${(xs[i] += moveX) + (options?.offsetX || 0)}px, ${(ys[i] += moveY) + (options?.offsetY || 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`)
);
};
- let hideDragShowOriginalElements = () => {
+ const hideDragShowOriginalElements = () => {
dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
- eles.map(ele => ele.hidden = false);
+ eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.parentElement.hidden = false) : (ele.hidden = false));
};
- let endDrag = () => {
+ const endDrag = () => {
document.removeEventListener("pointermove", moveHandler, true);
document.removeEventListener("pointerup", upHandler);
- if (options) {
- options.handlers.dragComplete({});
- }
};
AbortDrag = () => {
hideDragShowOriginalElements();
SelectionManager.SetIsDragging(false);
+ options?.dragComplete?.(new DragCompleteEvent(true, dragData));
endDrag();
};
const upHandler = (e: PointerEvent) => {
hideDragShowOriginalElements();
dispatchDrag(eles, e, dragData, options, finishDrag);
SelectionManager.SetIsDragging(false);
+ options?.dragComplete?.(new DragCompleteEvent(false, dragData));
endDrag();
};
document.addEventListener("pointermove", moveHandler, true);
document.addEventListener("pointerup", upHandler);
}
- function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) {
- let removed = dragData.dontHideOnDrop ? [] : dragEles.map(dragEle => {
- // let parent = dragEle.parentElement;
- // if (parent) parent.removeChild(dragEle);
- let ret = [dragEle, dragEle.style.width, dragEle.style.height];
+ function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (e: DragCompleteEvent) => void) {
+ const removed = dragData.dontHideOnDrop ? [] : dragEles.map(dragEle => {
+ const ret = { ele: dragEle, w: dragEle.style.width, h: dragEle.style.height };
dragEle.style.width = "0";
dragEle.style.height = "0";
return ret;
});
const target = document.elementFromPoint(e.x, e.y);
removed.map(r => {
- let dragEle = r[0] as HTMLElement;
- dragEle.style.width = r[1] as string;
- dragEle.style.height = r[2] as string;
- // let parent = r[1];
- // if (parent && dragEle) parent.appendChild(dragEle);
+ r.ele.style.width = r.w;
+ r.ele.style.height = r.h;
});
if (target) {
- finishDrag && finishDrag(dragData);
+ const complete = new DragCompleteEvent(false, dragData);
+ finishDrag?.(complete);
target.dispatchEvent(
new CustomEvent<DropEvent>("dashOnDrop", {
@@ -491,8 +408,10 @@ export namespace DragManager {
detail: {
x: e.x,
y: e.y,
- data: dragData,
- mods: e.altKey ? "AltKey" : e.ctrlKey ? "CtrlKey" : e.metaKey ? "MetaKey" : ""
+ complete: complete,
+ altKey: e.altKey,
+ metaKey: e.metaKey,
+ ctrlKey: e.ctrlKey
}
})
);
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 6b53333d7..9e036d6c2 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -9,10 +9,10 @@ import { ScriptField } from "../../new_fields/ScriptField";
function makeTemplate(doc: Doc): boolean {
- let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
- let layout = StrCast(layoutDoc.layout).match(/fieldKey={"[^"]*"}/)![0];
- let fieldKey = layout.replace('fieldKey={"', "").replace(/"}$/, "");
- let docs = DocListCast(layoutDoc[fieldKey]);
+ const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
+ const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)![0];
+ const fieldKey = layout.replace("fieldKey={'", "").replace(/'}$/, "");
+ const docs = DocListCast(layoutDoc[fieldKey]);
let any = false;
docs.map(d => {
if (!StrCast(d.title).startsWith("-")) {
@@ -28,7 +28,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
data && data.draggedDocuments.map((doc, i) => {
let dbox = doc;
if (!doc.onDragStart && !doc.onClick && doc.viewType !== CollectionViewType.Linear) {
- let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
+ const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
if (layoutDoc.type === DocumentType.COL) {
layoutDoc.isTemplateDoc = makeTemplate(layoutDoc);
} else {
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 899abbe40..545e8acb4 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,6 +1,5 @@
-import { Doc, Opt, Field } from "../../new_fields/Doc";
+import { Doc } from "../../new_fields/Doc";
import { DocServer } from "../DocServer";
-import { RouteStore } from "../../server/RouteStore";
import { MainView } from "../views/MainView";
import * as qs from 'query-string';
import { Utils, OmitKeys } from "../../Utils";
@@ -26,7 +25,7 @@ export namespace HistoryUtil {
// const handlers: ((state: ParsedUrl | null) => void)[] = [];
function onHistory(e: PopStateEvent) {
- if (window.location.pathname !== RouteStore.home) {
+ if (window.location.pathname !== "/home") {
const url = e.state as ParsedUrl || parseUrl(window.location);
if (url) {
switch (url.type) {
@@ -54,7 +53,7 @@ export namespace HistoryUtil {
}
export function getState(): ParsedUrl {
- let state = copyState(history.state);
+ const state = copyState(history.state);
state.initializers = state.initializers || {};
return state;
}
@@ -161,7 +160,7 @@ export namespace HistoryUtil {
const pathname = location.pathname.substring(1);
const search = location.search;
const opts = search.length ? qs.parse(search, { sort: false }) : {};
- let pathnameSplit = pathname.split("/");
+ const pathnameSplit = pathname.split("/");
const type = pathnameSplit[0];
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 5904088fc..5b5bffd8c 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -1,8 +1,7 @@
import "fs";
import React = require("react");
import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
-import { RouteStore } from "../../../server/RouteStore";
-import { action, observable, autorun, runInAction, computed, reaction, IReactionDisposer } from "mobx";
+import { action, observable, runInAction, computed, reaction, IReactionDisposer } from "mobx";
import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
import Measure, { ContentRect } from "react-measure";
import { library } from '@fortawesome/fontawesome-svg-core';
@@ -20,19 +19,13 @@ import { listSpec } from "../../../new_fields/Schema";
import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils";
import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
import "./DirectoryImportBox.scss";
-import { Identified } from "../../Network";
+import { Networking } from "../../Network";
import { BatchedArray } from "array-batcher";
-import { ExifData } from "exif";
+import * as path from 'path';
+import { AcceptibleMedia } from "../../../server/SharedMediaTypes";
const unsupported = ["text/html", "text/plain"];
-interface ImageUploadResponse {
- name: string;
- path: string;
- type: string;
- exif: any;
-}
-
@observer
export default class DirectoryImportBox extends React.Component<FieldViewProps> {
private selector = React.createRef<HTMLInputElement>();
@@ -55,7 +48,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
constructor(props: FieldViewProps) {
super(props);
library.add(faTag, faPlus);
- let doc = this.props.Document;
+ const doc = this.props.Document;
this.editingMetadata = this.editingMetadata || false;
this.persistent = this.persistent || false;
!Cast(doc.data, listSpec(Doc)) && (doc.data = new List<Doc>());
@@ -85,17 +78,22 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
this.phase = "Initializing download...";
});
- let docs: Doc[] = [];
+ const docs: Doc[] = [];
- let files = e.target.files;
+ const files = e.target.files;
if (!files || files.length === 0) return;
- let directory = (files.item(0) as any).webkitRelativePath.split("/", 1)[0];
+ const directory = (files.item(0) as any).webkitRelativePath.split("/", 1)[0];
- let validated: File[] = [];
+ const validated: File[] = [];
for (let i = 0; i < files.length; i++) {
- let file = files.item(i);
- file && !unsupported.includes(file.type) && validated.push(file);
+ const file = files.item(i);
+ if (file && !unsupported.includes(file.type)) {
+ const ext = path.extname(file.name).toLowerCase();
+ if (AcceptibleMedia.imageFormats.includes(ext)) {
+ validated.push(file);
+ }
+ }
}
runInAction(() => {
@@ -103,13 +101,13 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
this.completed = 0;
});
- let sizes: number[] = [];
- let modifiedDates: number[] = [];
+ const sizes: number[] = [];
+ const modifiedDates: number[] = [];
runInAction(() => this.phase = `Internal: uploading ${this.quota - this.completed} files to Dash...`);
const batched = BatchedArray.from(validated, { batchSize: 15 });
- const uploads = await batched.batchedMapAsync<ImageUploadResponse>(async (batch, collector) => {
+ const uploads = await batched.batchedMapAsync<any>(async (batch, collector) => {
const formData = new FormData();
batch.forEach(file => {
@@ -118,20 +116,14 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
formData.append(Utils.GenerateGuid(), file);
});
- collector.push(...(await Identified.PostFormDataToServer(RouteStore.upload, formData)));
+ collector.push(...(await Networking.PostFormDataToServer("/upload", formData)));
runInAction(() => this.completed += batch.length);
});
- await Promise.all(uploads.map(async upload => {
- const type = upload.type;
- const path = Utils.prepend(upload.path);
- const options = {
- nativeWidth: 300,
- width: 300,
- title: upload.name
- };
- const document = await Docs.Get.DocumentFromType(type, path, options);
- const { data, error } = upload.exif;
+ await Promise.all(uploads.map(async ({ name, type, clientAccessPath, exifData }) => {
+ const path = Utils.prepend(clientAccessPath);
+ const document = await Docs.Get.DocumentFromType(type, path, { width: 300, title: name });
+ const { data, error } = exifData;
if (document) {
Doc.GetProto(document).exif = error || Docs.Get.DocumentHierarchyFromJson(data);
docs.push(document);
@@ -139,26 +131,26 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
}));
for (let i = 0; i < docs.length; i++) {
- let doc = docs[i];
+ const doc = docs[i];
doc.size = sizes[i];
doc.modified = modifiedDates[i];
this.entries.forEach(entry => {
- let target = entry.onDataDoc ? Doc.GetProto(doc) : doc;
+ const target = entry.onDataDoc ? Doc.GetProto(doc) : doc;
target[entry.key] = entry.value;
});
}
- let doc = this.props.Document;
- let height: number = NumCast(doc.height) || 0;
- let offset: number = this.persistent ? (height === 0 ? 0 : height + 30) : 0;
- let options: DocumentOptions = {
+ const doc = this.props.Document;
+ const height: number = NumCast(doc.height) || 0;
+ const offset: number = this.persistent ? (height === 0 ? 0 : height + 30) : 0;
+ const options: DocumentOptions = {
title: `Import of ${directory}`,
width: 1105,
height: 500,
x: NumCast(doc.x),
y: NumCast(doc.y) + offset
};
- let parent = this.props.ContainingCollectionView;
+ const parent = this.props.ContainingCollectionView;
if (parent) {
let importContainer: Doc;
if (docs.length < 50) {
@@ -197,18 +189,18 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
@action
preserveCentering = (rect: ContentRect) => {
- let bounds = rect.offset!;
+ const bounds = rect.offset!;
if (bounds.width === 0 || bounds.height === 0) {
return;
}
- let offset = this.dimensions / 2;
+ const offset = this.dimensions / 2;
this.left = bounds.width / 2 - offset;
this.top = bounds.height / 2 - offset;
}
@action
addMetadataEntry = async () => {
- let entryDoc = new Doc();
+ const entryDoc = new Doc();
entryDoc.checked = false;
entryDoc.key = keyPlaceholder;
entryDoc.value = valuePlaceholder;
@@ -217,7 +209,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
@action
remove = async (entry: ImportMetadataEntry) => {
- let metadata = await DocListCastAsync(this.props.Document.data);
+ const metadata = await DocListCastAsync(this.props.Document.data);
if (metadata) {
let index = this.entries.indexOf(entry);
if (index !== -1) {
@@ -231,18 +223,18 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
}
render() {
- let dimensions = 50;
- let entries = DocListCast(this.props.Document.data);
- let isEditing = this.editingMetadata;
- let completed = this.completed;
- let quota = this.quota;
- let uploading = this.uploading;
- let showRemoveLabel = this.removeHover;
- let persistent = this.persistent;
+ const dimensions = 50;
+ const entries = DocListCast(this.props.Document.data);
+ const isEditing = this.editingMetadata;
+ const completed = this.completed;
+ const quota = this.quota;
+ const uploading = this.uploading;
+ const showRemoveLabel = this.removeHover;
+ const persistent = this.persistent;
let percent = `${completed / quota * 100}`;
percent = percent.split(".")[0];
percent = percent.startsWith("100") ? "99" : percent;
- let marginOffset = (percent.length === 1 ? 5 : 0) - 1.6;
+ const marginOffset = (percent.length === 1 ? 5 : 0) - 1.6;
const message = <span className={"phase"}>{this.phase}</span>;
const centerPiece = this.phase.includes("Google Photos") ?
<img src={"/assets/google_photos.png"} style={{
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index c9abf38fa..6a9486f83 100644
--- a/src/client/util/Import & Export/ImageUtils.ts
+++ b/src/client/util/Import & Export/ImageUtils.ts
@@ -1,9 +1,8 @@
-import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
import { ImageField } from "../../../new_fields/URLField";
import { Cast, StrCast } from "../../../new_fields/Types";
-import { RouteStore } from "../../../server/RouteStore";
import { Docs } from "../../documents/Documents";
-import { Identified } from "../../Network";
+import { Networking } from "../../Network";
import { Id } from "../../../new_fields/FieldSymbols";
import { Utils } from "../../../Utils";
@@ -15,7 +14,7 @@ export namespace ImageUtils {
return false;
}
const source = field.url.href;
- const response = await Identified.PostToServer(RouteStore.inspectImage, { source });
+ const response = await Networking.PostToServer("/inspectImage", { source });
const { error, data } = response.exifData;
document.exif = error || Docs.Get.DocumentHierarchyFromJson(data);
return data !== undefined;
@@ -23,7 +22,7 @@ export namespace ImageUtils {
export const ExportHierarchyToFileSystem = async (collection: Doc): Promise<void> => {
const a = document.createElement("a");
- a.href = Utils.prepend(`${RouteStore.imageHierarchyExport}/${collection[Id]}`);
+ a.href = Utils.prepend(`/imageHierarchyExport/${collection[Id]}`);
a.download = `Dash Export [${StrCast(collection.title)}].zip`;
a.click();
};
diff --git a/src/client/util/Import & Export/ImportMetadataEntry.tsx b/src/client/util/Import & Export/ImportMetadataEntry.tsx
index f5198c39b..8e1c50bea 100644
--- a/src/client/util/Import & Export/ImportMetadataEntry.tsx
+++ b/src/client/util/Import & Export/ImportMetadataEntry.tsx
@@ -1,11 +1,11 @@
import React = require("react");
import { observer } from "mobx-react";
import { EditableView } from "../../views/EditableView";
-import { observable, action, computed } from "mobx";
+import { action, computed } from "mobx";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { library } from '@fortawesome/fontawesome-svg-core';
-import { Opt, Doc } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
import { StrCast, BoolCast } from "../../../new_fields/Types";
interface KeyValueProps {
@@ -85,7 +85,7 @@ export default class ImportMetadataEntry extends React.Component<KeyValueProps>
}
render() {
- let keyValueStyle: React.CSSProperties = {
+ const keyValueStyle: React.CSSProperties = {
paddingLeft: 10,
width: "50%",
opacity: this.valid ? 1 : 0.5,
diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts
index 2d3671041..2e4e8c7ca 100644
--- a/src/client/util/InteractionUtils.ts
+++ b/src/client/util/InteractionUtils.ts
@@ -9,9 +9,9 @@ export namespace InteractionUtils {
const ERASER_BUTTON = 5;
export function GetMyTargetTouches(e: TouchEvent | React.TouchEvent, prevPoints: Map<number, React.Touch>): React.Touch[] {
- let myTouches = new Array<React.Touch>();
+ const myTouches = new Array<React.Touch>();
for (let i = 0; i < e.targetTouches.length; i++) {
- let pt = e.targetTouches.item(i);
+ const pt = e.targetTouches.item(i);
if (pt && prevPoints.has(pt.identifier)) {
myTouches.push(pt);
}
@@ -40,8 +40,8 @@ export namespace InteractionUtils {
* @param pts - n-arbitrary long list of points
*/
export function CenterPoint(pts: React.Touch[]): { X: number, Y: number } {
- let centerX = pts.map(pt => pt.clientX).reduce((a, b) => a + b, 0) / pts.length;
- let centerY = pts.map(pt => pt.clientY).reduce((a, b) => a + b, 0) / pts.length;
+ const centerX = pts.map(pt => pt.clientX).reduce((a, b) => a + b, 0) / pts.length;
+ const centerY = pts.map(pt => pt.clientY).reduce((a, b) => a + b, 0) / pts.length;
return { X: centerX, Y: centerY };
}
@@ -53,9 +53,9 @@ export namespace InteractionUtils {
* @param oldPoint2 - previous point 2
*/
export function Pinching(pt1: React.Touch, pt2: React.Touch, oldPoint1: React.Touch, oldPoint2: React.Touch): number {
- let threshold = window.devicePixelRatio;
- let oldDist = TwoPointEuclidist(oldPoint1, oldPoint2);
- let newDist = TwoPointEuclidist(pt1, pt2);
+ const threshold = 4;
+ const oldDist = TwoPointEuclidist(oldPoint1, oldPoint2);
+ const newDist = TwoPointEuclidist(pt1, pt2);
/** if they have the same sign, then we are either pinching in or out.
* threshold it by 10 (it has to be pinching by at least threshold to be a valid pinch)
@@ -75,12 +75,12 @@ export namespace InteractionUtils {
* @param oldPoint2 - previous point 2
*/
export function Pinning(pt1: React.Touch, pt2: React.Touch, oldPoint1: React.Touch, oldPoint2: React.Touch): number {
- let threshold = 4;
+ const threshold = 4;
- let pt1Dist = TwoPointEuclidist(oldPoint1, pt1);
- let pt2Dist = TwoPointEuclidist(oldPoint2, pt2);
+ const pt1Dist = TwoPointEuclidist(oldPoint1, pt1);
+ const pt2Dist = TwoPointEuclidist(oldPoint2, pt2);
- let pinching = Pinching(pt1, pt2, oldPoint1, oldPoint2);
+ const pinching = Pinching(pt1, pt2, oldPoint1, oldPoint2);
if (pinching !== 0) {
if ((pt1Dist < threshold && pt2Dist > threshold) || (pt1Dist > threshold && pt2Dist < threshold)) {
@@ -90,6 +90,20 @@ export namespace InteractionUtils {
return 0;
}
+ export function IsDragging(oldTouches: Map<number, React.Touch>, newTouches: React.Touch[], leniency: number): boolean {
+ for (const touch of newTouches) {
+ if (touch) {
+ const oldTouch = oldTouches.get(touch.identifier);
+ if (oldTouch) {
+ if (TwoPointEuclidist(touch, oldTouch) >= leniency) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
// These might not be very useful anymore, but I'll leave them here for now -syip2
{
@@ -145,20 +159,5 @@ export namespace InteractionUtils {
// return { type: undefined };
// }
// }
-
- // export function IsDragging(oldTouches: Map<number, React.Touch>, newTouches: TouchList, leniency: number): boolean {
- // for (let i = 0; i < newTouches.length; i++) {
- // let touch = newTouches.item(i);
- // if (touch) {
- // let oldTouch = oldTouches.get(touch.identifier);
- // if (oldTouch) {
- // if (TwoPointEuclidist(touch, oldTouch) >= leniency) {
- // return true;
- // }
- // }
- // }
- // }
- // return false;
- // }
}
} \ No newline at end of file
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index eedc4967d..5f3667acc 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -38,16 +38,16 @@ export class LinkManager {
}
public getAllLinks(): Doc[] {
- let ldoc = LinkManager.Instance.LinkManagerDoc;
+ const ldoc = LinkManager.Instance.LinkManagerDoc;
if (ldoc) {
- let docs = DocListCast(ldoc.allLinks);
+ const docs = DocListCast(ldoc.allLinks);
return docs;
}
return [];
}
public addLink(linkDoc: Doc): boolean {
- let linkList = LinkManager.Instance.getAllLinks();
+ const linkList = LinkManager.Instance.getAllLinks();
linkList.push(linkDoc);
if (LinkManager.Instance.LinkManagerDoc) {
LinkManager.Instance.LinkManagerDoc.allLinks = new List<Doc>(linkList);
@@ -57,8 +57,8 @@ export class LinkManager {
}
public deleteLink(linkDoc: Doc): boolean {
- let linkList = LinkManager.Instance.getAllLinks();
- let index = LinkManager.Instance.getAllLinks().indexOf(linkDoc);
+ const linkList = LinkManager.Instance.getAllLinks();
+ const index = LinkManager.Instance.getAllLinks().indexOf(linkDoc);
if (index > -1) {
linkList.splice(index, 1);
if (LinkManager.Instance.LinkManagerDoc) {
@@ -70,24 +70,24 @@ export class LinkManager {
}
// finds all links that contain the given anchor
- public getAllRelatedLinks(anchor: Doc): Doc[] {//List<Doc> {
- let related = LinkManager.Instance.getAllLinks().filter(link => {
- let protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, null));
- let protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, null));
+ public getAllRelatedLinks(anchor: Doc): Doc[] {
+ const related = LinkManager.Instance.getAllLinks().filter(link => {
+ const protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, null));
+ const protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, null));
return protomatch1 || protomatch2 || Doc.AreProtosEqual(link, anchor);
});
return related;
}
public deleteAllLinksOnAnchor(anchor: Doc) {
- let related = LinkManager.Instance.getAllRelatedLinks(anchor);
+ const related = LinkManager.Instance.getAllRelatedLinks(anchor);
related.forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc));
}
public addGroupType(groupType: string): boolean {
if (LinkManager.Instance.LinkManagerDoc) {
LinkManager.Instance.LinkManagerDoc[groupType] = new List<string>([]);
- let groupTypes = LinkManager.Instance.getAllGroupTypes();
+ const groupTypes = LinkManager.Instance.getAllGroupTypes();
groupTypes.push(groupType);
LinkManager.Instance.LinkManagerDoc.allGroupTypes = new List<string>(groupTypes);
return true;
@@ -99,8 +99,8 @@ export class LinkManager {
public deleteGroupType(groupType: string): boolean {
if (LinkManager.Instance.LinkManagerDoc) {
if (LinkManager.Instance.LinkManagerDoc[groupType]) {
- let groupTypes = LinkManager.Instance.getAllGroupTypes();
- let index = groupTypes.findIndex(type => type.toUpperCase() === groupType.toUpperCase());
+ const groupTypes = LinkManager.Instance.getAllGroupTypes();
+ const index = groupTypes.findIndex(type => type.toUpperCase() === groupType.toUpperCase());
if (index > -1) groupTypes.splice(index, 1);
LinkManager.Instance.LinkManagerDoc.allGroupTypes = new List<string>(groupTypes);
LinkManager.Instance.LinkManagerDoc[groupType] = undefined;
@@ -146,8 +146,8 @@ export class LinkManager {
}
public addGroupToAnchor(linkDoc: Doc, anchor: Doc, groupDoc: Doc, replace: boolean = false) {
- let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor);
- let index = groups.findIndex(gDoc => {
+ const groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor);
+ const index = groups.findIndex(gDoc => {
return StrCast(groupDoc.type).toUpperCase() === StrCast(gDoc.type).toUpperCase();
});
if (index > -1 && replace) {
@@ -161,32 +161,32 @@ export class LinkManager {
// removes group doc of given group type only from given anchor on given link
public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) {
- let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor);
- let newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase());
+ const groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor);
+ const newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase());
LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups);
}
// returns map of group type to anchor's links in that group type
public getRelatedGroupedLinks(anchor: Doc): Map<string, Array<Doc>> {
- let related = this.getAllRelatedLinks(anchor);
- let anchorGroups = new Map<string, Array<Doc>>();
+ const related = this.getAllRelatedLinks(anchor);
+ const anchorGroups = new Map<string, Array<Doc>>();
related.forEach(link => {
- let groups = LinkManager.Instance.getAnchorGroups(link, anchor);
+ const groups = LinkManager.Instance.getAnchorGroups(link, anchor);
if (groups.length > 0) {
groups.forEach(groupDoc => {
- let groupType = StrCast(groupDoc.type);
+ const groupType = StrCast(groupDoc.type);
if (groupType === "") {
- let group = anchorGroups.get("*");
+ const group = anchorGroups.get("*");
anchorGroups.set("*", group ? [...group, link] : [link]);
} else {
- let group = anchorGroups.get(groupType);
+ const group = anchorGroups.get(groupType);
anchorGroups.set(groupType, group ? [...group, link] : [link]);
}
});
} else {
// if link is in no groups then put it in default group
- let group = anchorGroups.get("*");
+ const group = anchorGroups.get("*");
anchorGroups.set("*", group ? [...group, link] : [link]);
}
@@ -212,11 +212,11 @@ export class LinkManager {
// returns a list of all metadata docs associated with the given group type
public getAllMetadataDocsInGroup(groupType: string): Array<Doc> {
- let md: Doc[] = [];
- let allLinks = LinkManager.Instance.getAllLinks();
+ const md: Doc[] = [];
+ const allLinks = LinkManager.Instance.getAllLinks();
allLinks.forEach(linkDoc => {
- let anchor1Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor1, Doc, null));
- let anchor2Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor2, Doc, null));
+ const anchor1Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor1, Doc, null));
+ const anchor2Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor2, Doc, null));
anchor1Groups.forEach(groupDoc => { if (StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase()) { const meta = Cast(groupDoc.metadata, Doc, null); meta && md.push(meta); } });
anchor2Groups.forEach(groupDoc => { if (StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase()) { const meta = Cast(groupDoc.metadata, Doc, null); meta && md.push(meta); } });
});
@@ -225,8 +225,8 @@ export class LinkManager {
// checks if a link with the given anchors exists
public doesLinkExist(anchor1: Doc, anchor2: Doc): boolean {
- let allLinks = LinkManager.Instance.getAllLinks();
- let index = allLinks.findIndex(linkDoc => {
+ const allLinks = LinkManager.Instance.getAllLinks();
+ const index = allLinks.findIndex(linkDoc => {
return (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor2)) ||
(Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor1));
});
@@ -237,14 +237,12 @@ export class LinkManager {
//TODO This should probably return undefined if there isn't an opposite anchor
//TODO This should also await the return value of the anchor so we don't filter out promises
public getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc | undefined {
- let a1 = Cast(linkDoc.anchor1, Doc, null);
- let a2 = Cast(linkDoc.anchor2, Doc, null);
+ const a1 = Cast(linkDoc.anchor1, Doc, null);
+ const a2 = Cast(linkDoc.anchor2, Doc, null);
if (Doc.AreProtosEqual(anchor, a1)) return a2;
if (Doc.AreProtosEqual(anchor, a2)) return a1;
if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc;
}
}
-Scripting.addGlobal(function links(doc: any) {
- return new List(LinkManager.Instance.getAllRelatedLinks(doc));
-});
+Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }); \ No newline at end of file
diff --git a/src/client/util/ParagraphNodeSpec.ts b/src/client/util/ParagraphNodeSpec.ts
index 3a993e1ff..0a3b68217 100644
--- a/src/client/util/ParagraphNodeSpec.ts
+++ b/src/client/util/ParagraphNodeSpec.ts
@@ -34,6 +34,7 @@ const ParagraphNodeSpec: NodeSpec = {
color: { default: null },
id: { default: null },
indent: { default: null },
+ inset: { default: null },
lineSpacing: { default: null },
// TODO: Add UI to let user edit / clear padding.
paddingBottom: { default: null },
@@ -76,6 +77,7 @@ function toDOM(node: Node): DOMOutputSpec {
const {
align,
indent,
+ inset,
lineSpacing,
paddingTop,
paddingBottom,
@@ -105,6 +107,14 @@ function toDOM(node: Node): DOMOutputSpec {
style += `padding-bottom: ${paddingBottom};`;
}
+ if (indent) {
+ style += `text-indent: ${indent}; padding-left: ${indent < 0 ? -indent : undefined};`;
+ }
+
+ if (inset) {
+ style += `margin-left: ${inset}; margin-right: ${inset};`;
+ }
+
style && (attrs.style = style);
if (indent) {
diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts
index 003ff6272..c028dbf8b 100644
--- a/src/client/util/ProsemirrorExampleTransfer.ts
+++ b/src/client/util/ProsemirrorExampleTransfer.ts
@@ -4,8 +4,10 @@ import { undoInputRule } from "prosemirror-inputrules";
import { Schema } from "prosemirror-model";
import { liftListItem, sinkListItem } from "./prosemirrorPatches.js";
import { splitListItem, wrapInList, } from "prosemirror-schema-list";
-import { EditorState, Transaction, TextSelection, NodeSelection } from "prosemirror-state";
+import { EditorState, Transaction, TextSelection } from "prosemirror-state";
import { TooltipTextMenu } from "./TooltipTextMenu";
+import { SelectionManager } from "./SelectionManager";
+import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
@@ -15,22 +17,22 @@ export let updateBullets = (tx2: Transaction, schema: Schema, mapStyle?: string)
let fontSize: number | undefined = undefined;
tx2.doc.descendants((node: any, offset: any, index: any) => {
if (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item) {
- let path = (tx2.doc.resolve(offset) as any).path;
+ const path = (tx2.doc.resolve(offset) as any).path;
let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty("type") && c.type === schema.nodes.ordered_list ? 1 : 0), 0);
if (node.type === schema.nodes.ordered_list) depth++;
fontSize = depth === 1 && node.attrs.setFontSize ? Number(node.attrs.setFontSize) : fontSize;
- let fsize = fontSize && node.type === schema.nodes.ordered_list ? Math.max(6, fontSize - (depth - 1) * 4) : undefined;
+ const fsize = fontSize && node.type === schema.nodes.ordered_list ? Math.max(6, fontSize - (depth - 1) * 4) : undefined;
tx2.setNodeMarkup(offset, node.type, { ...node.attrs, mapStyle: mapStyle ? mapStyle : node.attrs.mapStyle, bulletStyle: depth, inheritedFontSize: fsize }, node.marks);
}
});
return tx2;
};
export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?: KeyMap): KeyMap {
- let keys: { [key: string]: any } = {}, type;
+ const keys: { [key: string]: any } = {};
function bind(key: string, cmd: any) {
if (mapKeys) {
- let mapped = mapKeys[key];
+ const mapped = mapKeys[key];
if (mapped === false) return;
if (mapped) key = mapped;
}
@@ -46,7 +48,11 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
bind("Alt-ArrowUp", joinUp);
bind("Alt-ArrowDown", joinDown);
bind("Mod-BracketLeft", lift);
- bind("Escape", selectParentNode);
+ bind("Escape", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+ dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
+ (document.activeElement as any).blur?.();
+ SelectionManager.DeselectAll();
+ });
bind("Mod-b", toggleMark(schema.marks.strong));
bind("Mod-B", toggleMark(schema.marks.strong));
@@ -79,7 +85,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
// });
- let cmd = chainCommands(exitCode, (state, dispatch) => {
+ const cmd = chainCommands(exitCode, (state, dispatch) => {
if (dispatch) {
dispatch(state.tr.replaceSelectionWith(schema.nodes.hard_break.create()).scrollIntoView());
return true;
@@ -99,27 +105,25 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
bind("Shift-Ctrl-" + i, setBlockType(schema.nodes.heading, { level: i }));
}
- let hr = schema.nodes.horizontal_rule;
+ const hr = schema.nodes.horizontal_rule;
bind("Mod-_", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView());
return true;
});
- bind("Mod-s", TooltipTextMenu.insertStar);
-
bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
- var ref = state.selection;
- var range = ref.$from.blockRange(ref.$to);
- var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
+ const ref = state.selection;
+ const range = ref.$from.blockRange(ref.$to);
+ const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
if (!sinkListItem(schema.nodes.list_item)(state, (tx2: Transaction) => {
- let tx3 = updateBullets(tx2, schema);
+ const tx3 = updateBullets(tx2, schema);
marks && tx3.ensureMarks([...marks]);
marks && tx3.setStoredMarks([...marks]);
dispatch(tx3);
})) { // couldn't sink into an existing list, so wrap in a new one
- let newstate = state.applyTransaction(state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)));
+ const newstate = state.applyTransaction(state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)));
if (!wrapInList(schema.nodes.ordered_list)(newstate.state, (tx2: Transaction) => {
- let tx3 = updateBullets(tx2, schema);
+ 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]);
marks && tx3.setStoredMarks([...marks]);
@@ -131,10 +135,10 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
});
bind("Shift-Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
- var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
+ const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => {
- let tx3 = updateBullets(tx2, schema);
+ const tx3 = updateBullets(tx2, schema);
marks && tx3.ensureMarks([...marks]);
marks && tx3.setStoredMarks([...marks]);
dispatch(tx3);
@@ -143,14 +147,14 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
}
});
- let splitMetadata = (marks: any, tx: Transaction) => {
+ const splitMetadata = (marks: any, tx: Transaction) => {
marks && tx.ensureMarks(marks.filter((val: any) => val.type !== schema.marks.metadata && val.type !== schema.marks.metadataKey && val.type !== schema.marks.metadataVal));
marks && tx.setStoredMarks(marks.filter((val: any) => val.type !== schema.marks.metadata && val.type !== schema.marks.metadataKey && val.type !== schema.marks.metadataVal));
return tx;
};
- bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
- var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
- if (!splitListItem(schema.nodes.list_item)(state, (tx3: Transaction) => dispatch(tx3))) {
+ bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
+ const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
+ if (!splitListItem(schema.nodes.list_item)(state, dispatch)) {
if (!splitBlockKeepMarks(state, (tx3: Transaction) => {
splitMetadata(marks, tx3);
if (!liftListItem(schema.nodes.list_item)(tx3, dispatch as ((tx: Transaction<Schema<any, any>>) => void))) {
@@ -163,18 +167,18 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
return true;
});
bind("Space", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
- var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
+ const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
dispatch(splitMetadata(marks, state.tr));
return false;
});
bind(":", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
- let range = state.selection.$from.blockRange(state.selection.$to, (node: any) => {
+ const range = state.selection.$from.blockRange(state.selection.$to, (node: any) => {
return !node.marks || !node.marks.find((m: any) => m.type === schema.marks.metadata);
});
- let path = (state.doc.resolve(state.selection.from - 1) as any).path;
- let spaceSeparator = path[path.length - 3].childCount > 1 ? 0 : -1;
- let textsel = TextSelection.create(state.doc, range!.end - path[path.length - 3].lastChild.nodeSize + spaceSeparator, range!.end);
- let text = range ? state.doc.textBetween(textsel.from, textsel.to) : "";
+ const path = (state.doc.resolve(state.selection.from - 1) as any).path;
+ const spaceSeparator = path[path.length - 3].childCount > 1 ? 0 : -1;
+ const textsel = TextSelection.create(state.doc, range!.end - path[path.length - 3].lastChild.nodeSize + spaceSeparator, range!.end);
+ const text = range ? state.doc.textBetween(textsel.from, textsel.to) : "";
let whitespace = text.length - 1;
for (; whitespace >= 0 && text[whitespace] !== " "; whitespace--) { }
if (text.endsWith(":")) {
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index ebb9bda8a..29b378299 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -5,8 +5,11 @@ import { NodeSelection, TextSelection } from "prosemirror-state";
import { NumCast, Cast } from "../../new_fields/Types";
import { Doc } from "../../new_fields/Doc";
import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
-import { Docs } from "../documents/Documents";
+import { TooltipTextMenuManager } from "../util/TooltipTextMenu";
+import { Docs, DocUtils } from "../documents/Documents";
import { Id } from "../../new_fields/FieldSymbols";
+import { DocServer } from "../DocServer";
+import { returnFalse, Utils } from "../../Utils";
export const inpRules = {
rules: [
@@ -59,137 +62,222 @@ export const inpRules = {
}
),
+ // set the font size using #<font-size>
new InputRule(
- new RegExp(/^#([0-9]+)\s$/),
+ new RegExp(/^%([0-9]+)\s$/),
(state, match, start, end) => {
- let size = Number(match[1]);
- let ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
- let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
+ const size = Number(match[1]);
+ const ruleProvider = FormattedTextBox.FocusedBox!.props.ruleProvider;
+ const heading = NumCast(FormattedTextBox.FocusedBox!.props.Document.heading);
if (ruleProvider && heading) {
- (Cast(FormattedTextBox.InputBoxOverlay!.props.Document, Doc) as Doc).heading = Number(match[1]);
+ (Cast(FormattedTextBox.FocusedBox!.props.Document, Doc) as Doc).heading = size;
return state.tr.deleteRange(start, end);
}
- return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: Number(match[1]) }));
+ return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: size }));
}),
+
+ // make current selection a hyperlink portal (assumes % was used to initiate an EnteringStyle mode)
+ new InputRule(
+ new RegExp(/@$/),
+ (state, match, start, end) => {
+ if (state.selection.to === state.selection.from || !(schema as any).EnteringStyle) return null;
+
+ const value = state.doc.textBetween(start, end);
+ if (value) {
+ DocServer.GetRefField(value).then(docx => {
+ const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500, }, value);
+ DocUtils.Publish(target, value, returnFalse, returnFalse);
+ DocUtils.MakeLink({ doc: (schema as any).Document }, { doc: target }, "portal link", "");
+ });
+ const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + value), location: "onRight", title: value, targetId: value });
+ return state.tr.addMark(start, end, link);
+ }
+ return state.tr;
+ }),
+ // stop using active style
new InputRule(
- new RegExp(/t/),
+ new RegExp(/%%$/),
(state, match, start, end) => {
- if (state.selection.to === state.selection.from) return null;
- let node = (state.doc.resolve(start) as any).nodeAfter;
- return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: "todo", modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
+ const tr = state.tr.deleteRange(start, end);
+ const marks = state.tr.selection.$anchor.nodeBefore?.marks;
+ return marks ? Array.from(marks).filter(m => m !== state.schema.marks.user_mark).reduce((tr, m) => tr.removeStoredMark(m), tr) : tr;
}),
+
+ // set the Todo user-tag on the current selection (assumes % was used to initiate an EnteringStyle mode)
+ new InputRule(
+ new RegExp(/[ti!x]$/),
+ (state, match, start, end) => {
+ if (state.selection.to === state.selection.from || !(schema as any).EnteringStyle) return null;
+ const tag = match[0] === "t" ? "todo" : match[0] === "i" ? "ignore" : match[0] === "x" ? "disagree" : match[0] === "!" ? "important" : "??";
+ const node = (state.doc.resolve(start) as any).nodeAfter;
+ if (node?.marks.findIndex((m: any) => m.type === schema.marks.user_tag) !== -1) return state.tr.removeMark(start, end, schema.marks.user_tag);
+ return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: tag, modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
+ }),
+
+ // set the First-line indent node type for the selection's paragraph (assumes % was used to initiate an EnteringStyle mode)
new InputRule(
- new RegExp(/i/),
+ new RegExp(/(%d|d)$/),
(state, match, start, end) => {
- if (state.selection.to === state.selection.from) return null;
- let node = (state.doc.resolve(start) as any).nodeAfter;
- return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: "ignore", modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
+ if (!match[0].startsWith("%") && !(schema as any).EnteringStyle) return null;
+ const pos = (state.doc.resolve(start) as any);
+ for (let depth = pos.path.length / 3 - 1; depth >= 0; depth--) {
+ const node = pos.node(depth);
+ if (node.type === schema.nodes.paragraph) {
+ const replaced = state.tr.setNodeMarkup(pos.pos - pos.parentOffset - 1, node.type, { ...node.attrs, indent: node.attrs.indent === 25 ? undefined : 25 });
+ const result = replaced.setSelection(new TextSelection(replaced.doc.resolve(start)));
+ return match[0].startsWith("%") ? result.deleteRange(start, end) : result;
+ }
+ }
+ return null;
}),
+
+ // set the Hanging indent node type for the current selection's paragraph (assumes % was used to initiate an EnteringStyle mode)
new InputRule(
- new RegExp(/\!/),
+ new RegExp(/(%h|h)$/),
(state, match, start, end) => {
- if (state.selection.to === state.selection.from) return null;
- let node = (state.doc.resolve(start) as any).nodeAfter;
- return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: "important", modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
+ if (!match[0].startsWith("%") && !(schema as any).EnteringStyle) return null;
+ const pos = (state.doc.resolve(start) as any);
+ for (let depth = pos.path.length / 3 - 1; depth >= 0; depth--) {
+ const node = pos.node(depth);
+ if (node.type === schema.nodes.paragraph) {
+ const replaced = state.tr.setNodeMarkup(pos.pos - pos.parentOffset - 1, node.type, { ...node.attrs, indent: node.attrs.indent === -25 ? undefined : -25 });
+ const result = replaced.setSelection(new TextSelection(replaced.doc.resolve(start)));
+ return match[0].startsWith("%") ? result.deleteRange(start, end) : result;
+ }
+ }
+ return null;
}),
+ // set the Quoted indent node type for the current selection's paragraph (assumes % was used to initiate an EnteringStyle mode)
new InputRule(
- new RegExp(/\x/),
+ new RegExp(/(%q|q)$/),
(state, match, start, end) => {
- if (state.selection.to === state.selection.from) return null;
- let node = (state.doc.resolve(start) as any).nodeAfter;
- return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: "disagree", modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
+ if (!match[0].startsWith("%") && !(schema as any).EnteringStyle) return null;
+ const pos = (state.doc.resolve(start) as any);
+ if (state.selection instanceof NodeSelection && state.selection.node.type === schema.nodes.ordered_list) {
+ const node = state.selection.node;
+ return state.tr.setNodeMarkup(pos.pos, node.type, { ...node.attrs, indent: node.attrs.indent === 30 ? undefined : 30 });
+ }
+ for (let depth = pos.path.length / 3 - 1; depth >= 0; depth--) {
+ const node = pos.node(depth);
+ if (node.type === schema.nodes.paragraph) {
+ const replaced = state.tr.setNodeMarkup(pos.pos - pos.parentOffset - 1, node.type, { ...node.attrs, inset: node.attrs.inset === 30 ? undefined : 30 });
+ const result = replaced.setSelection(new TextSelection(replaced.doc.resolve(start)));
+ return match[0].startsWith("%") ? result.deleteRange(start, end) : result;
+ }
+ }
+ return null;
}),
+
+
+ // center justify text
new InputRule(
- new RegExp(/^\^\^\s$/),
+ new RegExp(/%\^$/),
(state, match, start, end) => {
- let node = (state.doc.resolve(start) as any).nodeAfter;
- let sm = state.storedMarks || undefined;
- let ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
- let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
+ const node = (state.doc.resolve(start) as any).nodeAfter;
+ const sm = state.storedMarks || undefined;
+ const ruleProvider = FormattedTextBox.FocusedBox!.props.ruleProvider;
+ const heading = NumCast(FormattedTextBox.FocusedBox!.props.Document.heading);
if (ruleProvider && heading) {
ruleProvider["ruleAlign_" + heading] = "center";
return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
}
- let replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "center" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+ const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "center" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
state.tr;
return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
}),
+ // left justify text
new InputRule(
- new RegExp(/^\[\[\s$/),
+ new RegExp(/%\[$/),
(state, match, start, end) => {
- let node = (state.doc.resolve(start) as any).nodeAfter;
- let sm = state.storedMarks || undefined;
- let ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
- let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
+ const node = (state.doc.resolve(start) as any).nodeAfter;
+ const sm = state.storedMarks || undefined;
+ const ruleProvider = FormattedTextBox.FocusedBox!.props.ruleProvider;
+ const heading = NumCast(FormattedTextBox.FocusedBox!.props.Document.heading);
if (ruleProvider && heading) {
ruleProvider["ruleAlign_" + heading] = "left";
return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
}
- let replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "left" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+ const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "left" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
state.tr;
return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
}),
+ // right justify text
new InputRule(
- new RegExp(/^\]\]\s$/),
+ new RegExp(/%\]$/),
(state, match, start, end) => {
- let node = (state.doc.resolve(start) as any).nodeAfter;
- let sm = state.storedMarks || undefined;
- let ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
- let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
+ const node = (state.doc.resolve(start) as any).nodeAfter;
+ const sm = state.storedMarks || undefined;
+ const ruleProvider = FormattedTextBox.FocusedBox!.props.ruleProvider;
+ const heading = NumCast(FormattedTextBox.FocusedBox!.props.Document.heading);
if (ruleProvider && heading) {
ruleProvider["ruleAlign_" + heading] = "right";
return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
}
- let replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "right" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+ const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "right" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
state.tr;
return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
}),
new InputRule(
- new RegExp(/##\s$/),
+ new RegExp(/%#$/),
(state, match, start, end) => {
- let node = (state.doc.resolve(start) as any).nodeAfter;
- let sm = state.storedMarks || undefined;
- let target = Docs.Create.TextDocument({ width: 75, height: 35, autoHeight: true, fontSize: 9, title: "inline comment" });
- let replaced = node ? state.tr.insertText("←", start).replaceRangeWith(start + 1, end + 1, schema.nodes.dashDoc.create({
- width: 75, height: 35,
- title: "dashDoc", docid: target[Id],
- float: "right"
- })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+ const target = Docs.Create.TextDocument({ width: 75, height: 35, backgroundColor: "yellow", annotationOn: FormattedTextBox.FocusedBox!.dataDoc, autoHeight: true, fontSize: 9, title: "inline comment" });
+ const node = (state.doc.resolve(start) as any).nodeAfter;
+ const newNode = schema.nodes.dashComment.create({ docid: target[Id] });
+ const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: target[Id], float: "right" });
+ const sm = state.storedMarks || undefined;
+ const replaced = node ? state.tr.insert(start, newNode).replaceRangeWith(start + 1, end + 1, dashDoc).insertText(" ", start + 2).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
state.tr;
- return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 1)));
+ return replaced;//.setSelection(new NodeSelection(replaced.doc.resolve(end)));
}),
new InputRule(
- new RegExp(/\(\(/),
+ new RegExp(/%\(/),
(state, match, start, end) => {
- let node = (state.doc.resolve(start) as any).nodeAfter;
- let sm = state.storedMarks || undefined;
- let mark = state.schema.marks.highlight.create();
- let selected = state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).addMark(start, end, mark);
- let content = selected.selection.content();
- let replaced = node ? selected.replaceRangeWith(start, start,
- schema.nodes.star.create({ visibility: true, text: content, textslice: content.toJSON() })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+ const node = (state.doc.resolve(start) as any).nodeAfter;
+ const sm = state.storedMarks || [];
+ const mark = state.schema.marks.summarizeInclusive.create();
+ sm.push(mark);
+ const selected = state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).addMark(start, end, mark);
+ const content = selected.selection.content();
+ const replaced = node ? selected.replaceRangeWith(start, end,
+ schema.nodes.summary.create({ visibility: true, text: content, textslice: content.toJSON() })) :
state.tr;
- return replaced.setSelection(new TextSelection(replaced.doc.resolve(end + 1)));
+ return replaced.setSelection(new TextSelection(replaced.doc.resolve(end + 1))).setStoredMarks([...node.marks, ...sm]);
}),
new InputRule(
- new RegExp(/\)\)/),
+ new RegExp(/%\)/),
(state, match, start, end) => {
- let mark = state.schema.marks.highlight.create();
- return state.tr.removeStoredMark(mark);
+ return state.tr.deleteRange(start, end).removeStoredMark(state.schema.marks.summarizeInclusive.create());
}),
new InputRule(
- new RegExp(/\^f\s$/),
+ new RegExp(/%f$/),
(state, match, start, end) => {
- let newNode = schema.nodes.footnote.create({});
- let tr = state.tr;
+ const newNode = schema.nodes.footnote.create({});
+ const tr = state.tr;
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)));
}),
- // let newNode = schema.nodes.footnote.create({});
- // if (dispatch && state.selection.from === state.selection.to) {
- // return true;
- // }
+
+ // activate a style by name using prefix '%'
+ new InputRule(
+ new RegExp(/%[a-z]+$/),
+ (state, match, start, end) => {
+ const color = match[0].substring(1, match[0].length);
+ const marks = TooltipTextMenuManager.Instance._brushMap.get(color);
+ if (marks) {
+ const tr = state.tr.deleteRange(start, end);
+ return marks ? Array.from(marks).reduce((tr, m) => tr.addStoredMark(m), tr) : tr;
+ }
+ const isValidColor = (strColor: string) => {
+ const s = new Option().style;
+ s.color = strColor;
+ return s.color === strColor.toLowerCase(); // 'false' if color wasn't assigned
+ };
+ if (isValidColor(color)) {
+ return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontColor.create({ color: color }));
+ }
+ return null;
+ }),
]
};
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 0a717dff1..ef90a7294 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -1,4 +1,4 @@
-import { action, observable, runInAction, reaction, IReactionDisposer } from "mobx";
+import { reaction, IReactionDisposer } from "mobx";
import { baseKeymap, toggleMark } from "prosemirror-commands";
import { redo, undo } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
@@ -16,10 +16,10 @@ import { DocumentManager } from "./DocumentManager";
import ParagraphNodeSpec from "./ParagraphNodeSpec";
import { Transform } from "./Transform";
import React = require("react");
-import { BoolCast, NumCast } from "../../new_fields/Types";
+import { BoolCast, NumCast, Cast } from "../../new_fields/Types";
import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
-const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
+const blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
// :: Object
@@ -30,7 +30,6 @@ export const nodes: { [index: string]: NodeSpec } = {
content: "block+"
},
-
footnote: {
group: "inline",
content: "inline*",
@@ -45,15 +44,6 @@ export const nodes: { [index: string]: NodeSpec } = {
parseDOM: [{ tag: "footnote" }]
},
- // // :: NodeSpec A plain paragraph textblock. Represented in the DOM
- // // as a `<p>` element.
- // paragraph: {
- // content: "inline*",
- // group: "block",
- // parseDOM: [{ tag: "p" }],
- // toDOM() { return pDOM; }
- // },
-
paragraph: ParagraphNodeSpec,
// :: NodeSpec A blockquote (`<blockquote>`) wrapping one or more blocks.
@@ -107,7 +97,19 @@ export const nodes: { [index: string]: NodeSpec } = {
group: "inline"
},
- star: {
+ dashComment: {
+ attrs: {
+ docid: { default: "" },
+ },
+ inline: true,
+ group: "inline",
+ toDOM(node) {
+ const attrs = { style: `width: 40px` };
+ return ["span", { ...node.attrs, ...attrs }, "←"];
+ },
+ },
+
+ summary: {
inline: true,
attrs: {
visibility: { default: false },
@@ -119,15 +121,6 @@ export const nodes: { [index: string]: NodeSpec } = {
const attrs = { style: `width: 40px` };
return ["span", { ...node.attrs, ...attrs }];
},
- // parseDOM: [{
- // tag: "star", getAttrs(dom: any) {
- // return {
- // visibility: dom.getAttribute("visibility"),
- // oldtext: dom.getAttribute("oldtext"),
- // oldtextlen: dom.getAttribute("oldtextlen"),
- // }
- // }
- // }]
},
// :: NodeSpec An inline image (`<img>`) node. Supports `src`,
@@ -171,21 +164,11 @@ export const nodes: { [index: string]: NodeSpec } = {
title: { default: null },
float: { default: "right" },
location: { default: "onRight" },
- docid: { default: "" }
+ hidden: { default: false },
+ docid: { default: "" },
},
group: "inline",
- draggable: true,
- // parseDOM: [{
- // tag: "img[src]", getAttrs(dom: any) {
- // return {
- // src: dom.getAttribute("src"),
- // title: dom.getAttribute("title"),
- // alt: dom.getAttribute("alt"),
- // width: Math.min(100, Number(dom.getAttribute("width"))),
- // };
- // }
- // }],
- // TODO if we don't define toDom, dragging the image crashes. Why?
+ draggable: false,
toDOM(node) {
const attrs = { style: `width: ${node.attrs.width}, height: ${node.attrs.height}` };
return ["div", { ...node.attrs, ...attrs }];
@@ -235,20 +218,21 @@ export const nodes: { [index: string]: NodeSpec } = {
bulletStyle: { default: 0 },
mapStyle: { default: "decimal" },
setFontSize: { default: undefined },
- setFontFamily: { default: undefined },
+ setFontFamily: { default: "inherit" },
+ setFontColor: { default: "inherit" },
inheritedFontSize: { default: undefined },
- visibility: { default: true }
+ visibility: { default: true },
+ indent: { default: undefined }
},
toDOM(node: Node<any>) {
- const bs = node.attrs.bulletStyle;
if (node.attrs.mapStyle === "bullet") return ['ul', 0];
- const decMap = bs ? "decimal" + bs : "";
- const multiMap = bs === 1 ? "decimal1" : bs === 2 ? "upper-alpha" : bs === 3 ? "lower-roman" : bs === 4 ? "lower-alpha" : "";
- let map = node.attrs.mapStyle === "decimal" ? decMap : multiMap;
- let fsize = node.attrs.setFontSize ? node.attrs.setFontSize : node.attrs.inheritedFontSize;
- let ffam = node.attrs.setFontFamily;
- return node.attrs.visibility ? ['ol', { class: `${map}-ol`, style: `list-style: none; font-size: ${fsize}; font-family: ${ffam}` }, 0] :
- ['ol', { class: `${map}-ol`, style: `list-style: none; font-size: ${fsize}; font-family: ${ffam}` }];
+ const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : "";
+ const fsize = node.attrs.setFontSize ? node.attrs.setFontSize : node.attrs.inheritedFontSize;
+ const ffam = node.attrs.setFontFamily;
+ const color = node.attrs.setFontColor;
+ return node.attrs.visibility ?
+ ['ol', { class: `${map}-ol`, style: `list-style: none; font-size: ${fsize}; font-family: ${ffam}; color:${color}; margin-left: ${node.attrs.indent}` }, 0] :
+ ['ol', { class: `${map}-ol`, style: `list-style: none;` }];
}
},
@@ -271,10 +255,7 @@ export const nodes: { [index: string]: NodeSpec } = {
...listItem,
content: 'paragraph block*',
toDOM(node: any) {
- const bs = node.attrs.bulletStyle;
- const decMap = bs ? "decimal" + bs : "";
- const multiMap = bs === 1 ? "decimal1" : bs === 2 ? "upper-alpha" : bs === 3 ? "lower-roman" : bs === 4 ? "lower-alpha" : "";
- let map = node.attrs.mapStyle === "decimal" ? decMap : node.attrs.mapStyle === "multi" ? multiMap : "";
+ const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : "";
return node.attrs.visibility ? ["li", { class: `${map}` }, 0] : ["li", { class: `${map}` }, "..."];
//return ["li", { class: `${map}` }, 0];
}
@@ -293,6 +274,8 @@ export const marks: { [index: string]: MarkSpec } = {
link: {
attrs: {
href: {},
+ targetId: { default: "" },
+ showPreview: { default: true },
location: { default: null },
title: { default: null },
docref: { default: false } // flags whether the linked text comes from a document within Dash. If so, an attribution label is appended after the text
@@ -300,22 +283,23 @@ export const marks: { [index: string]: MarkSpec } = {
inclusive: false,
parseDOM: [{
tag: "a[href]", getAttrs(dom: any) {
- return { href: dom.getAttribute("href"), location: dom.getAttribute("location"), title: dom.getAttribute("title") };
+ return { href: dom.getAttribute("href"), location: dom.getAttribute("location"), title: dom.getAttribute("title"), targetId: dom.getAttribute("id") };
}
}],
toDOM(node: any) {
return node.attrs.docref && node.attrs.title ?
["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, class: "prosemirror-attribution", title: `${node.attrs.title}` }, node.attrs.title], ["br"]] :
- ["a", { ...node.attrs, title: `${node.attrs.title}` }, 0];
+ ["a", { ...node.attrs, id: node.attrs.targetId, title: `${node.attrs.title}` }, 0];
}
},
+
// :: MarkSpec Coloring on text. Has `color` attribute that defined the color of the marked text.
- color: {
+ pFontColor: {
attrs: {
color: { default: "#000" }
},
- inclusive: false,
+ inclusive: true,
parseDOM: [{
tag: "span", getAttrs(dom: any) {
return { color: dom.getAttribute("color") };
@@ -330,7 +314,7 @@ export const marks: { [index: string]: MarkSpec } = {
attrs: {
highlight: { default: "transparent" }
},
- inclusive: false,
+ inclusive: true,
parseDOM: [{
tag: "span", getAttrs(dom: any) {
return { highlight: dom.getAttribute("backgroundColor") };
@@ -413,16 +397,16 @@ export const marks: { [index: string]: MarkSpec } = {
}
},
- highlight: {
+ summarizeInclusive: {
parseDOM: [
{
tag: "span",
getAttrs: (p: any) => {
if (typeof (p) !== "string") {
- let style = getComputedStyle(p);
+ const style = getComputedStyle(p);
if (style.textDecoration === "underline") return null;
if (p.parentElement.outerHTML.indexOf("text-decoration: underline") !== -1 &&
- p.parentElement.outerHTML.indexOf("text-decoration-style: dotted") !== -1) {
+ p.parentElement.outerHTML.indexOf("text-decoration-style: solid") !== -1) {
return null;
}
}
@@ -433,6 +417,31 @@ export const marks: { [index: string]: MarkSpec } = {
inclusive: true,
toDOM() {
return ['span', {
+ style: 'text-decoration: underline; text-decoration-style: solid; text-decoration-color: rgba(204, 206, 210, 0.92)'
+ }];
+ }
+ },
+
+ summarize: {
+ inclusive: false,
+ parseDOM: [
+ {
+ tag: "span",
+ getAttrs: (p: any) => {
+ if (typeof (p) !== "string") {
+ const style = getComputedStyle(p);
+ if (style.textDecoration === "underline") return null;
+ if (p.parentElement.outerHTML.indexOf("text-decoration: underline") !== -1 &&
+ p.parentElement.outerHTML.indexOf("text-decoration-style: dotted") !== -1) {
+ return null;
+ }
+ }
+ return false;
+ }
+ },
+ ],
+ toDOM() {
+ return ['span', {
style: 'text-decoration: underline; text-decoration-style: dotted; text-decoration-color: rgba(204, 206, 210, 0.92)'
}];
}
@@ -444,7 +453,7 @@ export const marks: { [index: string]: MarkSpec } = {
tag: "span",
getAttrs: (p: any) => {
if (typeof (p) !== "string") {
- let style = getComputedStyle(p);
+ const style = getComputedStyle(p);
if (style.textDecoration === "underline" || p.parentElement.outerHTML.indexOf("text-decoration-style:line") !== -1) {
return null;
}
@@ -475,35 +484,30 @@ export const marks: { [index: string]: MarkSpec } = {
user_mark: {
attrs: {
userid: { default: "" },
- opened: { default: true },
modified: { default: "when?" }, // 5 second intervals since 1970
},
group: "inline",
toDOM(node: any) {
- let uid = node.attrs.userid.replace(".", "").replace("@", "");
- let min = Math.round(node.attrs.modified / 12);
- let hr = Math.round(min / 60);
- let day = Math.round(hr / 60 / 24);
- let remote = node.attrs.userid !== Doc.CurrentUserEmail ? " userMark-remote" : "";
- return node.attrs.opened ?
- ['span', { class: "userMark-" + uid + remote + " userMark-min-" + min + " userMark-hr-" + hr + " userMark-day-" + day }, 0] :
- ['span', { class: "userMark-" + uid + remote + " userMark-min-" + min + " userMark-hr-" + hr + " userMark-day-" + day }, ['span', 0]];
+ const uid = node.attrs.userid.replace(".", "").replace("@", "");
+ const min = Math.round(node.attrs.modified / 12);
+ const hr = Math.round(min / 60);
+ const day = Math.round(hr / 60 / 24);
+ const remote = node.attrs.userid !== Doc.CurrentUserEmail ? " userMark-remote" : "";
+ return ['span', { class: "userMark-" + uid + remote + " userMark-min-" + min + " userMark-hr-" + hr + " userMark-day-" + day }, 0];
}
},
// the id of the user who entered the text
user_tag: {
attrs: {
userid: { default: "" },
- opened: { default: true },
modified: { default: "when?" }, // 5 second intervals since 1970
tag: { default: "" }
},
group: "inline",
+ inclusive: false,
toDOM(node: any) {
- let uid = node.attrs.userid.replace(".", "").replace("@", "");
- return node.attrs.opened ?
- ['span', { class: "userTag-" + uid + " userTag-" + node.attrs.tag }, 0] :
- ['span', { class: "userTag-" + uid + " userTag-" + node.attrs.tag }, ['span', 0]];
+ const uid = node.attrs.userid.replace(".", "").replace("@", "");
+ return ['span', { class: "userTag-" + uid + " userTag-" + node.attrs.tag }, 0];
}
},
@@ -521,7 +525,7 @@ export const marks: { [index: string]: MarkSpec } = {
},
parseDOM: [{
tag: "span", getAttrs(dom: any) {
- let cstyle = getComputedStyle(dom);
+ const cstyle = getComputedStyle(dom);
if (cstyle.font) {
if (cstyle.font.indexOf("Times New Roman") !== -1) return { family: "Times New Roman" };
if (cstyle.font.indexOf("Arial") !== -1) return { family: "Arial" };
@@ -537,18 +541,6 @@ export const marks: { [index: string]: MarkSpec } = {
}]
},
- pFontColor: {
- attrs: {
- color: { default: "yellow" }
- },
- parseDOM: [{ style: 'background: #d9dbdd' }],
- toDOM: (node) => {
- return ['span', {
- style: `color: ${node.attrs.color}`
- }];
- }
- },
-
/** FONT SIZES */
pFontSize: {
attrs: {
@@ -586,7 +578,7 @@ export class ImageResizeView {
this._handle.style.display = "none";
this._handle.style.bottom = "-10px";
this._handle.style.right = "-10px";
- let self = this;
+ const self = this;
this._img.onclick = function (e: any) {
e.stopPropagation();
e.preventDefault();
@@ -607,8 +599,8 @@ export class ImageResizeView {
this._handle.onpointerdown = function (e: any) {
e.preventDefault();
e.stopPropagation();
- let wid = Number(getComputedStyle(self._img).width.replace(/px/, ""));
- let hgt = Number(getComputedStyle(self._img).height.replace(/px/, ""));
+ const wid = Number(getComputedStyle(self._img).width.replace(/px/, ""));
+ const hgt = Number(getComputedStyle(self._img).height.replace(/px/, ""));
const startX = e.pageX;
const startWidth = parseFloat(node.attrs.width);
const onpointermove = (e: any) => {
@@ -621,7 +613,7 @@ export class ImageResizeView {
const onpointerup = () => {
document.removeEventListener("pointermove", onpointermove);
document.removeEventListener("pointerup", onpointerup);
- let pos = view.state.selection.from;
+ const pos = view.state.selection.from;
view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: self._outer.style.width, height: self._outer.style.height }));
view.dispatch(view.state.tr.setSelection(new NodeSelection(view.state.doc.resolve(pos))));
};
@@ -648,6 +640,58 @@ export class ImageResizeView {
}
}
+
+export class DashDocCommentView {
+ _collapsed: HTMLElement;
+ _view: any;
+ constructor(node: any, view: any, getPos: any) {
+ this._collapsed = document.createElement("span");
+ this._collapsed.className = "formattedTextBox-inlineComment";
+ this._collapsed.id = "DashDocCommentView-" + node.attrs.docid;
+ this._view = view;
+ const targetNode = () => { // search forward in the prosemirror doc for the attached dashDocNode that is the target of the comment anchor
+ for (let i = getPos() + 1; i < view.state.doc.content.size; i++) {
+ const m = view.state.doc.nodeAt(i);
+ if (m && m.type === view.state.schema.nodes.dashDoc && m.attrs.docid === node.attrs.docid) {
+ return { node: m, pos: i, hidden: m.attrs.hidden } as { node: any, pos: number, hidden: boolean };
+ }
+ }
+ const dashDoc = view.state.schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: node.attrs.docid, float: "right" });
+ view.dispatch(view.state.tr.insert(getPos() + 1, dashDoc));
+ setTimeout(() => { try { view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.tr.doc, getPos() + 2))); } catch (e) { } }, 0);
+ return undefined;
+ };
+ this._collapsed.onpointerdown = (e: any) => {
+ e.stopPropagation();
+ };
+ this._collapsed.onpointerup = (e: any) => {
+ const target = targetNode();
+ if (target) {
+ const expand = target.hidden;
+ const tr = view.state.tr.setNodeMarkup(target.pos, undefined, { ...target.node.attrs, hidden: target.node.attrs.hidden ? false : true });
+ view.dispatch(tr.setSelection(TextSelection.create(tr.doc, getPos() + (expand ? 2 : 1)))); // update the attrs
+ setTimeout(() => {
+ expand && DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
+ try { view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.tr.doc, getPos() + (expand ? 2 : 1)))); } catch (e) { }
+ }, 0);
+ }
+ e.stopPropagation();
+ };
+ this._collapsed.onpointerenter = (e: any) => {
+ DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
+ e.preventDefault();
+ e.stopPropagation();
+ };
+ this._collapsed.onpointerleave = (e: any) => {
+ DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowUnhighlight());
+ e.preventDefault();
+ e.stopPropagation();
+ };
+ (this as any).dom = this._collapsed;
+ }
+ selectNode() { }
+}
+
export class DashDocView {
_dashSpan: HTMLDivElement;
_outer: HTMLElement;
@@ -656,36 +700,55 @@ export class DashDocView {
_textBox: FormattedTextBox;
getDocTransform = () => {
- let { scale, translateX, translateY } = Utils.GetScreenTransform(this._outer);
+ const { scale, translateX, translateY } = Utils.GetScreenTransform(this._outer);
return new Transform(-translateX, -translateY, 1).scale(1 / this.contentScaling() / scale);
}
contentScaling = () => NumCast(this._dashDoc!.nativeWidth) > 0 && !this._dashDoc!.ignoreAspect ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!.nativeWidth) : 1;
+ outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document); // ideally, this would scroll to show the focus target
constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
this._textBox = tbox;
this._dashSpan = document.createElement("div");
this._outer = document.createElement("span");
this._outer.style.position = "relative";
+ this._outer.style.textIndent = "0";
this._outer.style.width = node.attrs.width;
this._outer.style.height = node.attrs.height;
- this._outer.style.display = "inline-block";
- this._outer.style.overflow = "hidden";
+ this._outer.style.display = node.attrs.hidden ? "none" : "inline-block";
+ // this._outer.style.overflow = "hidden"; // bcz: not sure if this is needed. if it's used, then the doc doesn't highlight when you hover over a docComment
(this._outer.style as any).float = node.attrs.float;
this._dashSpan.style.width = node.attrs.width;
this._dashSpan.style.height = node.attrs.height;
this._dashSpan.style.position = "absolute";
this._dashSpan.style.display = "inline-block";
- let removeDoc = () => {
- let pos = getPos();
- let ns = new NodeSelection(view.state.doc.resolve(pos));
+ const removeDoc = () => {
+ const pos = getPos();
+ const ns = new NodeSelection(view.state.doc.resolve(pos));
view.dispatch(view.state.tr.setSelection(ns).deleteSelection());
return true;
};
+ this._dashSpan.onpointerleave = () => {
+ const ele = document.getElementById("DashDocCommentView-" + node.attrs.docid);
+ if (ele) {
+ (ele as HTMLDivElement).style.backgroundColor = "";
+ }
+ };
+ this._dashSpan.onpointerenter = () => {
+ const ele = document.getElementById("DashDocCommentView-" + node.attrs.docid);
+ if (ele) {
+ (ele as HTMLDivElement).style.backgroundColor = "orange";
+ }
+ };
DocServer.GetRefField(node.attrs.docid).then(async dashDoc => {
if (dashDoc instanceof Doc) {
self._dashDoc = dashDoc;
+ dashDoc.hideSidebar = true;
if (node.attrs.width !== dashDoc.width + "px" || node.attrs.height !== dashDoc.height + "px") {
- view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: dashDoc.width + "px", height: dashDoc.height + "px" }));
+ try { // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made
+ view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: dashDoc.width + "px", height: dashDoc.height + "px" }));
+ } catch (e) {
+ console.log(e);
+ }
}
this._reactionDisposer && this._reactionDisposer();
this._reactionDisposer = reaction(() => dashDoc[HeightSym]() + dashDoc[WidthSym](), () => {
@@ -693,8 +756,9 @@ export class DashDocView {
this._dashSpan.style.width = this._outer.style.width = dashDoc[WidthSym]() + "px";
});
ReactDOM.render(<DocumentView
- fitToBox={BoolCast(dashDoc.fitToBox)}
Document={dashDoc}
+ LibraryPath={tbox.props.LibraryPath}
+ fitToBox={BoolCast(dashDoc.fitToBox)}
addDocument={returnFalse}
removeDocument={removeDoc}
ruleProvider={undefined}
@@ -704,22 +768,27 @@ export class DashDocView {
renderDepth={1}
PanelWidth={self._dashDoc[WidthSym]}
PanelHeight={self._dashDoc[HeightSym]}
- focus={emptyFunction}
+ focus={self.outerFocus}
backgroundColor={returnEmptyString}
parentActive={returnFalse}
whenActiveChanged={returnFalse}
bringToFront={emptyFunction}
zoomToScale={emptyFunction}
getScale={returnOne}
- dontRegisterView={true}
+ dontRegisterView={false}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
ContentScaling={this.contentScaling}
/>, this._dashSpan);
}
});
- let self = this;
- this._dashSpan.onkeydown = function (e: any) { e.stopPropagation(); };
+ const self = this;
+ this._dashSpan.onkeydown = function (e: any) {
+ e.stopPropagation();
+ if (e.key === "Tab" || e.key === "Enter") {
+ e.preventDefault();
+ }
+ };
this._dashSpan.onkeypress = function (e: any) { e.stopPropagation(); };
this._dashSpan.onwheel = function (e: any) { e.preventDefault(); };
this._dashSpan.onkeyup = function (e: any) { e.stopPropagation(); };
@@ -771,7 +840,7 @@ export class FootnoteView {
}
open() {
// Append a tooltip to the outer node
- let tooltip = this.dom.appendChild(document.createElement("div"));
+ const tooltip = this.dom.appendChild(document.createElement("div"));
tooltip.className = "footnote-tooltip";
// And put a sub-ProseMirror into that
this.innerView = new EditorView(tooltip, {
@@ -826,14 +895,14 @@ export class FootnoteView {
this.dom.textContent = "";
}
dispatchInner(tr: any) {
- let { state, transactions } = this.innerView.state.applyTransaction(tr);
+ const { state, transactions } = this.innerView.state.applyTransaction(tr);
this.innerView.updateState(state);
if (!tr.getMeta("fromOutside")) {
- let outerTr = this.outerView.state.tr, offsetMap = StepMap.offset(this.getPos() + 1);
- for (let transaction of transactions) {
- let steps = transaction.steps;
- for (let step of steps) {
+ const outerTr = this.outerView.state.tr, offsetMap = StepMap.offset(this.getPos() + 1);
+ for (const transaction of transactions) {
+ const steps = transaction.steps;
+ for (const step of steps) {
outerTr.step(step.map(offsetMap));
}
}
@@ -844,11 +913,11 @@ export class FootnoteView {
if (!node.sameMarkup(this.node)) return false;
this.node = node;
if (this.innerView) {
- let state = this.innerView.state;
- let start = node.content.findDiffStart(state.doc.content);
+ const state = this.innerView.state;
+ const start = node.content.findDiffStart(state.doc.content);
if (start !== null) {
let { a: endA, b: endB } = node.content.findDiffEnd(state.doc.content);
- let overlap = start - Math.min(endA, endB);
+ const overlap = start - Math.min(endA, endB);
if (overlap > 0) { endA += overlap; endB += overlap; }
this.innerView.dispatch(
state.tr
@@ -870,7 +939,7 @@ export class FootnoteView {
ignoreMutation() { return true; }
}
-export class SummarizedView {
+export class SummaryView {
_collapsed: HTMLElement;
_view: any;
constructor(node: any, view: any, getPos: any) {
@@ -908,15 +977,16 @@ export class SummarizedView {
className = (visible: boolean) => "formattedTextBox-summarizer" + (visible ? "" : "-collapsed");
updateSummarizedText(start?: any) {
- let mark = this._view.state.schema.marks.highlight.create();
+ const mtype = this._view.state.schema.marks.summarize;
+ const mtypeInc = this._view.state.schema.marks.summarizeInclusive;
let endPos = start;
- let visited = new Set();
+ const visited = new Set();
for (let i: number = start + 1; i < this._view.state.doc.nodeSize - 1; i++) {
let skip = false;
this._view.state.doc.nodesBetween(start, i, (node: Node, pos: number, parent: Node, index: number) => {
if (node.isLeaf && !visited.has(node) && !skip) {
- if (node.marks.find((m: any) => m.type === mark.type)) {
+ if (node.marks.find((m: any) => m.type === mtype || m.type === mtypeInc)) {
visited.add(node);
endPos = i + node.nodeSize - 1;
}
@@ -940,8 +1010,8 @@ export const schema = new Schema({ nodes, marks });
const fromJson = schema.nodeFromJSON;
schema.nodeFromJSON = (json: any) => {
- let node = fromJson(json);
- if (json.type === "star") {
+ const node = fromJson(json);
+ if (json.type === schema.marks.summarize.name) {
node.attrs.text = Slice.fromJSON(schema, node.attrs.textslice);
}
return node;
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index ff4451824..0fa96963e 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -94,16 +94,16 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
return { compiled: false, errors: diagnostics };
}
- let paramNames = Object.keys(scriptingGlobals);
- let params = paramNames.map(key => scriptingGlobals[key]);
+ const paramNames = Object.keys(scriptingGlobals);
+ const params = paramNames.map(key => scriptingGlobals[key]);
// let fieldTypes = [Doc, ImageField, PdfField, VideoField, AudioField, List, RichTextField, ScriptField, ComputedField, CompileScript];
// let paramNames = ["Docs", ...fieldTypes.map(fn => fn.name)];
// let params: any[] = [Docs, ...fieldTypes];
- let compiledFunction = new Function(...paramNames, `return ${script}`);
- let { capturedVariables = {} } = options;
- let run = (args: { [name: string]: any } = {}, onError?: (e: any) => void, errorVal?: any): ScriptResult => {
- let argsArray: any[] = [];
- for (let name of customParams) {
+ const compiledFunction = new Function(...paramNames, `return ${script}`);
+ const { capturedVariables = {} } = options;
+ const run = (args: { [name: string]: any } = {}, onError?: (e: any) => void, errorVal?: any): ScriptResult => {
+ const argsArray: any[] = [];
+ for (const name of customParams) {
if (name === "this") {
continue;
}
@@ -113,7 +113,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
argsArray.push(capturedVariables[name]);
}
}
- let thisParam = args.this || capturedVariables.this;
+ const thisParam = args.this || capturedVariables.this;
let batch: { end(): void } | undefined = undefined;
try {
if (!options.editable) {
@@ -146,7 +146,7 @@ class ScriptingCompilerHost {
// getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined): ts.SourceFile | undefined {
getSourceFile(fileName: string, languageVersion: any, onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined): any | undefined {
- let contents = this.readFile(fileName);
+ const contents = this.readFile(fileName);
if (contents !== undefined) {
return ts.createSourceFile(fileName, contents, languageVersion, true);
}
@@ -180,7 +180,7 @@ class ScriptingCompilerHost {
return this.files.some(file => file.fileName === fileName);
}
readFile(fileName: string): string | undefined {
- let file = this.files.find(file => file.fileName === fileName);
+ const file = this.files.find(file => file.fileName === fileName);
if (file) {
return file.content;
}
@@ -218,7 +218,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
if (options.globals) {
Scripting.setScriptingGlobals(options.globals);
}
- let host = new ScriptingCompilerHost;
+ const host = new ScriptingCompilerHost;
if (options.traverser) {
const sourceFile = ts.createSourceFile('script.ts', script, ts.ScriptTarget.ES2015, true);
const onEnter = typeof options.traverser === "object" ? options.traverser.onEnter : options.traverser;
@@ -240,7 +240,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
script = printer.printFile(transformed[0]);
result.dispose();
}
- let paramNames: string[] = [];
+ const paramNames: string[] = [];
if ("this" in params || "this" in capturedVariables) {
paramNames.push("this");
}
@@ -248,7 +248,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
if (key === "this") continue;
paramNames.push(key);
}
- let paramList = paramNames.map(key => {
+ const paramList = paramNames.map(key => {
const val = params[key];
return `${key}: ${val}`;
});
@@ -258,18 +258,18 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
paramNames.push(key);
paramList.push(`${key}: ${typeof val === "object" ? Object.getPrototypeOf(val).constructor.name : typeof val}`);
}
- let paramString = paramList.join(", ");
- let funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} {
+ const paramString = paramList.join(", ");
+ const funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} {
${addReturn ? `return ${script};` : script}
})`;
host.writeFile("file.ts", funcScript);
if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
- let program = ts.createProgram(["file.ts"], {}, host);
- let testResult = program.emit();
- let outputText = host.readFile("file.js");
+ const program = ts.createProgram(["file.ts"], {}, host);
+ const testResult = program.emit();
+ const outputText = host.readFile("file.js");
- let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
+ const diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
const result = Run(outputText, paramNames, diagnostics, script, options);
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 2cf13680a..8ff54d052 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -34,37 +34,37 @@ export namespace SearchUtil {
export function Search(query: string, returnDocs: false, options?: SearchParams): Promise<IdSearchResult>;
export async function Search(query: string, returnDocs: boolean, options: SearchParams = {}) {
query = query || "*"; //If we just have a filter query, search for * as the query
- let result: IdSearchResult = JSON.parse(await rp.get(Utils.prepend("/search"), {
- qs: { ...options, q: query },
- }));
+ const rpquery = Utils.prepend("/search");
+ const gotten = await rp.get(rpquery, { qs: { ...options, q: query } });
+ const result: IdSearchResult = gotten.startsWith("<") ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten);
if (!returnDocs) {
return result;
}
- let { ids, numFound, highlighting } = result;
+ const { ids, highlighting } = result;
- let txtresult = query !== "*" && JSON.parse(await rp.get(Utils.prepend("/textsearch"), {
+ const txtresult = query !== "*" && JSON.parse(await rp.get(Utils.prepend("/textsearch"), {
qs: { ...options, q: query },
}));
- let fileids = txtresult ? txtresult.ids : [];
- let newIds: string[] = [];
- let newLines: string[][] = [];
+ const fileids = txtresult ? txtresult.ids : [];
+ const newIds: string[] = [];
+ const newLines: string[][] = [];
await Promise.all(fileids.map(async (tr: string, i: number) => {
- let docQuery = "fileUpload_t:" + tr.substr(0, 7); //If we just have a filter query, search for * as the query
- let docResult = JSON.parse(await rp.get(Utils.prepend("/search"), { qs: { ...options, q: docQuery } }));
+ const docQuery = "fileUpload_t:" + tr.substr(0, 7); //If we just have a filter query, search for * as the query
+ const docResult = JSON.parse(await rp.get(Utils.prepend("/search"), { qs: { ...options, q: docQuery } }));
newIds.push(...docResult.ids);
newLines.push(...docResult.ids.map((dr: any) => txtresult.lines[i]));
}));
- let theDocs: Doc[] = [];
- let theLines: string[][] = [];
+ const theDocs: Doc[] = [];
+ const theLines: string[][] = [];
const textDocMap = await DocServer.GetRefFields(newIds);
const textDocs = newIds.map((id: string) => textDocMap[id]).map(doc => doc as Doc);
for (let i = 0; i < textDocs.length; i++) {
- let testDoc = textDocs[i];
- if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1) {
+ const testDoc = textDocs[i];
+ if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && testDoc.type !== DocumentType.EXTENSION && theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1) {
theDocs.push(Doc.GetProto(testDoc));
theLines.push(newLines[i].map(line => line.replace(query, query.toUpperCase())));
}
@@ -73,8 +73,8 @@ export namespace SearchUtil {
const docMap = await DocServer.GetRefFields(ids);
const docs = ids.map((id: string) => docMap[id]).map(doc => doc as Doc);
for (let i = 0; i < ids.length; i++) {
- let testDoc = docs[i];
- if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowAliases || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
+ const testDoc = docs[i];
+ if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && testDoc.type !== DocumentType.EXTENSION && (options.allowAliases || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
theDocs.push(testDoc);
theLines.push([]);
}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index e01216e0f..4612f10f4 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -2,6 +2,7 @@ import { observable, action, runInAction, ObservableMap } from "mobx";
import { Doc } from "../../new_fields/Doc";
import { DocumentView } from "../views/nodes/DocumentView";
import { computedFn } from "mobx-utils";
+import { List } from "../../new_fields/List";
export namespace SelectionManager {
@@ -27,18 +28,21 @@ export namespace SelectionManager {
manager.SelectedDocuments.clear();
manager.SelectedDocuments.set(docView, true);
}
+ Doc.UserDoc().SelectedDocs = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
}
@action
DeselectDoc(docView: DocumentView): void {
if (manager.SelectedDocuments.get(docView)) {
manager.SelectedDocuments.delete(docView);
docView.props.whenActiveChanged(false);
+ Doc.UserDoc().SelectedDocs = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
}
}
@action
DeselectAll(): void {
Array.from(manager.SelectedDocuments.keys()).map(dv => dv.props.whenActiveChanged(false));
manager.SelectedDocuments.clear();
+ Doc.UserDoc().SelectedDocs = new List<Doc>([]);
}
}
@@ -78,3 +82,4 @@ export namespace SelectionManager {
return Array.from(manager.SelectedDocuments.keys());
}
}
+
diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts
index ff048f647..1f6b939d3 100644
--- a/src/client/util/SerializationHelper.ts
+++ b/src/client/util/SerializationHelper.ts
@@ -1,7 +1,6 @@
-import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema, primitive, SKIP } from "serializr";
-import { Field, Doc } from "../../new_fields/Doc";
+import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema } from "serializr";
+import { Field } from "../../new_fields/Doc";
import { ClientUtils } from "./ClientUtils";
-import { emptyFunction } from "../../Utils";
let serializing = 0;
export function afterDocDeserialize(cb: (err: any, val: any) => void, err: any, newValue: any) {
@@ -65,8 +64,8 @@ export namespace SerializationHelper {
}
}
-let serializationTypes: { [name: string]: { ctor: { new(): any }, afterDeserialize?: (obj: any) => void | Promise<any> } } = {};
-let reverseMap: { [ctor: string]: string } = {};
+const serializationTypes: { [name: string]: { ctor: { new(): any }, afterDeserialize?: (obj: any) => void | Promise<any> } } = {};
+const reverseMap: { [ctor: string]: string } = {};
export interface DeserializableOpts {
(constructor: { new(...args: any[]): any }): void;
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 2082d6324..7496ac73c 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -4,13 +4,11 @@ import MainViewModal from "../views/MainViewModal";
import { Doc, Opt, DocCastAsync } from "../../new_fields/Doc";
import { DocServer } from "../DocServer";
import { Cast, StrCast } from "../../new_fields/Types";
-import { RouteStore } from "../../server/RouteStore";
import * as RequestPromise from "request-promise";
import { Utils } from "../../Utils";
import "./SharingManager.scss";
import { Id } from "../../new_fields/FieldSymbols";
import { observer } from "mobx-react";
-import { MainView } from "../views/MainView";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { library } from '@fortawesome/fontawesome-svg-core';
import * as fa from '@fortawesome/free-solid-svg-icons';
@@ -104,10 +102,10 @@ export default class SharingManager extends React.Component<{}> {
}
populateUsers = async () => {
- let userList = await RequestPromise.get(Utils.prepend(RouteStore.getUsers));
+ const userList = await RequestPromise.get(Utils.prepend("/getUsers"));
const raw = JSON.parse(userList) as User[];
const evaluating = raw.map(async user => {
- let isCandidate = user.email !== Doc.CurrentUserEmail;
+ const isCandidate = user.email !== Doc.CurrentUserEmail;
if (isCandidate) {
const userDocument = await DocServer.GetRefField(user.userDocumentId);
if (userDocument instanceof Doc) {
@@ -131,7 +129,7 @@ export default class SharingManager extends React.Component<{}> {
if (state === SharingPermissions.None) {
const metadata = (await DocCastAsync(manager[key]));
if (metadata) {
- let sharedAlias = (await DocCastAsync(metadata.sharedAlias))!;
+ const sharedAlias = (await DocCastAsync(metadata.sharedAlias))!;
Doc.RemoveDocFromList(notificationDoc, storage, sharedAlias);
manager[key] = undefined;
}
@@ -146,7 +144,7 @@ export default class SharingManager extends React.Component<{}> {
}
private setExternalSharing = (state: string) => {
- let sharingDoc = this.sharingDoc;
+ const sharingDoc = this.sharingDoc;
if (!sharingDoc) {
return;
}
@@ -157,7 +155,7 @@ export default class SharingManager extends React.Component<{}> {
if (!this.targetDoc) {
return undefined;
}
- let baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
+ const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
return `${baseUrl}?sharing=true`;
}
@@ -179,7 +177,7 @@ export default class SharingManager extends React.Component<{}> {
}
private focusOn = (contents: string) => {
- let title = this.targetDoc ? StrCast(this.targetDoc.title) : "";
+ const title = this.targetDoc ? StrCast(this.targetDoc.title) : "";
return (
<span
className={"focus-span"}
diff --git a/src/client/util/TooltipLinkingMenu.tsx b/src/client/util/TooltipLinkingMenu.tsx
index e6d6c471f..b46675a04 100644
--- a/src/client/util/TooltipLinkingMenu.tsx
+++ b/src/client/util/TooltipLinkingMenu.tsx
@@ -2,10 +2,6 @@ import { EditorState } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { FieldViewProps } from "../views/nodes/FieldView";
import "./TooltipTextMenu.scss";
-import React = require("react");
-const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
-
-const SVG = "http://www.w3.org/2000/svg";
//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
export class TooltipLinkingMenu {
@@ -23,9 +19,9 @@ export class TooltipLinkingMenu {
//add the div which is the tooltip
view.dom.parentNode!.parentNode!.appendChild(this.tooltip);
- let target = "https://www.google.com";
+ const target = "https://www.google.com";
- let link = document.createElement("a");
+ const link = document.createElement("a");
link.href = target;
link.textContent = target;
link.target = "_blank";
@@ -37,7 +33,7 @@ export class TooltipLinkingMenu {
//updates the tooltip menu when the selection changes
update(view: EditorView, lastState: EditorState | undefined) {
- let state = view.state;
+ const state = view.state;
// Don't do anything if the document/selection didn't change
if (lastState && lastState.doc.eq(state.doc) &&
lastState.selection.eq(state.selection)) return;
@@ -53,16 +49,16 @@ export class TooltipLinkingMenu {
// Otherwise, reposition it and update its content
this.tooltip.style.display = "";
- let { from, to } = state.selection;
- let start = view.coordsAtPos(from), end = view.coordsAtPos(to);
+ const { from, to } = state.selection;
+ const start = view.coordsAtPos(from), end = view.coordsAtPos(to);
// The box in which the tooltip is positioned, to use as base
- let box = this.tooltip.offsetParent!.getBoundingClientRect();
+ const box = this.tooltip.offsetParent!.getBoundingClientRect();
// Find a center-ish x position from the selection endpoints (when
// crossing lines, end may be more to the left)
- let left = Math.max((start.left + end.left) / 2, start.left + 3);
+ const left = Math.max((start.left + end.left) / 2, start.left + 3);
this.tooltip.style.left = (left - box.left) * this.editorProps.ScreenToLocalTransform().Scale + "px";
- let width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale;
- let mid = Math.min(start.left, end.left) + width;
+ const width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale;
+ const mid = Math.min(start.left, end.left) + width;
this.tooltip.style.width = "auto";
this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px";
diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index 8310a0da6..2a38fe118 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -149,7 +149,7 @@
}
svg {
- fill: white;
+ fill: inherit;
width: 18px;
height: 18px;
}
@@ -181,7 +181,7 @@
}
}
-#colorPicker {
+.colorPicker {
position: relative;
svg {
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 5136089b3..8aa304fad 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -1,17 +1,13 @@
-import { action } from "mobx";
import { Dropdown, icons, MenuItem } from "prosemirror-menu"; //no import css
import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos, Schema } from "prosemirror-model";
import { wrapInList } from 'prosemirror-schema-list';
-import { EditorState, NodeSelection, TextSelection, Transaction } from "prosemirror-state";
+import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { Doc, Field, Opt } from "../../new_fields/Doc";
-import { Id } from "../../new_fields/FieldSymbols";
import { Utils } from "../../Utils";
import { DocServer } from "../DocServer";
import { FieldViewProps } from "../views/nodes/FieldView";
import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox";
-import { DocumentManager } from "./DocumentManager";
-import { DragManager } from "./DragManager";
import { LinkManager } from "./LinkManager";
import { schema } from "./RichTextSchema";
import "./TooltipTextMenu.scss";
@@ -20,12 +16,10 @@ import { updateBullets } from './ProsemirrorExampleTransfer';
import { DocumentDecorations } from '../views/DocumentDecorations';
import { SelectionManager } from './SelectionManager';
import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../new_fields/SchemaHeaderField';
-const { toggleMark, setBlockType } = require("prosemirror-commands");
-const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js");
+const { toggleMark } = require("prosemirror-commands");
//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
export class TooltipTextMenu {
-
public static Toolbar: HTMLDivElement | undefined;
// editor state properties
@@ -34,10 +28,7 @@ export class TooltipTextMenu {
private fontStyles: Mark[] = [];
private fontSizes: Mark[] = [];
- private listTypes: (NodeType | any)[] = [];
- private listTypeToIcon: Map<NodeType | any, string> = new Map();
- private _activeMarks: Mark[] = [];
- private _marksToDoms: Map<Mark, HTMLSpanElement> = new Map();
+ private _marksToDoms: Map<MarkType, HTMLSpanElement> = new Map();
private _collapsed: boolean = false;
// editor doms
@@ -47,20 +38,18 @@ export class TooltipTextMenu {
// editor button doms
private colorDom?: Node;
private colorDropdownDom?: Node;
- private highlightDom?: Node;
- private highlightDropdownDom?: Node;
- private linkEditor?: HTMLDivElement;
- private linkText?: HTMLDivElement;
- private linkDrag?: HTMLImageElement;
- private _linkDropdownDom?: Node;
+ private linkDom?: Node;
+ private highighterDom?: Node;
+ private highlighterDropdownDom?: Node;
+ private linkDropdownDom?: Node;
private _brushdom?: Node;
private _brushDropdownDom?: Node;
private fontSizeDom?: Node;
private fontStyleDom?: Node;
- private listTypeBtnDom?: Node;
private basicTools?: HTMLElement;
-
+ static createDiv(className: string) { const div = document.createElement("div"); div.className = className; return div; }
+ static createSpan(className: string) { const div = document.createElement("span"); div.className = className; return div; }
constructor(view: EditorView) {
this.view = view;
@@ -68,74 +57,64 @@ export class TooltipTextMenu {
this.initTooltip(view);
// initialize the wrapper
- this.wrapper = document.createElement("div");
- this.wrapper.className = "wrapper";
+ this.wrapper = TooltipTextMenu.createDiv("wrapper");
this.wrapper.appendChild(this.tooltip);
- // initialize the dragger -- appends it to the wrapper
- this.createDragger();
-
TooltipTextMenu.Toolbar = this.wrapper;
}
private async initTooltip(view: EditorView) {
- // initialize tooltip dom
- this.tooltip = document.createElement("div");
- this.tooltip.className = "tooltipMenu";
- this.basicTools = document.createElement("div");
- this.basicTools.className = "basic-tools";
-
- // init buttons to the tooltip -- paths to svgs are obtained from fontawesome
- let items = [
- { command: toggleMark(schema.marks.strong), dom: this.svgIcon("strong", "Bold", "M333.49 238a122 122 0 0 0 27-65.21C367.87 96.49 308 32 233.42 32H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h31.87v288H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h209.32c70.8 0 134.14-51.75 141-122.4 4.74-48.45-16.39-92.06-50.83-119.6zM145.66 112h87.76a48 48 0 0 1 0 96h-87.76zm87.76 288h-87.76V288h87.76a56 56 0 0 1 0 112z") },
- { command: toggleMark(schema.marks.em), dom: this.svgIcon("em", "Italic", "M320 48v32a16 16 0 0 1-16 16h-62.76l-80 320H208a16 16 0 0 1 16 16v32a16 16 0 0 1-16 16H16a16 16 0 0 1-16-16v-32a16 16 0 0 1 16-16h62.76l80-320H112a16 16 0 0 1-16-16V48a16 16 0 0 1 16-16h192a16 16 0 0 1 16 16z") },
- { command: toggleMark(schema.marks.underline), dom: this.svgIcon("underline", "Underline", "M32 64h32v160c0 88.22 71.78 160 160 160s160-71.78 160-160V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H272a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h32v160a80 80 0 0 1-160 0V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H32a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16zm400 384H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z") },
- { command: toggleMark(schema.marks.strikethrough), dom: this.svgIcon("strikethrough", "Strikethrough", "M496 224H293.9l-87.17-26.83A43.55 43.55 0 0 1 219.55 112h66.79A49.89 49.89 0 0 1 331 139.58a16 16 0 0 0 21.46 7.15l42.94-21.47a16 16 0 0 0 7.16-21.46l-.53-1A128 128 0 0 0 287.51 32h-68a123.68 123.68 0 0 0-123 135.64c2 20.89 10.1 39.83 21.78 56.36H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h480a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm-180.24 96A43 43 0 0 1 336 356.45 43.59 43.59 0 0 1 292.45 400h-66.79A49.89 49.89 0 0 1 181 372.42a16 16 0 0 0-21.46-7.15l-42.94 21.47a16 16 0 0 0-7.16 21.46l.53 1A128 128 0 0 0 224.49 480h68a123.68 123.68 0 0 0 123-135.64 114.25 114.25 0 0 0-5.34-24.36z") },
- { command: toggleMark(schema.marks.superscript), dom: this.svgIcon("superscript", "Superscript", "M496 160h-16V16a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 64h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") },
- { command: toggleMark(schema.marks.subscript), dom: this.svgIcon("subscript", "Subscript", "M496 448h-16V304a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 352h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") },
- // { command: toggleMark(schema.marks.highlight), dom: this.icon("H", 'blue', 'Blue') }
+ const self = this;
+ this.tooltip = TooltipTextMenu.createDiv("tooltipMenu");
+ this.basicTools = TooltipTextMenu.createDiv("basic-tools");
+
+ const svgIcon = (name: string, title: string = name, dpath: string) => {
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ svg.setAttribute("viewBox", "-100 -100 650 650");
+ const path = document.createElementNS('http://www.w3.org/2000/svg', "path");
+ path.setAttributeNS(null, "d", dpath);
+ svg.appendChild(path);
+
+ const span = TooltipTextMenu.createSpan(name + " menuicon");
+ span.title = title;
+ span.appendChild(svg);
+
+ return span;
+ }
+
+ const basicItems = [ // init basicItems in minimized toolbar -- paths to svgs are obtained from fontawesome
+ { mark: schema.marks.strong, dom: svgIcon("strong", "Bold", "M333.49 238a122 122 0 0 0 27-65.21C367.87 96.49 308 32 233.42 32H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h31.87v288H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h209.32c70.8 0 134.14-51.75 141-122.4 4.74-48.45-16.39-92.06-50.83-119.6zM145.66 112h87.76a48 48 0 0 1 0 96h-87.76zm87.76 288h-87.76V288h87.76a56 56 0 0 1 0 112z") },
+ { mark: schema.marks.em, dom: svgIcon("em", "Italic", "M320 48v32a16 16 0 0 1-16 16h-62.76l-80 320H208a16 16 0 0 1 16 16v32a16 16 0 0 1-16 16H16a16 16 0 0 1-16-16v-32a16 16 0 0 1 16-16h62.76l80-320H112a16 16 0 0 1-16-16V48a16 16 0 0 1 16-16h192a16 16 0 0 1 16 16z") },
+ { mark: schema.marks.underline, dom: svgIcon("underline", "Underline", "M32 64h32v160c0 88.22 71.78 160 160 160s160-71.78 160-160V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H272a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h32v160a80 80 0 0 1-160 0V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H32a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16zm400 384H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z") },
+ ];
+ const items = [ // init items in full size toolbar
+ { mark: schema.marks.strikethrough, dom: svgIcon("strikethrough", "Strikethrough", "M496 224H293.9l-87.17-26.83A43.55 43.55 0 0 1 219.55 112h66.79A49.89 49.89 0 0 1 331 139.58a16 16 0 0 0 21.46 7.15l42.94-21.47a16 16 0 0 0 7.16-21.46l-.53-1A128 128 0 0 0 287.51 32h-68a123.68 123.68 0 0 0-123 135.64c2 20.89 10.1 39.83 21.78 56.36H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h480a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm-180.24 96A43 43 0 0 1 336 356.45 43.59 43.59 0 0 1 292.45 400h-66.79A49.89 49.89 0 0 1 181 372.42a16 16 0 0 0-21.46-7.15l-42.94 21.47a16 16 0 0 0-7.16 21.46l.53 1A128 128 0 0 0 224.49 480h68a123.68 123.68 0 0 0 123-135.64 114.25 114.25 0 0 0-5.34-24.36z") },
+ { mark: schema.marks.superscript, dom: svgIcon("superscript", "Superscript", "M496 160h-16V16a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 64h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") },
+ { mark: schema.marks.subscript, dom: svgIcon("subscript", "Subscript", "M496 448h-16V304a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 352h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") },
];
- // add menu items
- this._marksToDoms = new Map();
- items.forEach(({ dom, command }) => {
+ basicItems.map(({ dom, mark }) => this.basicTools?.appendChild(dom.cloneNode(true)));
+ basicItems.concat(items).forEach(({ dom, mark }) => {
this.tooltip.appendChild(dom);
- switch (dom.title) {
- case "Bold":
- this._marksToDoms.set(schema.mark(schema.marks.strong), dom);
- this.basicTools && this.basicTools.appendChild(dom.cloneNode(true));
- break;
- case "Italic":
- this._marksToDoms.set(schema.mark(schema.marks.em), dom);
- this.basicTools && this.basicTools.appendChild(dom.cloneNode(true));
- break;
- case "Underline":
- this._marksToDoms.set(schema.mark(schema.marks.underline), dom);
- this.basicTools && this.basicTools.appendChild(dom.cloneNode(true));
- break;
- }
+ this._marksToDoms.set(mark, dom);
//pointer down handler to activate button effects
dom.addEventListener("pointerdown", e => {
- e.preventDefault();
this.view.focus();
if (dom.contains(e.target as Node)) {
+ e.preventDefault();
e.stopPropagation();
- command(this.view.state, this.view.dispatch, this.view);
- // if (this.view.state.selection.empty) {
- // if (dom.style.color === "white") { dom.style.color = "greenyellow"; }
- // else { dom.style.color = "white"; }
- // }
+ toggleMark(mark)(this.view.state, this.view.dispatch, this.view);
+ this.updateHighlightStateOfButtons();
}
});
-
});
- // highlight menu
- this.highlightDom = this.createHighlightTool().render(this.view).dom;
- this.highlightDropdownDom = this.createHighlightDropdown().render(this.view).dom;
- this.tooltip.appendChild(this.highlightDom);
- this.tooltip.appendChild(this.highlightDropdownDom);
+ // summarize menu
+ this.highighterDom = this.createHighlightTool().render(this.view).dom;
+ this.highlighterDropdownDom = this.createHighlightDropdown().render(this.view).dom;
+ this.tooltip.appendChild(this.highighterDom);
+ this.tooltip.appendChild(this.highlighterDropdownDom);
// color menu
this.colorDom = this.createColorTool().render(this.view).dom;
@@ -144,46 +123,15 @@ export class TooltipTextMenu {
this.tooltip.appendChild(this.colorDropdownDom);
// link menu
- this.updateLinkMenu();
- let dropdown = await this.createLinkDropdown();
- this._linkDropdownDom = dropdown.render(this.view).dom;
- this.tooltip.appendChild(this._linkDropdownDom);
+ this.linkDom = this.createLinkTool().render(this.view).dom;
+ this.linkDropdownDom = this.createLinkDropdown("").render(this.view).dom;
+ this.tooltip.appendChild(this.linkDom);
+ this.tooltip.appendChild(this.linkDropdownDom);
// list of font styles
- this.initFontStyles();
-
- // font sizes
- this.initFontSizes();
-
- // list types
- this.initListTypes();
-
- // init brush tool
- this._brushdom = this.createBrush().render(this.view).dom;
- this.tooltip.appendChild(this._brushdom);
- this._brushDropdownDom = this.createBrushDropdown().render(this.view).dom;
- this.tooltip.appendChild(this._brushDropdownDom);
-
- // star
- this.tooltip.appendChild(this.createStar().render(this.view).dom);
-
- // list types dropdown
- this.updateListItemDropdown(":", this.listTypeBtnDom);
-
- await this.updateFromDash(view, undefined, undefined);
- }
-
- initFontStyles() {
- this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Times New Roman" }));
- this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Arial" }));
- this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Georgia" }));
- this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Comic Sans MS" }));
- this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Tahoma" }));
- this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Impact" }));
- this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Crimson Text" }));
- }
-
- initFontSizes() {
+ this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 7 }));
+ this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 8 }));
+ this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 9 }));
this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 10 }));
this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 12 }));
this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 14 }));
@@ -194,56 +142,89 @@ export class TooltipTextMenu {
this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 32 }));
this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 48 }));
this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 72 }));
- }
- initListTypes() {
- this.listTypeToIcon = new Map();
- //this.listTypeToIcon.set(schema.nodes.bullet_list, ":");
- this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "bullet" }), ":");
- this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "decimal" }), "1.1)");
- this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "multi" }), "1.A)");
- // this.listTypeToIcon.set(schema.nodes.bullet_list, "⬜");
- this.listTypes = Array.from(this.listTypeToIcon.keys());
- }
+ // font sizes
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Times New Roman" }));
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Arial" }));
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Georgia" }));
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Comic Sans MS" }));
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Tahoma" }));
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Impact" }));
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Crimson Text" }));
- // creates dragger element that allows dragging and collapsing (on double click)
- // of editor and appends it to the wrapper
- createDragger() {
- let draggerWrapper = document.createElement("div");
- draggerWrapper.className = "dragger-wrapper";
- let dragger = document.createElement("div");
- dragger.className = "dragger";
+ // init brush tool
+ this._brushdom = this.createBrushTool().render(this.view).dom;
+ this.tooltip.appendChild(this._brushdom);
+ this._brushDropdownDom = this.createBrushDropdown().render(this.view).dom;
+ this.tooltip.appendChild(this._brushDropdownDom);
- let line1 = document.createElement("span");
- line1.className = "dragger-line";
- let line2 = document.createElement("span");
- line2.className = "dragger-line";
- let line3 = document.createElement("span");
- line3.className = "dragger-line";
+ // summarizer tool
+ const summarizer = new MenuItem({
+ title: "Summarize",
+ label: "Summarize",
+ icon: icons.join,
+ css: "fill:white;",
+ class: "menuicon",
+ execEvent: "",
+ run: (state, dispatch) => TooltipTextMenu.insertSummarizer(state, dispatch)
+ });
+ this.tooltip.appendChild(summarizer.render(this.view).dom);
- dragger.appendChild(line1);
- dragger.appendChild(line2);
- dragger.appendChild(line3);
+ // list types dropdown
+ const listDropdownTypes = [{ mapStyle: "bullet", label: ":" }, { mapStyle: "decimal", label: "1.1" }, { mapStyle: "multi", label: "A.1" }, { label: "X" }];
+ const listTypes = new Dropdown(listDropdownTypes.map(({ mapStyle, label }) =>
+ new MenuItem({
+ title: "Set Bullet Style",
+ label: label,
+ execEvent: "",
+ class: "dropdown-item",
+ css: "color: black; width: 40px;",
+ enable() { return true; },
+ run() {
+ const marks = self.view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks());
+ if (!wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => {
+ const tx3 = updateBullets(tx2, schema, mapStyle);
+ marks && tx3.ensureMarks([...marks]);
+ marks && tx3.setStoredMarks([...marks]);
+
+ view.dispatch(tx2);
+ })) {
+ const tx2 = view.state.tr;
+ const tx3 = updateBullets(tx2, schema, mapStyle);
+ marks && tx3.ensureMarks([...marks]);
+ marks && tx3.setStoredMarks([...marks]);
+
+ view.dispatch(tx3);
+ }
+ }
+ })), { label: ":", css: "color:black; width: 40px;" });
+ this.tooltip.appendChild(listTypes.render(this.view).dom);
- draggerWrapper.appendChild(dragger);
+ await this.updateFromDash(view, undefined, undefined);
+ const draggerWrapper = TooltipTextMenu.createDiv("dragger-wrapper");
+ const dragger = TooltipTextMenu.createDiv("dragger");
+ dragger.appendChild(TooltipTextMenu.createSpan("dragger-line"));
+ dragger.appendChild(TooltipTextMenu.createSpan("dragger-line"));
+ dragger.appendChild(TooltipTextMenu.createSpan("dragger-line"));
+ draggerWrapper.appendChild(dragger);
this.wrapper.appendChild(draggerWrapper);
- this.dragElement(draggerWrapper);
+ this.setupDragElementInteractions(draggerWrapper);
}
- dragElement(elmnt: HTMLElement) {
+ setupDragElementInteractions(elmnt: HTMLElement) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (elmnt) {
// if present, the header is where you move the DIV from:
- elmnt.onpointerdown = dragMouseDown;
+ elmnt.onpointerdown = dragPointerDown;
elmnt.ondblclick = onClick;
}
const self = this;
- function dragMouseDown(e: PointerEvent) {
+ function dragPointerDown(e: PointerEvent) {
e = e || window.event;
- //e.preventDefault();
+ e.preventDefault();
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
@@ -285,24 +266,40 @@ export class TooltipTextMenu {
// stop moving when mouse button is released:
document.onpointerup = null;
document.onpointermove = null;
- //self.highlightSearchTerms(self.state, ["hello"]);
- //FormattedTextBox.Instance.unhighlightSearchTerms();
}
}
//label of dropdown will change to given label
updateFontSizeDropdown(label: string) {
//font SIZES
- let fontSizeBtns: MenuItem[] = [];
- this.fontSizes.forEach(mark => {
- fontSizeBtns.push(this.dropdownFontSizeBtn(String(mark.attrs.fontSize), "color: black; width: 50px;", mark, this.view, this.changeToFontSize));
- });
+ const fontSizeBtns: MenuItem[] = [];
+ const self = this;
+ this.fontSizes.forEach(mark =>
+ fontSizeBtns.push(new MenuItem({
+ title: "Set Font Size",
+ label: String(mark.attrs.fontSize),
+ execEvent: "",
+ class: "dropdown-item",
+ css: "color: black; width: 50px;",
+ enable() { return true; },
+ run() {
+ const size = mark.attrs.fontSize;
+ if (size) { self.updateFontSizeDropdown(String(size) + " pt"); }
+ if (self.editorProps) {
+ const ruleProvider = self.editorProps.ruleProvider;
+ const heading = NumCast(self.editorProps.Document.heading);
+ if (ruleProvider && heading) {
+ ruleProvider["ruleSize_" + heading] = size;
+ }
+ }
+ TooltipTextMenu.setMark(self.view.state.schema.marks.pFontSize.create({ fontSize: size }), self.view.state, self.view.dispatch);
+ }
+ })));
- let newfontSizeDom = (new Dropdown(fontSizeBtns, {
- label: label,
- css: "color:black; min-width: 60px;"
- }) as MenuItem).render(this.view).dom;
- if (this.fontSizeDom) { this.tooltip.replaceChild(newfontSizeDom, this.fontSizeDom); }
+ const newfontSizeDom = (new Dropdown(fontSizeBtns, { label: label, css: "color:black; min-width: 60px;" }) as MenuItem).render(this.view).dom;
+ if (this.fontSizeDom) {
+ this.tooltip.replaceChild(newfontSizeDom, this.fontSizeDom);
+ }
else {
this.tooltip.appendChild(newfontSizeDom);
}
@@ -312,127 +309,53 @@ export class TooltipTextMenu {
//label of dropdown will change to given label
updateFontStyleDropdown(label: string) {
//font STYLES
- let fontBtns: MenuItem[] = [];
- this.fontStyles.forEach((mark) => {
- fontBtns.push(this.dropdownFontFamilyBtn(mark.attrs.family, "color: black; font-family: " + mark.attrs.family + ", sans-serif; width: 125px;", mark, this.view, this.changeToFontFamily));
- });
+ const fontBtns: MenuItem[] = [];
+ const self = this;
+ this.fontStyles.forEach(mark =>
+ fontBtns.push(new MenuItem({
+ title: "Set Font Family",
+ label: mark.attrs.family,
+ execEvent: "",
+ class: "dropdown-item",
+ css: "color: black; font-family: " + mark.attrs.family + ", sans-serif; width: 125px;",
+ enable() { return true; },
+ run() {
+ const fontName = mark.attrs.family;
+ if (fontName) { self.updateFontStyleDropdown(fontName); }
+ if (self.editorProps) {
+ const ruleProvider = self.editorProps.ruleProvider;
+ const heading = NumCast(self.editorProps.Document.heading);
+ if (ruleProvider && heading) {
+ ruleProvider["ruleFont_" + heading] = fontName;
+ }
+ }
+ TooltipTextMenu.setMark(self.view.state.schema.marks.pFontFamily.create({ family: fontName }), self.view.state, self.view.dispatch);
+ }
+ })));
- let newfontStyleDom = (new Dropdown(fontBtns, {
- label: label,
- css: "color:black; width: 125px;"
- }) as MenuItem).render(this.view).dom;
- if (this.fontStyleDom) { this.tooltip.replaceChild(newfontStyleDom, this.fontStyleDom); }
+ const newfontStyleDom = (new Dropdown(fontBtns, { label: label, css: "color:black; width: 125px;" }) as MenuItem).render(this.view).dom;
+ if (this.fontStyleDom) {
+ this.tooltip.replaceChild(newfontStyleDom, this.fontStyleDom);
+ }
else {
this.tooltip.appendChild(newfontStyleDom);
}
this.fontStyleDom = newfontStyleDom;
}
-
- updateLinkMenu() {
- if (!this.linkEditor || !this.linkText) {
- this.linkEditor = document.createElement("div");
- this.linkEditor.className = "ProseMirror-icon menuicon";
- this.linkText = document.createElement("div");
- this.linkText.setAttribute("contenteditable", "true");
- this.linkText.style.whiteSpace = "nowrap";
- this.linkText.style.width = "150px";
- this.linkText.style.overflow = "hidden";
- this.linkText.style.color = "white";
- this.linkText.onpointerdown = (e: PointerEvent) => { e.stopPropagation(); };
- let linkBtn = document.createElement("div");
- linkBtn.textContent = ">>";
- linkBtn.style.width = "10px";
- linkBtn.style.height = "10px";
- linkBtn.style.color = "white";
- linkBtn.style.cssFloat = "left";
- linkBtn.onpointerdown = (e: PointerEvent) => {
- let node = this.view.state.selection.$from.nodeAfter;
- let link = node && node.marks.find(m => m.type.name === "link");
- if (link) {
- let href: string = link.attrs.href;
- if (href.indexOf(Utils.prepend("/doc/")) === 0) {
- let docid = href.replace(Utils.prepend("/doc/"), "");
- DocServer.GetRefField(docid).then(action((f: Opt<Field>) => {
- if (f instanceof Doc) {
- if (DocumentManager.Instance.getDocumentView(f)) {
- DocumentManager.Instance.getDocumentView(f)!.props.focus(f, false);
- }
- else this.editorProps && this.editorProps.addDocTab(f, undefined, "onRight");
- }
- }));
- }
- // TODO This should have an else to handle external links
- e.stopPropagation();
- e.preventDefault();
- }
- };
- this.linkDrag = document.createElement("img");
- this.linkDrag.src = "https://seogurusnyc.com/wp-content/uploads/2016/12/link-1.png";
- this.linkDrag.style.width = "15px";
- this.linkDrag.style.height = "15px";
- this.linkDrag.title = "Drag to create link";
- this.linkDrag.id = "link-drag";
- this.linkDrag.onpointerdown = (e: PointerEvent) => {
- if (!this.editorProps) return;
- let dragData = new DragManager.LinkDragData(this.editorProps.Document);
- dragData.dontClearTextBox = true;
- // hack to get source context -sy
- let docView = DocumentManager.Instance.getDocumentView(this.editorProps.Document);
- e.stopPropagation();
- let ctrlKey = e.ctrlKey;
- DragManager.StartLinkDrag(this.linkDrag!, dragData, e.clientX, e.clientY,
- {
- handlers: {
- dragComplete: action(() => {
- if (dragData.linkDocument) {
- let linkDoc = dragData.linkDocument;
- let proto = Doc.GetProto(linkDoc);
- if (proto && docView) {
- proto.sourceContext = docView.props.ContainingCollectionDoc;
- }
- let text = this.makeLink(linkDoc, StrCast(linkDoc.anchor2.title), ctrlKey ? "onRight" : "inTab");
- if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) {
- proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link
- }
- }
- }),
- },
- hideSource: false
- });
- e.stopPropagation();
- e.preventDefault();
- };
- this.linkEditor.appendChild(this.linkDrag);
- this.tooltip.appendChild(this.linkEditor);
- }
-
- let node = this.view.state.selection.$from.nodeAfter;
- let link = node && node.marks.find(m => m.type.name === "link");
- this.linkText.textContent = link ? link.attrs.href : "-empty-";
-
- this.linkText.onkeydown = (e: KeyboardEvent) => {
- if (e.key === "Enter") {
- // this.makeLink(this.linkText!.textContent!);
- e.stopPropagation();
- e.preventDefault();
- }
- };
- }
-
async getTextLinkTargetTitle() {
- let node = this.view.state.selection.$from.nodeAfter;
- let link = node && node.marks.find(m => m.type.name === "link");
+ const node = this.view.state.selection.$from.nodeAfter;
+ const link = node && node.marks.find(m => m.type.name === "link");
if (link) {
- let href = link.attrs.href;
+ const href = link.attrs.href;
if (href) {
if (href.indexOf(Utils.prepend("/doc/")) === 0) {
const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
if (linkclicked) {
- let linkDoc = await DocServer.GetRefField(linkclicked);
+ const linkDoc = await DocServer.GetRefField(linkclicked);
if (linkDoc instanceof Doc) {
- let anchor1 = await Cast(linkDoc.anchor1, Doc);
- let anchor2 = await Cast(linkDoc.anchor2, Doc);
- let currentDoc = SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0].props.Document;
+ const anchor1 = await Cast(linkDoc.anchor1, Doc);
+ const anchor2 = await Cast(linkDoc.anchor2, Doc);
+ const currentDoc = SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0].props.Document;
if (currentDoc && anchor1 && anchor2) {
if (Doc.AreProtosEqual(currentDoc, anchor1)) {
return StrCast(anchor2.title);
@@ -452,19 +375,32 @@ export class TooltipTextMenu {
}
}
- async createLinkDropdown() {
- let targetTitle = await this.getTextLinkTargetTitle();
- let input = document.createElement("input");
+ // LINK TOOL
+ createLinkTool(active: boolean = false) {
+ return new MenuItem({
+ title: "Link tool",
+ label: "Link tool",
+ icon: icons.link,
+ css: "fill:white;",
+ class: active ? "menuicon-active" : "menuicon",
+ execEvent: "",
+ run: async (state, dispatch) => { },
+ active: (state) => true
+ });
+ }
+
+ createLinkDropdown(targetTitle: string) {
+ const input = document.createElement("input");
// menu item for input for hyperlink url
// TODO: integrate search to allow users to search for a doc to link to
- let linkInfo = new MenuItem({
+ const linkInfo = new MenuItem({
title: "",
execEvent: "",
class: "button-setting-disabled",
css: "",
render() {
- let p = document.createElement("p");
+ const p = document.createElement("p");
p.textContent = "Linked to:";
input.type = "text";
@@ -475,286 +411,156 @@ export class TooltipTextMenu {
input.focus();
};
- let div = document.createElement("div");
+ const div = document.createElement("div");
div.appendChild(p);
div.appendChild(input);
return div;
},
enable() { return false; },
- run(p1, p2, p3, event) {
- event.stopPropagation();
- }
+ run(p1, p2, p3, event) { event.stopPropagation(); }
});
// menu item to update/apply the hyperlink to the selected text
- let linkApply = new MenuItem({
+ const linkApply = new MenuItem({
title: "",
execEvent: "",
class: "",
css: "",
render() {
- let button = document.createElement("button");
+ const button = document.createElement("button");
button.className = "link-url-button";
button.textContent = "Apply hyperlink";
return button;
},
enable() { return false; },
- run: (state, dispatch, view, event) => {
+ run: async (state, dispatch, view, event) => {
event.stopPropagation();
- this.makeLinkToURL(input.value, "onRight");
+ let node = this.view.state.selection.$from.nodeAfter;
+ let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: input.value, location: "onRight" });
+ this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link));
+ this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link));
+ node = this.view.state.selection.$from.nodeAfter;
+ link = node && node.marks.find(m => m.type.name === "link");
+
+ // update link menu
+ const linkDom = self.createLinkTool(true).render(self.view).dom;
+ const linkDropdownDom = self.createLinkDropdown(await self.getTextLinkTargetTitle()).render(self.view).dom;
+ self.linkDom && self.tooltip.replaceChild(linkDom, self.linkDom);
+ self.linkDropdownDom && self.tooltip.replaceChild(linkDropdownDom, self.linkDropdownDom);
+ self.linkDom = linkDom;
+ self.linkDropdownDom = linkDropdownDom;
}
});
// menu item to remove the link
// TODO: allow this to be undoable
- let self = this;
- let deleteLink = new MenuItem({
+ const self = this;
+ const deleteLink = new MenuItem({
title: "Delete link",
execEvent: "",
class: "separated-button",
css: "",
render() {
- let button = document.createElement("button");
+ const button = document.createElement("button");
button.textContent = "Remove link";
- let wrapper = document.createElement("div");
+ const wrapper = document.createElement("div");
wrapper.appendChild(button);
return wrapper;
},
enable() { return true; },
async run() {
- self.deleteLink();
- // update link dropdown
- let dropdown = await self.createLinkDropdown();
- let newLinkDropdowndom = dropdown.render(self.view).dom;
- self._linkDropdownDom && self.tooltip.replaceChild(newLinkDropdowndom, self._linkDropdownDom);
- self._linkDropdownDom = newLinkDropdowndom;
- }
- });
-
-
- let linkDropdown = new Dropdown(targetTitle ? [linkInfo, linkApply, deleteLink] : [linkInfo, linkApply], { class: "buttonSettings-dropdown" }) as MenuItem;
- return linkDropdown;
- }
-
- // makeLinkWithState = (state: EditorState, target: string, location: string) => {
- // let link = state.schema.mark(state.schema.marks.link, { href: target, location: location });
- // }
-
- makeLink = (targetDoc: Doc, title: string, location: string): string => {
- let link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + targetDoc[Id]), title: title, location: location });
- this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link).
- addMark(this.view.state.selection.from, this.view.state.selection.to, link));
- let node = this.view.state.selection.$from.nodeAfter;
- if (node && node.text) {
- return node.text;
- }
- return "";
- }
-
- makeLinkToURL = (target: String, lcoation: string) => {
- let node = this.view.state.selection.$from.nodeAfter;
- let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location });
- this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link));
- this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link));
- node = this.view.state.selection.$from.nodeAfter;
- link = node && node.marks.find(m => m.type.name === "link");
- }
-
- deleteLink = () => {
- let node = this.view.state.selection.$from.nodeAfter;
- let link = node && node.marks.find(m => m.type === this.view.state.schema.marks.link);
- let href = link!.attrs.href;
- if (href) {
- if (href.indexOf(Utils.prepend("/doc/")) === 0) {
- const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
- if (linkclicked) {
- DocServer.GetRefField(linkclicked).then(async linkDoc => {
+ // delete the link
+ const node = self.view.state.selection.$from.nodeAfter;
+ const link = node && node.marks.find(m => m.type === self.view.state.schema.marks.link);
+ const href = link!.attrs.href;
+ if (href?.indexOf(Utils.prepend("/doc/")) === 0) {
+ const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+ linkclicked && DocServer.GetRefField(linkclicked).then(async linkDoc => {
if (linkDoc instanceof Doc) {
LinkManager.Instance.deleteLink(linkDoc);
- this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link));
+ self.view.dispatch(self.view.state.tr.removeMark(self.view.state.selection.from, self.view.state.selection.to, self.view.state.schema.marks.link));
}
});
}
- }
- }
- }
-
- deleteLinkItem() {
- const icon = {
- height: 16, width: 16,
- path: "M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z"
- };
- return new MenuItem({
- title: "Delete Link",
- label: "X",
- icon: icon,
- css: "color: red",
- class: "summarize",
- execEvent: "",
- run: (state, dispatch) => {
- this.deleteLink();
- }
- });
- }
-
- createLink() {
- let markType = schema.marks.link;
- return new MenuItem({
- title: "Add or remove link",
- label: "Add or remove link",
- execEvent: "",
- icon: icons.link,
- css: "color:white;",
- class: "menuicon",
- enable(state) { return !state.selection.empty; },
- run: (state, dispatch, view) => {
- // to remove link
- let curLink = "";
- if (this.markActive(state, markType)) {
-
- let { from, $from, to, empty } = state.selection;
- let node = state.doc.nodeAt(from);
- node && node.marks.map(m => {
- m.type === markType && (curLink = m.attrs.href);
- });
- //toggleMark(markType)(state, dispatch);
- //return true;
- }
- // to create link
- openPrompt({
- title: "Create a link",
- fields: {
- href: new TextField({
- value: curLink,
- label: "Link Target",
- required: true
- }),
- title: new TextField({ label: "Title" })
- },
- callback(attrs: any) {
- toggleMark(markType, attrs)(view.state, view.dispatch);
- view.focus();
- },
- flyout_top: 0,
- flyout_left: 0
- });
+ // update link menu
+ const linkDom = self.createLinkTool(false).render(self.view).dom;
+ const linkDropdownDom = self.createLinkDropdown("").render(self.view).dom;
+ self.linkDom && self.tooltip.replaceChild(linkDom, self.linkDom);
+ self.linkDropdownDom && self.tooltip.replaceChild(linkDropdownDom, self.linkDropdownDom);
+ self.linkDom = linkDom;
+ self.linkDropdownDom = linkDropdownDom;
}
});
- }
- //will display a remove-list-type button if selection is in list, otherwise will show list type dropdown
- updateListItemDropdown(label: string, listTypeBtn: any) {
- //remove old btn
- if (listTypeBtn) { this.tooltip.removeChild(listTypeBtn); }
-
- //Make a dropdown of all list types
- let toAdd: MenuItem[] = [];
- this.listTypeToIcon.forEach((icon, type) => {
- toAdd.push(this.dropdownNodeBtn(icon, "color: black; width: 40px;", type, this.view, this.listTypes, this.changeToNodeType));
- });
- //option to remove the list formatting
- toAdd.push(this.dropdownNodeBtn("X", "color: black; width: 40px;", undefined, this.view, this.listTypes, this.changeToNodeType));
-
- listTypeBtn = (new Dropdown(toAdd, {
- label: label,
- css: "color:black; width: 40px;"
- }) as MenuItem).render(this.view).dom;
-
- //add this new button and return it
- this.tooltip.appendChild(listTypeBtn);
- return listTypeBtn;
- }
-
- createStar() {
- return new MenuItem({
- title: "Summarize",
- label: "Summarize",
- icon: icons.join,
- css: "color:white;",
- class: "menuicon",
- execEvent: "",
- run: (state, dispatch) => {
- TooltipTextMenu.insertStar(this.view.state, this.view.dispatch);
- }
-
- });
+ return new Dropdown(targetTitle ? [linkInfo, linkApply, deleteLink] : [linkInfo, linkApply], { class: "buttonSettings-dropdown" }) as MenuItem;
}
- public static insertStar(state: EditorState<any>, dispatch: any) {
- if (state.selection.empty) return false;
- let mark = state.schema.marks.highlight.create();
- let tr = state.tr;
- tr.addMark(state.selection.from, state.selection.to, mark);
- let content = tr.selection.content();
- let newNode = state.schema.nodes.star.create({ visibility: false, text: content, textslice: content.toJSON() });
- dispatch && dispatch(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
- return true;
+ public MakeLinkToSelection = (linkDocId: string, title: string, location: string, targetDocId: string): string => {
+ const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, targetId: targetDocId });
+ this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link).
+ addMark(this.view.state.selection.from, this.view.state.selection.to, link));
+ return this.view.state.selection.$from.nodeAfter?.text || "";
}
- public static insertComment(state: EditorState<any>, dispatch: any) {
- if (state.selection.empty) return false;
- let mark = state.schema.marks.highlight.create();
- let tr = state.tr;
- tr.addMark(state.selection.from, state.selection.to, mark);
- let content = tr.selection.content();
- let newNode = state.schema.nodes.star.create({ visibility: false, text: content, textslice: content.toJSON() });
- dispatch && dispatch(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
- return true;
+ // SUMMARIZER TOOL
+ static insertSummarizer(state: EditorState<any>, dispatch: any) {
+ if (!state.selection.empty) {
+ const mark = state.schema.marks.summarize.create();
+ const tr = state.tr.addMark(state.selection.from, state.selection.to, mark);
+ const content = tr.selection.content();
+ const newNode = state.schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() });
+ dispatch?.(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
+ }
}
+ // HIGHLIGHTER TOOL
createHighlightTool() {
return new MenuItem({
title: "Highlight",
- css: "color:white;",
+ css: "fill:white;",
class: "menuicon",
execEvent: "",
render() {
- let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("viewBox", "-100 -100 650 650");
- let path = document.createElementNS('http://www.w3.org/2000/svg', "path");
+ const path = document.createElementNS('http://www.w3.org/2000/svg', "path");
path.setAttributeNS(null, "d", "M0 479.98L99.92 512l35.45-35.45-67.04-67.04L0 479.98zm124.61-240.01a36.592 36.592 0 0 0-10.79 38.1l13.05 42.83-50.93 50.94 96.23 96.23 50.86-50.86 42.74 13.08c13.73 4.2 28.65-.01 38.15-10.78l35.55-41.64-173.34-173.34-41.52 35.44zm403.31-160.7l-63.2-63.2c-20.49-20.49-53.38-21.52-75.12-2.35L190.55 183.68l169.77 169.78L530.27 154.4c19.18-21.74 18.15-54.63-2.35-75.13z");
svg.appendChild(path);
- let color = document.createElement("div");
- color.className = "buttonColor";
- color.style.backgroundColor = TooltipTextMenuManager.Instance.highlight.toString();
+ const color = TooltipTextMenu.createDiv("buttonColor");
+ color.style.backgroundColor = TooltipTextMenuManager.Instance.highlighter.toString();
- let wrapper = document.createElement("div");
- wrapper.id = "colorPicker";
+ const wrapper = TooltipTextMenu.createDiv("colorPicker");
wrapper.appendChild(svg);
wrapper.appendChild(color);
return wrapper;
},
- run: (state, dispatch) => {
- TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlight, this.view.state, this.view.dispatch);
- }
+ run: (state, dispatch) => TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlighter, state, dispatch)
});
}
- public static insertHighlight(color: String, state: EditorState<any>, dispatch: any) {
- if (state.selection.empty) return false;
-
- let highlightMark = state.schema.mark(state.schema.marks.marker, { highlight: color });
- dispatch(state.tr.addMark(state.selection.from, state.selection.to, highlightMark));
+ static insertHighlight(color: String, state: EditorState<any>, dispatch: any) {
+ if (!state.selection.empty) {
+ toggleMark(state.schema.marks.marker, { highlight: color })(state, dispatch);
+ }
}
createHighlightDropdown() {
// menu item for color picker
- let self = this;
- let colors = new MenuItem({
+ const self = this;
+ const colors = new MenuItem({
title: "",
execEvent: "",
class: "button-setting-disabled",
css: "",
render() {
- let p = document.createElement("p");
+ const p = document.createElement("p");
p.textContent = "Change highlight:";
- let colorsWrapper = document.createElement("div");
- colorsWrapper.className = "colorPicker-wrapper";
+ const colorsWrapper = TooltipTextMenu.createDiv("colorPicker-wrapper");
- let colors = [
+ const colors = [
PastelSchemaPalette.get("pink2"),
PastelSchemaPalette.get("purple4"),
PastelSchemaPalette.get("bluegreen1"),
@@ -768,29 +574,29 @@ export class TooltipTextMenu {
];
colors.forEach(color => {
- let button = document.createElement("button");
- button.className = color === TooltipTextMenuManager.Instance.highlight ? "colorPicker active" : "colorPicker";
+ const button = document.createElement("button");
+ button.className = color === TooltipTextMenuManager.Instance.highlighter ? "colorPicker active" : "colorPicker";
if (color) {
button.style.backgroundColor = color;
button.textContent = color === "transparent" ? "X" : "";
button.onclick = e => {
- TooltipTextMenuManager.Instance.highlight = color;
+ TooltipTextMenuManager.Instance.highlighter = color;
- TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlight, self.view.state, self.view.dispatch);
+ TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlighter, self.view.state, self.view.dispatch);
// update color menu
- let highlightDom = self.createHighlightTool().render(self.view).dom;
- let highlightDropdownDom = self.createHighlightDropdown().render(self.view).dom;
- self.highlightDom && self.tooltip.replaceChild(highlightDom, self.highlightDom);
- self.highlightDropdownDom && self.tooltip.replaceChild(highlightDropdownDom, self.highlightDropdownDom);
- self.highlightDom = highlightDom;
- self.highlightDropdownDom = highlightDropdownDom;
+ const highlightDom = self.createHighlightTool().render(self.view).dom;
+ const highlightDropdownDom = self.createHighlightDropdown().render(self.view).dom;
+ self.highighterDom && self.tooltip.replaceChild(highlightDom, self.highighterDom);
+ self.highlighterDropdownDom && self.tooltip.replaceChild(highlightDropdownDom, self.highlighterDropdownDom);
+ self.highighterDom = highlightDom;
+ self.highlighterDropdownDom = highlightDropdownDom;
};
}
colorsWrapper.appendChild(button);
});
- let div = document.createElement("div");
+ const div = document.createElement("div");
div.appendChild(p);
div.appendChild(colorsWrapper);
return div;
@@ -801,62 +607,59 @@ export class TooltipTextMenu {
}
});
- let colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem;
- return colorDropdown;
+ return new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem;
}
+ // COLOR TOOL
createColorTool() {
return new MenuItem({
title: "Color",
- css: "color:white;",
+ css: "fill:white;",
class: "menuicon",
execEvent: "",
render() {
- let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("viewBox", "-100 -100 650 650");
- let path = document.createElementNS('http://www.w3.org/2000/svg', "path");
+ const path = document.createElementNS('http://www.w3.org/2000/svg', "path");
path.setAttributeNS(null, "d", "M204.3 5C104.9 24.4 24.8 104.3 5.2 203.4c-37 187 131.7 326.4 258.8 306.7 41.2-6.4 61.4-54.6 42.5-91.7-23.1-45.4 9.9-98.4 60.9-98.4h79.7c35.8 0 64.8-29.6 64.9-65.3C511.5 97.1 368.1-26.9 204.3 5zM96 320c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm32-128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128-64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128 64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z");
svg.appendChild(path);
- let color = document.createElement("div");
- color.className = "buttonColor";
+ const color = TooltipTextMenu.createDiv("buttonColor");
color.style.backgroundColor = TooltipTextMenuManager.Instance.color.toString();
- let wrapper = document.createElement("div");
- wrapper.id = "colorPicker";
+ const wrapper = TooltipTextMenu.createDiv("colorPicker");
wrapper.appendChild(svg);
wrapper.appendChild(color);
return wrapper;
},
- run: (state, dispatch) => {
- TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, this.view.state, this.view.dispatch);
- }
+ run: (state, dispatch) => TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, state, dispatch)
});
}
- public static insertColor(color: String, state: EditorState<any>, dispatch: any) {
- if (state.selection.empty) return false;
-
- let colorMark = state.schema.mark(state.schema.marks.color, { color: color });
- dispatch(state.tr.addMark(state.selection.from, state.selection.to, colorMark));
+ static insertColor(color: String, state: EditorState<any>, dispatch: any) {
+ const colorMark = state.schema.mark(state.schema.marks.pFontColor, { color: color });
+ if (state.selection.empty) {
+ dispatch(state.tr.addStoredMark(colorMark));
+ } else {
+ this.setMark(colorMark, state, dispatch);
+ }
}
createColorDropdown() {
// menu item for color picker
- let self = this;
- let colors = new MenuItem({
+ const self = this;
+ const colors = new MenuItem({
title: "",
execEvent: "",
class: "button-setting-disabled",
css: "",
render() {
- let p = document.createElement("p");
+ const p = document.createElement("p");
p.textContent = "Change color:";
- let colorsWrapper = document.createElement("div");
- colorsWrapper.className = "colorPicker-wrapper";
+ const colorsWrapper = TooltipTextMenu.createDiv("colorPicker-wrapper");
- let colors = [
+ const colors = [
DarkPastelSchemaPalette.get("pink2"),
DarkPastelSchemaPalette.get("purple4"),
DarkPastelSchemaPalette.get("bluegreen1"),
@@ -870,7 +673,7 @@ export class TooltipTextMenu {
];
colors.forEach(color => {
- let button = document.createElement("button");
+ const button = document.createElement("button");
button.className = color === TooltipTextMenuManager.Instance.color ? "colorPicker active" : "colorPicker";
if (color) {
button.style.backgroundColor = color;
@@ -880,8 +683,8 @@ export class TooltipTextMenu {
TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, self.view.state, self.view.dispatch);
// update color menu
- let colorDom = self.createColorTool().render(self.view).dom;
- let colorDropdownDom = self.createColorDropdown().render(self.view).dom;
+ const colorDom = self.createColorTool().render(self.view).dom;
+ const colorDropdownDom = self.createColorDropdown().render(self.view).dom;
self.colorDom && self.tooltip.replaceChild(colorDom, self.colorDom);
self.colorDropdownDom && self.tooltip.replaceChild(colorDropdownDom, self.colorDropdownDom);
self.colorDom = colorDom;
@@ -891,75 +694,72 @@ export class TooltipTextMenu {
colorsWrapper.appendChild(button);
});
- let div = document.createElement("div");
+ const div = document.createElement("div");
div.appendChild(p);
div.appendChild(colorsWrapper);
return div;
},
enable() { return false; },
- run(p1, p2, p3, event) {
- event.stopPropagation();
- }
+ run(p1, p2, p3, event) { event.stopPropagation(); }
});
- let colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem;
- return colorDropdown;
+ return new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem;
}
- createBrush(active: boolean = false) {
+ // BRUSH TOOL
+ createBrushTool(active: boolean = false) {
const icon = {
height: 32, width: 32,
path: "M30.828 1.172c-1.562-1.562-4.095-1.562-5.657 0l-5.379 5.379-3.793-3.793-4.243 4.243 3.326 3.326-14.754 14.754c-0.252 0.252-0.358 0.592-0.322 0.921h-0.008v5c0 0.552 0.448 1 1 1h5c0 0 0.083 0 0.125 0 0.288 0 0.576-0.11 0.795-0.329l14.754-14.754 3.326 3.326 4.243-4.243-3.793-3.793 5.379-5.379c1.562-1.562 1.562-4.095 0-5.657zM5.409 30h-3.409v-3.409l14.674-14.674 3.409 3.409-14.674 14.674z"
};
- let self = this;
+ const self = this;
return new MenuItem({
title: "Brush tool",
label: "Brush tool",
icon: icon,
- css: "color:white;",
+ css: "fill:white;",
class: active ? "menuicon-active" : "menuicon",
execEvent: "",
run: (state, dispatch) => {
this.brush_function(state, dispatch);
// update dropdown with marks
- let newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
+ const newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom);
self._brushDropdownDom = newBrushDropdowndom;
},
- active: (state) => {
- return true;
- }
+ active: (state) => true
});
}
brush_function(state: EditorState<any>, dispatch: any) {
if (TooltipTextMenuManager.Instance._brushIsEmpty) {
- const selected_marks = this.getMarksInSelection(this.view.state);
- if (this._brushdom) {
- if (selected_marks.size >= 0) {
- TooltipTextMenuManager.Instance._brushMarks = selected_marks;
- const newbrush = this.createBrush(true).render(this.view).dom;
- this.tooltip.replaceChild(newbrush, this._brushdom);
- this._brushdom = newbrush;
- TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty;
- }
+ // get marks in the selection
+ const selected_marks = new Set<Mark>();
+ const { from, to } = state.selection as TextSelection;
+ state.doc.nodesBetween(from, to, (node) => node.marks?.forEach(m => selected_marks.add(m)));
+
+ if (this._brushdom && selected_marks.size >= 0) {
+ TooltipTextMenuManager.Instance._brushMarks = selected_marks;
+ const newbrush = this.createBrushTool(true).render(this.view).dom;
+ this.tooltip.replaceChild(newbrush, this._brushdom);
+ this._brushdom = newbrush;
+ TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty;
}
}
else {
- let { from, to, $from } = this.view.state.selection;
+ const { from, to, $from } = this.view.state.selection;
if (this._brushdom) {
if (!this.view.state.selection.empty && $from && $from.nodeAfter) {
if (TooltipTextMenuManager.Instance._brushMarks && to - from > 0) {
this.view.dispatch(this.view.state.tr.removeMark(from, to));
Array.from(TooltipTextMenuManager.Instance._brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => {
- const markType = mark.type;
- this.changeToMarkInGroup(markType, this.view, []);
+ TooltipTextMenu.setMark(mark, this.view.state, this.view.dispatch);
});
}
}
else {
- const newbrush = this.createBrush(false).render(this.view).dom;
+ const newbrush = this.createBrushTool(false).render(this.view).dom;
this.tooltip.replaceChild(newbrush, this._brushdom);
this._brushdom = newbrush;
TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty;
@@ -971,40 +771,58 @@ export class TooltipTextMenu {
createBrushDropdown(active: boolean = false) {
let label = "Stored marks: ";
if (TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0) {
- TooltipTextMenuManager.Instance._brushMarks.forEach((mark: Mark) => {
- const markType = mark.type;
- label += markType.name;
- label += ", ";
- });
+ TooltipTextMenuManager.Instance._brushMarks.forEach((mark: Mark) => label += mark.type.name + ", ");
label = label.substring(0, label.length - 2);
} else {
label = "No marks are currently stored";
}
-
- let brushInfo = new MenuItem({
+ const brushInfo = new MenuItem({
title: "",
label: label,
execEvent: "",
class: "button-setting-disabled",
css: "",
enable() { return false; },
- run(p1, p2, p3, event) {
- event.stopPropagation();
- }
+ run(p1, p2, p3, event) { event.stopPropagation(); }
});
- let self = this;
- let clearBrush = new MenuItem({
+ const self = this;
+ const input = document.createElement("input");
+ const clearBrush = new MenuItem({
title: "Clear brush",
execEvent: "",
class: "separated-button",
css: "",
render() {
- let button = document.createElement("button");
+ const button = document.createElement("button");
button.textContent = "Clear brush";
- let wrapper = document.createElement("div");
+ input.textContent = "editme";
+ input.style.width = "75px";
+ input.style.height = "30px";
+ input.style.background = "white";
+ input.setAttribute("contenteditable", "true");
+ input.style.whiteSpace = "nowrap";
+ input.type = "text";
+ input.placeholder = "Enter URL";
+ input.onpointerdown = (e: PointerEvent) => {
+ e.stopPropagation();
+ e.preventDefault();
+ };
+ input.onclick = (e: MouseEvent) => {
+ input.select();
+ input.focus();
+ };
+ input.onkeypress = (e: KeyboardEvent) => {
+ if (e.key === "Enter") {
+ TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMap.set(input.value, TooltipTextMenuManager.Instance._brushMarks);
+ input.style.background = "lightGray";
+ }
+ };
+
+ const wrapper = document.createElement("div");
+ wrapper.appendChild(input);
wrapper.appendChild(button);
return wrapper;
},
@@ -1015,305 +833,41 @@ export class TooltipTextMenu {
// update brush tool
// TODO: this probably isn't very clean
- let newBrushdom = self.createBrush().render(self.view).dom;
+ const newBrushdom = self.createBrushTool().render(self.view).dom;
self._brushdom && self.tooltip.replaceChild(newBrushdom, self._brushdom);
self._brushdom = newBrushdom;
- let newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
+ const newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom);
self._brushDropdownDom = newBrushDropdowndom;
}
});
- let hasMarks = TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0;
- let brushDom = new Dropdown(hasMarks ? [brushInfo, clearBrush] : [brushInfo], { class: "buttonSettings-dropdown" }) as MenuItem;
- return brushDom;
+ const hasMarks = TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0;
+ return new Dropdown(hasMarks ? [brushInfo, clearBrush] : [brushInfo], { class: "buttonSettings-dropdown" }) as MenuItem;
}
- //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected textchangeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => {
- changeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => {
- let { $cursor, ranges } = view.state.selection as TextSelection;
- let state = view.state;
- let dispatch = view.dispatch;
-
- //remove all other active font marks
- fontMarks.forEach((type) => {
- if (dispatch) {
- if ($cursor) {
- if (type.isInSet(state.storedMarks || $cursor.marks())) {
- dispatch(state.tr.removeStoredMark(type));
- }
- } else {
- let has = false;
- for (let i = 0; !has && i < ranges.length; i++) {
- let { $from, $to } = ranges[i];
- has = state.doc.rangeHasMark($from.pos, $to.pos, type);
- }
- for (let i of ranges) {
- if (has) {
- toggleMark(type)(view.state, view.dispatch, view);
- }
- }
- }
- }
- });
-
- if (markType) {
- //actually apply font
- if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
- let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
- { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: markType.name, setFontSize: Number(markType.name.replace(/p/, "")) }), view.state.schema);
- view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
- }
- else toggleMark(markType)(view.state, view.dispatch, view);
- }
- }
-
- changeToFontFamily = (mark: Mark, view: EditorView) => {
- let { $cursor, ranges } = view.state.selection as TextSelection;
- let state = view.state;
- let dispatch = view.dispatch;
-
- //remove all other active font marks
- if ($cursor) {
- if (view.state.schema.marks.pFontFamily.isInSet(state.storedMarks || $cursor.marks())) {
- dispatch(state.tr.removeStoredMark(view.state.schema.marks.pFontFamily));
- }
- } else {
- let has = false;
- for (let i = 0; !has && i < ranges.length; i++) {
- let { $from, $to } = ranges[i];
- has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontFamily);
- }
- for (let i of ranges) {
- if (has) {
- toggleMark(view.state.schema.marks.pFontFamily)(view.state, view.dispatch, view);
- }
- }
- }
- let fontName = mark.attrs.family;
- if (fontName) { this.updateFontStyleDropdown(fontName); }
- if (this.editorProps) {
- let ruleProvider = this.editorProps.ruleProvider;
- let heading = NumCast(this.editorProps.Document.heading);
- if (ruleProvider && heading) {
- ruleProvider["ruleFont_" + heading] = fontName;
- }
- }
- //actually apply font
- if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
- let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
- { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: fontName }), view.state.schema);
- view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
- }
- else view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, view.state.schema.marks.pFontFamily.create({ family: fontName })));
- view.state.storedMarks = [...(view.state.storedMarks || []), view.state.schema.marks.pFontFamily.create({ family: fontName })];
- }
-
- changeToFontSize = (mark: Mark, view: EditorView) => {
- let { $cursor, ranges } = view.state.selection as TextSelection;
- let state = view.state;
- let dispatch = view.dispatch;
-
- //remove all other active font marks
- if ($cursor) {
- if (view.state.schema.marks.pFontSize.isInSet(state.storedMarks || $cursor.marks())) {
- dispatch(state.tr.removeStoredMark(view.state.schema.marks.pFontSize));
- }
- } else {
- let has = false;
- for (let i = 0; !has && i < ranges.length; i++) {
- let { $from, $to } = ranges[i];
- has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontSize);
- }
- for (let i of ranges) {
- if (has) {
- toggleMark(view.state.schema.marks.pFontSize)(view.state, view.dispatch, view);
- }
- }
- }
-
- let size = mark.attrs.fontSize;
- if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
- if (this.editorProps) {
- let ruleProvider = this.editorProps.ruleProvider;
- let heading = NumCast(this.editorProps.Document.heading);
- if (ruleProvider && heading) {
- ruleProvider["ruleSize_" + heading] = size;
- }
- }
- //actually apply font
- if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
- let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
- { ...(view.state.selection as NodeSelection).node.attrs, setFontSize: size }), view.state.schema);
- view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
- }
- else view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, view.state.schema.marks.pFontSize.create({ fontSize: size })));
- view.state.storedMarks = [...(view.state.storedMarks || []), view.state.schema.marks.pFontSize.create({ fontSize: size })];
- }
-
- //remove all node typeand apply the passed-in one to the selected text
- changeToNodeType = (nodeType: NodeType | undefined) => {
- //remove oldif (nodeType) { //add new
- let view = this.view;
- if (nodeType === schema.nodes.bullet_list) {
- wrapInList(nodeType)(view.state, view.dispatch);
- } else {
- var marks = view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks());
- if (!wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => {
- let tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
- marks && tx3.ensureMarks([...marks]);
- marks && tx3.setStoredMarks([...marks]);
-
- view.dispatch(tx2);
- })) {
- let tx2 = view.state.tr;
- let tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
- marks && tx3.ensureMarks([...marks]);
- marks && tx3.setStoredMarks([...marks]);
-
- view.dispatch(tx3);
- }
- }
- }
-
- //makes a button for the drop down FOR MARKS
- //css is the style you want applied to the button
- dropdownFontFamilyBtn(label: string, css: string, mark: Mark, view: EditorView, changeFontFamily: (mark: Mark<any>, view: EditorView) => any) {
- return new MenuItem({
- title: "",
- label: label,
- execEvent: "",
- class: "dropdown-item",
- css: css,
- enable() { return true; },
- run() {
- changeFontFamily(mark, view);
- }
- });
- }
- //makes a button for the drop down FOR MARKS
- //css is the style you want applied to the button
- dropdownFontSizeBtn(label: string, css: string, mark: Mark, view: EditorView, changeFontSize: (markType: Mark<any>, view: EditorView) => any) {
- return new MenuItem({
- title: "",
- label: label,
- execEvent: "",
- class: "dropdown-item",
- css: css,
- enable() { return true; },
- run() {
- changeFontSize(mark, view);
- }
- });
- }
-
- //makes a button for the drop down FOR NODE TYPES
- //css is the style you want applied to the button
- dropdownNodeBtn(label: string, css: string, nodeType: NodeType | undefined, view: EditorView, groupNodes: NodeType[], changeToNodeInGroup: (nodeType: NodeType<any> | undefined, view: EditorView, groupNodes: NodeType[]) => any) {
- return new MenuItem({
- title: "",
- label: label,
- execEvent: "",
- class: "dropdown-item",
- css: css,
- enable() { return true; },
- run() {
- changeToNodeInGroup(nodeType, view, groupNodes);
- }
- });
- }
-
- markActive = function(state: EditorState<any>, type: MarkType<Schema<string, string>>) {
- let { from, $from, to, empty } = state.selection;
- if (empty) return type.isInSet(state.storedMarks || $from.marks());
- else return state.doc.rangeHasMark(from, to, type);
- };
-
- // Helper function to create menu icons
- icon(text: string, name: string, title: string = name) {
- let span = document.createElement("span");
- span.className = name + " menuicon";
- span.title = title;
- span.textContent = text;
- span.style.color = "white";
- return span;
- }
-
- svgIcon(name: string, title: string = name, dpath: string) {
- let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
- svg.setAttribute("viewBox", "-100 -100 650 650");
- let path = document.createElementNS('http://www.w3.org/2000/svg', "path");
- path.setAttributeNS(null, "d", dpath);
- svg.appendChild(path);
-
- let span = document.createElement("span");
- span.className = name + " menuicon";
- span.title = title;
- span.appendChild(svg);
-
- return span;
- }
-
- //method for checking whether node can be inserted
- canInsert(state: EditorState, nodeType: NodeType<Schema<string, string>>) {
- let $from = state.selection.$from;
- for (let d = $from.depth; d >= 0; d--) {
- let index = $from.index(d);
- if ($from.node(d).canReplaceWith(index, index, nodeType)) return true;
- }
- return false;
- }
-
-
- //adapted this method - use it to check if block has a tag (ie bulleting)
- blockActive(type: NodeType<Schema<string, string>>, state: EditorState) {
- let attrs = {};
-
- if (state.selection instanceof NodeSelection) {
- const sel: NodeSelection = state.selection;
- let $from = sel.$from;
- let to = sel.to;
- let node = sel.node;
-
- if (node) {
- return node.hasMarkup(type, attrs);
- }
-
- return to <= $from.end() && $from.parent.hasMarkup(type, attrs);
- }
- }
-
- // Create an icon for a heading at the given level
- heading(level: number) {
- return {
- command: setBlockType(schema.nodes.heading, { level }),
- dom: this.icon("H" + level, "heading")
- };
- }
-
- getMarksInSelection(state: EditorState<any>) {
- let found = new Set<Mark>();
- let { from, to } = state.selection as TextSelection;
- state.doc.nodesBetween(from, to, (node) => {
- let marks = node.marks;
- if (marks) {
- marks.forEach(m => {
- found.add(m);
+ static setMark = (mark: Mark, state: EditorState<any>, dispatch: any) => {
+ if (mark) {
+ const node = (state.selection as NodeSelection).node;
+ if (node?.type === schema.nodes.ordered_list) {
+ let attrs = node.attrs;
+ if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, setFontFamily: mark.attrs.family };
+ if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, setFontSize: mark.attrs.fontSize };
+ if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, setFontColor: mark.attrs.color };
+ const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema);
+ dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from))));
+ } else {
+ toggleMark(mark.type, mark.attrs)(state, (tx: any) => {
+ const { from, $from, to, empty } = tx.selection;
+ if (!tx.doc.rangeHasMark(from, to, mark.type)) {
+ toggleMark(mark.type, mark.attrs)({ tr: tx, doc: tx.doc, selection: tx.selection, storedMarks: tx.storedMarks }, dispatch);
+ } else dispatch(tx);
});
}
- });
- return found;
- }
-
- reset_mark_doms() {
- let iterator = this._marksToDoms.values();
- let next = iterator.next();
- while (!next.done) {
- next.value.style.color = "white";
- next = iterator.next();
}
}
+ // called by Prosemirror
update(view: EditorView, lastState: EditorState | undefined) { this.updateFromDash(view, lastState, this.editorProps); }
//updates the tooltip menu when the selection changes
public async updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) {
@@ -1322,71 +876,52 @@ export class TooltipTextMenu {
return;
}
this.view = view;
- let state = view.state;
DocumentDecorations.Instance.showTextBar();
props && (this.editorProps = props);
- // Don't do anything if the document/selection didn't change
- if (lastState && lastState.doc.eq(state.doc) &&
- lastState.selection.eq(state.selection)) return;
-
- this.reset_mark_doms();
-
- // Hide the tooltip if the selection is empty
- if (state.selection.empty) {
- //this.tooltip.style.display = "none";
- //return;
- }
- // update link dropdown
- let linkDropdown = await this.createLinkDropdown();
- let newLinkDropdowndom = linkDropdown.render(this.view).dom;
- this._linkDropdownDom && this.tooltip.replaceChild(newLinkDropdowndom, this._linkDropdownDom);
- this._linkDropdownDom = newLinkDropdowndom;
-
- //UPDATE FONT STYLE DROPDOWN
- let activeStyles = this.activeFontFamilyOnSelection();
- if (activeStyles !== undefined) {
- if (activeStyles.length === 1) {
- console.log("updating font style dropdown", activeStyles[0]);
- activeStyles[0] && this.updateFontStyleDropdown(activeStyles[0]);
- } else this.updateFontStyleDropdown(activeStyles.length ? "various" : "default");
- }
-
- //UPDATE FONT SIZE DROPDOWN
- let activeSizes = this.activeFontSizeOnSelection();
- if (activeSizes !== undefined) {
- if (activeSizes.length === 1) { //if there's only one active font size
- activeSizes[0] && this.updateFontSizeDropdown(String(activeSizes[0]) + " pt");
- } else this.updateFontSizeDropdown(activeSizes.length ? "various" : "default");
+ // Don't do anything if the document/selection didn't change
+ if (!lastState || !lastState.doc.eq(view.state.doc) || !lastState.selection.eq(view.state.selection)) {
+
+ // UPDATE LINK DROPDOWN
+ const linkTarget = await this.getTextLinkTargetTitle()
+ const linkDom = this.createLinkTool(linkTarget ? true : false).render(this.view).dom;
+ const linkDropdownDom = this.createLinkDropdown(linkTarget).render(this.view).dom;
+ this.linkDom && this.tooltip.replaceChild(linkDom, this.linkDom);
+ this.linkDropdownDom && this.tooltip.replaceChild(linkDropdownDom, this.linkDropdownDom);
+ this.linkDom = linkDom;
+ this.linkDropdownDom = linkDropdownDom;
+
+ //UPDATE FONT STYLE DROPDOWN
+ const activeStyles = this.activeFontFamilyOnSelection();
+ this.updateFontStyleDropdown(activeStyles.length === 1 ? activeStyles[0] : activeStyles.length ? "various" : "default");
+
+ //UPDATE FONT SIZE DROPDOWN
+ const activeSizes = this.activeFontSizeOnSelection();
+ this.updateFontSizeDropdown(activeSizes.length === 1 ? String(activeSizes[0]) + " pt" : activeSizes.length ? "various" : "default");
+
+ //UPDATE ALL OTHER BUTTONS
+ this.updateHighlightStateOfButtons();
}
-
- this.update_mark_doms();
}
- update_mark_doms() {
- this.reset_mark_doms();
- this._activeMarks.forEach((mark) => {
- if (this._marksToDoms.has(mark)) {
- let dom = this._marksToDoms.get(mark);
- if (dom) dom.style.color = "greenyellow";
- }
- });
+
+ updateHighlightStateOfButtons() {
+ Array.from(this._marksToDoms.values()).forEach(val => val.style.fill = "white");
+ this.activeMarksOnSelection().filter(mark => this._marksToDoms.has(mark)).forEach(mark =>
+ this._marksToDoms.get(mark)!.style.fill = "greenyellow");
// keeps brush tool highlighted if active when switching between textboxes
- if (!TooltipTextMenuManager.Instance._brushIsEmpty) {
- if (this._brushdom) {
- const newbrush = this.createBrush(true).render(this.view).dom;
- this.tooltip.replaceChild(newbrush, this._brushdom);
- this._brushdom = newbrush;
- }
+ if (!TooltipTextMenuManager.Instance._brushIsEmpty && this._brushdom) {
+ const newbrush = this.createBrushTool(true).render(this.view).dom;
+ this.tooltip.replaceChild(newbrush, this._brushdom);
+ this._brushdom = newbrush;
}
-
}
//finds fontSize at start of selection
activeFontSizeOnSelection() {
//current selection
- let state = this.view.state;
- let activeSizes: number[] = [];
+ const state = this.view.state;
+ const activeSizes: number[] = [];
const pos = this.view.state.selection.$from;
const ref_node: ProsNode = this.reference_node(pos);
if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
@@ -1397,8 +932,8 @@ export class TooltipTextMenu {
//finds fontSize at start of selection
activeFontFamilyOnSelection() {
//current selection
- let state = this.view.state;
- let activeFamilies: string[] = [];
+ const state = this.view.state;
+ const activeFamilies: string[] = [];
const pos = this.view.state.selection.$from;
const ref_node: ProsNode = this.reference_node(pos);
if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
@@ -1407,24 +942,21 @@ export class TooltipTextMenu {
return activeFamilies;
}
//finds all active marks on selection in given group
- activeMarksOnSelection(markGroup: MarkType[]) {
+ activeMarksOnSelection() {
+ const markGroup = Array.from(this._marksToDoms.keys());
+ if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type);
//current selection
- let { empty, ranges, $to } = this.view.state.selection as TextSelection;
- let state = this.view.state;
- let dispatch = this.view.dispatch;
- let activeMarks: MarkType[];
+ const { empty, ranges, $to } = this.view.state.selection as TextSelection;
+ const state = this.view.state;
+ let activeMarks: MarkType[] = [];
if (!empty) {
activeMarks = markGroup.filter(mark => {
- let has = false;
+ const has = false;
for (let i = 0; !has && i < ranges.length; i++) {
- let { $from, $to } = ranges[i];
- return state.doc.rangeHasMark($from.pos, $to.pos, mark);
+ return state.doc.rangeHasMark(ranges[i].$from.pos, ranges[i].$to.pos, mark);
}
return false;
});
-
- const refnode = this.reference_node($to);
- this._activeMarks = refnode.marks;
}
else {
const pos = this.view.state.selection.$from;
@@ -1435,20 +967,14 @@ export class TooltipTextMenu {
else {
return [];
}
- this._activeMarks = ref_node.marks;
activeMarks = markGroup.filter(mark_type => {
if (mark_type === state.schema.marks.pFontSize) {
return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name);
}
- let mark = state.schema.mark(mark_type);
+ const mark = state.schema.mark(mark_type);
return ref_node.marks.includes(mark);
- return false;
});
}
- else {
- return [];
- }
-
}
return activeMarks;
}
@@ -1485,20 +1011,21 @@ export class TooltipTextMenu {
}
-class TooltipTextMenuManager {
+export class TooltipTextMenuManager {
private static _instance: TooltipTextMenuManager;
+ private _isPinned: boolean = false;
public pinnedX: number = 0;
public pinnedY: number = 0;
public unpinnedX: number = 0;
public unpinnedY: number = 0;
- private _isPinned: boolean = false;
public _brushMarks: Set<Mark> | undefined;
+ public _brushMap: Map<string, Set<Mark>> = new Map();
public _brushIsEmpty: boolean = true;
public color: String = "#000";
- public highlight: String = "transparent";
+ public highlighter: String = "transparent";
public activeMenu: TooltipTextMenu | undefined;
@@ -1509,11 +1036,7 @@ class TooltipTextMenuManager {
return TooltipTextMenuManager._instance;
}
- public get isPinned() {
- return this._isPinned;
- }
+ public get isPinned() { return this._isPinned; }
- public toggleIsPinned() {
- this._isPinned = !this._isPinned;
- }
+ public toggleIsPinned() { this._isPinned = !this._isPinned; }
}
diff --git a/src/client/util/TypedEvent.ts b/src/client/util/TypedEvent.ts
index 532ba78eb..90fd299c1 100644
--- a/src/client/util/TypedEvent.ts
+++ b/src/client/util/TypedEvent.ts
@@ -1,40 +1,40 @@
export interface Listener<T> {
- (event: T): any;
+ (event: T): any;
}
export interface Disposable {
- dispose(): void;
+ dispose(): void;
}
/** passes through events as they happen. You will not get events from before you start listening */
export class TypedEvent<T> {
- private listeners: Listener<T>[] = [];
- private listenersOncer: Listener<T>[] = [];
-
- on = (listener: Listener<T>): Disposable => {
- this.listeners.push(listener);
- return {
- dispose: () => this.off(listener)
- };
- }
-
- once = (listener: Listener<T>): void => {
- this.listenersOncer.push(listener);
- }
-
- off = (listener: Listener<T>) => {
- var callbackIndex = this.listeners.indexOf(listener);
- if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);
- }
-
- emit = (event: T) => {
- /** Update any general listeners */
- this.listeners.forEach((listener) => listener(event));
-
- /** Clear the `once` queue */
- this.listenersOncer.forEach((listener) => listener(event));
- this.listenersOncer = [];
- }
-
- pipe = (te: TypedEvent<T>): Disposable => this.on((e) => te.emit(e));
+ private listeners: Listener<T>[] = [];
+ private listenersOncer: Listener<T>[] = [];
+
+ on = (listener: Listener<T>): Disposable => {
+ this.listeners.push(listener);
+ return {
+ dispose: () => this.off(listener)
+ };
+ }
+
+ once = (listener: Listener<T>): void => {
+ this.listenersOncer.push(listener);
+ }
+
+ off = (listener: Listener<T>) => {
+ const callbackIndex = this.listeners.indexOf(listener);
+ if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);
+ }
+
+ emit = (event: T) => {
+ /** Update any general listeners */
+ this.listeners.forEach((listener) => listener(event));
+
+ /** Clear the `once` queue */
+ this.listenersOncer.forEach((listener) => listener(event));
+ this.listenersOncer = [];
+ }
+
+ pipe = (te: TypedEvent<T>): Disposable => this.on((e) => te.emit(e));
} \ No newline at end of file
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 472afac1d..314b52bf3 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -3,7 +3,7 @@ import 'source-map-support/register';
import { Without } from "../../Utils";
function getBatchName(target: any, key: string | symbol): string {
- let keyName = key.toString();
+ const keyName = key.toString();
if (target && target.constructor && target.constructor.name) {
return `${target.constructor.name}.${keyName}`;
}
@@ -23,7 +23,7 @@ function propertyDecorator(target: any, key: string | symbol) {
writable: true,
configurable: true,
value: function (...args: any[]) {
- let batch = UndoManager.StartBatch(getBatchName(target, key));
+ const batch = UndoManager.StartBatch(getBatchName(target, key));
try {
return value.apply(this, args);
} finally {
@@ -40,7 +40,7 @@ export function undoBatch(fn: (...args: any[]) => any): (...args: any[]) => any;
export function undoBatch(target: any, key?: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any {
if (!key) {
return function () {
- let batch = UndoManager.StartBatch("");
+ const batch = UndoManager.StartBatch("");
try {
return target.apply(undefined, arguments);
} finally {
@@ -55,7 +55,7 @@ export function undoBatch(target: any, key?: string | symbol, descriptor?: Typed
const oldFunction = descriptor.value;
descriptor.value = function (...args: any[]) {
- let batch = UndoManager.StartBatch(getBatchName(target, key));
+ const batch = UndoManager.StartBatch(getBatchName(target, key));
try {
return oldFunction.apply(this, args);
} finally {
@@ -98,7 +98,7 @@ export namespace UndoManager {
GetOpenBatches().forEach(batch => console.log(batch.batchName));
}
- let openBatches: Batch[] = [];
+ const openBatches: Batch[] = [];
export function GetOpenBatches(): Without<Batch, 'end'>[] {
return openBatches;
}
@@ -146,7 +146,7 @@ export namespace UndoManager {
//TODO Make this return the return value
export function RunInBatch<T>(fn: () => T, batchName: string) {
- let batch = StartBatch(batchName);
+ const batch = StartBatch(batchName);
try {
return runInAction(fn);
} finally {
@@ -159,7 +159,7 @@ export namespace UndoManager {
return;
}
- let commands = undoStack.pop();
+ const commands = undoStack.pop();
if (!commands) {
return;
}
@@ -178,7 +178,7 @@ export namespace UndoManager {
return;
}
- let commands = redoStack.pop();
+ const commands = redoStack.pop();
if (!commands) {
return;
}
diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx
index f718735a8..5ca861f71 100644
--- a/src/client/views/CollectionLinearView.tsx
+++ b/src/client/views/CollectionLinearView.tsx
@@ -39,7 +39,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
protected createDropTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
this._dropDisposer && this._dropDisposer();
if (ele) {
- this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+ this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
}
}
@@ -48,12 +48,12 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
dimension = () => NumCast(this.props.Document.height); // 2 * the padding
getTransform = (ele: React.RefObject<HTMLDivElement>) => () => {
if (!ele.current) return Transform.Identity();
- let { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current);
+ const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current);
return new Transform(-translateX, -translateY, 1 / scale);
}
render() {
- let guid = Utils.GenerateGuid();
+ const guid = Utils.GenerateGuid();
return <div className="collectionLinearView-outer">
<div className="collectionLinearView" ref={this.createDropTarget} >
<input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.isExpanded)} ref={this.addMenuToggle}
@@ -62,10 +62,10 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
<div className="collectionLinearView-content" style={{ height: this.dimension(), width: NumCast(this.props.Document.width, 25) }}>
{this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => {
- let nested = pair.layout.viewType === CollectionViewType.Linear;
- let dref = React.createRef<HTMLDivElement>();
- let nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension());
- let deltaSize = nativeWidth * .15 / 2;
+ const nested = pair.layout.viewType === CollectionViewType.Linear;
+ const dref = React.createRef<HTMLDivElement>();
+ const nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension());
+ const deltaSize = nativeWidth * .15 / 2;
return <div className={`collectionLinearView-docBtn` + (pair.layout.onClick || pair.layout.onDragStart ? "-scalable" : "")} key={pair.layout[Id]} ref={dref}
style={{
width: nested ? pair.layout[WidthSym]() : this.dimension() - deltaSize,
@@ -74,6 +74,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
<DocumentView
Document={pair.layout}
DataDoc={pair.data}
+ LibraryPath={this.props.LibraryPath}
addDocument={this.props.addDocument}
moveDocument={this.props.moveDocument}
addDocTab={this.props.addDocTab}
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 5d452e72e..937aff0d6 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -49,8 +49,8 @@ export class ContextMenu extends React.Component {
@action
onPointerUp = (e: PointerEvent) => {
this._mouseDown = false;
- let curX = e.clientX;
- let curY = e.clientY;
+ const curX = e.clientX;
+ const curY = e.clientY;
if (this._mouseX !== curX || this._mouseY !== curY) {
this._shouldDisplay = false;
}
@@ -208,7 +208,7 @@ export class ContextMenu extends React.Component {
if (!this._display) {
return null;
}
- let style = this._yRelativeToTop ? { left: this.pageX, top: this.pageY } :
+ const style = this._yRelativeToTop ? { left: this.pageX, top: this.pageY } :
{ left: this.pageX, bottom: this.pageY };
const contents = (
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 330b94afa..fef9e5f60 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -88,7 +88,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
</div>
);
} else if ("subitems" in this.props) {
- let submenu = !this.overItem ? (null) :
+ const submenu = !this.overItem ? (null) :
<div className="contextMenu-subMenu-cont" style={{ marginLeft: "25%", left: "0px" }}>
{this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
</div>;
diff --git a/src/client/views/DictationOverlay.tsx b/src/client/views/DictationOverlay.tsx
index 2accf9bfd..65770c0bb 100644
--- a/src/client/views/DictationOverlay.tsx
+++ b/src/client/views/DictationOverlay.tsx
@@ -24,7 +24,7 @@ export class DictationOverlay extends React.Component {
}
public initiateDictationFade = () => {
- let duration = DictationManager.Commands.dictationFadeDuration;
+ const duration = DictationManager.Commands.dictationFadeDuration;
this.overlayTimeout = setTimeout(() => {
this.dictationOverlayVisible = false;
this.dictationSuccess = undefined;
@@ -50,14 +50,14 @@ export class DictationOverlay extends React.Component {
public set isListening(value: DictationManager.Controls.ListeningUIStatus) { runInAction(() => this._dictationListeningState = value); }
render() {
- let success = this.dictationSuccess;
- let result = this.isListening && !this.isListening.interim ? DictationManager.placeholder : `"${this.dictatedPhrase}"`;
- let dialogueBoxStyle = {
+ const success = this.dictationSuccess;
+ const result = this.isListening && !this.isListening.interim ? DictationManager.placeholder : `"${this.dictatedPhrase}"`;
+ const dialogueBoxStyle = {
background: success === undefined ? "gainsboro" : success ? "lawngreen" : "red",
borderColor: this.isListening ? "red" : "black",
fontStyle: "italic"
};
- let overlayStyle = {
+ const overlayStyle = {
backgroundColor: this.isListening ? "red" : "darkslategrey"
};
return (<MainViewModal
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index b3a130b33..4dbf26956 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,4 +1,3 @@
-import * as React from 'react';
import { Doc } from '../../new_fields/Doc';
import { Touchable } from './Touchable';
import { computed, action, observable } from 'mobx';
@@ -48,6 +47,7 @@ interface DocAnnotatableProps {
Document: Doc;
DataDoc?: Doc;
fieldKey: string;
+ active: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
isSelected: (outsideReaction?: boolean) => boolean;
renderDepth: number;
@@ -58,21 +58,22 @@ export function DocAnnotatableComponent<P extends DocAnnotatableProps, T>(schema
//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); }
@computed get layoutDoc() { return Doc.Layout(this.props.Document); }
- @computed get dataDoc() { return (this.props.DataDoc && this.props.Document.isTemplateField ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; }
+ @computed get dataDoc() { return (this.props.DataDoc && (this.props.Document.isTemplateField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; }
@computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
+ @computed get extensionDocSync() { return Doc.fieldExtensionDocSync(this.dataDoc, this.props.fieldKey); }
@computed get annotationsKey() { return "annotations"; }
@action.bound
removeDocument(doc: Doc): boolean {
Doc.GetProto(doc).annotationOn = undefined;
- let value = this.extensionDoc && Cast(this.extensionDoc[this.annotationsKey], listSpec(Doc), []);
- let index = value ? Doc.IndexOf(doc, value.map(d => d as Doc), true) : -1;
+ const value = this.extensionDoc && Cast(this.extensionDoc[this.annotationsKey], listSpec(Doc), []);
+ const index = value ? Doc.IndexOf(doc, value.map(d => d as Doc), true) : -1;
return index !== -1 && value && value.splice(index, 1) ? true : false;
}
// if the moved document is already in this overlay collection nothing needs to be done.
// otherwise, if the document can be removed from where it was, it will then be added to this document's overlay collection.
@action.bound
- moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
+ moveDocument(doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean {
return Doc.AreProtosEqual(this.props.Document, targetCollection) ? true : this.removeDocument(doc) ? addDocument(doc) : false;
}
@action.bound
diff --git a/src/client/views/DocumentButtonBar.scss b/src/client/views/DocumentButtonBar.scss
index db6bf2ba0..c2ca93900 100644
--- a/src/client/views/DocumentButtonBar.scss
+++ b/src/client/views/DocumentButtonBar.scss
@@ -17,6 +17,7 @@ $linkGap : 3px;
transform: scale(1.05);
cursor: pointer;
}
+
.documentButtonBar-linkButton-empty,
.documentButtonBar-linkButton-nonempty {
height: 20px;
@@ -74,6 +75,31 @@ $linkGap : 3px;
}
-@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
-@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
-@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } } \ No newline at end of file
+@-moz-keyframes spin {
+ 100% {
+ -moz-transform: rotate(360deg);
+ }
+}
+
+@-webkit-keyframes spin {
+ 100% {
+ -webkit-transform: rotate(360deg);
+ }
+}
+
+@keyframes spin {
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes shadow-pulse {
+ 0% {
+ box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.8);
+ }
+
+ 100% {
+ box-shadow: 0 0 0 10px rgba(0, 255, 0, 0);
+ }
+} \ No newline at end of file
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 1fefc70f1..202bfe400 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -3,13 +3,12 @@ import { faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadA
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, observable, runInAction, computed } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../new_fields/Doc";
+import { Doc, DocListCast } from "../../new_fields/Doc";
import { RichTextField } from '../../new_fields/RichTextField';
-import { NumCast, StrCast } from "../../new_fields/Types";
+import { NumCast, StrCast, Cast } from "../../new_fields/Types";
import { emptyFunction } from "../../Utils";
import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
import { DragManager } from "../util/DragManager";
-import { LinkManager } from '../util/LinkManager';
import { UndoManager } from "../util/UndoManager";
import './DocumentButtonBar.scss';
import './collections/ParentDocumentSelector.scss';
@@ -21,6 +20,7 @@ import React = require("react");
import { DocumentView } from './nodes/DocumentView';
import { ParentDocSelector } from './collections/ParentDocumentSelector';
import { CollectionDockingView } from './collections/CollectionDockingView';
+import { Id } from '../../new_fields/FieldSymbols';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -40,7 +40,7 @@ const cloud: IconProp = "cloud-upload-alt";
const fetch: IconProp = "sync-alt";
@observer
-export class DocumentButtonBar extends React.Component<{ views: DocumentView[], stack?: any }, {}> {
+export class DocumentButtonBar extends React.Component<{ views: (DocumentView | undefined)[], stack?: any }, {}> {
private _linkButton = React.createRef<HTMLDivElement>();
private _downX = 0;
private _downY = 0;
@@ -51,16 +51,18 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
@observable private pushIcon: IconProp = "arrow-alt-circle-up";
@observable private pullIcon: IconProp = "arrow-alt-circle-down";
@observable private pullColor: string = "white";
- @observable private isAnimatingFetch = false;
+ @observable public isAnimatingFetch = false;
+ @observable public isAnimatingPulse = false;
+
@observable private openHover = false;
- public static Instance: DocumentButtonBar;
+ @observable public static Instance: DocumentButtonBar;
public static hasPushedHack = false;
public static hasPulledHack = false;
- constructor(props: { views: DocumentView[] }) {
+ constructor(props: { views: (DocumentView | undefined)[] }) {
super(props);
- DocumentButtonBar.Instance = this;
+ runInAction(() => DocumentButtonBar.Instance = this);
}
public startPullOutcome = action((success: boolean) => {
@@ -75,6 +77,7 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
});
public startPushOutcome = action((success: boolean) => {
+ this.isAnimatingPulse = false;
if (!this._pushAnimating) {
this._pushAnimating = true;
this.pushIcon = success ? "check-circle" : "stop-circle";
@@ -99,33 +102,28 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
this._pullColorAnimating = false;
});
+ get view0() { return this.props.views && this.props.views.length ? this.props.views[0] : undefined; }
@action
onLinkButtonMoved = (e: PointerEvent): void => {
if (this._linkButton.current !== null && (Math.abs(e.clientX - this._downX) > 3 || Math.abs(e.clientY - this._downY) > 3)) {
document.removeEventListener("pointermove", this.onLinkButtonMoved);
document.removeEventListener("pointerup", this.onLinkButtonUp);
- let docView = this.props.views[0];
- let container = docView.props.ContainingCollectionDoc?.proto;
- let dragData = new DragManager.LinkDragData(docView.props.Document, container ? [container] : []);
- let linkDrag = UndoManager.StartBatch("Drag Link");
- DragManager.StartLinkDrag(this._linkButton.current, dragData, e.pageX, e.pageY, {
- handlers: {
- dragComplete: () => {
- let tooltipmenu = FormattedTextBox.ToolTipTextMenu;
- let linkDoc = dragData.linkDocument;
- if (linkDoc && tooltipmenu) {
- let proto = Doc.GetProto(linkDoc);
- if (proto && docView) {
- proto.sourceContext = docView.props.ContainingCollectionDoc;
- }
- let text = tooltipmenu.makeLink(linkDoc, StrCast(linkDoc.anchor2.title), e.ctrlKey ? "onRight" : "inTab");
- if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) {
- proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link
- }
+ const linkDrag = UndoManager.StartBatch("Drag Link");
+ this.view0 && DragManager.StartLinkDrag(this._linkButton.current, this.view0.props.Document, e.pageX, e.pageY, {
+ dragComplete: dropEv => {
+ const linkDoc = dropEv.linkDragData?.linkDocument; // equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
+ if (this.view0 && linkDoc && FormattedTextBox.ToolTipTextMenu) {
+ const proto = Doc.GetProto(linkDoc);
+ proto.sourceContext = this.view0.props.ContainingCollectionDoc;
+
+ const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast(linkDoc.anchor2.title) : "-untitled-";
+ if (linkDoc.anchor2 instanceof Doc) {
+ const text = FormattedTextBox.ToolTipTextMenu.MakeLinkToSelection(linkDoc[Id], anchor2Title, e.ctrlKey ? "onRight" : "inTab", linkDoc.anchor2[Id]);
+ proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODO open to more descriptive descriptions of following in text link
}
- linkDrag && linkDrag.end();
}
+ linkDrag?.end();
},
hideSource: false
});
@@ -152,22 +150,28 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
@computed
get considerGoogleDocsPush() {
- let targetDoc = this.props.views[0].props.Document;
- let published = Doc.GetProto(targetDoc)[GoogleRef] !== undefined;
- return <div title={`${published ? "Push" : "Publish"} to Google Docs`} className="documentButtonBar-linker" onClick={() => {
- DocumentButtonBar.hasPushedHack = false;
- targetDoc[Pushes] = NumCast(targetDoc[Pushes]) + 1;
- }}>
+ const targetDoc = this.view0?.props.Document;
+ const published = targetDoc && Doc.GetProto(targetDoc)[GoogleRef] !== undefined;
+ const animation = this.isAnimatingPulse ? "shadow-pulse 1s linear infinite" : "none";
+ return !targetDoc ? (null) : <div
+ title={`${published ? "Push" : "Publish"} to Google Docs`}
+ className="documentButtonBar-linker"
+ style={{ animation }}
+ onClick={() => {
+ !published && runInAction(() => this.isAnimatingPulse = true);
+ DocumentButtonBar.hasPushedHack = false;
+ targetDoc[Pushes] = NumCast(targetDoc[Pushes]) + 1;
+ }}>
<FontAwesomeIcon className="documentdecorations-icon" icon={published ? (this.pushIcon as any) : cloud} size={published ? "sm" : "xs"} />
</div>;
}
@computed
get considerGoogleDocsPull() {
- let targetDoc = this.props.views[0].props.Document;
- let dataDoc = Doc.GetProto(targetDoc);
- let animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none";
- return !dataDoc[GoogleRef] ? (null) : <div className="documentButtonBar-linker"
+ const targetDoc = this.view0?.props.Document;
+ const dataDoc = targetDoc && Doc.GetProto(targetDoc);
+ const animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none";
+ return !targetDoc || !dataDoc || !dataDoc[GoogleRef] ? (null) : <div className="documentButtonBar-linker"
title={`${!dataDoc.unchanged ? "Pull from" : "Fetch"} Google Docs`}
style={{ backgroundColor: this.pullColor }}
onPointerEnter={e => e.altKey && runInAction(() => this.openHover = true)}
@@ -192,10 +196,11 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
@computed
get linkButton() {
- let linkCount = LinkManager.Instance.getAllRelatedLinks(this.props.views[0].props.Document).length;
- return <div title="Drag(create link) Tap(view links)" className="documentButtonBar-linkFlyout" ref={this._linkButton}>
+ const view0 = this.view0;
+ const linkCount = view0 && DocListCast(view0.props.Document.links).length;
+ return !view0 ? (null) : <div title="Drag(create link) Tap(view links)" className="documentButtonBar-linkFlyout" ref={this._linkButton}>
<Flyout anchorPoint={anchorPoints.RIGHT_TOP}
- content={<LinkMenu docView={this.props.views[0]} addDocTab={this.props.views[0].props.addDocTab} changeFlyout={emptyFunction} />}>
+ content={<LinkMenu docView={view0} addDocTab={view0.props.addDocTab} changeFlyout={emptyFunction} />}>
<div className={"documentButtonBar-linkButton-" + (linkCount ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >
{linkCount ? linkCount : <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />}
</div>
@@ -205,28 +210,29 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
@computed
get contextButton() {
- return <ParentDocSelector Views={this.props.views} Document={this.props.views[0].props.Document} addDocTab={(doc, data, where) => {
+ return !this.view0 ? (null) : <ParentDocSelector Views={this.props.views.filter(v => v).map(v => v as DocumentView)} Document={this.view0.props.Document} addDocTab={(doc, data, where) => {
where === "onRight" ? CollectionDockingView.AddRightSplit(doc, data) :
this.props.stack ? CollectionDockingView.Instance.AddTab(this.props.stack, doc, data) :
- this.props.views[0].props.addDocTab(doc, data, "onRight");
+ this.view0?.props.addDocTab(doc, data, "onRight");
return true;
}} />;
}
render() {
- let templates: Map<Template, boolean> = new Map();
+ if (!this.view0) return (null);
+ const templates: Map<Template, boolean> = new Map();
Array.from(Object.values(Templates.TemplateList)).map(template =>
- templates.set(template, this.props.views.reduce((checked, doc) => checked || doc.getLayoutPropStr("show" + template.Name) ? true : false, false as boolean)));
+ templates.set(template, this.props.views.reduce((checked, doc) => checked || doc?.getLayoutPropStr("show" + template.Name) ? true : false, false as boolean)));
- let isText = this.props.views[0].props.Document.data instanceof RichTextField; // bcz: Todo - can't assume layout is using the 'data' field. need to add fieldKey to DocumentView
- let considerPull = isText && this.considerGoogleDocsPull;
- let considerPush = isText && this.considerGoogleDocsPush;
+ const isText = this.view0.props.Document.data instanceof RichTextField; // bcz: Todo - can't assume layout is using the 'data' field. need to add fieldKey to DocumentView
+ const considerPull = isText && this.considerGoogleDocsPull;
+ const considerPush = isText && this.considerGoogleDocsPush;
return <div className="documentButtonBar">
<div className="documentButtonBar-button">
{this.linkButton}
</div>
<div className="documentButtonBar-button">
- <TemplateMenu docs={this.props.views} templates={templates} />
+ <TemplateMenu docs={this.props.views.filter(v => v).map(v => v as DocumentView)} templates={templates} />
</div>
<div className="documentButtonBar-button" style={{ display: !considerPush ? "none" : "" }}>
{this.considerGoogleDocsPush}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 66f47147f..4bc24fa93 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -24,8 +24,8 @@ import { DocumentView } from "./nodes/DocumentView";
import { FieldView } from "./nodes/FieldView";
import { IconBox } from "./nodes/IconBox";
import React = require("react");
-import { PointData } from '../../new_fields/InkField';
import { DocumentType } from '../documents/DocumentTypes';
+import { ScriptField } from '../../new_fields/ScriptField';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -55,11 +55,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
private _iconDoc?: Doc = undefined;
private _resizeUndo?: UndoManager.Batch;
private _radiusDown = [0, 0];
+ @observable private _accumulatedTitle = "";
@observable private _minimizedX = 0;
@observable private _minimizedY = 0;
- @observable private _title: string = "";
+ @observable private _titleControlString: string = "#title";
@observable private _edtingTitle = false;
- @observable private _fieldKey = "title";
@observable private _hidden = false;
@observable private _opacity = 1;
@observable private _removeIcon = false;
@@ -68,39 +68,49 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@observable public pushIcon: IconProp = "arrow-alt-circle-up";
@observable public pullIcon: IconProp = "arrow-alt-circle-down";
@observable public pullColor: string = "white";
- @observable public isAnimatingFetch = false;
- @observable public isAnimatingPulse = false;
@observable public openHover = false;
constructor(props: Readonly<{}>) {
super(props);
DocumentDecorations.Instance = this;
this._keyinput = React.createRef();
- reaction(() => SelectionManager.SelectedDocuments().slice(), docs => this._edtingTitle = false);
+ reaction(() => SelectionManager.SelectedDocuments().slice(), docs => this.titleBlur(false));
}
- @action titleChanged = (event: any) => { this._title = event.target.value; };
- @action titleBlur = () => { this._edtingTitle = false; };
+ @action titleChanged = (event: any) => this._accumulatedTitle = event.target.value;
+
+ titleBlur = undoBatch(action((commit: boolean) => {
+ this._edtingTitle = false;
+ if (commit) {
+ if (this._accumulatedTitle.startsWith("#") || this._accumulatedTitle.startsWith("=")) {
+ this._titleControlString = this._accumulatedTitle;
+ } else if (this._titleControlString.startsWith("#")) {
+ const selectionTitleFieldKey = this._titleControlString.substring(1);
+ selectionTitleFieldKey === "title" && (SelectionManager.SelectedDocuments()[0].props.Document.customTitle = !this._accumulatedTitle.startsWith("-"));
+ selectionTitleFieldKey && SelectionManager.SelectedDocuments().forEach(d =>
+ Doc.SetInPlace(d.props.Document, selectionTitleFieldKey, typeof d.props.Document[selectionTitleFieldKey] === "number" ? +this._accumulatedTitle : this._accumulatedTitle, true)
+ );
+ }
+ }
+ }));
+
@action titleEntered = (e: any) => {
- var key = e.keyCode || e.which;
+ const key = e.keyCode || e.which;
// enter pressed
if (key === 13) {
- var text = e.target.value;
- if (text[0] === '#') {
- this._fieldKey = text.slice(1, text.length);
- this._title = this.selectionTitle;
- } else if (text.startsWith("::")) {
- let targetID = text.slice(2, text.length);
- let promoteDoc = SelectionManager.SelectedDocuments()[0];
+ const text = e.target.value;
+ if (text.startsWith("::")) {
+ const targetID = text.slice(2, text.length);
+ const promoteDoc = SelectionManager.SelectedDocuments()[0];
DocUtils.Publish(promoteDoc.props.Document, targetID, promoteDoc.props.addDocument, promoteDoc.props.removeDocument);
} else if (text.startsWith(">")) {
- let fieldTemplateView = SelectionManager.SelectedDocuments()[0];
+ const fieldTemplateView = SelectionManager.SelectedDocuments()[0];
SelectionManager.DeselectAll();
- let fieldTemplate = fieldTemplateView.props.Document;
- let containerView = fieldTemplateView.props.ContainingCollectionView;
- let docTemplate = fieldTemplateView.props.ContainingCollectionDoc;
+ const fieldTemplate = fieldTemplateView.props.Document;
+ const containerView = fieldTemplateView.props.ContainingCollectionView;
+ const docTemplate = fieldTemplateView.props.ContainingCollectionDoc;
if (containerView && docTemplate) {
- let metaKey = text.startsWith(">>") ? text.slice(2, text.length) : text.slice(1, text.length);
+ const metaKey = text.startsWith(">>") ? text.slice(2, text.length) : text.slice(1, text.length);
if (metaKey !== containerView.props.fieldKey && containerView.props.DataDoc) {
const fd = fieldTemplate.data;
fd instanceof ObjectField && (Doc.GetProto(containerView.props.DataDoc)[metaKey] = ObjectField.MakeCopy(fd));
@@ -108,24 +118,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
fieldTemplate.title = metaKey;
Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate));
if (text.startsWith(">>")) {
- Doc.GetProto(docTemplate).layout = StrCast(fieldTemplateView.props.Document.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
- }
- }
- }
- else {
- if (SelectionManager.SelectedDocuments().length > 0) {
- SelectionManager.SelectedDocuments()[0].props.Document.customTitle = !this._title.startsWith("-");
- let field = SelectionManager.SelectedDocuments()[0].props.Document[this._fieldKey];
- if (typeof field === "number") {
- SelectionManager.SelectedDocuments().forEach(d => {
- let doc = d.props.Document.proto ? d.props.Document.proto : d.props.Document;
- doc[this._fieldKey] = +this._title;
- });
- } else {
- SelectionManager.SelectedDocuments().forEach(d => {
- let doc = d.props.Document.proto ? d.props.Document.proto : d.props.Document;
- doc[this._fieldKey] = this._title;
- });
+ Doc.GetProto(docTemplate).layout = StrCast(fieldTemplateView.props.Document.layout).replace(/fieldKey={'[^']*'}/, `fieldKey={"${metaKey}"}`);
}
}
}
@@ -150,8 +143,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
@action onTitleUp = (e: PointerEvent): void => {
if (Math.abs(e.clientX - this._downX) < 4 || Math.abs(e.clientY - this._downY) < 4) {
- this._title = this.selectionTitle;
+ !this._edtingTitle && (this._accumulatedTitle = this._titleControlString.startsWith("#") ? this.selectionTitle : this._titleControlString);
this._edtingTitle = true;
+ setTimeout(() => this._keyinput.current!.focus(), 0);
}
document.removeEventListener("pointermove", this.onTitleMove);
document.removeEventListener("pointerup", this.onTitleUp);
@@ -165,11 +159,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
Doc.AreProtosEqual(documentView.props.Document, CurrentUserUtils.UserDocument)) {
return bounds;
}
- let transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse();
+ const transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse();
var [sptX, sptY] = transform.transformPoint(0, 0);
let [bptX, bptY] = transform.transformPoint(documentView.props.PanelWidth(), documentView.props.PanelHeight());
if (documentView.props.Document.type === DocumentType.LINK) {
- let rect = documentView.ContentDiv!.getElementsByClassName("docuLinkBox-cont")[0].getBoundingClientRect();
+ const rect = documentView.ContentDiv!.getElementsByClassName("docuLinkBox-cont")[0].getBoundingClientRect();
sptX = rect.left;
sptY = rect.top;
bptX = rect.right;
@@ -192,8 +186,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@action
onBackgroundMove = (e: PointerEvent): void => {
- let dragDocView = SelectionManager.SelectedDocuments()[0];
- let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
+ const dragDocView = SelectionManager.SelectedDocuments()[0];
+ const dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0);
dragData.offset = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top);
dragData.moveDocument = SelectionManager.SelectedDocuments()[0].props.moveDocument;
@@ -205,7 +199,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
document.removeEventListener("pointermove", this.onTitleMove);
document.removeEventListener("pointerup", this.onTitleUp);
DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(documentView => documentView.ContentDiv!), dragData, e.x, e.y, {
- handlers: { dragComplete: action(() => this._hidden = this.Interacting = false) },
+ dragComplete: action(e => this._hidden = this.Interacting = false),
hideSource: true
});
e.stopPropagation();
@@ -239,11 +233,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
e.stopPropagation();
if (e.button === 0) {
const recent = Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc) as Doc;
- SelectionManager.SelectedDocuments().map(dv => {
+ const selected = SelectionManager.SelectedDocuments().slice();
+ SelectionManager.DeselectAll();
+ selected.map(dv => {
recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true);
dv.props.removeDocument && dv.props.removeDocument(dv.props.Document);
});
- SelectionManager.DeselectAll();
document.removeEventListener("pointermove", this.onCloseMove);
document.removeEventListener("pointerup", this.onCloseUp);
}
@@ -256,8 +251,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
this._downX = e.pageX;
this._downY = e.pageY;
this._removeIcon = false;
- let selDoc = SelectionManager.SelectedDocuments()[0];
- let selDocPos = selDoc.props.ScreenToLocalTransform().scale(selDoc.props.ContentScaling()).inverse().transformPoint(0, 0);
+ const selDoc = SelectionManager.SelectedDocuments()[0];
+ const selDocPos = selDoc.props.ScreenToLocalTransform().scale(selDoc.props.ContentScaling()).inverse().transformPoint(0, 0);
this._minimizedX = selDocPos[0] + 12;
this._minimizedY = selDocPos[1] + 12;
document.removeEventListener("pointermove", this.onMinimizeMove);
@@ -272,12 +267,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
e.stopPropagation();
if (Math.abs(e.pageX - this._downX) > Utils.DRAG_THRESHOLD ||
Math.abs(e.pageY - this._downY) > Utils.DRAG_THRESHOLD) {
- let selDoc = SelectionManager.SelectedDocuments()[0];
- let selDocPos = selDoc.props.ScreenToLocalTransform().scale(selDoc.props.ContentScaling()).inverse().transformPoint(0, 0);
- let snapped = Math.abs(e.pageX - selDocPos[0]) < 20 && Math.abs(e.pageY - selDocPos[1]) < 20;
+ const selDoc = SelectionManager.SelectedDocuments()[0];
+ const selDocPos = selDoc.props.ScreenToLocalTransform().scale(selDoc.props.ContentScaling()).inverse().transformPoint(0, 0);
+ const snapped = Math.abs(e.pageX - selDocPos[0]) < 20 && Math.abs(e.pageY - selDocPos[1]) < 20;
this._minimizedX = snapped ? selDocPos[0] + 4 : e.clientX;
this._minimizedY = snapped ? selDocPos[1] - 18 : e.clientY;
- let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
+ const selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
if (selectedDocs.length > 1) {
this._iconDoc = this._iconDoc ? this._iconDoc : this.createIcon(SelectionManager.SelectedDocuments(), CollectionView.LayoutString(""));
@@ -295,15 +290,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
if (e.button === 0) {
document.removeEventListener("pointermove", this.onMinimizeMove);
document.removeEventListener("pointerup", this.onMinimizeUp);
- let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
+ const selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
if (this._iconDoc && selectedDocs.length === 1 && this._removeIcon) {
selectedDocs[0].props.removeDocument && selectedDocs[0].props.removeDocument(this._iconDoc);
}
if (!this._removeIcon && selectedDocs.length === 1) { // if you click on the top-left button when just 1 doc is selected, then collapse it. not sure why we don't do it for multiple selections
this.getIconDoc(selectedDocs[0]).then(async icon => {
- let minimizedDoc = await Cast(selectedDocs[0].props.Document.minimizedDoc, Doc);
+ const minimizedDoc = await Cast(selectedDocs[0].props.Document.minimizedDoc, Doc);
if (minimizedDoc) {
- let scrpt = selectedDocs[0].props.ScreenToLocalTransform().scale(selectedDocs[0].props.ContentScaling()).inverse().transformPoint(
+ const scrpt = selectedDocs[0].props.ScreenToLocalTransform().scale(selectedDocs[0].props.ContentScaling()).inverse().transformPoint(
NumCast(minimizedDoc.x) - NumCast(selectedDocs[0].Document.x), NumCast(minimizedDoc.y) - NumCast(selectedDocs[0].Document.y));
SelectionManager.DeselectAll();
DocumentManager.Instance.animateBetweenPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs));
@@ -317,8 +312,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@undoBatch
@action createIcon = (selected: DocumentView[], layoutString: string): Doc => {
- let doc = selected[0].props.Document;
- let iconDoc = Docs.Create.IconDocument(layoutString);
+ const doc = selected[0].props.Document;
+ const iconDoc = Docs.Create.IconDocument(layoutString);
iconDoc.isButton = true;
IconBox.AutomaticTitle(iconDoc);
@@ -334,7 +329,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
@action
public getIconDoc = async (docView: DocumentView): Promise<Doc | undefined> => {
- let doc = docView.props.Document;
+ const doc = docView.props.Document;
let iconDoc: Doc | undefined = await Cast(doc.minimizedDoc, Doc);
if (!iconDoc || !DocumentManager.Instance.getDocumentView(iconDoc)) {
@@ -344,8 +339,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
return iconDoc;
}
moveIconDoc(iconDoc: Doc) {
- let selView = SelectionManager.SelectedDocuments()[0];
- let where = (selView.props.ScreenToLocalTransform()).scale(selView.props.ContentScaling()).
+ const selView = SelectionManager.SelectedDocuments()[0];
+ const where = (selView.props.ScreenToLocalTransform()).scale(selView.props.ContentScaling()).
transformPoint(this._minimizedX - 12, this._minimizedY - 12);
iconDoc.x = where[0] + NumCast(selView.props.Document.x);
iconDoc.y = where[1] + NumCast(selView.props.Document.y);
@@ -370,8 +365,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
dist = dist < 3 ? 0 : dist;
let usingRule = false;
SelectionManager.SelectedDocuments().map(dv => {
- let ruleProvider = dv.props.ruleProvider;
- let heading = NumCast(dv.props.Document.heading);
+ const ruleProvider = dv.props.ruleProvider;
+ const heading = NumCast(dv.props.Document.heading);
ruleProvider && heading && (Doc.GetProto(ruleProvider)["ruleRounding_" + heading] = `${Math.min(100, dist)}%`);
usingRule = usingRule || (ruleProvider && heading ? true : false);
});
@@ -419,8 +414,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
let dX = 0, dY = 0, dW = 0, dH = 0;
- let moveX = e.clientX - this._lastX; // e.movementX;
- let moveY = e.clientY - this._lastY; // e.movementY;
+ const moveX = e.clientX - this._lastX; // e.movementX;
+ const moveY = e.clientY - this._lastY; // e.movementY;
this._lastX = e.clientX;
this._lastY = e.clientY;
@@ -465,18 +460,18 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
- let doc = PositionDocument(element.props.Document);
- let layoutDoc = PositionDocument(Doc.Layout(element.props.Document));
+ const doc = PositionDocument(element.props.Document);
+ const layoutDoc = PositionDocument(Doc.Layout(element.props.Document));
let nwidth = layoutDoc.nativeWidth || 0;
let nheight = layoutDoc.nativeHeight || 0;
- let width = (layoutDoc.width || 0);
- let height = (layoutDoc.height || (nheight / nwidth * width));
- let scale = element.props.ScreenToLocalTransform().Scale * element.props.ContentScaling();
- let actualdW = Math.max(width + (dW * scale), 20);
- let actualdH = Math.max(height + (dH * scale), 20);
+ const width = (layoutDoc.width || 0);
+ const height = (layoutDoc.height || (nheight / nwidth * width));
+ const scale = element.props.ScreenToLocalTransform().Scale * element.props.ContentScaling();
+ const actualdW = Math.max(width + (dW * scale), 20);
+ const actualdH = Math.max(height + (dH * scale), 20);
doc.x = (doc.x || 0) + dX * (actualdW - width);
doc.y = (doc.y || 0) + dY * (actualdH - height);
- let fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight);
+ const fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight);
if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) {
layoutDoc.ignoreAspect = false;
layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0;
@@ -529,14 +524,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@computed
get selectionTitle(): string {
if (SelectionManager.SelectedDocuments().length === 1) {
- let selected = SelectionManager.SelectedDocuments()[0];
- let field = selected.props.Document[this._fieldKey];
- if (typeof field === "string") {
- return field;
+ const selected = SelectionManager.SelectedDocuments()[0];
+ if (this._titleControlString.startsWith("=")) {
+ return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ this: selected.props.Document }, console.log).result?.toString() || "";
}
- else if (typeof field === "number") {
- return field.toString();
+ if (this._titleControlString.startsWith("#")) {
+ return selected.props.Document[this._titleControlString.substring(1)]?.toString() || "-unset-";
}
+ return this._accumulatedTitle;
} else if (SelectionManager.SelectedDocuments().length > 1) {
return "-multiple-";
}
@@ -555,12 +550,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
}
render() {
- var bounds = this.Bounds;
- let seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
+ const bounds = this.Bounds;
+ const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
if (SelectionManager.GetIsDragging() || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
return (null);
}
- let minimizeIcon = (
+ const minimizeIcon = (
<div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown}>
{/* 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*/}
{SelectionManager.SelectedDocuments().length === 1 ? IconBox.DocumentIcon(StrCast(SelectionManager.SelectedDocuments()[0].props.Document.layout, "...")) : "..."}
@@ -597,7 +592,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
{minimizeIcon}
{this._edtingTitle ?
- <input ref={this._keyinput} className="title" type="text" name="dynbox" value={this._title} onBlur={this.titleBlur} onChange={this.titleChanged} onKeyPress={this.titleEntered} /> :
+ <input ref={this._keyinput} className="title" type="text" name="dynbox" value={this._accumulatedTitle} onBlur={e => this.titleBlur(true)} onChange={this.titleChanged} onKeyPress={this.titleEntered} /> :
<div className="title" onPointerDown={this.onTitleDown} ><span>{`${this.selectionTitle}`}</span></div>}
<div className="documentDecorations-closeButton" title="Close Document" onPointerDown={this.onCloseDown}>
<FontAwesomeIcon className="documentdecorations-times" icon={faTimes} size="lg" />
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 8e86f58ee..54def38b5 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -10,7 +10,7 @@ export interface EditableProps {
/**
* Called to get the initial value for editing
* */
- GetValue(): string;
+ GetValue(): string | undefined;
/**
* Called to apply changes
@@ -21,7 +21,7 @@ export interface EditableProps {
OnFillDown?(value: string): void;
- OnTab?(): void;
+ OnTab?(shift?: boolean): void;
/**
* The contents to render when not editing
@@ -79,7 +79,7 @@ export class EditableView extends React.Component<EditableProps> {
if (e.key === "Tab") {
e.stopPropagation();
this.finalizeEdit(e.currentTarget.value, e.shiftKey);
- this.props.OnTab && this.props.OnTab();
+ this.props.OnTab && this.props.OnTab(e.shiftKey);
} else if (e.key === "Enter") {
e.stopPropagation();
if (!e.ctrlKey) {
@@ -108,8 +108,8 @@ export class EditableView extends React.Component<EditableProps> {
@action
private finalizeEdit(value: string, shiftDown: boolean) {
+ this._editing = false;
if (this.props.SetValue(value, shiftDown)) {
- this._editing = false;
this.props.isEditingCallback && this.props.isEditingCallback(false);
}
}
@@ -120,11 +120,13 @@ export class EditableView extends React.Component<EditableProps> {
@action
setIsFocused = (value: boolean) => {
+ const wasFocused = this._editing;
this._editing = value;
+ return wasFocused !== this._editing;
}
render() {
- if (this._editing) {
+ if (this._editing && this.props.GetValue() !== undefined) {
return this.props.autosuggestProps
? <Autosuggest
{...this.props.autosuggestProps.autosuggestProps}
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 8f397e331..979687ffb 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -12,6 +12,7 @@ import { Cast, PromiseValue } from "../../new_fields/Types";
import { ScriptField } from "../../new_fields/ScriptField";
import { InkingControl } from "./InkingControl";
import { InkTool } from "../../new_fields/InkField";
+import { DocumentView } from "./nodes/DocumentView";
const modifiers = ["control", "meta", "shift", "alt"];
type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
@@ -25,7 +26,7 @@ export default class KeyManager {
private router = new Map<string, KeyHandler>();
constructor() {
- let isMac = navigator.platform.toLowerCase().indexOf("mac") >= 0;
+ const isMac = navigator.platform.toLowerCase().indexOf("mac") >= 0;
// SHIFT CONTROL ALT META
this.router.set("0000", this.unmodified);
@@ -36,22 +37,22 @@ export default class KeyManager {
}
public handle = async (e: KeyboardEvent) => {
- let keyname = e.key && e.key.toLowerCase();
+ const keyname = e.key && e.key.toLowerCase();
this.handleGreedy(keyname);
if (modifiers.includes(keyname)) {
return;
}
- let bit = (value: boolean) => value ? "1" : "0";
- let modifierIndex = bit(e.shiftKey) + bit(e.ctrlKey) + bit(e.altKey) + bit(e.metaKey);
+ const bit = (value: boolean) => value ? "1" : "0";
+ const modifierIndex = bit(e.shiftKey) + bit(e.ctrlKey) + bit(e.altKey) + bit(e.metaKey);
- let handleConstrained = this.router.get(modifierIndex);
+ const handleConstrained = this.router.get(modifierIndex);
if (!handleConstrained) {
return;
}
- let control = await handleConstrained(keyname, e);
+ const control = await handleConstrained(keyname, e);
control.stopPropagation && e.stopPropagation();
control.preventDefault && e.preventDefault();
@@ -65,7 +66,7 @@ export default class KeyManager {
private unmodified = action((keyname: string, e: KeyboardEvent) => {
switch (keyname) {
case "escape":
- let main = MainView.Instance;
+ const main = MainView.Instance;
InkingControl.Instance.switchTool(InkTool.None);
if (main.isPointerDown) {
DragManager.AbortDrag();
@@ -89,8 +90,8 @@ export default class KeyManager {
}
UndoManager.RunInBatch(() => {
SelectionManager.SelectedDocuments().map(docView => {
- let doc = docView.props.Document;
- let remove = docView.props.removeDocument;
+ const doc = docView.props.Document;
+ const remove = docView.props.removeDocument;
remove && remove(doc);
});
}, "delete");
@@ -108,7 +109,7 @@ export default class KeyManager {
let preventDefault = false;
switch (keyname) {
- case " ":
+ case "~":
DictationManager.Controls.listen({ useOverlay: true, tryExecute: true });
stopPropagation = true;
preventDefault = true;
@@ -121,10 +122,17 @@ export default class KeyManager {
}
private alt = action((keyname: string) => {
- let stopPropagation = true;
- let preventDefault = true;
+ const stopPropagation = true;
+ const preventDefault = true;
switch (keyname) {
+ case "f":
+ const dv = SelectionManager.SelectedDocuments()?.[0];
+ if (dv) {
+ const ex = dv.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[0];
+ const ey = dv.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[1];
+ DocumentView.FloatDoc(dv, ex, ey);
+ }
// case "n":
// let toggle = MainView.Instance.addMenuToggle.current!;
// toggle.checked = !toggle.checked;
@@ -190,7 +198,7 @@ export default class KeyManager {
}
break;
case "o":
- let target = SelectionManager.SelectedDocuments()[0];
+ const target = SelectionManager.SelectedDocuments()[0];
target && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(target);
break;
case "r":
@@ -220,12 +228,12 @@ export default class KeyManager {
});
async printClipboard() {
- let text: string = await navigator.clipboard.readText();
+ const text: string = await navigator.clipboard.readText();
}
private ctrl_shift = action((keyname: string) => {
- let stopPropagation = true;
- let preventDefault = true;
+ const stopPropagation = true;
+ const preventDefault = true;
switch (keyname) {
case "z":
diff --git a/src/client/views/InkSelectDecorations.tsx b/src/client/views/InkSelectDecorations.tsx
index d40df9b75..3ad50762d 100644
--- a/src/client/views/InkSelectDecorations.tsx
+++ b/src/client/views/InkSelectDecorations.tsx
@@ -29,10 +29,10 @@ export default class InkSelectDecorations extends Touchable {
@computed
get Bounds(): { x: number, y: number, b: number, r: number } {
- let left = Number.MAX_VALUE;
- let top = Number.MAX_VALUE;
- let right = -Number.MAX_VALUE;
- let bottom = -Number.MAX_VALUE;
+ const left = Number.MAX_VALUE;
+ const top = Number.MAX_VALUE;
+ const right = -Number.MAX_VALUE;
+ const bottom = -Number.MAX_VALUE;
this._selectedInkNodes.forEach((value: PointData, key: string) => {
// value.pathData.map(val => {
// left = Math.min(val.x, left);
@@ -45,7 +45,7 @@ export default class InkSelectDecorations extends Touchable {
}
render() {
- let bounds = this.Bounds;
+ const bounds = this.Bounds;
return <div style={{
top: bounds.y, left: bounds.x,
height: bounds.b - bounds.y,
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 75faa9641..e33f193b8 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -1,5 +1,5 @@
import { action, computed, observable } from "mobx";
-import { ColorResult } from 'react-color';
+import { ColorState } from 'react-color';
import { Doc } from "../../new_fields/Doc";
import { InkTool } from "../../new_fields/InkField";
import { List } from "../../new_fields/List";
@@ -35,16 +35,16 @@ export class InkingControl {
}
@undoBatch
- switchColor = action((color: ColorResult): void => {
+ switchColor = action((color: ColorState): void => {
this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff");
if (InkingControl.Instance.selectedTool === InkTool.None) {
- let selected = SelectionManager.SelectedDocuments();
- let oldColors = selected.map(view => {
- let targetDoc = view.props.Document.dragFactory instanceof Doc ? view.props.Document.dragFactory :
+ const selected = SelectionManager.SelectedDocuments();
+ const oldColors = selected.map(view => {
+ const targetDoc = view.props.Document.dragFactory instanceof Doc ? view.props.Document.dragFactory :
view.props.Document.layout instanceof Doc ? view.props.Document.layout :
view.props.Document.isTemplateField ? view.props.Document : Doc.GetProto(view.props.Document);
- let sel = window.getSelection();
+ const sel = window.getSelection();
if (StrCast(targetDoc.layout).indexOf("FormattedTextBox") !== -1 && (!sel || sel.toString() !== "")) {
targetDoc.color = this._selectedColor;
return {
@@ -52,24 +52,24 @@ export class InkingControl {
previous: StrCast(targetDoc.color)
};
}
- let oldColor = StrCast(targetDoc.backgroundColor);
+ const oldColor = StrCast(targetDoc.backgroundColor);
let matchedColor = this._selectedColor;
const cvd = view.props.ContainingCollectionDoc;
let ruleProvider = view.props.ruleProvider;
if (cvd) {
if (!cvd.colorPalette) {
- let defaultPalette = ["rg(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
+ const defaultPalette = ["rg(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
"rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
- let colorPalette = Cast(cvd.colorPalette, listSpec("string"));
+ const colorPalette = Cast(cvd.colorPalette, listSpec("string"));
if (!colorPalette) cvd.colorPalette = new List<string>(defaultPalette);
}
- let cp = Cast(cvd.colorPalette, listSpec("string")) as string[];
+ const cp = Cast(cvd.colorPalette, listSpec("string")) as string[];
let closest = 0;
let dist = 10000000;
- let ccol = Utils.fromRGBAstr(StrCast(targetDoc.backgroundColor));
+ const ccol = Utils.fromRGBAstr(StrCast(targetDoc.backgroundColor));
for (let i = 0; i < cp.length; i++) {
- let cpcol = Utils.fromRGBAstr(cp[i]);
- let d = Math.sqrt((ccol.r - cpcol.r) * (ccol.r - cpcol.r) + (ccol.b - cpcol.b) * (ccol.b - cpcol.b) + (ccol.g - cpcol.g) * (ccol.g - cpcol.g));
+ const cpcol = Utils.fromRGBAstr(cp[i]);
+ const d = Math.sqrt((ccol.r - cpcol.r) * (ccol.r - cpcol.r) + (ccol.b - cpcol.b) * (ccol.b - cpcol.b) + (ccol.g - cpcol.g) * (ccol.g - cpcol.g));
if (d < dist) {
dist = d;
closest = i;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index a2e9f0e55..a413eebc9 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -14,7 +14,7 @@ type InkDocument = makeInterface<[typeof documentSchema]>;
const InkDocument = makeInterface(documentSchema);
export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color?: string, width?: number) {
- let pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, "");
+ const pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, "");
return (
<polyline
points={pts}
@@ -35,25 +35,25 @@ export class InkingStroke extends DocExtendableComponent<FieldViewProps, InkDocu
@computed get PanelHeight() { return this.props.PanelHeight(); }
render() {
- let data: InkData = Cast(this.Document.data, InkField)?.inkData ?? [];
- let xs = data.map(p => p.X);
- let ys = data.map(p => p.Y);
- let left = Math.min(...xs);
- let top = Math.min(...ys);
- let right = Math.max(...xs);
- let bottom = Math.max(...ys);
- let points = CreatePolyline(data, 0, 0, this.Document.color, this.Document.strokeWidth);
- let width = right - left;
- let height = bottom - top;
- let scaleX = this.PanelWidth / width;
- let scaleY = this.PanelHeight / height;
+ const data: InkData = Cast(this.Document.data, InkField)?.inkData ?? [];
+ const xs = data.map(p => p.X);
+ const ys = data.map(p => p.Y);
+ const left = Math.min(...xs);
+ const top = Math.min(...ys);
+ const right = Math.max(...xs);
+ const bottom = Math.max(...ys);
+ const points = CreatePolyline(data, 0, 0, this.Document.color, this.Document.strokeWidth);
+ const width = right - left;
+ const height = bottom - top;
+ const scaleX = this.PanelWidth / width;
+ const scaleY = this.PanelHeight / height;
return (
<svg width={width} height={height} style={{
transformOrigin: "top left",
transform: `translate(${left}px, ${top}px) scale(${scaleX}, ${scaleY})`,
mixBlendMode: this.Document.tool === InkTool.Highlighter ? "multiply" : "unset",
pointerEvents: "all"
- }} onTouchStart={this.onTouchStart}>
+ }}>
{points}
</svg>
);
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 3b66160fb..4709e7ef2 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -13,12 +13,12 @@ body {
left: 0;
}
-div {
- user-select: none;
- -moz-user-select: none;
- -webkit-user-select: none;
- -ms-user-select: none;
-}
+// div {
+// user-select: none;
+// -moz-user-select: none;
+// -webkit-user-select: none;
+// -ms-user-select: none;
+// }
.jsx-parser {
@@ -38,7 +38,7 @@ p {
::-webkit-scrollbar {
-webkit-appearance: none;
height: 8px;
- width: 20px;
+ width: 8px;
}
::-webkit-scrollbar-thumb {
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 0ee30f117..4c8c95529 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -7,11 +7,18 @@
width: 100%;
}
+.mainContent-div {
+ position: relative;
+ width:100%;
+ height:100%;
+}
+
// add nodes menu. Note that the + button is actually an input label, not an actual button.
.mainView-docButtons {
position: absolute;
bottom: 20px;
- left: 250px;
+ left: calc(100% + 5px);
+ z-index: 1;
}
#mainView-container {
@@ -27,13 +34,13 @@
width: 100%;
height: 100%;
position: absolute;
+ display: flex;
}
.mainView-flyoutContainer {
display: flex;
flex-direction: column;
- position: absolute;
- width: 100%;
+ position: relative;
height: 100%;
.documentView-node-topmost {
@@ -52,16 +59,18 @@
.mainView-logout {
position: absolute;
- right: 0;
- bottom: 0;
+ right: 5;
+ bottom: 5;
font-size: 8px;
}
.mainView-libraryFlyout {
height: 100%;
+ width:100%;
position: absolute;
display: flex;
flex-direction: column;
+ z-index: 2;
}
.mainView-expandFlyoutButton {
@@ -73,13 +82,15 @@
.mainView-libraryHandle {
width: 20px;
+ left: calc(100% - 10px);
height: 40px;
top: 50%;
border: 1px solid black;
border-radius: 5px;
position: absolute;
- z-index: 1;
+ z-index: 2;
touch-action: none;
+ cursor: ew-resize;
}
.mainView-workspace {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index e6dd2fcad..a1196ee1c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -15,8 +15,7 @@ import { List } from '../../new_fields/List';
import { listSpec } from '../../new_fields/Schema';
import { Cast, FieldValue, StrCast } from '../../new_fields/Types';
import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
-import { RouteStore } from '../../server/RouteStore';
-import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, Utils } from '../../Utils';
+import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, Utils, emptyPath } from '../../Utils';
import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
import { Docs, DocumentOptions } from '../documents/Documents';
@@ -40,6 +39,7 @@ import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsM
import InkSelectDecorations from './InkSelectDecorations';
import { Scripting } from '../util/Scripting';
import { AudioBox } from './nodes/AudioBox';
+import { TraceMobx } from '../../new_fields/util';
@observer
export class MainView extends React.Component {
@@ -57,14 +57,15 @@ export class MainView extends React.Component {
@computed private get userDoc() { return CurrentUserUtils.UserDocument; }
@computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
@computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
+ @computed public get sidebarButtonsDoc() { return Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc; }
public isPointerDown = false;
componentWillMount() {
- var tag = document.createElement('script');
+ const tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
- var firstScriptTag = document.getElementsByTagName('script')[0];
+ const firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag);
window.removeEventListener("keydown", KeyManager.Instance.handle);
window.addEventListener("keydown", KeyManager.Instance.handle);
@@ -82,10 +83,10 @@ export class MainView extends React.Component {
this._urlState = HistoryUtil.parseUrl(window.location) || {} as any;
// causes errors to be generated when modifying an observable outside of an action
configure({ enforceActions: "observed" });
- if (window.location.pathname !== RouteStore.home) {
- let pathname = window.location.pathname.substr(1).split("/");
+ if (window.location.pathname !== "/home") {
+ const pathname = window.location.pathname.substr(1).split("/");
if (pathname.length > 1) {
- let type = pathname[0];
+ const type = pathname[0];
if (type === "doc") {
CurrentUserUtils.MainDocId = pathname[1];
if (!this.userDoc) {
@@ -159,7 +160,7 @@ export class MainView extends React.Component {
initAuthenticationRouters = async () => {
// Load the user's active workspace, or create a new one if initial session after signup
- let received = CurrentUserUtils.MainDocId;
+ const received = CurrentUserUtils.MainDocId;
if (received && !this.userDoc) {
reaction(
() => CurrentUserUtils.GuestTarget,
@@ -176,7 +177,7 @@ export class MainView extends React.Component {
}),
);
}
- let doc = this.userDoc && await Cast(this.userDoc.activeWorkspace, Doc);
+ const doc = this.userDoc && await Cast(this.userDoc.activeWorkspace, Doc);
if (doc) {
this.openWorkspace(doc);
} else {
@@ -187,35 +188,33 @@ export class MainView extends React.Component {
@action
createNewWorkspace = async (id?: string) => {
- let freeformOptions: DocumentOptions = {
+ const workspaces = Cast(this.userDoc.workspaces, Doc) as Doc;
+ const workspaceCount = DocListCast(workspaces.data).length + 1;
+ const freeformOptions: DocumentOptions = {
x: 0,
y: 400,
width: this._panelWidth * .7,
height: this._panelHeight,
- title: "My Blank Collection",
+ title: "Collection " + workspaceCount,
backgroundColor: "white"
};
- let workspaces: FieldResult<Doc>;
- let freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
- var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] };
- let mainDoc = Docs.Create.DockDocument([freeformDoc], JSON.stringify(dockingLayout), {}, id);
- if (this.userDoc && ((workspaces = Cast(this.userDoc.workspaces, Doc)) instanceof Doc)) {
- Doc.AddDocToList(workspaces, "data", mainDoc);
- mainDoc.title = `Workspace ${DocListCast(workspaces.data).length}`;
- }
+ const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
+ Doc.AddDocToList(Doc.GetProto(CurrentUserUtils.UserDocument.documents as Doc), "data", freeformDoc);
+ const mainDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().documents as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
+ Doc.AddDocToList(workspaces, "data", mainDoc);
// bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
setTimeout(() => this.openWorkspace(mainDoc), 0);
}
@action
- openWorkspace = async (doc: Doc, fromHistory = false) => {
+ openWorkspace = (doc: Doc, fromHistory = false) => {
CurrentUserUtils.MainDocId = doc[Id];
if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest workspace
!("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc);
}
- let state = this._urlState;
+ const state = this._urlState;
if (state.sharing === true && !this.userDoc) {
DocServer.Control.makeReadOnly();
} else {
@@ -263,37 +262,40 @@ export class MainView extends React.Component {
getPHeight = () => this._panelHeight;
getContentsHeight = () => this._panelHeight - this._buttonBarHeight;
+ @computed get mainDocView() {
+ return <DocumentView Document={this.mainContainer!}
+ DataDoc={undefined}
+ LibraryPath={emptyPath}
+ addDocument={undefined}
+ addDocTab={this.addDocTabFunc}
+ pinToPres={emptyFunction}
+ onClick={undefined}
+ ruleProvider={undefined}
+ removeDocument={undefined}
+ ScreenToLocalTransform={Transform.Identity}
+ ContentScaling={returnOne}
+ PanelWidth={this.getPWidth}
+ PanelHeight={this.getPHeight}
+ renderDepth={0}
+ backgroundColor={returnEmptyString}
+ focus={emptyFunction}
+ parentActive={returnTrue}
+ whenActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ zoomToScale={emptyFunction}
+ getScale={returnOne}
+ />;
+ }
@computed get dockingContent() {
+ TraceMobx();
const mainContainer = this.mainContainer;
- let flyoutWidth = this.flyoutWidth; // bcz: need to be here because Measure messes with observables.
- let flyoutTranslate = this._flyoutTranslate;
+ const width = this.flyoutWidth;
return <Measure offset onResize={this.onResize}>
{({ measureRef }) =>
- <div ref={measureRef} id="mainContent-div" style={{ width: `calc(100% - ${flyoutTranslate ? flyoutWidth : 0}px`, transform: `translate(${flyoutTranslate ? flyoutWidth : 0}px, 0px)` }} onDrop={this.onDrop}>
- {!mainContainer ? (null) :
- <DocumentView Document={mainContainer}
- DataDoc={undefined}
- addDocument={undefined}
- addDocTab={this.addDocTabFunc}
- pinToPres={emptyFunction}
- onClick={undefined}
- ruleProvider={undefined}
- removeDocument={undefined}
- ScreenToLocalTransform={Transform.Identity}
- ContentScaling={returnOne}
- PanelWidth={this.getPWidth}
- PanelHeight={this.getPHeight}
- renderDepth={0}
- backgroundColor={returnEmptyString}
- focus={emptyFunction}
- parentActive={returnTrue}
- whenActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
- zoomToScale={emptyFunction}
- getScale={returnOne}
- />}
+ <div ref={measureRef} className="mainContent-div" onDrop={this.onDrop} style={{ width: `calc(100% - ${width}px)` }}>
+ {!mainContainer ? (null) : this.mainDocView}
</div>
}
</Measure>;
@@ -313,10 +315,11 @@ export class MainView extends React.Component {
@action
pointerOverDragger = () => {
- if (this.flyoutWidth === 0) {
- this.flyoutWidth = 250;
- this._flyoutTranslate = false;
- }
+ // if (this.flyoutWidth === 0) {
+ // this.flyoutWidth = 250;
+ // this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30;
+ // this._flyoutTranslate = false;
+ // }
}
@action
@@ -330,41 +333,37 @@ export class MainView extends React.Component {
@action
onPointerMove = (e: PointerEvent) => {
this.flyoutWidth = Math.max(e.clientX, 0);
+ this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30;
}
@action
onPointerUp = (e: PointerEvent) => {
if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4) {
- this.flyoutWidth = this.flyoutWidth < 5 ? 250 : 0;
+ this.flyoutWidth = this.flyoutWidth < 15 ? 250 : 0;
+ this.flyoutWidth && (this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30);
}
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
}
flyoutWidthFunc = () => this.flyoutWidth;
- addDocTabFunc = (doc: Doc, data: Opt<Doc>, where: string) => {
- if (where === "close") {
- return CollectionDockingView.CloseRightSplit(doc);
- }
- if (doc.dockingConfig) {
- this.openWorkspace(doc);
- return true;
- } else {
- return CollectionDockingView.AddRightSplit(doc, undefined);
- }
+ addDocTabFunc = (doc: Doc, data: Opt<Doc>, where: string, libraryPath?: Doc[]): boolean => {
+ return where === "close" ? CollectionDockingView.CloseRightSplit(doc) :
+ doc.dockingConfig ? this.openWorkspace(doc) :
+ CollectionDockingView.AddRightSplit(doc, undefined, undefined, libraryPath);
}
mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1);
@computed get flyout() {
- let sidebarContent = this.userDoc && this.userDoc.sidebarContainer;
+ const sidebarContent = this.userDoc && this.userDoc.sidebarContainer;
if (!(sidebarContent instanceof Doc)) {
return (null);
}
- let sidebarButtonsDoc = Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc;
- sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30;
+ const sidebarButtonsDoc = Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc;
return <div className="mainView-flyoutContainer" >
<div className="mainView-tabButtons" style={{ height: `${this._buttonBarHeight}px` }}>
<DocumentView
Document={sidebarButtonsDoc}
DataDoc={undefined}
+ LibraryPath={emptyPath}
addDocument={undefined}
addDocTab={this.addDocTabFunc}
pinToPres={emptyFunction}
@@ -387,10 +386,11 @@ export class MainView extends React.Component {
getScale={returnOne}>
</DocumentView>
</div>
- <div style={{ position: "relative", height: `calc(100% - ${this._buttonBarHeight}px)`, width: "100%", overflow: "auto" }}>
+ <div className="mainView-contentArea" style={{ position: "relative", height: `calc(100% - ${this._buttonBarHeight}px)`, width: "100%", overflow: "visible" }}>
<DocumentView
Document={sidebarContent}
DataDoc={undefined}
+ LibraryPath={emptyPath}
addDocument={undefined}
addDocTab={this.addDocTabFunc}
pinToPres={emptyFunction}
@@ -412,33 +412,32 @@ export class MainView extends React.Component {
zoomToScale={emptyFunction}
getScale={returnOne}>
</DocumentView>
- <button className="mainView-logout" key="logout" onClick={() => window.location.assign(Utils.prepend(RouteStore.logout))}>
+ <button className="mainView-logout" key="logout" onClick={() => window.location.assign(Utils.prepend("/logout"))}>
{CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}
</button>
- </div></div>;
+ </div>
+ {this.docButtons}
+ </div>;
}
@computed get mainContent() {
const sidebar = this.userDoc && this.userDoc.sidebarContainer;
return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
<div className="mainView-mainContent" >
- <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger}>
- <div className="mainView-libraryHandle"
- style={{ cursor: "ew-resize", left: `${(this.flyoutWidth * (this._flyoutTranslate ? 1 : 0)) - 10}px`, backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }}
- onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}>
+ <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
+ <div className="mainView-libraryHandle" onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}
+ style={{ backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }} >
<span title="library View Dragger" style={{
width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw",
- height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh",
+ //height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh",
position: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "absolute" : "fixed",
top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "0"
}} />
</div>
<div className="mainView-libraryFlyout" style={{
- width: `${this.flyoutWidth}px`,
- zIndex: 1,
- transformOrigin: this._flyoutTranslate ? "" : "left center",
+ //transformOrigin: this._flyoutTranslate ? "" : "left center",
transition: this._flyoutTranslate ? "" : "width .5s",
- transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`,
+ //transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`,
boxShadow: this._flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.8vw"
}}>
{this.flyout}
@@ -451,7 +450,8 @@ export class MainView extends React.Component {
public static expandFlyout = action(() => {
MainView.Instance._flyoutTranslate = true;
- MainView.Instance.flyoutWidth = 250;
+ MainView.Instance.flyoutWidth = (MainView.Instance.flyoutWidth || 250);
+ MainView.Instance.sidebarButtonsDoc.columnWidth = MainView.Instance.flyoutWidth / 3 - 30;
});
@computed get expandButton() {
@@ -460,21 +460,22 @@ export class MainView extends React.Component {
addButtonDoc = (doc: Doc) => Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc);
remButtonDoc = (doc: Doc) => Doc.RemoveDocFromList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc);
- moveButtonDoc = (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
+ moveButtonDoc = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
buttonBarXf = () => {
if (!this._docBtnRef.current) return Transform.Identity();
- let { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
+ const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
return new Transform(-translateX, -translateY, 1 / scale);
}
@computed get docButtons() {
if (CurrentUserUtils.UserDocument?.expandingButtons instanceof Doc) {
return <div className="mainView-docButtons" ref={this._docBtnRef}
- style={{ left: (this._flyoutTranslate ? this.flyoutWidth : 0) + 20, height: !CurrentUserUtils.UserDocument.expandingButtons.isExpanded ? "42px" : undefined }} >
+ style={{ height: !CurrentUserUtils.UserDocument.expandingButtons.isExpanded ? "42px" : undefined }} >
<MainViewNotifs />
<CollectionLinearView
Document={CurrentUserUtils.UserDocument.expandingButtons}
DataDoc={undefined}
+ LibraryPath={emptyPath}
fieldKey={"data"}
annotationsKey={""}
select={emptyFunction}
@@ -513,7 +514,6 @@ export class MainView extends React.Component {
{this.mainContent}
<PreviewCursor />
<ContextMenu />
- {this.docButtons}
<PDFMenu />
<MarqueeOptionsMenu />
<OverlayView />
diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx
index 221a0260a..9198fe3e3 100644
--- a/src/client/views/MainViewModal.tsx
+++ b/src/client/views/MainViewModal.tsx
@@ -14,9 +14,9 @@ export interface MainViewOverlayProps {
export default class MainViewModal extends React.Component<MainViewOverlayProps> {
render() {
- let p = this.props;
- let dialogueOpacity = p.dialogueBoxDisplayedOpacity || 1;
- let overlayOpacity = p.overlayDisplayedOpacity || 0.4;
+ const p = this.props;
+ const dialogueOpacity = p.dialogueBoxDisplayedOpacity || 1;
+ const overlayOpacity = p.overlayDisplayedOpacity || 0.4;
return !p.isDisplayed ? (null) : (
<div style={{ pointerEvents: p.isDisplayed ? p.interactive ? "all" : "none" : "none" }}>
<div
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index 41453f8b2..243cdb8f6 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -6,7 +6,7 @@ import { KeyValueBox } from './nodes/KeyValueBox';
import { Doc, Field, DocListCastAsync } from '../../new_fields/Doc';
import * as Autosuggest from 'react-autosuggest';
import { undoBatch } from '../util/UndoManager';
-import { emptyFunction } from '../../Utils';
+import { emptyFunction, emptyPath } from '../../Utils';
export type DocLike = Doc | Doc[] | Promise<Doc> | Promise<Doc[]>;
export interface MetadataEntryProps {
@@ -99,8 +99,8 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
} else {
let childSuccess = true;
if (this._addChildren) {
- for (let document of doc) {
- let collectionChildren = await DocListCastAsync(document.data);
+ for (const document of doc) {
+ const collectionChildren = await DocListCastAsync(document.data);
if (collectionChildren) {
childSuccess = collectionChildren.every(c => KeyValueBox.ApplyKVPScript(c, this._currentKey, script));
}
@@ -194,6 +194,7 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
);
}
+ _ref = React.createRef<HTMLInputElement>();
render() {
return (
<div className="metadataEntry-outerDiv">
@@ -201,14 +202,14 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
Key:
<Autosuggest inputProps={{ value: this._currentKey, onChange: this.onKeyChange }}
getSuggestionValue={this.getSuggestionValue}
- suggestions={[]}
+ suggestions={emptyPath}
alwaysRenderSuggestions={false}
renderSuggestion={this.renderSuggestion}
onSuggestionsFetchRequested={emptyFunction}
onSuggestionsClearRequested={emptyFunction}
ref={this.autosuggestRef} />
Value:
- <input className="metadataEntry-input" value={this._currentValue} onChange={this.onValueChange} onKeyDown={this.onValueKeyDown} />
+ <input className="metadataEntry-input" ref={this._ref} value={this._currentValue} onClick={e => this._ref.current!.focus()} onChange={this.onValueChange} onKeyDown={this.onValueKeyDown} />
{this.considerChildOptions}
</div>
<div className="metadataEntry-keys" >
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 9869e24d1..350a75d29 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,7 +1,7 @@
import * as React from "react";
import { observer } from "mobx-react";
import { observable, action, trace, computed } from "mobx";
-import { Utils, emptyFunction, returnOne, returnTrue, returnEmptyString, returnZero, returnFalse } from "../../Utils";
+import { Utils, emptyFunction, returnOne, returnTrue, returnEmptyString, returnZero, returnFalse, emptyPath } from "../../Utils";
import './OverlayView.scss';
import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
@@ -9,8 +9,6 @@ import { DocListCast, Doc } from "../../new_fields/Doc";
import { Id } from "../../new_fields/FieldSymbols";
import { DocumentView } from "./nodes/DocumentView";
import { Transform } from "../util/Transform";
-import { CollectionFreeFormDocumentView } from "./nodes/CollectionFreeFormDocumentView";
-import { DocumentContentsView } from "./nodes/DocumentContentsView";
import { NumCast } from "../../new_fields/Types";
import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
@@ -148,7 +146,7 @@ export class OverlayView extends React.Component {
return CurrentUserUtils.UserDocument.overlays instanceof Doc && DocListCast(CurrentUserUtils.UserDocument.overlays.data).map(d => {
d.inOverlay = true;
let offsetx = 0, offsety = 0;
- let onPointerMove = action((e: PointerEvent) => {
+ const onPointerMove = action((e: PointerEvent) => {
if (e.buttons === 1) {
d.x = e.clientX + offsetx;
d.y = e.clientY + offsety;
@@ -156,14 +154,14 @@ export class OverlayView extends React.Component {
e.preventDefault();
}
});
- let onPointerUp = action((e: PointerEvent) => {
+ const onPointerUp = action((e: PointerEvent) => {
document.removeEventListener("pointermove", onPointerMove);
document.removeEventListener("pointerup", onPointerUp);
e.stopPropagation();
e.preventDefault();
});
- let onPointerDown = (e: React.PointerEvent) => {
+ const onPointerDown = (e: React.PointerEvent) => {
offsetx = NumCast(d.x) - e.clientX;
offsety = NumCast(d.y) - e.clientY;
e.stopPropagation();
@@ -174,6 +172,7 @@ export class OverlayView extends React.Component {
return <div className="overlayView-doc" key={d[Id]} onPointerDown={onPointerDown} style={{ transform: `translate(${d.x}px, ${d.y}px)`, display: d.isMinimized ? "none" : "" }}>
<DocumentView
Document={d}
+ LibraryPath={emptyPath}
ChromeHeight={returnZero}
// isSelected={returnFalse}
// select={emptyFunction}
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 136a272ab..9706d0f99 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -1,13 +1,11 @@
-import { action, observable, runInAction, trace } from 'mobx';
+import { action, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import "normalize.css";
import * as React from 'react';
import "./PreviewCursor.scss";
import { Docs } from '../documents/Documents';
-// import { Transform } from 'prosemirror-transform';
import { Doc } from '../../new_fields/Doc';
import { Transform } from "../util/Transform";
-import { TraceMobx } from '../../new_fields/util';
@observer
export class PreviewCursor extends React.Component<{}> {
@@ -24,64 +22,53 @@ export class PreviewCursor extends React.Component<{}> {
}
paste = (e: ClipboardEvent) => {
- if (PreviewCursor.Visible) {
- if (e.clipboardData) {
- let newPoint = PreviewCursor._getTransform().transformPoint(PreviewCursor._clickPoint[0], PreviewCursor._clickPoint[1]);
- runInAction(() => { PreviewCursor.Visible = false; });
+ if (PreviewCursor.Visible && e.clipboardData) {
+ const newPoint = PreviewCursor._getTransform().transformPoint(PreviewCursor._clickPoint[0], PreviewCursor._clickPoint[1]);
+ runInAction(() => PreviewCursor.Visible = false);
-
- if (e.clipboardData.getData("text/plain") !== "") {
-
- // tests for youtube and makes video document
- if (e.clipboardData.getData("text/plain").indexOf("www.youtube.com/watch") !== -1) {
- const url = e.clipboardData.getData("text/plain").replace("youtube.com/watch?v=", "youtube.com/embed/");
- PreviewCursor._addDocument(Docs.Create.VideoDocument(url, {
- title: url, width: 400, height: 315,
- nativeWidth: 600, nativeHeight: 472.5,
- x: newPoint[0], y: newPoint[1]
- }));
- return;
- }
-
- // tests for URL and makes web document
- let re: any = /^https?:\/\//g;
- if (re.test(e.clipboardData.getData("text/plain"))) {
- const url = e.clipboardData.getData("text/plain");
- PreviewCursor._addDocument(Docs.Create.WebDocument(url, {
- title: url, width: 300, height: 300,
- // nativeWidth: 300, nativeHeight: 472.5,
- x: newPoint[0], y: newPoint[1]
- }));
- return;
- }
-
- // creates text document
- let newBox = Docs.Create.TextDocument({
- width: 200, height: 100,
- x: newPoint[0],
- y: newPoint[1],
- title: "-pasted text-"
- });
-
- newBox.proto!.autoHeight = true;
- PreviewCursor._addLiveTextDoc(newBox);
- return;
+ if (e.clipboardData.getData("text/plain") !== "") {
+ // tests for youtube and makes video document
+ if (e.clipboardData.getData("text/plain").indexOf("www.youtube.com/watch") !== -1) {
+ const url = e.clipboardData.getData("text/plain").replace("youtube.com/watch?v=", "youtube.com/embed/");
+ return PreviewCursor._addDocument(Docs.Create.VideoDocument(url, {
+ title: url, width: 400, height: 315,
+ nativeWidth: 600, nativeHeight: 472.5,
+ x: newPoint[0], y: newPoint[1]
+ }));
}
- //pasting in images
- if (e.clipboardData.getData("text/html") !== "" && e.clipboardData.getData("text/html").includes("<img src=")) {
- let re: any = /<img src="(.*?)"/g;
- let arr: any[] = re.exec(e.clipboardData.getData("text/html"));
- let img: Doc = Docs.Create.ImageDocument(
- arr[1], {
- width: 300, title: arr[1],
- x: newPoint[0],
- y: newPoint[1],
- });
- PreviewCursor._addDocument(img);
- return;
+ // tests for URL and makes web document
+ const re: any = /^https?:\/\//g;
+ if (re.test(e.clipboardData.getData("text/plain"))) {
+ const url = e.clipboardData.getData("text/plain");
+ return PreviewCursor._addDocument(Docs.Create.WebDocument(url, {
+ title: url, width: 500, height: 300,
+ // nativeWidth: 300, nativeHeight: 472.5,
+ x: newPoint[0], y: newPoint[1]
+ }));
}
+ // creates text document
+ return PreviewCursor._addLiveTextDoc(Docs.Create.TextDocument({
+ width: 500,
+ limitHeight: 400,
+ autoHeight: true,
+ x: newPoint[0],
+ y: newPoint[1],
+ title: "-pasted text-"
+ }));
+ }
+ //pasting in images
+ if (e.clipboardData.getData("text/html") !== "" && e.clipboardData.getData("text/html").includes("<img src=")) {
+ const re: any = /<img src="(.*?)"/g;
+ const arr: any[] = re.exec(e.clipboardData.getData("text/html"));
+
+ return PreviewCursor._addDocument(Docs.Create.ImageDocument(
+ arr[1], {
+ width: 300, title: arr[1],
+ x: newPoint[0],
+ y: newPoint[1],
+ }));
}
}
}
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 8ef9f3be6..ded2329b4 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -59,7 +59,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
onFocus = this.onFocus;
onBlur = this.onBlur;
}
- let params = <EditableView
+ const params = <EditableView
contents={""}
display={"block"}
maxHeight={72}
@@ -96,9 +96,9 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
}
}
// tslint:disable-next-line: no-unnecessary-callback-wrapper
- let params: string[] = [];
- let setParams = (p: string[]) => params.splice(0, params.length, ...p);
- let scriptingBox = <ScriptBox initialText={originalText} setParams={setParams} onCancel={overlayDisposer} onSave={(text, onError) => {
+ const params: string[] = [];
+ const setParams = (p: string[]) => params.splice(0, params.length, ...p);
+ const scriptingBox = <ScriptBox initialText={originalText} setParams={setParams} onCancel={overlayDisposer} onSave={(text, onError) => {
if (prewrapper) {
text = prewrapper + text + (postwrapper ? postwrapper : "");
}
@@ -113,7 +113,15 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
return;
}
- params.length && DragManager.StartButtonDrag([], text, "a script", {}, params, (button: Doc) => { }, clientX, clientY);
+ const div = document.createElement("div");
+ div.style.width = "90";
+ div.style.height = "20";
+ div.style.background = "gray";
+ div.style.position = "absolute";
+ div.style.display = "inline-block";
+ div.style.transform = `translate(${clientX}px, ${clientY}px)`;
+ div.innerHTML = "button";
+ params.length && DragManager.StartButtonDrag([div], text, doc.title + "-instance", {}, params, (button: Doc) => { }, clientX, clientY);
doc[fieldKey] = new ScriptField(script);
overlayDisposer();
diff --git a/src/client/views/TemplateMenu.scss b/src/client/views/TemplateMenu.scss
index 186d3ab0d..69bebe0e9 100644
--- a/src/client/views/TemplateMenu.scss
+++ b/src/client/views/TemplateMenu.scss
@@ -30,15 +30,15 @@
}
.template-list {
- position: absolute;
- top: 25px;
- left: 0px;
- width: max-content;
font-family: $sans-serif;
font-size: 12px;
background-color: $light-color-secondary;
padding: 2px 12px;
list-style: none;
+ position: relative;
+ display: inline-block;
+ height: 100%;
+ width: 100%;
.templateToggle, .chromeToggle {
text-align: left;
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index c65b338b4..10419ddb7 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -1,6 +1,5 @@
import { action, observable } from "mobx";
import { observer } from "mobx-react";
-import { DocumentManager } from "../util/DocumentManager";
import { DragManager } from "../util/DragManager";
import { SelectionManager } from "../util/SelectionManager";
import { undoBatch } from "../util/UndoManager";
@@ -10,7 +9,6 @@ import { Template, Templates } from "./Templates";
import React = require("react");
import { Doc } from "../../new_fields/Doc";
import { StrCast } from "../../new_fields/Types";
-import { emptyFunction } from "../../Utils";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -61,35 +59,13 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
toggleFloat = (e: React.ChangeEvent<HTMLInputElement>): void => {
SelectionManager.DeselectAll();
- let topDocView = this.props.docs[0];
- let topDoc = topDocView.props.Document;
- let xf = topDocView.props.ScreenToLocalTransform();
- let ex = e.target.clientLeft;
- let ey = e.target.clientTop;
- undoBatch(action(() => topDoc.z = topDoc.z ? 0 : 1))();
- if (e.target.checked) {
- setTimeout(() => {
- let newDocView = DocumentManager.Instance.getDocumentView(topDoc);
- if (newDocView) {
- let de = new DragManager.DocumentDragData([topDoc]);
- de.moveDocument = topDocView.props.moveDocument;
- let xf = newDocView.ContentDiv!.getBoundingClientRect();
- DragManager.StartDocumentDrag([newDocView.ContentDiv!], de, ex, ey, {
- offsetX: (ex - xf.left), offsetY: (ey - xf.top),
- handlers: { dragComplete: () => { }, },
- hideSource: false
- });
- }
- }, 10);
- } else if (topDocView.props.ContainingCollectionView) {
- let collView = topDocView.props.ContainingCollectionView;
- let [sx, sy] = xf.inverse().transformPoint(0, 0);
- let [x, y] = collView.props.ScreenToLocalTransform().transformPoint(sx, sy);
- topDoc.x = x;
- topDoc.y = y;
- }
+ const topDocView = this.props.docs[0];
+ const ex = e.target.getBoundingClientRect().left;
+ const ey = e.target.getBoundingClientRect().top;
+ DocumentView.FloatDoc(topDocView, ex, ey);
}
+
@undoBatch
@action
toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
@@ -122,7 +98,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
@action
toggleChrome = (): void => {
this.props.docs.map(dv => {
- let layout = Doc.Layout(dv.Document);
+ const layout = Doc.Layout(dv.Document);
layout.chromeStatus = (layout.chromeStatus !== "disabled" ? "disabled" : "enabled");
});
}
@@ -147,17 +123,14 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
document.removeEventListener("pointermove", this.onAliasButtonMoved);
document.removeEventListener("pointerup", this.onAliasButtonUp);
- let dragDocView = this.props.docs[0];
- let dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
+ const dragDocView = this.props.docs[0];
+ const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
dragData.embedDoc = true;
dragData.dropAction = "alias";
DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, {
offsetX: dragData.offset[0],
offsetY: dragData.offset[1],
- handlers: {
- dragComplete: action(emptyFunction),
- },
hideSource: false
});
}
@@ -165,21 +138,23 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
}
render() {
- let layout = Doc.Layout(this.props.docs[0].Document);
- let templateMenu: Array<JSX.Element> = [];
+ const layout = Doc.Layout(this.props.docs[0].Document);
+ const templateMenu: Array<JSX.Element> = [];
this.props.templates.forEach((checked, template) =>
templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />));
templateMenu.push(<OtherToggle key={"float"} name={"Float"} checked={this.props.docs[0].Document.z ? true : false} toggle={this.toggleFloat} />);
templateMenu.push(<OtherToggle key={"custom"} name={"Custom"} checked={StrCast(this.props.docs[0].Document.layoutKey, "layout") !== "layout"} toggle={this.toggleCustom} />);
templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={layout.chromeStatus !== "disabled"} toggle={this.toggleChrome} />);
return (
- <div className="templating-menu" onPointerDown={this.onAliasButtonDown}>
- <div title="Drag:(create alias). Tap:(modify layout)." className="templating-button" onClick={() => this.toggleTemplateActivity()}>+</div>
- <ul className="template-list" ref={this._dragRef} style={{ display: this._hidden ? "none" : "block" }}>
+ <Flyout anchorPoint={anchorPoints.RIGHT_TOP}
+ content={<ul className="template-list" ref={this._dragRef} style={{ display: this._hidden ? "none" : "block" }}>
{templateMenu}
{<button onClick={this.clearTemplates}>Restore Defaults</button>}
- </ul>
- </div>
+ </ul>}>
+ <div className="templating-menu" onPointerDown={this.onAliasButtonDown}>
+ <div title="Drag:(create alias). Tap:(modify layout)." className="templating-button" onClick={() => this.toggleTemplateActivity()}>+</div>
+ </div>
+ </Flyout>
);
}
} \ No newline at end of file
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index 7b0581376..b19984327 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -17,8 +17,8 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
@action
protected onTouchStart = (e: React.TouchEvent): void => {
for (let i = 0; i < e.targetTouches.length; i++) {
- let pt: any = e.targetTouches.item(i);
- // pen is also a touch, but with a radius of 0.5 (at least with the surface pens).
+ const pt: any = e.targetTouches.item(i);
+ // pen is also a touch, but with a radius of 0.5 (at least with the surface pens)
// and this seems to be the only way of differentiating pen and touch on touch events
if (pt.radiusX > 0.5 && pt.radiusY > 0.5) {
this.prevPoints.set(pt.identifier, pt);
@@ -42,10 +42,11 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
*/
@action
protected onTouch = (e: TouchEvent): void => {
- let myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);
+ const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);
// if we're not actually moving a lot, don't consider it as dragging yet
- // if (!InteractionUtils.IsDragging(this.prevPoints, e.targetTouches, 5) && !this._touchDrag) return;
+ // if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return;
+ console.log(myTouches.length)
this._touchDrag = true;
switch (myTouches.length) {
case 1:
@@ -57,7 +58,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
}
for (let i = 0; i < e.targetTouches.length; i++) {
- let pt = e.targetTouches.item(i);
+ const pt = e.targetTouches.item(i);
if (pt) {
if (this.prevPoints.has(pt.identifier)) {
this.prevPoints.set(pt.identifier, pt);
@@ -71,9 +72,11 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
// console.log(InteractionUtils.GetMyTargetTouches(e, this.prevPoints).length + " up");
// remove all the touches associated with the event
for (let i = 0; i < e.changedTouches.length; i++) {
- let pt = e.changedTouches.item(i);
- if (pt && this.prevPoints.has(pt.identifier)) {
- this.prevPoints.delete(pt.identifier);
+ const pt = e.changedTouches.item(i);
+ if (pt) {
+ if (this.prevPoints.has(pt.identifier)) {
+ this.prevPoints.delete(pt.identifier);
+ }
}
}
this._touchDrag = false;
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index bcdc9c97e..f518ef8fb 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -25,7 +25,7 @@
position: absolute;
top: 0;
left: 0;
- overflow: hidden;
+ // overflow: hidden; // bcz: menus don't show up when this is on (e.g., the parentSelectorMenu)
.collectionDockingView-dragAsDocument {
touch-action: none;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 57c59def6..151b84c50 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -20,7 +20,7 @@ import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, U
import { DocServer } from "../../DocServer";
import { Docs } from '../../documents/Documents';
import { DocumentManager } from '../../util/DocumentManager';
-import { DragLinksAsDocuments, DragManager } from "../../util/DragManager";
+import { DragManager } from "../../util/DragManager";
import { SelectionManager } from '../../util/SelectionManager';
import { Transform } from '../../util/Transform';
import { undoBatch } from "../../util/UndoManager";
@@ -33,6 +33,7 @@ import { ButtonSelector } from './ParentDocumentSelector';
import { DocumentType } from '../../documents/DocumentTypes';
import { ComputedField } from '../../../new_fields/ScriptField';
import { InteractionUtils } from '../../util/InteractionUtils';
+import { TraceMobx } from '../../../new_fields/util';
library.add(faFile);
const _global = (window /* browser */ || global /* node */) as any;
@@ -40,7 +41,7 @@ const _global = (window /* browser */ || global /* node */) as any;
export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
@observable public static Instances: CollectionDockingView[] = [];
@computed public static get Instance() { return CollectionDockingView.Instances[0]; }
- public static makeDocumentConfig(document: Doc, dataDoc: Doc | undefined, width?: number) {
+ public static makeDocumentConfig(document: Doc, dataDoc: Doc | undefined, width?: number, libraryPath?: Doc[]) {
return {
type: 'react-component',
component: 'DocumentFrameRenderer',
@@ -48,7 +49,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
width: width,
props: {
documentId: document[Id],
- dataDocumentId: dataDoc && dataDoc[Id] !== document[Id] ? dataDoc[Id] : ""
+ dataDocumentId: dataDoc && dataDoc[Id] !== document[Id] ? dataDoc[Id] : "",
+ libraryPath: libraryPath ? libraryPath.map(d => d[Id]) : []
//collectionDockingView: CollectionDockingView.Instance
}
};
@@ -96,14 +98,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@undoBatch
@action
- public OpenFullScreen(docView: DocumentView) {
- let document = Doc.MakeAlias(docView.props.Document);
- let dataDoc = docView.props.DataDoc;
- let newItemStackConfig = {
+ public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
+ const document = Doc.MakeAlias(docView.props.Document);
+ const dataDoc = docView.props.DataDoc;
+ const newItemStackConfig = {
type: 'stack',
- content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
+ content: [CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath)]
};
- var docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
+ const docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
this._goldenLayout.root.contentItems[0].addChild(docconfig);
docconfig.callDownwards('_$init');
this._goldenLayout._$maximiseItem(docconfig);
@@ -114,7 +116,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
public CloseFullScreen = () => {
- let target = this._goldenLayout._maximisedItem;
+ const target = this._goldenLayout._maximisedItem;
if (target !== null && this._maximizedSrc) {
this._goldenLayout._maximisedItem.remove();
SelectionManager.SelectDoc(this._maximizedSrc, false);
@@ -131,7 +133,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@action
public static CloseRightSplit(document: Doc): boolean {
if (!CollectionDockingView.Instance) return false;
- let instance = CollectionDockingView.Instance;
+ const instance = CollectionDockingView.Instance;
let retVal = false;
if (instance._goldenLayout.root.contentItems[0].isRow) {
retVal = Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
@@ -147,8 +149,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)!.Document, document)) {
child.contentItems[j].remove();
child.config.activeItemIndex = Math.max(child.contentItems.length - 1, 0);
- let docs = Cast(instance.props.Document.data, listSpec(Doc));
- docs && docs.indexOf(document) !== -1 && docs.splice(docs.indexOf(document), 1);
return true;
}
return false;
@@ -172,40 +172,28 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this.stateChanged();
}
- public Has = (document: Doc) => {
- let docs = Cast(this.props.Document.data, listSpec(Doc));
- if (!docs) {
- return false;
- }
- return docs.includes(document);
- }
-
//
// Creates a vertical split on the right side of the docking view, and then adds the Document to that split
//
@undoBatch
@action
- public static AddRightSplit(document: Doc, dataDoc: Doc | undefined, minimize: boolean = false) {
+ public static AddRightSplit(document: Doc, dataDoc: Doc | undefined, minimize: boolean = false, libraryPath?: Doc[]) {
if (!CollectionDockingView.Instance) return false;
- let instance = CollectionDockingView.Instance;
- let docs = Cast(instance.props.Document.data, listSpec(Doc));
- if (docs) {
- docs.push(document);
- }
- let newItemStackConfig = {
+ const instance = CollectionDockingView.Instance;
+ const newItemStackConfig = {
type: 'stack',
- content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
+ content: [CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath)]
};
- var newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
+ const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
if (instance._goldenLayout.root.contentItems.length === 0) {
instance._goldenLayout.root.addChild(newContentItem);
} else if (instance._goldenLayout.root.contentItems[0].isRow) {
instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
} else {
- var collayout = instance._goldenLayout.root.contentItems[0];
- var newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
+ const collayout = instance._goldenLayout.root.contentItems[0];
+ const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
collayout.parent.replaceChild(collayout, newRow);
newRow.addChild(newContentItem, undefined, true);
@@ -226,13 +214,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@undoBatch
@action
- public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined) => {
+ public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined, libraryPath?: Doc[]) => {
Doc.GetProto(document).lastOpened = new DateField;
- let docs = Cast(this.props.Document.data, listSpec(Doc));
- if (docs) {
- docs.push(document);
- }
- let docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument);
+ const docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument, undefined, libraryPath);
if (stack === undefined) {
let stack: any = this._goldenLayout.root;
while (!stack.isStack) {
@@ -255,7 +239,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
setupGoldenLayout() {
- var config = StrCast(this.props.Document.dockingConfig);
+ const config = StrCast(this.props.Document.dockingConfig);
if (config) {
if (!this._goldenLayout) {
runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
@@ -299,7 +283,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
// Because this is in a set timeout, if this component unmounts right after mounting,
// we will leak a GoldenLayout, because we try to destroy it before we ever create it
setTimeout(() => this.setupGoldenLayout(), 1);
- let userDoc = CurrentUserUtils.UserDocument;
+ const userDoc = CurrentUserUtils.UserDocument;
userDoc && DocListCast((userDoc.workspaces as Doc).data).map(d => d.workspaceBrush = false);
this.props.Document.workspaceBrush = true;
}
@@ -330,7 +314,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
@action
onResize = (event: any) => {
- var cur = this._containerRef.current;
+ const cur = this._containerRef.current;
// bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed
this._goldenLayout && this._goldenLayout.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height);
@@ -349,36 +333,43 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@action
onPointerDown = (e: React.PointerEvent): void => {
this._isPointerDown = true;
- let onPointerUp = action(() => {
+ const onPointerUp = action(() => {
window.removeEventListener("pointerup", onPointerUp);
this._isPointerDown = false;
});
window.addEventListener("pointerup", onPointerUp);
- var className = (e.target as any).className;
+ const className = (e.target as any).className;
if (className === "messageCounter") {
e.stopPropagation();
e.preventDefault();
- let x = e.clientX;
- let y = e.clientY;
- let docid = (e.target as any).DashDocId;
- let tab = (e.target as any).parentElement as HTMLElement;
+ const x = e.clientX;
+ const y = e.clientY;
+ const docid = (e.target as any).DashDocId;
+ const tab = (e.target as any).parentElement as HTMLElement;
DocServer.GetRefField(docid).then(action(async (sourceDoc: Opt<Field>) =>
- (sourceDoc instanceof Doc) && DragLinksAsDocuments(tab, x, y, sourceDoc)));
+ (sourceDoc instanceof Doc) && DragManager.StartLinkTargetsDrag(tab, x, y, sourceDoc)));
}
if (className === "lm_drag_handle" || className === "lm_close" || className === "lm_maximise" || className === "lm_minimise" || className === "lm_close_tab") {
this._flush = true;
}
}
+ updateDataField = async (json: string) => {
+ const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
+ const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", ""));
+
+ if (docids) {
+ const docs = (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc);
+ Doc.GetProto(this.props.Document)[this.props.fieldKey] = new List<Doc>(docs);
+ }
+ }
+
@undoBatch
stateChanged = () => {
- let docs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc));
- CollectionDockingView.Instance._removedDocs.map(theDoc =>
- docs && docs.indexOf(theDoc) !== -1 &&
- docs.splice(docs.indexOf(theDoc), 1));
- CollectionDockingView.Instance._removedDocs.length = 0;
- var json = JSON.stringify(this._goldenLayout.toConfig());
+ const json = JSON.stringify(this._goldenLayout.toConfig());
this.props.Document.dockingConfig = json;
+ this.updateDataField(json);
+
if (this.undohack && !this.hack) {
this.undohack.end();
this.undohack = undefined;
@@ -392,7 +383,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
htmlToElement(html: string) {
- var template = document.createElement('template');
+ const template = document.createElement('template');
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
return template.content.firstChild;
@@ -404,24 +395,24 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
tab.contentItem.parent.config.fixed = true;
}
- let doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
- let dataDoc = await DocServer.GetRefField(tab.contentItem.config.props.dataDocumentId) as Doc;
+ const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
+ const dataDoc = await DocServer.GetRefField(tab.contentItem.config.props.dataDocumentId) as Doc;
if (doc instanceof Doc) {
- let dragSpan = document.createElement("span");
+ const dragSpan = document.createElement("span");
dragSpan.style.position = "relative";
dragSpan.style.bottom = "6px";
dragSpan.style.paddingLeft = "4px";
dragSpan.style.paddingRight = "2px";
- let gearSpan = document.createElement("span");
+ const gearSpan = document.createElement("span");
gearSpan.style.position = "relative";
gearSpan.style.paddingLeft = "0px";
gearSpan.style.paddingRight = "12px";
- let upDiv = document.createElement("span");
+ const upDiv = document.createElement("span");
const stack = tab.contentItem.parent;
// shifts the focus to this tab when another tab is dragged over it
tab.element[0].onmouseenter = (e: any) => {
if (!this._isPointerDown || !SelectionManager.GetIsDragging()) return;
- var activeContentItem = tab.header.parent.getActiveContentItem();
+ const activeContentItem = tab.header.parent.getActiveContentItem();
if (tab.contentItem !== activeContentItem) {
tab.header.parent.setActiveContentItem(tab.contentItem);
}
@@ -429,20 +420,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
};
ReactDOM.render(<span title="Drag as document"
className="collectionDockingView-dragAsDocument"
- onPointerDown={
- e => {
- e.preventDefault();
- e.stopPropagation();
- DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, {
- handlers: { dragComplete: emptyFunction },
- hideSource: false
- });
- }}><FontAwesomeIcon icon="file" size="lg" /></span>, dragSpan);
+ onPointerDown={e => {
+ e.preventDefault();
+ e.stopPropagation();
+ DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
+ }}>
+ <FontAwesomeIcon icon="file" size="lg" />
+ </span>, dragSpan);
ReactDOM.render(<ButtonSelector Document={doc} Stack={stack} />, gearSpan);
- // ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, data, where) => {
- // where === "onRight" ? CollectionDockingView.AddRightSplit(doc, dataDoc) : CollectionDockingView.Instance.AddTab(stack, doc, dataDoc);
- // return true;
- // }} />, upDiv);
tab.reactComponents = [dragSpan, gearSpan, upDiv];
tab.element.append(dragSpan);
tab.element.append(gearSpan);
@@ -459,12 +444,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
tab.closeElement.off('click') //unbind the current click handler
.click(async function () {
tab.reactionDisposer && tab.reactionDisposer();
- let doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId);
+ const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId);
if (doc instanceof Doc) {
- let theDoc = doc;
+ const theDoc = doc;
CollectionDockingView.Instance._removedDocs.push(theDoc);
- let userDoc = CurrentUserUtils.UserDocument;
+ const userDoc = CurrentUserUtils.UserDocument;
let recent: Doc | undefined;
if (userDoc && (recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))) {
Doc.AddDocToList(recent, "data", doc, undefined, true, true);
@@ -523,13 +508,13 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
stack.remove();
stack.contentItems.forEach(async (contentItem: any) => {
- let doc = await DocServer.GetRefField(contentItem.config.props.documentId);
+ const doc = await DocServer.GetRefField(contentItem.config.props.documentId);
if (doc instanceof Doc) {
let recent: Doc | undefined;
if (CurrentUserUtils.UserDocument && (recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))) {
Doc.AddDocToList(recent, "data", doc, undefined, true, true);
}
- let theDoc = doc;
+ const theDoc = doc;
CollectionDockingView.Instance._removedDocs.push(theDoc);
}
});
@@ -539,7 +524,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
.off('click') //unbind the current click handler
.click(action(function () {
stack.config.fixed = !stack.config.fixed;
- // var url = Utils.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId);
+ // const url = Utils.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId);
// let win = window.open(url, stack.contentItems[0].tab.title, "width=300,height=400");
}));
}
@@ -566,11 +551,13 @@ interface DockedFrameProps {
documentId: FieldId;
dataDocumentId: FieldId;
glContainer: any;
+ libraryPath: (FieldId[]);
//collectionDockingView: CollectionDockingView
}
@observer
export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
_mainCont: HTMLDivElement | null = null;
+ @observable private _libraryPath: Doc[] = [];
@observable private _panelWidth = 0;
@observable private _panelHeight = 0;
@observable private _document: Opt<Doc>;
@@ -588,6 +575,14 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
DocServer.GetRefField(this.props.dataDocumentId).then(action((f: Opt<Field>) => this._dataDoc = f as Doc));
}
}));
+ this.props.libraryPath && this.setupLibraryPath();
+ }
+
+ async setupLibraryPath() {
+ Promise.all(this.props.libraryPath.map(async docid => {
+ const d = await DocServer.GetRefField(docid);
+ return d instanceof Doc ? d : undefined;
+ })).then(action((list: (Doc | undefined)[]) => this._libraryPath = list.filter(d => d).map(d => d as Doc)));
}
/**
@@ -597,9 +592,9 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
@action
public PinDoc(doc: Doc) {
//add this new doc to props.Document
- let curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
+ const curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
if (curPres) {
- let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" });
+ const pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" });
Doc.GetProto(pinDoc).presentationTargetDoc = doc;
Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()');
const data = Cast(curPres.data, listSpec(Doc));
@@ -615,8 +610,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
componentDidMount() {
- let observer = new _global.ResizeObserver(action((entries: any) => {
- for (let entry of entries) {
+ const observer = new _global.ResizeObserver(action((entries: any) => {
+ for (const entry of entries) {
this._panelWidth = entry.contentRect.width;
this._panelHeight = entry.contentRect.height;
}
@@ -659,39 +654,41 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
const nativeH = this.nativeHeight();
const nativeW = this.nativeWidth();
if (!nativeW || !nativeH) return 1;
- let wscale = this.panelWidth() / nativeW;
+ const wscale = this.panelWidth() / nativeW;
return wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale;
}
ScreenToLocalTransform = () => {
if (this._mainCont && this._mainCont.children) {
- let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0].firstChild as HTMLElement);
- scale = Utils.GetScreenTransform(this._mainCont).scale;
+ const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0].firstChild as HTMLElement);
+ const scale = Utils.GetScreenTransform(this._mainCont).scale;
return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale);
}
return Transform.Identity();
}
get previewPanelCenteringOffset() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
+ get widthpercent() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? `${(this.nativeWidth() * this.contentScaling()) / this.panelWidth() * 100}%` : undefined; }
- addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string) => {
+ addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string, libraryPath?: Doc[]) => {
SelectionManager.DeselectAll();
if (doc.dockingConfig) {
- MainView.Instance.openWorkspace(doc);
- return true;
+ return MainView.Instance.openWorkspace(doc);
} else if (location === "onRight") {
- return CollectionDockingView.AddRightSplit(doc, dataDoc);
+ return CollectionDockingView.AddRightSplit(doc, dataDoc, undefined, libraryPath);
} else if (location === "close") {
return CollectionDockingView.CloseRightSplit(doc);
} else {
- return CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc);
+ return CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc, libraryPath);
}
}
@computed get docView() {
+ TraceMobx();
if (!this._document) return (null);
const document = this._document;
- let resolvedDataDoc = document.layout instanceof Doc ? document : this._dataDoc;
+ const resolvedDataDoc = document.layout instanceof Doc ? document : this._dataDoc;
return <DocumentView key={document[Id]}
+ LibraryPath={this._libraryPath}
Document={document}
DataDoc={resolvedDataDoc}
bringToFront={emptyFunction}
@@ -720,9 +717,10 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
(<div className="collectionDockingView-content" ref={ref => this._mainCont = ref}
style={{
transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`,
- height: this.layoutDoc && this.layoutDoc.fitWidth ? undefined : "100%"
+ height: this.layoutDoc && this.layoutDoc.fitWidth ? undefined : "100%",
+ width: this.widthpercent
}}>
{this.docView}
</div >);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 52ebfafd3..80752303c 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -2,7 +2,7 @@ import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
import { faPalette } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable } from "mobx";
+import { action, observable, computed } from "mobx";
import { observer } from "mobx-react";
import Measure from "react-measure";
import { Doc } from "../../../new_fields/Doc";
@@ -20,7 +20,6 @@ import { anchorPoints, Flyout } from "../DocumentDecorations";
import { EditableView } from "../EditableView";
import { CollectionStackingView } from "./CollectionStackingView";
import "./CollectionStackingView.scss";
-import { undo } from "prosemirror-history";
library.add(faPalette);
@@ -57,7 +56,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
createRowDropRef = (ele: HTMLDivElement | null) => {
this._dropDisposer && this._dropDisposer();
if (ele) {
- this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.rowDrop.bind(this) } });
+ this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this));
}
}
@@ -65,9 +64,9 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
if (this._collapsed) {
this.props.setDocHeight(this._heading, 20);
} else {
- let rawHeight = this._contRef.current!.getBoundingClientRect().height + 15; //+ 15 accounts for the group header
- let transformScale = this.props.screenToLocalTransform().Scale;
- let trueHeight = rawHeight * transformScale;
+ const rawHeight = this._contRef.current!.getBoundingClientRect().height + 15; //+ 15 accounts for the group header
+ const transformScale = this.props.screenToLocalTransform().Scale;
+ const trueHeight = rawHeight * transformScale;
this.props.setDocHeight(this._heading, trueHeight);
}
}
@@ -75,19 +74,19 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
@undoBatch
rowDrop = action((e: Event, de: DragManager.DropEvent) => {
this._createAliasSelected = false;
- if (de.data instanceof DragManager.DocumentDragData) {
+ if (de.complete.docDragData) {
(this.props.parent.Document.dropConverter instanceof ScriptField) &&
- this.props.parent.Document.dropConverter.script.run({ dragData: de.data });
- let key = StrCast(this.props.parent.props.Document.sectionFilter);
- let castedValue = this.getValue(this._heading);
- de.data.droppedDocuments.forEach(d => d[key] = castedValue);
+ this.props.parent.Document.dropConverter.script.run({ dragData: de.complete.docDragData });
+ const key = StrCast(this.props.parent.props.Document.sectionFilter);
+ const castedValue = this.getValue(this._heading);
+ de.complete.docDragData.droppedDocuments.forEach(d => d[key] = castedValue);
this.props.parent.drop(e, de);
e.stopPropagation();
}
});
getValue = (value: string): any => {
- let parsed = parseInt(value);
+ const parsed = parseInt(value);
if (!isNaN(parsed)) return parsed;
if (value.toLowerCase().indexOf("true") > -1) return true;
if (value.toLowerCase().indexOf("false") > -1) return false;
@@ -97,8 +96,8 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
@action
headingChanged = (value: string, shiftDown?: boolean) => {
this._createAliasSelected = false;
- let key = StrCast(this.props.parent.props.Document.sectionFilter);
- let castedValue = this.getValue(value);
+ const key = StrCast(this.props.parent.props.Document.sectionFilter);
+ const castedValue = this.getValue(value);
if (castedValue) {
if (this.props.parent.sectionHeaders) {
if (this.props.parent.sectionHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
@@ -136,18 +135,18 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
@action
addDocument = (value: string, shiftDown?: boolean) => {
this._createAliasSelected = false;
- let key = StrCast(this.props.parent.props.Document.sectionFilter);
- let newDoc = Docs.Create.TextDocument({ height: 18, width: 200, title: value });
+ const key = StrCast(this.props.parent.props.Document.sectionFilter);
+ const newDoc = Docs.Create.TextDocument({ height: 18, width: 200, title: value });
newDoc[key] = this.getValue(this.props.heading);
return this.props.parent.props.addDocument(newDoc);
}
deleteRow = undoBatch(action(() => {
this._createAliasSelected = false;
- let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ const key = StrCast(this.props.parent.props.Document.sectionFilter);
this.props.docList.forEach(d => d[key] = undefined);
if (this.props.parent.sectionHeaders && this.props.headingObject) {
- let index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
+ const index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
this.props.parent.sectionHeaders.splice(index, 1);
}
}));
@@ -163,19 +162,17 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
}
startDrag = (e: PointerEvent) => {
- let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
+ const [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
- let alias = Doc.MakeAlias(this.props.parent.props.Document);
- let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ const alias = Doc.MakeAlias(this.props.parent.props.Document);
+ const key = StrCast(this.props.parent.props.Document.sectionFilter);
let value = this.getValue(this._heading);
value = typeof value === "string" ? `"${value}"` : value;
- let script = `return doc.${key} === ${value}`;
- let compiled = CompileScript(script, { params: { doc: Doc.name } });
+ const script = `return doc.${key} === ${value}`;
+ const compiled = CompileScript(script, { params: { doc: Doc.name } });
if (compiled.compiled) {
- let scriptField = new ScriptField(compiled);
- alias.viewSpecScript = scriptField;
- let dragData = new DragManager.DocumentDragData([alias]);
- DragManager.StartDocumentDrag([this._headerRef.current!], dragData, e.clientX, e.clientY);
+ alias.viewSpecScript = new ScriptField(compiled);
+ DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY);
}
e.stopPropagation();
@@ -197,7 +194,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
e.stopPropagation();
e.preventDefault();
- let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
+ const [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
this._startDragPosition = { x: dx, y: dy };
if (this._createAliasSelected) {
@@ -210,17 +207,17 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
}
renderColorPicker = () => {
- let selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
+ const selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
- let pink = PastelSchemaPalette.get("pink2");
- let purple = PastelSchemaPalette.get("purple4");
- let blue = PastelSchemaPalette.get("bluegreen1");
- let yellow = PastelSchemaPalette.get("yellow4");
- let red = PastelSchemaPalette.get("red2");
- let green = PastelSchemaPalette.get("bluegreen7");
- let cyan = PastelSchemaPalette.get("bluegreen5");
- let orange = PastelSchemaPalette.get("orange1");
- let gray = "#f1efeb";
+ const pink = PastelSchemaPalette.get("pink2");
+ const purple = PastelSchemaPalette.get("purple4");
+ const blue = PastelSchemaPalette.get("bluegreen1");
+ const yellow = PastelSchemaPalette.get("yellow4");
+ const red = PastelSchemaPalette.get("red2");
+ const green = PastelSchemaPalette.get("bluegreen7");
+ const cyan = PastelSchemaPalette.get("bluegreen5");
+ const orange = PastelSchemaPalette.get("orange1");
+ const gray = "#f1efeb";
return (
<div className="collectionStackingView-colorPicker">
@@ -243,7 +240,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
toggleVisibility = action(() => this._collapsed = !this._collapsed);
renderMenu = () => {
- let selected = this._createAliasSelected;
+ const selected = this._createAliasSelected;
return (<div className="collectionStackingView-optionPicker">
<div className="optionOptions">
<div className={"optionPicker" + (selected === true ? " active" : "")} onClick={this.toggleAlias}>Create Alias</div>
@@ -258,47 +255,66 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
}
}
-
- render() {
- let rows = Math.max(1, Math.min(this.props.docList.length, Math.floor((this.props.parent.props.PanelWidth() - 2 * this.props.parent.xMargin) / (this.props.parent.columnWidth + this.props.parent.gridGap))));
- let key = StrCast(this.props.parent.props.Document.sectionFilter);
- let heading = this._heading;
- let style = this.props.parent;
- let evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
- let headerEditableViewProps = {
- GetValue: () => evContents,
- SetValue: this.headingChanged,
- contents: evContents,
- oneLine: true,
+ @computed get contentLayout() {
+ const rows = Math.max(1, Math.min(this.props.docList.length, Math.floor((this.props.parent.props.PanelWidth() - 2 * this.props.parent.xMargin) / (this.props.parent.columnWidth + this.props.parent.gridGap))));
+ const style = this.props.parent; const collapsed = this._collapsed;
+ const chromeStatus = this.props.parent.props.Document.chromeStatus;
+ const newEditableViewProps = {
+ GetValue: () => "",
+ SetValue: this.addDocument,
+ contents: "+ NEW",
HeadingObject: this.props.headingObject,
HeadingsHack: this._headingsHack,
toggle: this.toggleVisibility,
color: this._color
};
- let newEditableViewProps = {
- GetValue: () => "",
- SetValue: this.addDocument,
- contents: "+ NEW",
+ return collapsed ? (null) :
+ <div style={{ position: "relative" }}>
+ <div className={`collectionStackingView-masonryGrid`}
+ ref={this._contRef}
+ style={{
+ padding: `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px`,
+ width: this.props.parent.NodeWidth,
+ gridGap: this.props.parent.gridGap,
+ gridTemplateColumns: numberRange(rows).reduce((list: string, i: any) => list + ` ${this.props.parent.columnWidth}px`, ""),
+ }}>
+ {this.props.parent.children(this.props.docList)}
+ {this.props.parent.columnDragger}
+ </div>
+ {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ?
+ <div className="collectionStackingView-addDocumentButton"
+ style={{ width: style.columnWidth / style.numGroupColumns }}>
+ <EditableView {...newEditableViewProps} />
+ </div> : null
+ }
+ </div>;
+ }
+
+ @computed get headingView() {
+ const heading = this._heading;
+ const key = StrCast(this.props.parent.props.Document.sectionFilter);
+ const evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
+ const headerEditableViewProps = {
+ GetValue: () => evContents,
+ SetValue: this.headingChanged,
+ contents: evContents,
+ oneLine: true,
HeadingObject: this.props.headingObject,
HeadingsHack: this._headingsHack,
toggle: this.toggleVisibility,
color: this._color
};
- let headingView = this.props.parent.props.Document.miniHeaders ?
- <div className="collectionStackingView-miniHeader" style={{ width: "100%" }}>
- {<EditableView {...headerEditableViewProps} />}
+ return this.props.parent.props.Document.miniHeaders ?
+ <div className="collectionStackingView-miniHeader">
+ <EditableView {...headerEditableViewProps} />
</div> :
- this.props.headingObject ?
+ !this.props.headingObject ? (null) :
<div className="collectionStackingView-sectionHeader" ref={this._headerRef} >
<div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
title={evContents === `NO ${key.toUpperCase()} VALUE` ?
`Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}
- style={{
- width: "100%",
- background: evContents !== `NO ${key.toUpperCase()} VALUE` ? this._color : "lightgrey",
- color: "grey"
- }}>
- {<EditableView {...headerEditableViewProps} />}
+ style={{ background: evContents !== `NO ${key.toUpperCase()} VALUE` ? this._color : "lightgrey", }}>
+ <EditableView {...headerEditableViewProps} />
{evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
<div className="collectionStackingView-sectionColor">
<Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderColorPicker()}>
@@ -321,47 +337,26 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
</div>
}
</div>
- </div > : (null);
+ </div>;
+ }
+ render() {
const background = this._background; //to account for observables in Measure
- const collapsed = this._collapsed;
- let chromeStatus = this.props.parent.props.Document.chromeStatus;
- return (
- <Measure offset onResize={this.handleResize}>
- {({ measureRef }) => {
- return <div ref={measureRef}>
- <div className="collectionStackingView-masonrySection"
- key={heading = "empty"}
- style={{ width: this.props.parent.NodeWidth, background }}
- ref={this.createRowDropRef}
- onPointerEnter={this.pointerEnteredRow}
- onPointerLeave={this.pointerLeaveRow}
- >
- {headingView}
- {collapsed ? (null) :
- < div style={{ position: "relative" }}>
- <div key={`${heading}-stack`} className={`collectionStackingView-masonryGrid`}
- ref={this._contRef}
- style={{
- padding: `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px`,
- width: this.props.parent.NodeWidth,
- gridGap: this.props.parent.gridGap,
- gridTemplateColumns: numberRange(rows).reduce((list: string, i: any) => list + ` ${this.props.parent.columnWidth}px`, ""),
- }}>
- {this.props.parent.children(this.props.docList)}
- {this.props.parent.columnDragger}
- </div>
- {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ?
- <div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
- style={{ width: style.columnWidth / style.numGroupColumns }}>
- <EditableView {...newEditableViewProps} />
- </div> : null
- }
- </div>
- }
- </div >
- </div>;
- }}
- </Measure>
- );
+ const contentlayout = this.contentLayout;
+ const headingview = this.headingView;
+ return <Measure offset onResize={this.handleResize}>
+ {({ measureRef }) => {
+ return <div ref={measureRef}>
+ <div className="collectionStackingView-masonrySection"
+ style={{ width: this.props.parent.NodeWidth, background }}
+ ref={this.createRowDropRef}
+ onPointerEnter={this.pointerEnteredRow}
+ onPointerLeave={this.pointerLeaveRow}
+ >
+ {headingview}
+ {contentlayout}
+ </div >
+ </div>;
+ }}
+ </Measure>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 54a36f691..79a34bc00 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -1,7 +1,7 @@
import React = require("react");
-import { action, computed, observable, trace, untracked, toJS } from "mobx";
+import { action, observable } from "mobx";
import { observer } from "mobx-react";
-import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults, Column } from "react-table";
+import { CellInfo } from "react-table";
import "react-table/react-table.css";
import { emptyFunction, returnFalse, returnZero, returnOne } from "../../../Utils";
import { Doc, DocListCast, DocListCastAsync, Field, Opt } from "../../../new_fields/Doc";
@@ -9,7 +9,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
import { SetupDrag, DragManager } from "../../util/DragManager";
import { CompileScript } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
-import { COLLECTION_BORDER_WIDTH, MAX_ROW_HEIGHT } from '../globalCssVariables.scss';
+import { MAX_ROW_HEIGHT } from '../globalCssVariables.scss';
import '../DocumentDecorations.scss';
import { EditableView } from "../EditableView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
@@ -37,7 +37,7 @@ export interface CellProps {
renderDepth: number;
addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
pinToPres: (document: Doc) => void;
- moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+ moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
isFocused: boolean;
changeFocusedCellByIndex: (row: number, col: number) => void;
setIsEditing: (isEditing: boolean) => void;
@@ -89,8 +89,8 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
// this._isEditing = true;
// this.props.setIsEditing(true);
- let field = this.props.rowProps.original[this.props.rowProps.column.id!];
- let doc = FieldValue(Cast(field, Doc));
+ const field = this.props.rowProps.original[this.props.rowProps.column.id!];
+ const doc = FieldValue(Cast(field, Doc));
if (typeof field === "object" && doc) this.props.setPreviewDoc(doc);
}
@@ -105,13 +105,13 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
}
private drop = (e: Event, de: DragManager.DropEvent) => {
- if (de.data instanceof DragManager.DocumentDragData) {
- let fieldKey = this.props.rowProps.column.id as string;
- if (de.data.draggedDocuments.length === 1) {
- this._document[fieldKey] = de.data.draggedDocuments[0];
+ if (de.complete.docDragData) {
+ const fieldKey = this.props.rowProps.column.id as string;
+ if (de.complete.docDragData.draggedDocuments.length === 1) {
+ this._document[fieldKey] = de.complete.docDragData.draggedDocuments[0];
}
else {
- let coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], de.data.draggedDocuments, {});
+ const coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], de.complete.docDragData.draggedDocuments, {});
this._document[fieldKey] = coll;
}
e.stopPropagation();
@@ -121,7 +121,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
private dropRef = (ele: HTMLElement | null) => {
this._dropDisposer && this._dropDisposer();
if (ele) {
- this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+ this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
}
}
@@ -138,11 +138,12 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
// }
renderCellWithType(type: string | undefined) {
- let dragRef: React.RefObject<HTMLDivElement> = React.createRef();
+ const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
- let props: FieldViewProps = {
+ const props: FieldViewProps = {
Document: this.props.rowProps.original,
DataDoc: this.props.rowProps.original,
+ LibraryPath: [],
fieldKey: this.props.rowProps.column.id as string,
ruleProvider: undefined,
ContainingCollectionView: this.props.CollectionView,
@@ -161,23 +162,22 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
ContentScaling: returnOne
};
- let field = props.Document[props.fieldKey];
- let doc = FieldValue(Cast(field, Doc));
- let fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc);
+ const field = props.Document[props.fieldKey];
+ const doc = FieldValue(Cast(field, Doc));
+ const fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc);
- let onItemDown = (e: React.PointerEvent) => {
- if (fieldIsDoc) {
- SetupDrag(this._focusRef, () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document,
- this._document[props.fieldKey] instanceof Doc ? (doc: Doc, target: Doc, addDoc: (newDoc: Doc) => any) => addDoc(doc) : this.props.moveDocument,
- this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
- }
+ const onItemDown = (e: React.PointerEvent) => {
+ fieldIsDoc && SetupDrag(this._focusRef,
+ () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document,
+ this._document[props.fieldKey] instanceof Doc ? (doc: Doc, target: Doc | undefined, addDoc: (newDoc: Doc) => any) => addDoc(doc) : this.props.moveDocument,
+ this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
};
- let onPointerEnter = (e: React.PointerEvent): void => {
+ const onPointerEnter = (e: React.PointerEvent): void => {
if (e.buttons === 1 && SelectionManager.GetIsDragging() && (type === "document" || type === undefined)) {
dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over";
}
};
- let onPointerLeave = (e: React.PointerEvent): void => {
+ const onPointerLeave = (e: React.PointerEvent): void => {
dragRef.current!.className = "collectionSchemaView-cellContainer";
};
@@ -187,7 +187,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
if (type === "string") contents = typeof field === "string" ? (StrCast(field) === "" ? "--" : StrCast(field)) : "--" + typeof field + "--";
if (type === "boolean") contents = typeof field === "boolean" ? (BoolCast(field) ? "true" : "false") : "--" + typeof field + "--";
if (type === "document") {
- let doc = FieldValue(Cast(field, Doc));
+ const doc = FieldValue(Cast(field, Doc));
contents = typeof field === "object" ? doc ? StrCast(doc.title) === "" ? "--" : StrCast(doc.title) : `--${typeof field}--` : `--${typeof field}--`;
}
@@ -215,7 +215,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
height={"auto"}
maxHeight={Number(MAX_ROW_HEIGHT)}
GetValue={() => {
- let field = props.Document[props.fieldKey];
+ const field = props.Document[props.fieldKey];
if (Field.IsField(field)) {
return Field.toScriptString(field);
}
@@ -226,7 +226,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
if (value.startsWith(":=")) {
return this.props.setComputed(value.substring(2), props.Document, this.props.rowProps.column.id!, this.props.row, this.props.col);
}
- let script = CompileScript(value, { requiredType: type, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
+ const script = CompileScript(value, { requiredType: type, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
if (!script.compiled) {
return false;
}
@@ -287,15 +287,15 @@ export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
@action
toggleChecked = (e: React.ChangeEvent<HTMLInputElement>) => {
this._isChecked = e.target.checked;
- let script = CompileScript(e.target.checked.toString(), { requiredType: "boolean", addReturn: true, params: { this: Doc.name } });
+ const script = CompileScript(e.target.checked.toString(), { requiredType: "boolean", addReturn: true, params: { this: Doc.name } });
if (script.compiled) {
this.applyToDoc(this._document, this.props.row, this.props.col, script.run);
}
}
render() {
- let reference = React.createRef<HTMLDivElement>();
- let onItemDown = (e: React.PointerEvent) => {
+ const reference = React.createRef<HTMLDivElement>();
+ const onItemDown = (e: React.PointerEvent) => {
(!this.props.CollectionView || !this.props.CollectionView.props.isSelected() ? undefined :
SetupDrag(reference, () => this._document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e));
};
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index d24f63fbb..0114342b9 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -1,5 +1,5 @@
import React = require("react");
-import { action, computed, observable, trace, untracked } from "mobx";
+import { action, observable } from "mobx";
import { observer } from "mobx-react";
import "./CollectionSchemaView.scss";
import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faSortAmountDown, faSortAmountUp, faTimes } from '@fortawesome/free-solid-svg-icons';
@@ -7,10 +7,8 @@ import { library, IconProp } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Flyout, anchorPoints } from "../DocumentDecorations";
import { ColumnType } from "./CollectionSchemaView";
-import { emptyFunction } from "../../../Utils";
-import { contains } from "typescript-collections/dist/lib/arrays";
import { faFile } from "@fortawesome/free-regular-svg-icons";
-import { SchemaHeaderField, RandomPastel, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField";
import { undoBatch } from "../../util/UndoManager";
library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile as any, faSortAmountDown, faSortAmountUp, faTimes);
@@ -32,7 +30,7 @@ export interface HeaderProps {
export class CollectionSchemaHeader extends React.Component<HeaderProps> {
render() {
- let icon: IconProp = this.props.keyType === ColumnType.Number ? "hashtag" : this.props.keyType === ColumnType.String ? "font" :
+ const icon: IconProp = this.props.keyType === ColumnType.Number ? "hashtag" : this.props.keyType === ColumnType.String ? "font" :
this.props.keyType === ColumnType.Boolean ? "check-square" : this.props.keyType === ColumnType.Doc ? "file" : "align-justify";
return (
<div className="collectionSchemaView-header" style={{ background: this.props.keyValue.color }}>
@@ -139,7 +137,7 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
renderTypes = () => {
if (this.props.typeConst) return <></>;
- let type = this.props.columnField.type;
+ const type = this.props.columnField.type;
return (
<div className="collectionSchema-headerMenu-group">
<label>Column type:</label>
@@ -170,7 +168,7 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
}
renderSorting = () => {
- let sort = this.props.columnField.desc;
+ const sort = this.props.columnField.desc;
return (
<div className="collectionSchema-headerMenu-group">
<label>Sort by:</label>
@@ -193,14 +191,14 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
}
renderColors = () => {
- let selected = this.props.columnField.color;
+ const selected = this.props.columnField.color;
- let pink = PastelSchemaPalette.get("pink2");
- let purple = PastelSchemaPalette.get("purple2");
- let blue = PastelSchemaPalette.get("bluegreen1");
- let yellow = PastelSchemaPalette.get("yellow4");
- let red = PastelSchemaPalette.get("red2");
- let gray = "#f1efeb";
+ const pink = PastelSchemaPalette.get("pink2");
+ const purple = PastelSchemaPalette.get("purple2");
+ const blue = PastelSchemaPalette.get("bluegreen1");
+ const yellow = PastelSchemaPalette.get("yellow4");
+ const red = PastelSchemaPalette.get("red2");
+ const gray = "#f1efeb";
return (
<div className="collectionSchema-headerMenu-group">
@@ -291,8 +289,8 @@ class KeysDropdown extends React.Component<KeysDropdownProps> {
@action
onKeyDown = (e: React.KeyboardEvent): void => {
if (e.key === "Enter") {
- let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
- let exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
+ const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+ const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) {
@@ -334,11 +332,11 @@ class KeysDropdown extends React.Component<KeysDropdownProps> {
renderOptions = (): JSX.Element[] | JSX.Element => {
if (!this._isOpen) return <></>;
- let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
- let exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
+ const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+ const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
- let options = keyOptions.map(key => {
+ const options = keyOptions.map(key => {
return <div key={key} className="key-option" onClick={() => { this.onSelect(key); this.setSearchTerm(""); }}>{key}</div>;
});
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index 274c8b6d1..153bbd410 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -1,18 +1,18 @@
import React = require("react");
-import { ReactTableDefaults, TableCellRenderer, ComponentPropsGetterR, ComponentPropsGetter0, RowInfo } from "react-table";
+import { ReactTableDefaults, TableCellRenderer, RowInfo } from "react-table";
import "./CollectionSchemaView.scss";
import { Transform } from "../../util/Transform";
import { Doc } from "../../../new_fields/Doc";
import { DragManager, SetupDrag } from "../../util/DragManager";
import { SelectionManager } from "../../util/SelectionManager";
-import { Cast, FieldValue, StrCast } from "../../../new_fields/Types";
+import { Cast, FieldValue } from "../../../new_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 { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
import { undoBatch } from "../../util/UndoManager";
library.add(faGripVertical, faTrash);
@@ -43,10 +43,10 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
document.removeEventListener("pointermove", this.onPointerMove);
}
onDragMove = (e: PointerEvent): void => {
- let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
- let rect = this._header!.current!.getBoundingClientRect();
- let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top);
- let before = x[0] < bounds[0];
+ const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
+ const rect = this._header!.current!.getBoundingClientRect();
+ const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top);
+ const before = x[0] < bounds[0];
this._header!.current!.className = "collectionSchema-col-wrapper";
if (before) this._header!.current!.className += " col-before";
if (!before) this._header!.current!.className += " col-after";
@@ -56,39 +56,39 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
createColDropTarget = (ele: HTMLDivElement) => {
this._colDropDisposer && this._colDropDisposer();
if (ele) {
- this._colDropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.colDrop.bind(this) } });
+ this._colDropDisposer = DragManager.MakeDropTarget(ele, this.colDrop.bind(this));
}
}
colDrop = (e: Event, de: DragManager.DropEvent) => {
document.removeEventListener("pointermove", this.onDragMove, true);
- let x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
- let rect = this._header!.current!.getBoundingClientRect();
- let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top);
- let before = x[0] < bounds[0];
- if (de.data instanceof DragManager.ColumnDragData) {
- this.props.reorderColumns(de.data.colKey, this.props.columnValue, before, this.props.allColumns);
+ const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+ const rect = this._header!.current!.getBoundingClientRect();
+ const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top);
+ const before = x[0] < bounds[0];
+ if (de.complete.columnDragData) {
+ this.props.reorderColumns(de.complete.columnDragData.colKey, this.props.columnValue, before, this.props.allColumns);
return true;
}
return false;
}
onPointerMove = (e: PointerEvent) => {
- let onRowMove = (e: PointerEvent) => {
+ const onRowMove = (e: PointerEvent) => {
e.stopPropagation();
e.preventDefault();
document.removeEventListener("pointermove", onRowMove);
document.removeEventListener('pointerup', onRowUp);
- let dragData = new DragManager.ColumnDragData(this.props.columnValue);
+ const dragData = new DragManager.ColumnDragData(this.props.columnValue);
DragManager.StartColumnDrag(this._dragRef.current!, dragData, e.x, e.y);
};
- let onRowUp = (): void => {
+ const onRowUp = (): void => {
document.removeEventListener("pointermove", onRowMove);
document.removeEventListener('pointerup', onRowUp);
};
if (e.buttons === 1) {
- let [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
+ const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
document.removeEventListener("pointermove", this.onPointerMove);
e.stopPropagation();
@@ -106,14 +106,14 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
@action
onPointerDown = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => {
this._dragRef = ref;
- let [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY);
+ const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY);
this._startDragPosition = { x: dx, y: dy };
document.addEventListener("pointermove", this.onPointerMove);
}
render() {
- let reference = React.createRef<HTMLDivElement>();
+ const reference = React.createRef<HTMLDivElement>();
return (
<div className="collectionSchema-col" ref={this.createColDropTarget}>
@@ -152,10 +152,10 @@ export class MovableRow extends React.Component<MovableRowProps> {
document.removeEventListener("pointermove", this.onDragMove, true);
}
onDragMove = (e: PointerEvent): void => {
- let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
- let rect = this._header!.current!.getBoundingClientRect();
- let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
- let before = x[1] < bounds[1];
+ const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
+ const rect = this._header!.current!.getBoundingClientRect();
+ const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
+ const before = x[1] < bounds[1];
this._header!.current!.className = "collectionSchema-row-wrapper";
if (before) this._header!.current!.className += " row-above";
if (!before) this._header!.current!.className += " row-below";
@@ -165,7 +165,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
createRowDropTarget = (ele: HTMLDivElement) => {
this._rowDropDisposer && this._rowDropDisposer();
if (ele) {
- this._rowDropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.rowDrop.bind(this) } });
+ this._rowDropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this));
}
}
@@ -173,38 +173,39 @@ export class MovableRow extends React.Component<MovableRowProps> {
const rowDoc = FieldValue(Cast(this.props.rowInfo.original, Doc));
if (!rowDoc) return false;
- let x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
- let rect = this._header!.current!.getBoundingClientRect();
- let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
- let before = x[1] < bounds[1];
+ const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+ const rect = this._header!.current!.getBoundingClientRect();
+ const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
+ const before = x[1] < bounds[1];
- if (de.data instanceof DragManager.DocumentDragData) {
+ const docDragData = de.complete.docDragData;
+ if (docDragData) {
e.stopPropagation();
- if (de.data.draggedDocuments[0] === rowDoc) return true;
- let addDocument = (doc: Doc) => this.props.addDoc(doc, rowDoc, before);
- let movedDocs = de.data.draggedDocuments;
- return (de.data.dropAction || de.data.userDropAction) ?
- de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
- : (de.data.moveDocument) ?
- movedDocs.reduce((added: boolean, d) => de.data.moveDocument(d, rowDoc, addDocument) || added, false)
- : de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false);
+ if (docDragData.draggedDocuments[0] === rowDoc) return true;
+ const addDocument = (doc: Doc) => this.props.addDoc(doc, rowDoc, before);
+ const movedDocs = docDragData.draggedDocuments;
+ return (docDragData.dropAction || docDragData.userDropAction) ?
+ docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
+ : (docDragData.moveDocument) ?
+ movedDocs.reduce((added: boolean, d) => docDragData.moveDocument?.(d, rowDoc, addDocument) || added, false)
+ : docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false);
}
return false;
}
onRowContextMenu = (e: React.MouseEvent): void => {
- let description = this.props.rowWrapped ? "Unwrap text on row" : "Text wrap row";
+ const description = this.props.rowWrapped ? "Unwrap text on row" : "Text wrap row";
ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: "file-pdf" });
}
@undoBatch
@action
- move: DragManager.MoveFunction = (doc: Doc, target: Doc, addDoc) => {
- let targetView = DocumentManager.Instance.getDocumentView(target);
+ move: DragManager.MoveFunction = (doc: Doc, targetCollection: Doc | undefined, addDoc) => {
+ const targetView = targetCollection && DocumentManager.Instance.getDocumentView(targetCollection);
if (targetView && targetView.props.ContainingCollectionDoc) {
- return doc !== target && doc !== targetView.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
+ return doc !== targetCollection && doc !== targetView.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
}
- return doc !== target && this.props.removeDoc(doc) && addDoc(doc);
+ return doc !== targetCollection && this.props.removeDoc(doc) && addDoc(doc);
}
render() {
@@ -217,8 +218,8 @@ export class MovableRow extends React.Component<MovableRowProps> {
const doc = FieldValue(Cast(original, Doc));
if (!doc) return <></>;
- let reference = React.createRef<HTMLDivElement>();
- let onItemDown = SetupDrag(reference, () => doc, this.move);
+ const reference = React.createRef<HTMLDivElement>();
+ const onItemDown = SetupDrag(reference, () => doc, this.move);
let className = "collectionSchema-row";
if (this.props.rowFocused) className += " row-focused";
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 65856cad3..bb706e528 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -94,11 +94,11 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
@action
onDividerMove = (e: PointerEvent): void => {
- let nativeWidth = this._mainCont!.getBoundingClientRect();
- let minWidth = 40;
- let maxWidth = 1000;
- let movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
- let width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
+ const nativeWidth = this._mainCont!.getBoundingClientRect();
+ const minWidth = 40;
+ const maxWidth = 1000;
+ const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
+ const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
this.props.Document.schemaPreviewWidth = width;
}
@action
@@ -136,11 +136,12 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@computed
get previewPanel() {
- let layoutDoc = this.previewDocument ? Doc.expandTemplateLayout(this.previewDocument, this.props.DataDoc) : undefined;
+ const layoutDoc = this.previewDocument ? Doc.expandTemplateLayout(this.previewDocument, this.props.DataDoc) : undefined;
return <div ref={this.createTarget}>
<ContentFittingDocumentView
Document={layoutDoc}
DataDocument={this.previewDocument !== this.props.DataDoc ? this.props.DataDoc : undefined}
+ LibraryPath={this.props.LibraryPath}
childDocs={this.childDocs}
renderDepth={this.props.renderDepth}
ruleProvider={this.props.Document.isRuleProvider && layoutDoc && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
@@ -223,7 +224,7 @@ export interface SchemaTableProps {
renderDepth: number;
deleteDocument: (document: Doc) => boolean;
addDocument: (document: Doc) => boolean;
- moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+ moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
active: (outsideReaction: boolean) => boolean;
onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
@@ -258,11 +259,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@computed get childDocs() {
if (this.props.childDocs) return this.props.childDocs;
- let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+ const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
return DocListCast(doc[this.props.fieldKey]);
}
set childDocs(docs: Doc[]) {
- let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+ const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
doc[this.props.fieldKey] = new List<Doc>(docs);
}
@@ -288,12 +289,12 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
@computed get tableColumns(): Column<Doc>[] {
- let possibleKeys = this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1);
- let columns: Column<Doc>[] = [];
- let tableIsFocused = this.props.isFocused(this.props.Document);
- let focusedRow = this._focusedCell.row;
- let focusedCol = this._focusedCell.col;
- let isEditable = !this._headerIsEditing;
+ const possibleKeys = this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1);
+ const columns: Column<Doc>[] = [];
+ const tableIsFocused = this.props.isFocused(this.props.Document);
+ const focusedRow = this._focusedCell.row;
+ const focusedCol = this._focusedCell.col;
+ const isEditable = !this._headerIsEditing;
if (this.childDocs.reduce((found, doc) => found || doc.type === "collection", false)) {
columns.push(
@@ -313,8 +314,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
);
}
- let cols = this.columns.map(col => {
- let header = <CollectionSchemaHeader
+ const cols = this.columns.map(col => {
+ const header = <CollectionSchemaHeader
keyValue={col}
possibleKeys={possibleKeys}
existingKeys={this.columns.map(c => c.heading)}
@@ -333,11 +334,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
accessor: (doc: Doc) => doc ? doc[col.heading] : 0,
id: col.heading,
Cell: (rowProps: CellInfo) => {
- let rowIndex = rowProps.index;
- let columnIndex = this.columns.map(c => c.heading).indexOf(rowProps.column.id!);
- let isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
+ const rowIndex = rowProps.index;
+ const columnIndex = this.columns.map(c => c.heading).indexOf(rowProps.column.id!);
+ const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
- let props: CellProps = {
+ const props: CellProps = {
row: rowIndex,
col: columnIndex,
rowProps: rowProps,
@@ -358,7 +359,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
getField: this.getField,
};
- let colType = this.getColumnType(col);
+ const colType = this.getColumnType(col);
if (colType === ColumnType.Number) return <CollectionSchemaNumberCell {...props} />;
if (colType === ColumnType.String) return <CollectionSchemaStringCell {...props} />;
if (colType === ColumnType.Boolean) return <CollectionSchemaCheckboxCell {...props} />;
@@ -384,9 +385,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
constructor(props: SchemaTableProps) {
super(props);
// convert old schema columns (list of strings) into new schema columns (list of schema header fields)
- let oldSchemaColumns = Cast(this.props.Document.schemaColumns, listSpec("string"), []);
+ const oldSchemaColumns = Cast(this.props.Document.schemaColumns, listSpec("string"), []);
if (oldSchemaColumns && oldSchemaColumns.length && typeof oldSchemaColumns[0] !== "object") {
- let newSchemaColumns = oldSchemaColumns.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
+ const newSchemaColumns = oldSchemaColumns.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
this.props.Document.schemaColumns = new List<SchemaHeaderField>(newSchemaColumns);
}
}
@@ -418,10 +419,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
private getTdProps: ComponentPropsGetterR = (state, rowInfo, column, instance) => {
if (!rowInfo || column) return {};
- let row = rowInfo.index;
+ const row = rowInfo.index;
//@ts-ignore
- let col = this.columns.map(c => c.heading).indexOf(column!.id);
- let isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document);
+ const col = this.columns.map(c => c.heading).indexOf(column!.id);
+ const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document);
// TODO: editing border doesn't work :(
return {
style: {
@@ -432,7 +433,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
onCloseCollection = (collection: Doc): void => {
- let index = this._openCollections.findIndex(col => col === collection[Id]);
+ const index = this._openCollections.findIndex(col => col === collection[Id]);
if (index > -1) this._openCollections.splice(index, 1);
}
@@ -450,7 +451,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
onKeyDown = (e: KeyboardEvent): void => {
if (!this._cellIsEditing && !this._headerIsEditing && this.props.isFocused(this.props.Document)) {// && this.props.isSelected(true)) {
- let direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : "";
+ const direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : "";
this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col);
const pdoc = FieldValue(this.childDocs[this._focusedCell.row]);
@@ -479,7 +480,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@undoBatch
createRow = () => {
- let newDoc = Docs.Create.TextDocument({ title: "", width: 100, height: 30 });
+ const newDoc = Docs.Create.TextDocument({ title: "", width: 100, height: 30 });
this.props.addDocument(newDoc);
}
@@ -498,7 +499,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@undoBatch
@action
deleteColumn = (key: string) => {
- let columns = this.columns;
+ const columns = this.columns;
if (columns === undefined) {
this.columns = new List<SchemaHeaderField>([]);
} else {
@@ -513,7 +514,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@undoBatch
@action
changeColumns = (oldKey: string, newKey: string, addNew: boolean) => {
- let columns = this.columns;
+ const columns = this.columns;
if (columns === undefined) {
this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, "f1efeb")]);
} else {
@@ -523,7 +524,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
} else {
const index = columns.map(c => c.heading).indexOf(oldKey);
if (index > -1) {
- let column = columns[index];
+ const column = columns[index];
column.setHeading(newKey);
columns[index] = column;
this.columns = columns;
@@ -554,8 +555,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
setColumnType = (columnField: SchemaHeaderField, type: ColumnType): void => {
if (columnTypes.get(columnField.heading)) return;
- let columns = this.columns;
- let index = columns.indexOf(columnField);
+ const columns = this.columns;
+ const index = columns.indexOf(columnField);
if (index > -1) {
columnField.setType(NumCast(type));
columns[index] = columnField;
@@ -575,8 +576,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@undoBatch
setColumnColor = (columnField: SchemaHeaderField, color: string): void => {
- let columns = this.columns;
- let index = columns.indexOf(columnField);
+ const columns = this.columns;
+ const index = columns.indexOf(columnField);
if (index > -1) {
columnField.setColor(color);
columns[index] = columnField;
@@ -589,10 +590,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@undoBatch
reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => {
- let columns = [...columnsValues];
- let oldIndex = columns.indexOf(toMove);
- let relIndex = columns.indexOf(relativeTo);
- let newIndex = (oldIndex > relIndex && !before) ? relIndex + 1 : (oldIndex < relIndex && before) ? relIndex - 1 : relIndex;
+ const columns = [...columnsValues];
+ const oldIndex = columns.indexOf(toMove);
+ const relIndex = columns.indexOf(relativeTo);
+ const newIndex = (oldIndex > relIndex && !before) ? relIndex + 1 : (oldIndex < relIndex && before) ? relIndex - 1 : relIndex;
if (oldIndex === newIndex) return;
@@ -603,17 +604,17 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@undoBatch
@action
setColumnSort = (columnField: SchemaHeaderField, descending: boolean | undefined) => {
- let columns = this.columns;
- let index = columns.findIndex(c => c.heading === columnField.heading);
- let column = columns[index];
+ const columns = this.columns;
+ const index = columns.findIndex(c => c.heading === columnField.heading);
+ const column = columns[index];
column.setDesc(descending);
columns[index] = column;
this.columns = columns;
}
get documentKeys() {
- let docs = this.childDocs;
- let keys: { [key: string]: boolean } = {};
+ const docs = this.childDocs;
+ const keys: { [key: string]: boolean } = {};
// bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields.
// then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be
// invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked.
@@ -628,8 +629,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
toggleTextWrapRow = (doc: Doc): void => {
- let textWrapped = this.textWrappedRows;
- let index = textWrapped.findIndex(id => doc[Id] === id);
+ const textWrapped = this.textWrappedRows;
+ const index = textWrapped.findIndex(id => doc[Id] === id);
index > -1 ? textWrapped.splice(index, 1) : textWrapped.push(doc[Id]);
@@ -638,10 +639,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@computed
get reactTable() {
- let children = this.childDocs;
- let hasCollectionChild = children.reduce((found, doc) => found || doc.type === "collection", false);
- let expandedRowsList = this._openCollections.map(col => children.findIndex(doc => doc[Id] === col).toString());
- let expanded = {};
+ const children = this.childDocs;
+ const hasCollectionChild = children.reduce((found, doc) => found || doc.type === "collection", false);
+ const expandedRowsList = this._openCollections.map(col => children.findIndex(doc => doc[Id] === col).toString());
+ const expanded = {};
//@ts-ignore
expandedRowsList.forEach(row => expanded[row] = true);
console.log("text wrapped rows", ...[...this.textWrappedRows]); // TODO: get component to rerender on text wrap change without needign to console.log :((((
@@ -668,10 +669,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
onResizedChange = (newResized: Resize[], event: any) => {
- let columns = this.columns;
+ const columns = this.columns;
newResized.forEach(resized => {
- let index = columns.findIndex(c => c.heading === resized.id);
- let column = columns[index];
+ const index = columns.findIndex(c => c.heading === resized.id);
+ const column = columns[index];
column.setWidth(resized.value);
columns[index] = column;
});
@@ -688,16 +689,16 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
makeDB = async () => {
let csv: string = this.columns.reduce((val, col) => val + col + ",", "");
csv = csv.substr(0, csv.length - 1) + "\n";
- let self = this;
+ const self = this;
this.childDocs.map(doc => {
csv += self.columns.reduce((val, col) => val + (doc[col.heading] ? doc[col.heading]!.toString() : "0") + ",", "");
csv = csv.substr(0, csv.length - 1) + "\n";
});
csv.substring(0, csv.length - 1);
- let dbName = StrCast(this.props.Document.title);
- let res = await Gateway.Instance.PostSchema(csv, dbName);
+ const dbName = StrCast(this.props.Document.title);
+ const res = await Gateway.Instance.PostSchema(csv, dbName);
if (self.props.CollectionView && self.props.CollectionView.props.addDocument) {
- let schemaDoc = await Docs.Create.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName }, { dbDoc: self.props.Document });
+ const schemaDoc = await Docs.Create.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName }, { dbDoc: self.props.Document });
if (schemaDoc) {
//self.props.CollectionView.props.addDocument(schemaDoc, false);
self.props.Document.schemaDoc = schemaDoc;
@@ -706,7 +707,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
getField = (row: number, col?: number) => {
- let docs = this.childDocs;
+ const docs = this.childDocs;
row = row % docs.length;
while (row < 0) row += docs.length;
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 29178b909..e1577cfee 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -97,6 +97,7 @@
.collectionStackingView-columnDoc {
display: inline-block;
+ margin: auto;
}
.collectionStackingView-masonryDoc {
@@ -177,7 +178,9 @@
.collectionStackingView-sectionHeader-subCont {
outline: none;
border: 0px;
- color: $light-color;
+ color: $light-color;
+ width: 100%;
+ color: grey;
letter-spacing: 2px;
font-size: 75%;
transition: transform 0.2s;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index be3bfca0a..e71e11b48 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,7 +1,7 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { CursorProperty } from "csstype";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import Switch from 'rc-switch';
import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
@@ -10,7 +10,7 @@ import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../new_fields/Types";
-import { emptyFunction, Utils, numberRange } from "../../../Utils";
+import { emptyFunction, Utils } from "../../../Utils";
import { DocumentType } from "../../documents/DocumentTypes";
import { DragManager } from "../../util/DragManager";
import { Transform } from "../../util/Transform";
@@ -24,6 +24,8 @@ import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import { ScriptBox } from "../ScriptBox";
import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
+import { TraceMobx } from "../../../new_fields/util";
+import { CollectionViewType } from "./CollectionView";
@observer
export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -40,7 +42,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); }
@computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); }
@computed get xMargin() { return NumCast(this.props.Document.xMargin, 2 * this.gridGap); }
- @computed get yMargin() { return NumCast(this.props.Document.yMargin, 2 * this.gridGap); }
+ @computed get yMargin() { return Math.max(this.props.Document.showTitle ? 30 : 0, NumCast(this.props.Document.yMargin, 2 * this.gridGap)); }
@computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); }
@computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
@computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
@@ -56,15 +58,15 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
children(docs: Doc[]) {
this._docXfs.length = 0;
return docs.map((d, i) => {
- let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d);
- let layoutDoc = pair.layout ? Doc.Layout(pair.layout) : d;
- let width = () => Math.min(layoutDoc.nativeWidth && !layoutDoc.ignoreAspect && !this.props.Document.fillColumn ? layoutDoc[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
- let height = () => this.getDocHeight(layoutDoc);
- let dref = React.createRef<HTMLDivElement>();
- let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
+ const pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d);
+ const layoutDoc = pair.layout ? Doc.Layout(pair.layout) : d;
+ const width = () => Math.min(layoutDoc.nativeWidth && !layoutDoc.ignoreAspect && !this.props.Document.fillColumn ? layoutDoc[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+ const height = () => this.getDocHeight(layoutDoc);
+ const dref = React.createRef<HTMLDivElement>();
+ const dxf = () => this.getDocTransform(layoutDoc, dref.current!);
this._docXfs.push({ dxf: dxf, width: width, height: height });
- let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
- let style = this.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+ const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
+ const style = this.isStackingView ? { width: width(), marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
{this.getDisplayDoc(pair.layout || d, pair.data, dxf, width)}
</div>;
@@ -83,20 +85,20 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
return new Map<SchemaHeaderField, Doc[]>();
}
const sectionHeaders = this.sectionHeaders;
- let fields = new Map<SchemaHeaderField, Doc[]>(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
+ const fields = new Map<SchemaHeaderField, Doc[]>(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
this.filteredChildren.map(d => {
- let sectionValue = (d[this.sectionFilter] ? d[this.sectionFilter] : `NO ${this.sectionFilter.toUpperCase()} VALUE`) as object;
+ const sectionValue = (d[this.sectionFilter] ? d[this.sectionFilter] : `NO ${this.sectionFilter.toUpperCase()} VALUE`) as object;
// the next five lines ensures that floating point rounding errors don't create more than one section -syip
- let parsed = parseInt(sectionValue.toString());
- let castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
+ const parsed = parseInt(sectionValue.toString());
+ const castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
// look for if header exists already
- let existingHeader = sectionHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`));
+ const existingHeader = sectionHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`));
if (existingHeader) {
fields.get(existingHeader)!.push(d);
}
else {
- let newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`);
+ const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`);
fields.set(newSchemaHeader, [d]);
sectionHeaders.push(newSchemaHeader);
}
@@ -108,26 +110,26 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
super.componentDidMount();
this._heightDisposer = reaction(() => {
if (this.props.Document.autoHeight) {
- let sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]);
+ const sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]);
if (this.isStackingView) {
- let res = this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => {
- let r1 = Math.max(maxHght,
+ const res = this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => {
+ const r1 = Math.max(maxHght,
(this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => {
- let val = height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap);
+ const val = height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap);
return val;
}, this.yMargin));
return r1;
}, 0);
return res;
} else {
- let sum = Array.from(this._heightMap.values()).reduce((acc: number, curr: number) => acc += curr, 0);
+ const sum = Array.from(this._heightMap.values()).reduce((acc: number, curr: number) => acc += curr, 0);
return this.props.ContentScaling() * (sum + (this.Sections.size ? (this.props.Document.miniHeaders ? 20 : 85) : -15));
}
}
return -1;
},
(hgt: number) => {
- let doc = hgt === -1 ? undefined : this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
+ const doc = hgt === -1 ? undefined : this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
doc && hgt > 0 && (Doc.Layout(doc).height = hgt);
},
{ fireImmediately: true }
@@ -146,7 +148,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
@action
- moveDocument = (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean): boolean => {
+ moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean): boolean => {
return this.props.removeDocument(doc) && addDocument(doc);
}
createRef = (ele: HTMLDivElement | null) => {
@@ -162,20 +164,20 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@computed get onClickHandler() { return ScriptCast(this.Document.onChildClick); }
getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
- let layoutDoc = Doc.Layout(doc);
- let height = () => this.getDocHeight(doc);
- let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]());
+ const layoutDoc = Doc.Layout(doc);
+ const height = () => this.getDocHeight(doc);
return <ContentFittingDocumentView
Document={doc}
DataDocument={dataDoc}
+ LibraryPath={this.props.LibraryPath}
showOverlays={this.overlays}
- renderDepth={this.props.renderDepth}
+ renderDepth={this.props.renderDepth + 1}
ruleProvider={this.props.Document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
fitToBox={this.props.fitToBox}
onClick={layoutDoc.isTemplateDoc ? this.onClickHandler : this.onChildClickHandler}
PanelWidth={width}
PanelHeight={height}
- getTransform={finalDxf}
+ getTransform={dxf}
focus={this.props.focus}
CollectionDoc={this.props.CollectionView && this.props.CollectionView.props.Document}
CollectionView={this.props.CollectionView}
@@ -192,12 +194,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
getDocHeight(d?: Doc) {
if (!d) return 0;
- let layoutDoc = Doc.Layout(d);
- let nw = NumCast(layoutDoc.nativeWidth);
- let nh = NumCast(layoutDoc.nativeHeight);
+ const layoutDoc = Doc.Layout(d);
+ const nw = NumCast(layoutDoc.nativeWidth);
+ const nh = NumCast(layoutDoc.nativeHeight);
let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
if (!layoutDoc.ignoreAspect && !layoutDoc.fitWidth && nw && nh) {
- let aspect = nw && nh ? nh / nw : 1;
+ const aspect = nw && nh ? nh / nw : 1;
if (!(d.nativeWidth && !layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
return wid * aspect;
}
@@ -215,8 +217,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
@action
onDividerMove = (e: PointerEvent): void => {
- let dragPos = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
- let delta = dragPos - this._columnStart;
+ const dragPos = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
+ const delta = dragPos - this._columnStart;
this._columnStart = dragPos;
this.layoutDoc.columnWidth = Math.max(10, this.columnWidth + delta);
}
@@ -229,7 +231,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
@computed get columnDragger() {
- return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px` }} >
+ return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef}
+ style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }} >
<FontAwesomeIcon icon={"arrows-alt-h"} />
</div>;
}
@@ -237,28 +240,29 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
- let where = [de.x, de.y];
+ const where = [de.x, de.y];
let targInd = -1;
- let plusOne = false;
- if (de.data instanceof DragManager.DocumentDragData) {
+ let plusOne = 0;
+ if (de.complete.docDragData) {
this._docXfs.map((cd, i) => {
- let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
- let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
+ const pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+ const pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
targInd = i;
- plusOne = (where[1] > (pos[1] + pos1[1]) / 2 ? 1 : 0) ? true : false;
+ const axis = this.Document.viewType === CollectionViewType.Masonry ? 0 : 1;
+ plusOne = where[axis] > (pos[axis] + pos1[axis]) / 2 ? 1 : 0;
}
});
- }
- if (super.drop(e, de)) {
- let newDoc = de.data.droppedDocuments[0];
- let docs = this.childDocList;
- if (docs) {
- if (targInd === -1) targInd = docs.length;
- else targInd = docs.indexOf(this.filteredChildren[targInd]);
- let srcInd = docs.indexOf(newDoc);
- docs.splice(srcInd, 1);
- docs.splice((targInd > srcInd ? targInd - 1 : targInd) + (plusOne ? 1 : 0), 0, newDoc);
+ if (super.drop(e, de)) {
+ const newDoc = de.complete.docDragData.droppedDocuments[0];
+ const docs = this.childDocList;
+ if (docs) {
+ if (targInd === -1) targInd = docs.length;
+ else targInd = docs.indexOf(this.filteredChildren[targInd]);
+ const srcInd = docs.indexOf(newDoc);
+ docs.splice(srcInd, 1);
+ docs.splice((targInd > srcInd ? targInd - 1 : targInd) + plusOne, 0, newDoc);
+ }
}
}
return false;
@@ -266,19 +270,19 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@undoBatch
@action
onDrop = async (e: React.DragEvent): Promise<void> => {
- let where = [e.clientX, e.clientY];
+ const where = [e.clientX, e.clientY];
let targInd = -1;
this._docXfs.map((cd, i) => {
- let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
- let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
+ const pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+ const pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
targInd = i;
}
});
super.onDrop(e, {}, () => {
if (targInd !== -1) {
- let newDoc = this.childDocs[this.childDocs.length - 1];
- let docs = this.childDocList;
+ const newDoc = this.childDocs[this.childDocs.length - 1];
+ const docs = this.childDocList;
if (docs) {
docs.splice(docs.length - 1, 1);
docs.splice(targInd, 0, newDoc);
@@ -288,13 +292,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
headings = () => Array.from(this.Sections.keys());
sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
- let key = this.sectionFilter;
+ const key = this.sectionFilter;
let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
- let types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]);
+ const types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]);
if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
type = types[0];
}
- let cols = () => this.isStackingView ? 1 : Math.max(1, Math.min(this.filteredChildren.length,
+ const cols = () => this.isStackingView ? 1 : Math.max(1, Math.min(this.filteredChildren.length,
Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
return <CollectionStackingViewFieldColumn
key={heading ? heading.heading : ""}
@@ -312,23 +316,22 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
getDocTransform(doc: Doc, dref: HTMLDivElement) {
if (!dref) return Transform.Identity();
- let y = this._scroll; // required for document decorations to update when the text box container is scrolled
- let { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
- let outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
- let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
+ const y = this._scroll; // required for document decorations to update when the text box container is scrolled
+ const { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
+ const outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
+ const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
return this.props.ScreenToLocalTransform().
- translate(offset[0], offset[1] + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0)).
- scale(NumCast(doc.width, 1) / this.columnWidth);
+ translate(offset[0], offset[1] + (this.props.ChromeHeight && this.props.ChromeHeight() < 0 ? this.props.ChromeHeight() : 0));
}
sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
- let key = this.sectionFilter;
+ const key = this.sectionFilter;
let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
- let types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]);
+ const types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]);
if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
type = types[0];
}
- let rows = () => !this.isStackingView ? 1 : Math.max(1, Math.min(docList.length,
+ const rows = () => !this.isStackingView ? 1 : Math.max(1, Math.min(docList.length,
Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
return <CollectionMasonryViewFieldRow
key={heading ? heading.heading : ""}
@@ -355,9 +358,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
sortFunc = (a: [SchemaHeaderField, Doc[]], b: [SchemaHeaderField, Doc[]]): 1 | -1 => {
- let descending = BoolCast(this.props.Document.stackingHeadersSortDescending);
- let firstEntry = descending ? b : a;
- let secondEntry = descending ? a : b;
+ const descending = BoolCast(this.props.Document.stackingHeadersSortDescending);
+ const firstEntry = descending ? b : a;
+ const secondEntry = descending ? a : b;
return firstEntry[0].heading > secondEntry[0].heading ? 1 : -1;
}
@@ -368,30 +371,35 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
if (!e.isPropagationStopped()) {
- let subItems: ContextMenuProps[] = [];
+ const subItems: ContextMenuProps[] = [];
subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" });
subItems.push({ description: `${this.props.Document.showTitles ? "Hide Titles" : "Show Titles"}`, event: () => this.props.Document.showTitles = !this.props.Document.showTitles ? "title" : "", icon: "plus" });
subItems.push({ description: `${this.props.Document.showCaptions ? "Hide Captions" : "Show Captions"}`, event: () => this.props.Document.showCaptions = !this.props.Document.showCaptions ? "caption" : "", icon: "plus" });
ContextMenu.Instance.addItem({ description: "Stacking Options ...", subitems: subItems, icon: "eye" });
- let existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
- let onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
+ const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
+ const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
onClicks.push({ description: "Edit onChildClick script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Child Clicked...", this.props.Document, "onChildClick", obj.x, obj.y) });
!existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
}
}
+ @computed get renderedSections() {
+ TraceMobx();
+ let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
+ if (this.sectionFilter) {
+ const entries = Array.from(this.Sections.entries());
+ sections = entries.sort(this.sortFunc);
+ }
+ return sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]));
+ }
render() {
- let editableViewProps = {
+ TraceMobx();
+ const editableViewProps = {
GetValue: () => "",
SetValue: this.addGroup,
contents: "+ ADD A GROUP"
};
- let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
- if (this.sectionFilter) {
- let entries = Array.from(this.Sections.entries());
- sections = entries.sort(this.sortFunc);
- }
return (
<div className="collectionStackingMasonry-cont" >
<div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
@@ -399,8 +407,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
onDrop={this.onDrop.bind(this)}
onContextMenu={this.onContextMenu}
- onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
- {sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]))}
+ onWheel={e => e.stopPropagation()} >
+ {this.renderedSections}
{!this.showAddAGroup ? (null) :
<div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
style={{ width: !this.isStackingView ? "100%" : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index b9d334b10..39b4e4e1d 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -2,17 +2,14 @@ import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
import { faPalette } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, trace, runInAction } from "mobx";
+import { action, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, WidthSym } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { Doc } from "../../../new_fields/Doc";
import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
import { ScriptField } from "../../../new_fields/ScriptField";
import { NumCast, StrCast } from "../../../new_fields/Types";
-import { Utils } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DragManager } from "../../util/DragManager";
-import { CompileScript } from "../../util/Scripting";
import { SelectionManager } from "../../util/SelectionManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
@@ -20,6 +17,7 @@ import { anchorPoints, Flyout } from "../DocumentDecorations";
import { EditableView } from "../EditableView";
import { CollectionStackingView } from "./CollectionStackingView";
import "./CollectionStackingView.scss";
+import { TraceMobx } from "../../../new_fields/util";
library.add(faPalette);
@@ -53,28 +51,28 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
this._dropRef = ele;
this.dropDisposer && this.dropDisposer();
if (ele) {
- this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.columnDrop.bind(this) } });
+ this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this));
}
}
@undoBatch
columnDrop = action((e: Event, de: DragManager.DropEvent) => {
this._createAliasSelected = false;
- if (de.data instanceof DragManager.DocumentDragData) {
- let key = StrCast(this.props.parent.props.Document.sectionFilter);
- let castedValue = this.getValue(this._heading);
+ if (de.complete.docDragData) {
+ const key = StrCast(this.props.parent.props.Document.sectionFilter);
+ const castedValue = this.getValue(this._heading);
if (castedValue) {
- de.data.droppedDocuments.forEach(d => d[key] = castedValue);
+ de.complete.docDragData.droppedDocuments.forEach(d => d[key] = castedValue);
}
else {
- de.data.droppedDocuments.forEach(d => d[key] = undefined);
+ de.complete.docDragData.droppedDocuments.forEach(d => d[key] = undefined);
}
this.props.parent.drop(e, de);
e.stopPropagation();
}
});
getValue = (value: string): any => {
- let parsed = parseInt(value);
+ const parsed = parseInt(value);
if (!isNaN(parsed)) {
return parsed;
}
@@ -90,8 +88,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
headingChanged = (value: string, shiftDown?: boolean) => {
this._createAliasSelected = false;
- let key = StrCast(this.props.parent.props.Document.sectionFilter);
- let castedValue = this.getValue(value);
+ const key = StrCast(this.props.parent.props.Document.sectionFilter);
+ const castedValue = this.getValue(value);
if (castedValue) {
if (this.props.parent.sectionHeaders) {
if (this.props.parent.sectionHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
@@ -135,11 +133,11 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
addDocument = (value: string, shiftDown?: boolean) => {
this._createAliasSelected = false;
- let key = StrCast(this.props.parent.props.Document.sectionFilter);
- let newDoc = Docs.Create.TextDocument({ height: 18, width: 200, documentText: "@@@" + value, title: value, autoHeight: true });
+ const key = StrCast(this.props.parent.props.Document.sectionFilter);
+ const newDoc = Docs.Create.TextDocument({ height: 18, width: 200, documentText: "@@@" + value, title: value, autoHeight: true });
newDoc[key] = this.getValue(this.props.heading);
- let maxHeading = this.props.docList.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
- let heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
+ const maxHeading = this.props.docList.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
+ const heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
newDoc.heading = heading;
return this.props.parent.props.addDocument(newDoc);
}
@@ -147,10 +145,10 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
deleteColumn = () => {
this._createAliasSelected = false;
- let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ const key = StrCast(this.props.parent.props.Document.sectionFilter);
this.props.docList.forEach(d => d[key] = undefined);
if (this.props.parent.sectionHeaders && this.props.headingObject) {
- let index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
+ const index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
this.props.parent.sectionHeaders.splice(index, 1);
}
}
@@ -166,10 +164,10 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
}
startDrag = (e: PointerEvent) => {
- let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
+ const [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
- let alias = Doc.MakeAlias(this.props.parent.props.Document);
- let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ const alias = Doc.MakeAlias(this.props.parent.props.Document);
+ const key = StrCast(this.props.parent.props.Document.sectionFilter);
let value = this.getValue(this._heading);
value = typeof value === "string" ? `"${value}"` : value;
alias.viewSpecScript = ScriptField.MakeFunction(`doc.${key} === ${value}`, { doc: Doc.name });
@@ -195,7 +193,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
e.stopPropagation();
e.preventDefault();
- let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
+ const [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
this._startDragPosition = { x: dx, y: dy };
if (this._createAliasSelected) {
@@ -208,17 +206,17 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
}
renderColorPicker = () => {
- let selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
+ const selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
- let pink = PastelSchemaPalette.get("pink2");
- let purple = PastelSchemaPalette.get("purple4");
- let blue = PastelSchemaPalette.get("bluegreen1");
- let yellow = PastelSchemaPalette.get("yellow4");
- let red = PastelSchemaPalette.get("red2");
- let green = PastelSchemaPalette.get("bluegreen7");
- let cyan = PastelSchemaPalette.get("bluegreen5");
- let orange = PastelSchemaPalette.get("orange1");
- let gray = "#f1efeb";
+ const pink = PastelSchemaPalette.get("pink2");
+ const purple = PastelSchemaPalette.get("purple4");
+ const blue = PastelSchemaPalette.get("bluegreen1");
+ const yellow = PastelSchemaPalette.get("yellow4");
+ const red = PastelSchemaPalette.get("red2");
+ const green = PastelSchemaPalette.get("bluegreen7");
+ const cyan = PastelSchemaPalette.get("bluegreen5");
+ const orange = PastelSchemaPalette.get("orange1");
+ const gray = "#f1efeb";
return (
<div className="collectionStackingView-colorPicker">
@@ -243,7 +241,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
}
renderMenu = () => {
- let selected = this._createAliasSelected;
+ const selected = this._createAliasSelected;
return (
<div className="collectionStackingView-optionPicker">
<div className="optionOptions">
@@ -255,23 +253,22 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@observable private collapsed: boolean = false;
- private toggleVisibility = action(() => {
- this.collapsed = !this.collapsed;
- });
+ private toggleVisibility = action(() => this.collapsed = !this.collapsed);
@observable _headingsHack: number = 1;
render() {
- let cols = this.props.cols();
- let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ TraceMobx();
+ const cols = this.props.cols();
+ const key = StrCast(this.props.parent.props.Document.sectionFilter);
let templatecols = "";
- let headings = this.props.headings();
- let heading = this._heading;
- let style = this.props.parent;
- let singleColumn = style.isStackingView;
- let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
- let evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
- let headerEditableViewProps = {
+ const headings = this.props.headings();
+ const heading = this._heading;
+ const style = this.props.parent;
+ const singleColumn = style.isStackingView;
+ const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
+ const evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
+ const headerEditableViewProps = {
GetValue: () => evContents,
SetValue: this.headingChanged,
contents: evContents,
@@ -281,7 +278,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
toggle: this.toggleVisibility,
color: this._color
};
- let newEditableViewProps = {
+ const newEditableViewProps = {
GetValue: () => "",
SetValue: this.addDocument,
contents: "+ NEW",
@@ -290,7 +287,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
toggle: this.toggleVisibility,
color: this._color
};
- let headingView = this.props.headingObject ?
+ const headingView = this.props.headingObject ?
<div key={heading} className="collectionStackingView-sectionHeader" ref={this._headerRef}
style={{
width: (style.columnWidth) /
@@ -335,7 +332,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
</div>
</div> : (null);
for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth / style.numGroupColumns}px `;
- let chromeStatus = this.props.parent.props.Document.chromeStatus;
+ const chromeStatus = this.props.parent.props.Document.chromeStatus;
return (
<div className="collectionStackingViewFieldColumn" key={heading} style={{ width: `${100 / ((uniqueHeadings.length + ((chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`, background: this._background }}
ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx
index 40e860b12..105061f46 100644
--- a/src/client/views/collections/CollectionStaffView.tsx
+++ b/src/client/views/collections/CollectionStaffView.tsx
@@ -2,7 +2,7 @@ import { CollectionSubView } from "./CollectionSubView";
import { Transform } from "../../util/Transform";
import React = require("react");
import { computed, action, IReactionDisposer, reaction, runInAction, observable } from "mobx";
-import { Doc, HeightSym } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
import { NumCast } from "../../../new_fields/Types";
import "./CollectionStaffView.scss";
import { observer } from "mobx-react";
@@ -32,9 +32,9 @@ export class CollectionStaffView extends CollectionSubView(doc => doc) {
}
@computed get staves() {
- let staves = [];
+ const staves = [];
for (let i = 0; i < this._staves; i++) {
- let rows = [];
+ const rows = [];
for (let j = 0; j < 5; j++) {
rows.push(<div key={`staff-${i}-${j}`} className="collectionStaffView-line"></div>);
}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d7e9494a3..062521690 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -6,9 +6,8 @@ import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, StrCast } from "../../../new_fields/Types";
+import { Cast } from "../../../new_fields/Types";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
-import { RouteStore } from "../../../server/RouteStore";
import { Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { DocumentType } from "../../documents/DocumentTypes";
@@ -20,14 +19,15 @@ import { FieldViewProps } from "../nodes/FieldView";
import { FormattedTextBox, GoogleRef } from "../nodes/FormattedTextBox";
import { CollectionView } from "./CollectionView";
import React = require("react");
-var path = require('path');
+import { basename } from 'path';
import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils";
import { ImageUtils } from "../../util/Import & Export/ImageUtils";
+import { Networking } from "../../Network";
export interface CollectionViewProps extends FieldViewProps {
addDocument: (document: Doc) => boolean;
removeDocument: (document: Doc) => boolean;
- moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+ moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
PanelWidth: () => number;
PanelHeight: () => number;
VisibleHeight?: () => number;
@@ -51,7 +51,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
protected createDropTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
this.dropDisposer && this.dropDisposer();
if (ele) {
- this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+ this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
}
}
protected CreateDropTarget(ele: HTMLDivElement) { //used in schema view
@@ -92,7 +92,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
return Cast(this.dataField, listSpec(Doc));
}
get childDocs() {
- let docs = DocListCast(this.dataField);
+ const docs = DocListCast(this.dataField);
const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField);
return viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
}
@@ -100,10 +100,10 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@action
protected async setCursorPosition(position: [number, number]) {
let ind;
- let doc = this.props.Document;
- let id = CurrentUserUtils.id;
- let email = Doc.CurrentUserEmail;
- let pos = { x: position[0], y: position[1] };
+ const doc = this.props.Document;
+ const id = CurrentUserUtils.id;
+ const email = Doc.CurrentUserEmail;
+ const pos = { x: position[0], y: position[1] };
if (id && email) {
const proto = Doc.GetProto(doc);
if (!proto) {
@@ -123,7 +123,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.data.metadata.id === id)) > -1) {
cursors[ind].setPosition(pos);
} else {
- let entry = new CursorField({ metadata: { id: id, identifier: email, timestamp: Date.now() }, position: pos });
+ const entry = new CursorField({ metadata: { id: id, identifier: email, timestamp: Date.now() }, position: pos });
cursors.push(entry);
}
}
@@ -132,32 +132,33 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@undoBatch
@action
protected drop(e: Event, de: DragManager.DropEvent): boolean {
+ const docDragData = de.complete.docDragData;
(this.props.Document.dropConverter instanceof ScriptField) &&
- this.props.Document.dropConverter.script.run({ dragData: de.data });
- if (de.data instanceof DragManager.DocumentDragData && !de.data.applyAsTemplate) {
- if (de.mods === "AltKey" && de.data.draggedDocuments.length) {
+ this.props.Document.dropConverter.script.run({ dragData: docDragData }); /// bcz: check this
+ if (docDragData && !docDragData.applyAsTemplate) {
+ if (de.altKey && docDragData.draggedDocuments.length) {
this.childDocs.map(doc =>
- Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc, "layoutFromParent"));
+ Doc.ApplyTemplateTo(docDragData.draggedDocuments[0], doc, "layoutFromParent"));
e.stopPropagation();
return true;
}
let added = false;
- if (de.data.dropAction || de.data.userDropAction) {
- added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
- } else if (de.data.moveDocument) {
- let movedDocs = de.data.draggedDocuments;
+ if (docDragData.dropAction || docDragData.userDropAction) {
+ added = docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
+ } else if (docDragData.moveDocument) {
+ const movedDocs = docDragData.draggedDocuments;
added = movedDocs.reduce((added: boolean, d, i) =>
- de.data.droppedDocuments[i] !== d ? this.props.addDocument(de.data.droppedDocuments[i]) :
- de.data.moveDocument(d, this.props.Document, this.props.addDocument) || added, false);
+ docDragData.droppedDocuments[i] !== d ? this.props.addDocument(docDragData.droppedDocuments[i]) :
+ docDragData.moveDocument?.(d, this.props.Document, this.props.addDocument) || added, false);
} else {
- added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
+ added = docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
}
e.stopPropagation();
return added;
}
- else if (de.data instanceof DragManager.AnnotationDragData) {
+ else if (de.complete.annoDragData) {
e.stopPropagation();
- return this.props.addDocument(de.data.dropDocument);
+ return this.props.addDocument(de.complete.annoDragData.dropDocument);
}
return false;
}
@@ -169,8 +170,8 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl
return;
}
- let html = e.dataTransfer.getData("text/html");
- let text = e.dataTransfer.getData("text/plain");
+ const html = e.dataTransfer.getData("text/html");
+ const text = e.dataTransfer.getData("text/plain");
if (text && text.startsWith("<div")) {
return;
@@ -179,9 +180,9 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
e.preventDefault();
if (html && FormattedTextBox.IsFragment(html)) {
- let href = FormattedTextBox.GetHref(html);
+ const href = FormattedTextBox.GetHref(html);
if (href) {
- let docid = FormattedTextBox.GetDocFromUrl(href);
+ const docid = FormattedTextBox.GetDocFromUrl(href);
if (docid) { // prosemirror text containing link to dash document
DocServer.GetRefField(docid).then(f => {
if (f instanceof Doc) {
@@ -190,7 +191,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
}
});
} else {
- this.props.addDocument && this.props.addDocument(Docs.Create.WebDocument(href, options));
+ this.props.addDocument && this.props.addDocument(Docs.Create.WebDocument(href, { ...options, title: href }));
}
} else if (text) {
this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument({ ...options, width: 100, height: 25, documentText: "@@@" + text }));
@@ -198,19 +199,19 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
return;
}
if (html && !html.startsWith("<a")) {
- let tags = html.split("<");
+ const tags = html.split("<");
if (tags[0] === "") tags.splice(0, 1);
- let img = tags[0].startsWith("img") ? tags[0] : tags.length > 1 && tags[1].startsWith("img") ? tags[1] : "";
+ const img = tags[0].startsWith("img") ? tags[0] : tags.length > 1 && tags[1].startsWith("img") ? tags[1] : "";
if (img) {
- let split = img.split("src=\"")[1].split("\"")[0];
- let doc = Docs.Create.ImageDocument(split, { ...options, width: 300 });
+ const split = img.split("src=\"")[1].split("\"")[0];
+ const doc = Docs.Create.ImageDocument(split, { ...options, width: 300 });
ImageUtils.ExtractExif(doc);
this.props.addDocument(doc);
return;
} else {
- let path = window.location.origin + "/doc/";
+ const path = window.location.origin + "/doc/";
if (text.startsWith(path)) {
- let docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0];
+ const docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0];
DocServer.GetRefField(docid).then(f => {
if (f instanceof Doc) {
if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
@@ -218,7 +219,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
}
});
} else {
- let htmlDoc = Docs.Create.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text });
+ const htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", width: 300, height: 300, documentText: text });
this.props.addDocument(htmlDoc);
}
return;
@@ -231,8 +232,8 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
}
let matches: RegExpExecArray | null;
if ((matches = /(https:\/\/)?docs\.google\.com\/document\/d\/([^\\]+)\/edit/g.exec(text)) !== null) {
- let newBox = Docs.Create.TextDocument({ ...options, width: 400, height: 200, title: "Awaiting title from Google Docs..." });
- let proto = newBox.proto!;
+ const newBox = Docs.Create.TextDocument({ ...options, width: 400, height: 200, title: "Awaiting title from Google Docs..." });
+ const proto = newBox.proto!;
const documentId = matches[2];
proto[GoogleRef] = documentId;
proto.data = "Please select this document and then click on its pull button to load its contents from from Google Docs...";
@@ -249,59 +250,54 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
const mediaItems = await GooglePhotos.Query.AlbumSearch(albumId);
console.log(mediaItems);
}
- let batch = UndoManager.StartBatch("collection view drop");
- let promises: Promise<void>[] = [];
+ const batch = UndoManager.StartBatch("collection view drop");
+ const promises: Promise<void>[] = [];
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < e.dataTransfer.items.length; i++) {
- const upload = window.location.origin + RouteStore.upload;
- let item = e.dataTransfer.items[i];
+ const item = e.dataTransfer.items[i];
if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
let str: string;
- let prom = new Promise<string>(resolve => e.dataTransfer.items[i].getAsString(resolve))
+ const prom = new Promise<string>(resolve => e.dataTransfer.items[i].getAsString(resolve))
.then(action((s: string) => rp.head(Utils.CorsProxy(str = s))))
.then(result => {
- let type = result["content-type"];
+ const type = result["content-type"];
if (type) {
- Docs.Get.DocumentFromType(type, str, { ...options, width: 300, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300 })
+ Docs.Get.DocumentFromType(type, str, options)
.then(doc => doc && this.props.addDocument(doc));
}
});
promises.push(prom);
}
- let type = item.type;
+ const type = item.type;
if (item.kind === "file") {
- let file = item.getAsFile();
- let formData = new FormData();
+ const file = item.getAsFile();
+ const formData = new FormData();
- if (file) {
- formData.append('file', file);
+ if (!file || !file.type) {
+ continue;
}
- let dropFileName = file ? file.name : "-empty-";
- let prom = fetch(upload, {
- method: 'POST',
- body: formData
- }).then(async (res: Response) => {
- (await res.json()).map(action((file: any) => {
- let full = { ...options, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300, width: 300, title: dropFileName };
- let pathname = Utils.prepend(file.path);
+ formData.append('file', file);
+ const dropFileName = file ? file.name : "-empty-";
+ promises.push(Networking.PostFormDataToServer("/upload", formData).then(results => {
+ results.map(action(({ clientAccessPath }: any) => {
+ const full = { ...options, width: 300, title: dropFileName };
+ const pathname = Utils.prepend(clientAccessPath);
Docs.Get.DocumentFromType(type, pathname, full).then(doc => {
- doc && (Doc.GetProto(doc).fileUpload = path.basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, ""));
+ doc && (Doc.GetProto(doc).fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, ""));
doc && this.props.addDocument(doc);
});
}));
- });
- promises.push(prom);
+ }));
}
}
- if (text) {
- this.props.addDocument(Docs.Create.TextDocument({ ...options, documentText: "@@@" + text, width: 400, height: 315 }));
- return;
- }
if (promises.length) {
Promise.all(promises).finally(() => { completed && completed(); batch.end(); });
} else {
+ if (text && !text.includes("https://")) {
+ this.props.addDocument(Docs.Create.TextDocument({ ...options, documentText: "@@@" + text, width: 400, height: 315 }));
+ }
batch.end();
}
}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 7d0c900a6..0b9dc2eb2 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -15,6 +15,7 @@
background: $light-color-secondary;
font-size: 13px;
overflow: auto;
+ user-select: none;
cursor: default;
ul {
@@ -114,6 +115,9 @@
.treeViewItem-header {
border: transparent 1px solid;
display: flex;
+ .editableView-container-editing-oneLine {
+ min-width: 15px;
+ }
}
.treeViewItem-header-above {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 8b993820b..2b13d87ee 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,15 +1,15 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faAngleRight, faArrowsAltH, faBell, faCamera, faCaretDown, faCaretRight, faCaretSquareDown, faCaretSquareRight, faExpand, faMinus, faPlus, faTrash, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, untracked, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc';
+import { Doc, DocListCast, Field, HeightSym, WidthSym } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
import { List } from '../../../new_fields/List';
import { Document, listSpec } from '../../../new_fields/Schema';
import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types';
-import { emptyFunction, Utils, returnFalse } from '../../../Utils';
+import { emptyFunction, Utils, returnFalse, emptyPath } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
import { DocumentManager } from '../../util/DocumentManager';
@@ -33,24 +33,28 @@ import { CurrentUserUtils } from '../../../server/authentication/models/current_
export interface TreeViewProps {
document: Doc;
dataDoc?: Doc;
+ libraryPath: Doc[] | undefined;
containingCollection: Doc;
+ prevSibling?: Doc;
renderDepth: number;
deleteDoc: (doc: Doc) => boolean;
ruleProvider: Doc | undefined;
moveDocument: DragManager.MoveFunction;
dropAction: "alias" | "copy" | undefined;
- addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+ addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string, libraryPath?: Doc[]) => boolean;
pinToPres: (document: Doc) => void;
panelWidth: () => number;
panelHeight: () => number;
+ ChromeHeight: undefined | (() => number);
addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean;
indentDocument?: () => void;
+ outdentDocument?: () => void;
ScreenToLocalTransform: () => Transform;
outerXf: () => { translateX: number, translateY: number };
treeViewId: string;
parentKey: string;
active: (outsideReaction?: boolean) => boolean;
- showHeaderFields: () => boolean;
+ hideHeaderFields: () => boolean;
preventTreeViewOpen: boolean;
renderedIds: string[];
}
@@ -81,19 +85,22 @@ class TreeView extends React.Component<TreeViewProps> {
private _header?: React.RefObject<HTMLDivElement> = React.createRef();
private _treedropDisposer?: DragManager.DragDropDisposer;
private _dref = React.createRef<HTMLDivElement>();
+
+ get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive
+
get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.props.document.defaultExpandedView, "fields"); }
@observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
- set treeViewOpen(c: boolean) { if (this.props.preventTreeViewOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = c; }
- @computed get treeViewOpen() { return (BoolCast(this.props.document.treeViewOpen) && !this.props.preventTreeViewOpen) || this._overrideTreeViewOpen; }
+ set treeViewOpen(c: boolean) { if (this.props.preventTreeViewOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = this._overrideTreeViewOpen = c; }
+ @computed get treeViewOpen() { return (!this.props.preventTreeViewOpen && BoolCast(this.props.document.treeViewOpen)) || this._overrideTreeViewOpen; }
@computed get treeViewExpandedView() { return StrCast(this.props.document.treeViewExpandedView, this.defaultExpandedView); }
@computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.document.maxEmbedHeight, 300); }
@computed get dataDoc() { return this.templateDataDoc ? this.templateDataDoc : this.props.document; }
@computed get fieldKey() {
- let splits = StrCast(Doc.LayoutField(this.props.document)).split("fieldKey={\"");
- return splits.length > 1 ? splits[1].split("\"")[0] : "data";
+ const splits = StrCast(Doc.LayoutField(this.props.document)).split("fieldKey={\'");
+ return splits.length > 1 ? splits[1].split("\'")[0] : "data";
}
childDocList(field: string) {
- let layout = Doc.LayoutField(this.props.document) instanceof Doc ? Doc.LayoutField(this.props.document) as Doc : undefined;
+ const layout = Doc.LayoutField(this.props.document) instanceof Doc ? Doc.LayoutField(this.props.document) as Doc : undefined;
return ((this.props.dataDoc ? Cast(this.props.dataDoc[field], listSpec(Doc)) : undefined) ||
(layout ? Cast(layout[field], listSpec(Doc)) : undefined) ||
Cast(this.props.document[field], listSpec(Doc))) as Doc[];
@@ -109,14 +116,14 @@ class TreeView extends React.Component<TreeViewProps> {
return this.props.dataDoc;
}
@computed get boundsOfCollectionDocument() {
- return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 ? undefined :
+ return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 || !DocListCast(this.props.document[this.fieldKey]).length ? undefined :
Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
}
@undoBatch delete = () => this.props.deleteDoc(this.props.document);
- @undoBatch openRight = () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight");
+ @undoBatch openRight = () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight", this.props.libraryPath);
@undoBatch indent = () => this.props.addDocument(this.props.document) && this.delete();
- @undoBatch move = (doc: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => {
+ @undoBatch move = (doc: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => {
return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc);
}
@undoBatch @action remove = (document: Document, key: string) => {
@@ -125,7 +132,7 @@ class TreeView extends React.Component<TreeViewProps> {
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this._treedropDisposer && this._treedropDisposer();
- ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.treeDrop.bind(this) } }));
+ ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this)));
}
onPointerDown = (e: React.PointerEvent) => e.stopPropagation();
@@ -143,11 +150,10 @@ class TreeView extends React.Component<TreeViewProps> {
}
onDragMove = (e: PointerEvent): void => {
Doc.UnBrushDoc(this.dataDoc);
- let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
- let rect = this._header!.current!.getBoundingClientRect();
- let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
- let before = x[1] < bounds[1];
- let inside = x[0] > bounds[0] + 75;
+ const pt = [e.clientX, e.clientY];
+ const rect = this._header!.current!.getBoundingClientRect();
+ const before = pt[1] < rect.top + rect.height / 2;
+ const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && DocListCast(this.dataDoc[this.fieldKey]).length);
this._header!.current!.className = "treeViewItem-header";
if (inside) this._header!.current!.className += " treeViewItem-header-inside";
else if (before) this._header!.current!.className += " treeViewItem-header-above";
@@ -157,22 +163,30 @@ class TreeView extends React.Component<TreeViewProps> {
editableView = (key: string, style?: string) => (<EditableView
oneLine={true}
- display={"inline"}
+ display={"inline-block"}
editing={this.dataDoc[Id] === TreeView.loadId}
contents={StrCast(this.props.document[key])}
- height={36}
+ height={12}
fontStyle={style}
fontSize={12}
GetValue={() => StrCast(this.props.document[key])}
SetValue={undoBatch((value: string) => Doc.SetInPlace(this.props.document, key, value, false) || true)}
OnFillDown={undoBatch((value: string) => {
Doc.SetInPlace(this.props.document, key, value, false);
- let doc = this.props.document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.layoutCustom)) : undefined;
- if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
+ const layoutDoc = this.props.document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.layoutCustom)) : undefined;
+ const doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
TreeView.loadId = doc[Id];
return this.props.addDocument(doc);
})}
- OnTab={() => { TreeView.loadId = ""; this.props.indentDocument && this.props.indentDocument(); }}
+ OnTab={undoBatch((shift?: boolean) => {
+ TreeView.loadId = this.dataDoc[Id];
+ shift ? this.props.outdentDocument?.() : this.props.indentDocument?.();
+ setTimeout(() => { // unsetting/setting brushing for this doc will recreate & refocus this editableView after all other treeview changes have been made to the Dom (which may remove focus from this document).
+ Doc.UnBrushDoc(this.props.document);
+ Doc.BrushDoc(this.props.document);
+ TreeView.loadId = "";
+ }, 0);
+ })}
/>)
onWorkspaceContextMenu = (e: React.MouseEvent): void => {
@@ -181,18 +195,17 @@ class TreeView extends React.Component<TreeViewProps> {
ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.GetProto(CurrentUserUtils.UserDocument.recentlyClosed as Doc).data = new List<Doc>(), icon: "plus" });
} else if (this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.document), icon: "tv" });
- ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "inTab"), icon: "folder" });
- ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight"), icon: "caret-square-right" });
+ ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "inTab", this.props.libraryPath), icon: "folder" });
+ ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight", this.props.libraryPath), icon: "caret-square-right" });
if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) {
- ContextMenu.Instance.addItem({ description: "Focus", event: () => (view => view && view.props.focus(this.props.document, true))(DocumentManager.Instance.getFirstDocumentView(this.dataDoc)), icon: "camera" });
+ ContextMenu.Instance.addItem({ description: "Focus", event: () => (view => view && view.props.focus(this.props.document, true))(DocumentManager.Instance.getFirstDocumentView(this.props.document)), icon: "camera" });
}
ContextMenu.Instance.addItem({ description: "Delete Item", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
} else {
- ContextMenu.Instance.addItem({ description: "Open as Workspace", event: () => MainView.Instance.openWorkspace(this.dataDoc), icon: "caret-square-right" });
ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
ContextMenu.Instance.addItem({ description: "Create New Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
}
- ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.Create.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
+ ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" });
ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15);
e.stopPropagation();
@@ -202,52 +215,51 @@ class TreeView extends React.Component<TreeViewProps> {
@undoBatch
treeDrop = (e: Event, de: DragManager.DropEvent) => {
- let x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
- let rect = this._header!.current!.getBoundingClientRect();
- let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
- let before = x[1] < bounds[1];
- let inside = x[0] > bounds[0] + 75 || (!before && this.treeViewOpen);
- if (de.data instanceof DragManager.LinkDragData) {
- let sourceDoc = de.data.linkSourceDocument;
- let destDoc = this.props.document;
+ const pt = [de.x, de.y];
+ const rect = this._header!.current!.getBoundingClientRect();
+ const before = pt[1] < rect.top + rect.height / 2;
+ const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && DocListCast(this.dataDoc[this.fieldKey]).length);
+ if (de.complete.linkDragData) {
+ const sourceDoc = de.complete.linkDragData.linkSourceDocument;
+ const destDoc = this.props.document;
DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc });
e.stopPropagation();
}
- if (de.data instanceof DragManager.DocumentDragData) {
+ if (de.complete.docDragData) {
e.stopPropagation();
- if (de.data.draggedDocuments[0] === this.props.document) return true;
+ if (de.complete.docDragData.draggedDocuments[0] === this.props.document) return true;
let addDoc = (doc: Doc) => this.props.addDocument(doc, undefined, before);
if (inside) {
addDoc = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) || addDoc(doc);
}
- let movedDocs = (de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments);
- return (de.data.dropAction || de.data.userDropAction) ?
- de.data.droppedDocuments.reduce((added, d) => addDoc(d) || added, false)
- : de.data.moveDocument ?
- movedDocs.reduce((added, d) => de.data.moveDocument(d, undefined, addDoc) || added, false)
- : de.data.droppedDocuments.reduce((added, d) => addDoc(d), false);
+ const movedDocs = (de.complete.docDragData.treeViewId === this.props.treeViewId ? de.complete.docDragData.draggedDocuments : de.complete.docDragData.droppedDocuments);
+ return ((de.complete.docDragData.dropAction && (de.complete.docDragData.treeViewId !== this.props.treeViewId)) || de.complete.docDragData.userDropAction) ?
+ de.complete.docDragData.droppedDocuments.reduce((added, d) => addDoc(d) || added, false)
+ : de.complete.docDragData.moveDocument ?
+ movedDocs.reduce((added, d) => de.complete.docDragData?.moveDocument?.(d, undefined, addDoc) || added, false)
+ : de.complete.docDragData.droppedDocuments.reduce((added, d) => addDoc(d), false);
}
return false;
}
docTransform = () => {
- let { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!);
- let outerXf = this.props.outerXf();
- let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
- let finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
+ const { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!);
+ const outerXf = this.props.outerXf();
+ const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
+ const finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1] + (this.props.ChromeHeight && this.props.ChromeHeight() < 0 ? this.props.ChromeHeight() : 0));
return finalXf;
}
docWidth = () => {
- let layoutDoc = Doc.Layout(this.props.document);
- let aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth);
+ const layoutDoc = Doc.Layout(this.props.document);
+ const aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth);
if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20));
return NumCast(layoutDoc.nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20;
}
docHeight = () => {
- let layoutDoc = Doc.Layout(this.props.document);
- let bounds = this.boundsOfCollectionDocument;
+ const layoutDoc = Doc.Layout(this.props.document);
+ const bounds = this.boundsOfCollectionDocument;
return Math.min(this.MAX_EMBED_HEIGHT, (() => {
- let aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth, 1);
+ const aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth, 1);
if (aspect) return this.docWidth() * aspect;
if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
return layoutDoc.fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection.height) :
@@ -257,23 +269,24 @@ class TreeView extends React.Component<TreeViewProps> {
})());
}
- expandedField = (doc: Doc) => {
- let ids: { [key: string]: string } = {};
+ @computed get expandedField() {
+ const ids: { [key: string]: string } = {};
+ const doc = this.props.document;
doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
- let rows: JSX.Element[] = [];
- for (let key of Object.keys(ids).slice().sort()) {
- let contents = doc[key];
+ const rows: JSX.Element[] = [];
+ for (const key of Object.keys(ids).slice().sort()) {
+ const contents = doc[key];
let contentElement: (JSX.Element | null)[] | JSX.Element = [];
- if (contents instanceof Doc || Cast(contents, listSpec(Doc))) {
- let remDoc = (doc: Doc) => this.remove(doc, key);
- let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
+ if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc))) {
+ const remDoc = (doc: Doc) => this.remove(doc, key);
+ const addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
- DocListCast(contents), this.props.treeViewId, doc, undefined, key, addDoc, remDoc, this.move,
+ DocListCast(contents), this.props.treeViewId, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
- this.props.panelWidth, this.props.renderDepth, this.props.showHeaderFields, this.props.preventTreeViewOpen,
- [...this.props.renderedIds, doc[Id]]);
+ this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.hideHeaderFields, this.props.preventTreeViewOpen,
+ [...this.props.renderedIds, doc[Id]], this.props.libraryPath);
} else {
contentElement = <EditableView
key="editableView"
@@ -281,7 +294,7 @@ class TreeView extends React.Component<TreeViewProps> {
height={13}
fontSize={12}
GetValue={() => Field.toKeyValueString(doc, key)}
- SetValue={(value: string) => KeyValueBox.SetField(doc, key, value)} />;
+ SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} />;
}
rows.push(<div style={{ display: "flex" }} key={key}>
<span style={{ fontWeight: "bold" }}>{key + ":"}</span>
@@ -289,6 +302,18 @@ class TreeView extends React.Component<TreeViewProps> {
{contentElement}
</div>);
}
+ rows.push(<div style={{ display: "flex" }} key={"newKeyValue"}>
+ <EditableView
+ key="editableView"
+ contents={"+key:value"}
+ height={13}
+ fontSize={12}
+ GetValue={() => ""}
+ SetValue={(value: string) => {
+ value.indexOf(":") !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(":")), value.substring(value.indexOf(":") + 1, value.length), true);
+ return true;
+ }} />
+ </div>);
return rows;
}
@@ -297,28 +322,29 @@ class TreeView extends React.Component<TreeViewProps> {
@computed get renderContent() {
const expandKey = this.treeViewExpandedView === this.fieldKey ? this.fieldKey : this.treeViewExpandedView === "links" ? "links" : undefined;
if (expandKey !== undefined) {
- let remDoc = (doc: Doc) => this.remove(doc, expandKey);
- let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
- let docs = expandKey === "links" ? this.childLinks : this.childDocs;
+ const remDoc = (doc: Doc) => this.remove(doc, expandKey);
+ const addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
+ const docs = expandKey === "links" ? this.childLinks : this.childDocs;
return <ul key={expandKey + "more"}>
{!docs ? (null) :
TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document),
- this.templateDataDoc, expandKey, addDoc, remDoc, this.move,
+ this.templateDataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
- this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth, this.props.showHeaderFields, this.props.preventTreeViewOpen,
- [...this.props.renderedIds, this.props.document[Id]])}
+ this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.hideHeaderFields, this.props.preventTreeViewOpen,
+ [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath)}
</ul >;
} else if (this.treeViewExpandedView === "fields") {
return <ul><div ref={this._dref} style={{ display: "inline-block" }} key={this.props.document[Id] + this.props.document.title}>
- {this.expandedField(this.props.document)}
+ {this.expandedField}
</div></ul>;
} else {
- let layoutDoc = Doc.Layout(this.props.document);
+ const layoutDoc = Doc.Layout(this.props.document);
return <div ref={this._dref} style={{ display: "inline-block", height: this.docHeight() }} key={this.props.document[Id] + this.props.document.title}>
<ContentFittingDocumentView
Document={layoutDoc}
DataDocument={this.templateDataDoc}
- renderDepth={this.props.renderDepth}
+ LibraryPath={emptyPath}
+ renderDepth={this.props.renderDepth + 1}
showOverlays={this.noOverlays}
ruleProvider={this.props.document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.document : this.props.ruleProvider}
fitToBox={this.boundsOfCollectionDocument !== undefined}
@@ -350,10 +376,10 @@ class TreeView extends React.Component<TreeViewProps> {
*/
@computed
get renderTitle() {
- let reference = React.createRef<HTMLDivElement>();
- let onItemDown = SetupDrag(reference, () => this.dataDoc, this.move, this.props.dropAction, this.props.treeViewId, true);
+ const reference = React.createRef<HTMLDivElement>();
+ const onItemDown = SetupDrag(reference, () => this.dataDoc, this.move, this.props.dropAction, this.props.treeViewId, true);
- let headerElements = (
+ const headerElements = (
<span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
onPointerDown={action(() => {
if (this.treeViewOpen) {
@@ -366,26 +392,27 @@ class TreeView extends React.Component<TreeViewProps> {
})}>
{this.treeViewExpandedView}
</span>);
- let openRight = (<div className="treeViewItem-openRight" onPointerDown={this.onPointerDown} onClick={this.openRight}>
+ const openRight = (<div className="treeViewItem-openRight" onPointerDown={this.onPointerDown} onClick={this.openRight}>
<FontAwesomeIcon title="open in pane on right" icon="angle-right" size="lg" />
</div>);
return <>
<div className="docContainer" title="click to edit title" id={`docContainer-${this.props.parentKey}`} ref={reference} onPointerDown={onItemDown}
style={{
color: this.props.document.isMinimized ? "red" : "black",
- background: Doc.IsBrushed(this.props.document) ? "#06121212" : "0",
- fontWeight: this.props.document.search_string ? "bold" : undefined,
+ background: Doc.IsHighlighted(this.props.document) ? "orange" : Doc.IsBrushed(this.props.document) ? "#06121212" : "0",
+ fontWeight: this.props.document.searchMatch ? "bold" : undefined,
outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? "all" : "none"
}} >
{this.editableView("title")}
</div >
- {this.props.showHeaderFields() ? headerElements : (null)}
+ {this.props.hideHeaderFields() ? (null) : headerElements}
{openRight}
</>;
}
render() {
+ setTimeout(() => runInAction(() => untracked(() => this._overrideTreeViewOpen = this.treeViewOpen)), 0);
return <div className="treeViewItem-container" ref={this.createTreeDropTarget} onContextMenu={this.onWorkspaceContextMenu}>
<li className="collection-child">
<div className="treeViewItem-header" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
@@ -399,11 +426,13 @@ class TreeView extends React.Component<TreeViewProps> {
</div>;
}
public static GetChildElements(
- docs: Doc[],
+ childDocs: Doc[],
treeViewId: string,
containingCollection: Doc,
dataDoc: Doc | undefined,
key: string,
+ parentCollectionDoc: Doc | undefined,
+ parentPrevSibling: Doc | undefined,
add: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean,
remove: ((doc: Doc) => boolean),
move: DragManager.MoveFunction,
@@ -414,29 +443,46 @@ class TreeView extends React.Component<TreeViewProps> {
outerXf: () => { translateX: number, translateY: number },
active: (outsideReaction?: boolean) => boolean,
panelWidth: () => number,
+ ChromeHeight: undefined | (() => number),
renderDepth: number,
- showHeaderFields: () => boolean,
+ hideHeaderFields: () => boolean,
preventTreeViewOpen: boolean,
- renderedIds: string[]
+ renderedIds: string[],
+ libraryPath: Doc[] | undefined
) {
const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
if (viewSpecScript) {
- docs = docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
+ childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
}
- let ascending = Cast(containingCollection.sortAscending, "boolean", null);
+ const docs = childDocs.slice();
+ const dataExtension = containingCollection[key + "_ext"] as Doc;
+ const ascending = dataExtension && BoolCast(dataExtension.sortAscending, null);
if (ascending !== undefined) {
- docs.sort(function (a, b): 1 | -1 {
- let descA = ascending ? b : a;
- let descB = ascending ? a : b;
- let first = descA.title;
- let second = descB.title;
+
+ const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => {
+ const reN = /[0-9]*$/;
+ const aA = a.replace(reN, ""); // get rid of trailing numbers
+ const bA = b.replace(reN, "");
+ if (aA === bA) { // if header string matches, then compare numbers numerically
+ const aN = parseInt(a.match(reN)![0], 10);
+ const bN = parseInt(b.match(reN)![0], 10);
+ return aN === bN ? 0 : aN > bN ? 1 : -1;
+ } else {
+ return aA > bA ? 1 : -1;
+ }
+ };
+ docs.sort(function (a, b): 0 | 1 | -1 {
+ const descA = ascending ? b : a;
+ const descB = ascending ? a : b;
+ const first = descA.title;
+ const second = descB.title;
// TODO find better way to sort how to sort..................
if (typeof first === 'number' && typeof second === 'number') {
return (first - second) > 0 ? 1 : -1;
}
if (typeof first === 'string' && typeof second === 'string') {
- return first > second ? 1 : -1;
+ return sortAlphaNum(first, second);
}
if (typeof first === 'boolean' && typeof second === 'boolean') {
// if (first === second) { // bugfixing?: otherwise, the list "flickers" because the list is resorted during every load
@@ -448,17 +494,17 @@ class TreeView extends React.Component<TreeViewProps> {
});
}
- let rowWidth = () => panelWidth() - 20;
+ const rowWidth = () => panelWidth() - 20;
return docs.map((child, i) => {
const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, key, child);
if (!pair.layout || pair.data instanceof Promise) {
return (null);
}
- let indent = i === 0 ? undefined : () => {
- if (StrCast(docs[i - 1].layout).indexOf("fieldKey") !== -1) {
- let fieldKeysub = StrCast(docs[i - 1].layout).split("fieldKey")[1];
- let fieldKey = fieldKeysub.split("\"")[1];
+ const indent = i === 0 ? undefined : () => {
+ if (StrCast(docs[i - 1].layout).indexOf('fieldKey') !== -1) {
+ const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1];
+ const fieldKey = fieldKeysub.split("\'")[1];
if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) {
Doc.AddDocToList(docs[i - 1], fieldKey, child);
docs[i - 1].treeViewOpen = true;
@@ -466,27 +512,40 @@ class TreeView extends React.Component<TreeViewProps> {
}
}
};
- let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
+ const outdent = !parentCollectionDoc ? undefined : () => {
+ if (StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
+ const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1];
+ const fieldKey = fieldKeysub.split("\'")[1];
+ Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
+ parentCollectionDoc.treeViewOpen = true;
+ remove(child);
+ }
+ };
+ const addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
return add(doc, relativeTo ? relativeTo : docs[i], before !== undefined ? before : false);
};
const childLayout = Doc.Layout(pair.layout);
- let rowHeight = () => {
- let aspect = NumCast(childLayout.nativeWidth, 0) / NumCast(childLayout.nativeHeight, 0);
+ const rowHeight = () => {
+ const aspect = NumCast(childLayout.nativeWidth, 0) / NumCast(childLayout.nativeHeight, 0);
return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
};
return !(child instanceof Doc) ? (null) : <TreeView
document={pair.layout}
dataDoc={pair.data}
+ libraryPath={libraryPath ? [...libraryPath, containingCollection] : undefined}
containingCollection={containingCollection}
+ prevSibling={docs[i]}
treeViewId={treeViewId}
ruleProvider={containingCollection.isRuleProvider && pair.layout.type !== DocumentType.TEXT ? containingCollection : containingCollection.ruleProvider as Doc}
key={child[Id]}
indentDocument={indent}
+ outdentDocument={outdent}
renderDepth={renderDepth}
deleteDoc={remove}
addDocument={addDocument}
panelWidth={rowWidth}
panelHeight={rowHeight}
+ ChromeHeight={ChromeHeight}
moveDocument={move}
dropAction={dropAction}
addDocTab={addDocTab}
@@ -495,7 +554,7 @@ class TreeView extends React.Component<TreeViewProps> {
outerXf={outerXf}
parentKey={key}
active={active}
- showHeaderFields={showHeaderFields}
+ hideHeaderFields={hideHeaderFields}
preventTreeViewOpen={preventTreeViewOpen}
renderedIds={renderedIds} />;
});
@@ -512,7 +571,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this.treedropDisposer && this.treedropDisposer();
if (this._mainEle = ele) {
- this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+ this.treedropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
}
}
@@ -523,7 +582,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
@action
remove = (document: Document): boolean => {
- let children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+ const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
if (children.indexOf(document) !== -1) {
children.splice(children.indexOf(document), 1);
return true;
@@ -544,8 +603,9 @@ export class CollectionTreeView extends CollectionSubView(Document) {
e.preventDefault();
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
} else {
- let layoutItems: ContextMenuProps[] = [];
- layoutItems.push({ description: this.props.Document.preventTreeViewOpen ? "Persist Treeview State" : "Abandon Treeview State", event: () => this.props.Document.preventTreeViewOpen = !this.props.Document.preventTreeViewOpen, icon: "paint-brush" });
+ const layoutItems: ContextMenuProps[] = [];
+ layoutItems.push({ description: (this.props.Document.preventTreeViewOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.preventTreeViewOpen = !this.props.Document.preventTreeViewOpen, icon: "paint-brush" });
+ layoutItems.push({ description: (this.props.Document.hideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.hideHeaderFields = !this.props.Document.hideHeaderFields, icon: "paint-brush" });
ContextMenu.Instance.addItem({ description: "Treeview Options ...", subitems: layoutItems, icon: "eye" });
}
}
@@ -562,12 +622,12 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
render() {
- let dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
- let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false);
- let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
+ const dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
+ const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false);
+ const moveDoc = (d: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
return !this.childDocs ? (null) : (
- <div id="body" className="collectionTreeView-dropTarget"
- style={{ overflow: "auto", background: StrCast(this.props.Document.backgroundColor, "lightgray"), paddingTop: `${NumCast(this.props.Document.yMargin, 20)}px` }}
+ <div className="collectionTreeView-dropTarget" id="body"
+ style={{ background: StrCast(this.props.Document.backgroundColor, "lightgray"), paddingTop: `${NumCast(this.props.Document.yMargin, 20)}px` }}
onContextMenu={this.onContextMenu}
onWheel={(e: React.WheelEvent) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
onDrop={this.onTreeDrop}
@@ -581,18 +641,18 @@ export class CollectionTreeView extends CollectionSubView(Document) {
SetValue={undoBatch((value: string) => Doc.SetInPlace(this.dataDoc, "title", value, false) || true)}
OnFillDown={undoBatch((value: string) => {
Doc.SetInPlace(this.dataDoc, "title", value, false);
- let doc = this.props.Document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.layoutCustom)) : undefined;
- if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
+ const layoutDoc = this.props.Document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.layoutCustom)) : undefined;
+ const doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
TreeView.loadId = doc[Id];
Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true, false, false, false);
})} />
{this.props.Document.allowClear ? this.renderClearButton : (null)}
<ul className="no-indent" style={{ width: "max-content" }} >
{
- TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove,
+ TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
- this.outerXf, this.props.active, this.props.PanelWidth, this.props.renderDepth, () => !this.props.Document.hideHeaderFields,
- BoolCast(this.props.Document.preventTreeViewOpen), [])
+ this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => BoolCast(this.props.Document.hideHeaderFields),
+ BoolCast(this.props.Document.preventTreeViewOpen), [], this.props.LibraryPath)
}
</ul>
</div >
diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss
index e4187e4d6..1c46081a1 100644
--- a/src/client/views/collections/CollectionView.scss
+++ b/src/client/views/collections/CollectionView.scss
@@ -9,7 +9,7 @@
border-radius: inherit;
width: 100%;
height: 100%;
- overflow: auto;
+ overflow: hidden; // bcz: used to be 'auto' which would create scrollbars when there's a floating doc that's not visible. not sure if that's better, but the scrollbars are annoying...
}
#google-tags {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 8387e95df..88023783b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -32,6 +32,9 @@ import { SelectionManager } from '../../util/SelectionManager';
import './CollectionView.scss';
import { FieldViewProps, FieldView } from '../nodes/FieldView';
import { Touchable } from '../Touchable';
+import { TraceMobx } from '../../../new_fields/util';
+import { Utils } from '../../../Utils';
+const path = require('path');
library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy);
export enum CollectionViewType {
@@ -66,7 +69,7 @@ export namespace CollectionViewType {
export interface CollectionRenderProps {
addDocument: (document: Doc) => boolean;
removeDocument: (document: Doc) => boolean;
- moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+ moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
active: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
}
@@ -84,7 +87,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; }
get collectionViewType(): CollectionViewType | undefined {
- let viewField = Cast(this.props.Document.viewType, "number");
+ const viewField = Cast(this.props.Document.viewType, "number");
if (CollectionView._safeMode) {
if (viewField === CollectionViewType.Freeform) {
return CollectionViewType.Tree;
@@ -101,7 +104,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
() => {
// chrome status is one of disabled, collapsed, or visible. this determines initial state from document
// chrome status may also be view-mode, in reference to stacking view's toggle mode. it is essentially disabled mode, but prevents the toggle button from showing up on the left sidebar.
- let chromeStatus = this.props.Document.chromeStatus;
+ const chromeStatus = this.props.Document.chromeStatus;
if (chromeStatus && (chromeStatus === "disabled" || chromeStatus === "collapsed")) {
runInAction(() => this._collapsed = true);
}
@@ -111,7 +114,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
componentWillUnmount = () => this._reactionDisposer && this._reactionDisposer();
// bcz: Argh? What's the height of the collection chromes??
- chromeHeight = () => (this.props.ChromeHeight ? this.props.ChromeHeight() : 0) + (this.props.Document.chromeStatus === "enabled" ? -60 : 0);
+ chromeHeight = () => (this.props.Document.chromeStatus === "enabled" ? -60 : 0);
active = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0;
@@ -119,9 +122,9 @@ export class CollectionView extends Touchable<FieldViewProps> {
@action.bound
addDocument(doc: Doc): boolean {
- let targetDataDoc = Doc.GetProto(this.props.Document);
+ const targetDataDoc = Doc.GetProto(this.props.Document);
Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
- let extension = Doc.fieldExtensionDoc(targetDataDoc, this.props.fieldKey); // set metadata about the field being rendered (ie, the set of documents) on an extension field for that field
+ const extension = Doc.fieldExtensionDoc(targetDataDoc, this.props.fieldKey); // set metadata about the field being rendered (ie, the set of documents) on an extension field for that field
extension && (extension.lastModified = new DateField(new Date(Date.now())));
Doc.GetProto(doc).lastOpened = new DateField;
return true;
@@ -129,9 +132,9 @@ export class CollectionView extends Touchable<FieldViewProps> {
@action.bound
removeDocument(doc: Doc): boolean {
- let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
+ const docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
docView && SelectionManager.DeselectDoc(docView);
- let value = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+ const value = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
@@ -148,7 +151,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
// otherwise, the document being moved must be able to be removed from its container before
// moving it into the target.
@action.bound
- moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
+ moveDocument(doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean {
if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
return true;
}
@@ -163,7 +166,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
}
private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
- let props = { ...this.props, ...renderProps, chromeCollapsed: this._collapsed, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
+ const props = { ...this.props, ...renderProps, chromeCollapsed: this._collapsed, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
switch (type) {
case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
@@ -186,7 +189,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
// currently cant think of a reason for collection docking view to have a chrome. mind may change if we ever have nested docking views -syip
- let chrome = this.props.Document.chromeStatus === "disabled" || type === CollectionViewType.Docking ? (null) :
+ const chrome = this.props.Document.chromeStatus === "disabled" || type === CollectionViewType.Docking ? (null) :
<CollectionViewBaseChrome CollectionView={this} key="chrome" type={type} collapse={this.collapse} />;
return [chrome, this.SubViewHelper(type, renderProps)];
}
@@ -194,8 +197,8 @@ export class CollectionView extends Touchable<FieldViewProps> {
onContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
- let existingVm = ContextMenu.Instance.findByDescription("View Modes...");
- let subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
+ const existingVm = ContextMenu.Instance.findByDescription("View Modes...");
+ const subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
subItems.push({ description: "Freeform", event: () => { this.props.Document.viewType = CollectionViewType.Freeform; }, icon: "signature" });
if (CollectionView._safeMode) {
ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" });
@@ -221,28 +224,36 @@ export class CollectionView extends Touchable<FieldViewProps> {
subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
!existingVm && ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
- let existing = ContextMenu.Instance.findByDescription("Layout...");
- let layoutItems = existing && "subitems" in existing ? existing.subitems : [];
+ const existing = ContextMenu.Instance.findByDescription("Layout...");
+ const layoutItems = existing && "subitems" in existing ? existing.subitems : [];
layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
!existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" });
- let more = ContextMenu.Instance.findByDescription("More...");
- let moreItems = more && "subitems" in more ? more.subitems : [];
+ const more = ContextMenu.Instance.findByDescription("More...");
+ const moreItems = more && "subitems" in more ? more.subitems : [];
moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) });
!more && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
}
}
lightbox = (images: string[]) => {
+ if (!images.length) return (null);
+ const mainPath = path.extname(images[this._curLightboxImg]);
+ const nextPath = path.extname(images[(this._curLightboxImg + 1) % images.length]);
+ const prevPath = path.extname(images[(this._curLightboxImg + images.length - 1) % images.length]);
+ const main = images[this._curLightboxImg].replace(mainPath, "_o" + mainPath);
+ const next = images[(this._curLightboxImg + 1) % images.length].replace(nextPath, "_o" + nextPath);
+ const prev = images[(this._curLightboxImg + images.length - 1) % images.length].replace(prevPath, "_o" + prevPath);
return !this._isLightboxOpen ? (null) : (<Lightbox key="lightbox"
- mainSrc={images[this._curLightboxImg]}
- nextSrc={images[(this._curLightboxImg + 1) % images.length]}
- prevSrc={images[(this._curLightboxImg + images.length - 1) % images.length]}
+ mainSrc={main}
+ nextSrc={next}
+ prevSrc={prev}
onCloseRequest={action(() => this._isLightboxOpen = false)}
onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)}
onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />);
}
render() {
+ TraceMobx();
const props: CollectionRenderProps = {
addDocument: this.addDocument,
removeDocument: this.removeDocument,
@@ -258,7 +269,12 @@ export class CollectionView extends Touchable<FieldViewProps> {
onContextMenu={this.onContextMenu}>
{this.showIsTagged()}
{this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
- {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => d.type === DocumentType.IMG).map(d => Cast(d.data, ImageField) ? Cast(d.data, ImageField)!.url.href : ""))}
+ {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => d.type === DocumentType.IMG).map(d =>
+ Cast(d.data, ImageField) ?
+ (Cast(d.data, ImageField)!.url.href.indexOf(window.location.origin) === -1) ?
+ Utils.CorsProxy(Cast(d.data, ImageField)!.url.href) : Cast(d.data, ImageField)!.url.href
+ :
+ ""))}
</div>);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index cfc6c2a3f..a870b6043 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -13,7 +13,6 @@ import { DragManager } from "../../util/DragManager";
import { undoBatch } from "../../util/UndoManager";
import { EditableView } from "../EditableView";
import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss";
-import { DocLike } from "../MetadataEntryMenu";
import { CollectionViewType } from "./CollectionView";
import { CollectionView } from "./CollectionView";
import "./CollectionViewChromes.scss";
@@ -33,7 +32,7 @@ interface Filter {
contains: boolean;
}
-let stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
+const stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
@observer
export class CollectionViewBaseChrome extends React.Component<CollectionViewChromeProps> {
@@ -80,11 +79,11 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
@computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); }
getFilters = (script: string) => {
- let re: any = /(!)?\(\(\(doc\.(\w+)\s+&&\s+\(doc\.\w+\s+as\s+\w+\)\.includes\(\"(\w+)\"\)/g;
- let arr: any[] = re.exec(script);
- let toReturn: Filter[] = [];
+ const re: any = /(!)?\(\(\(doc\.(\w+)\s+&&\s+\(doc\.\w+\s+as\s+\w+\)\.includes\(\"(\w+)\"\)/g;
+ const arr: any[] = re.exec(script);
+ const toReturn: Filter[] = [];
if (arr !== null) {
- let filter: Filter = {
+ const filter: Filter = {
key: arr[2],
value: arr[3],
contains: (arr[1] === "!") ? false : true,
@@ -120,14 +119,14 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
let fields: Filter[] = [];
if (this.filterValue) {
- let string = this.filterValue.script.originalScript;
+ const string = this.filterValue.script.originalScript;
fields = this.getFilters(string);
}
runInAction(() => {
this.addKeyRestrictions(fields);
// chrome status is one of disabled, collapsed, or visible. this determines initial state from document
- let chromeStatus = this.props.CollectionView.props.Document.chromeStatus;
+ const chromeStatus = this.props.CollectionView.props.Document.chromeStatus;
if (chromeStatus) {
if (chromeStatus === "disabled") {
throw new Error("how did you get here, if chrome status is 'disabled' on a collection, a chrome shouldn't even be instantiated!");
@@ -183,7 +182,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
@action
addKeyRestriction = (e: React.MouseEvent) => {
- let index = this._keyRestrictions.length;
+ const index = this._keyRestrictions.length;
this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[index][1] = value)} />, ""]);
this.openViewSpecs(e);
@@ -194,26 +193,26 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
this.openViewSpecs(e);
- let keyRestrictionScript = "(" + this._keyRestrictions.map(i => i[1]).filter(i => i.length > 0).join(" && ") + ")";
- let yearOffset = this._dateWithinValue[1] === 'y' ? 1 : 0;
- let monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0;
- let weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0;
- let dayOffset = (this._dateWithinValue[1] === 'd' ? parseInt(this._dateWithinValue[0]) : 0) + weekOffset * 7;
+ const keyRestrictionScript = "(" + this._keyRestrictions.map(i => i[1]).filter(i => i.length > 0).join(" && ") + ")";
+ const yearOffset = this._dateWithinValue[1] === 'y' ? 1 : 0;
+ const monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0;
+ const weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0;
+ const dayOffset = (this._dateWithinValue[1] === 'd' ? parseInt(this._dateWithinValue[0]) : 0) + weekOffset * 7;
let dateRestrictionScript = "";
if (this._dateValue instanceof Date) {
- let lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset);
- let upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1);
+ const lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset);
+ const upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1);
dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`;
}
else {
- let createdDate = new Date(this._dateValue);
+ const createdDate = new Date(this._dateValue);
if (!isNaN(createdDate.getTime())) {
- let lowerBound = new Date(createdDate.getFullYear() - yearOffset, createdDate.getMonth() - monthOffset, createdDate.getDate() - dayOffset);
- let upperBound = new Date(createdDate.getFullYear() + yearOffset, createdDate.getMonth() + monthOffset, createdDate.getDate() + dayOffset + 1);
+ const lowerBound = new Date(createdDate.getFullYear() - yearOffset, createdDate.getMonth() - monthOffset, createdDate.getDate() - dayOffset);
+ const upperBound = new Date(createdDate.getFullYear() + yearOffset, createdDate.getMonth() + monthOffset, createdDate.getDate() + dayOffset + 1);
dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`;
}
}
- let fullScript = dateRestrictionScript.length || keyRestrictionScript.length ? dateRestrictionScript.length ?
+ const fullScript = dateRestrictionScript.length || keyRestrictionScript.length ? dateRestrictionScript.length ?
`${dateRestrictionScript} ${keyRestrictionScript.length ? "&&" : ""} (${keyRestrictionScript})` :
`(${keyRestrictionScript}) ${dateRestrictionScript.length ? "&&" : ""} ${dateRestrictionScript}` :
"true";
@@ -270,7 +269,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
value={this.pivotKeyDisplay}
onChange={action((e: React.ChangeEvent<HTMLInputElement>) => this.pivotKeyDisplay = e.currentTarget.value)}
onKeyPress={action((e: React.KeyboardEvent<HTMLInputElement>) => {
- let value = e.currentTarget.value;
+ const value = e.currentTarget.value;
if (e.which === 13) {
this.pivotKey = value;
this.pivotKeyDisplay = "";
@@ -289,15 +288,15 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
protected createDropTarget = (ele: HTMLDivElement) => {
this.dropDisposer && this.dropDisposer();
if (ele) {
- this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+ this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
}
}
@undoBatch
@action
protected drop(e: Event, de: DragManager.DropEvent): boolean {
- if (de.data instanceof DragManager.DocumentDragData && de.data.draggedDocuments.length) {
- this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => c.immediate(de.data.draggedDocuments));
+ if (de.complete.docDragData && de.complete.docDragData.draggedDocuments.length) {
+ this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => c.immediate(de.complete.docDragData?.draggedDocuments || []));
e.stopPropagation();
}
return true;
@@ -357,7 +356,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
dragPointerMove = (e: PointerEvent) => {
e.stopPropagation();
e.preventDefault();
- let [dx, dy] = [e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y];
+ const [dx, dy] = [e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y];
if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c =>
DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title,
@@ -373,7 +372,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
}
render() {
- let collapsed = this.props.CollectionView.props.Document.chromeStatus !== "enabled";
+ const collapsed = this.props.CollectionView.props.Document.chromeStatus !== "enabled";
return (
<div className="collectionViewChrome-cont" style={{ top: collapsed ? -70 : 0, height: collapsed ? 0 : undefined }}>
<div className="collectionViewChrome">
@@ -480,7 +479,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
getKeySuggestions = async (value: string): Promise<string[]> => {
value = value.toLowerCase();
- let docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
+ const docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
if (docs instanceof Doc) {
return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value));
} else {
@@ -571,31 +570,31 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh
@undoBatch
togglePreview = () => {
- let dividerWidth = 4;
- let borderWidth = Number(COLLECTION_BORDER_WIDTH);
- let panelWidth = this.props.CollectionView.props.PanelWidth();
- let previewWidth = NumCast(this.props.CollectionView.props.Document.schemaPreviewWidth);
- let tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth;
+ const dividerWidth = 4;
+ const borderWidth = Number(COLLECTION_BORDER_WIDTH);
+ const panelWidth = this.props.CollectionView.props.PanelWidth();
+ const previewWidth = NumCast(this.props.CollectionView.props.Document.schemaPreviewWidth);
+ const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth;
this.props.CollectionView.props.Document.schemaPreviewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0;
}
@undoBatch
@action
toggleTextwrap = async () => {
- let textwrappedRows = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []);
+ const textwrappedRows = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []);
if (textwrappedRows.length) {
this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>([]);
} else {
- let docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
- let allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
+ const docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
+ const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows);
}
}
render() {
- let previewWidth = NumCast(this.props.CollectionView.props.Document.schemaPreviewWidth);
- let textWrapped = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
+ const previewWidth = NumCast(this.props.CollectionView.props.Document.schemaPreviewWidth);
+ const textWrapped = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
return (
<div className="collectionSchemaViewChrome-cont">
@@ -624,12 +623,19 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh
@observer
export class CollectionTreeViewChrome extends React.Component<CollectionViewChromeProps> {
- @computed private get descending() { return Cast(this.props.CollectionView.props.Document.sortAscending, "boolean", null); }
+ get dataExtension() {
+ return this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey + "_ext"] as Doc;
+ }
+ @computed private get descending() {
+ return this.dataExtension && Cast(this.dataExtension.sortAscending, "boolean", null);
+ }
@action toggleSort = () => {
- if (this.props.CollectionView.props.Document.sortAscending) this.props.CollectionView.props.Document.sortAscending = undefined;
- else if (this.props.CollectionView.props.Document.sortAscending === undefined) this.props.CollectionView.props.Document.sortAscending = false;
- else this.props.CollectionView.props.Document.sortAscending = true;
+ if (this.dataExtension) {
+ if (this.dataExtension.sortAscending) this.dataExtension.sortAscending = undefined;
+ else if (this.dataExtension.sortAscending === undefined) this.dataExtension.sortAscending = false;
+ else this.dataExtension.sortAscending = true;
+ }
}
render() {
diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx
index e35b7d7d3..f3071b316 100644
--- a/src/client/views/collections/KeyRestrictionRow.tsx
+++ b/src/client/views/collections/KeyRestrictionRow.tsx
@@ -1,8 +1,6 @@
import * as React from "react";
import { observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField";
-import { Doc } from "../../../new_fields/Doc";
interface IKeyRestrictionProps {
contains: boolean;
@@ -20,13 +18,13 @@ export default class KeyRestrictionRow extends React.Component<IKeyRestrictionPr
render() {
if (this._key && this._value) {
let parsedValue: string | number = `"${this._value}"`;
- let parsed = parseInt(this._value);
+ const parsed = parseInt(this._value);
let type = "string";
if (!isNaN(parsed)) {
parsedValue = parsed;
type = "number";
}
- let scriptText = `${this._contains ? "" : "!"}(((doc.${this._key} && (doc.${this._key} as ${type})${type === "string" ? ".includes" : "<="}(${parsedValue}))) ||
+ const scriptText = `${this._contains ? "" : "!"}(((doc.${this._key} && (doc.${this._key} as ${type})${type === "string" ? ".includes" : "<="}(${parsedValue}))) ||
((doc.data_ext && doc.data_ext.${this._key}) && (doc.data_ext.${this._key} as ${type})${type === "string" ? ".includes" : "<="}(${parsedValue}))))`;
// let doc = new Doc();
// ((doc.data_ext && doc.data_ext!.text) && (doc.data_ext!.text as string).includes("hello"));
diff --git a/src/client/views/collections/ParentDocumentSelector.scss b/src/client/views/collections/ParentDocumentSelector.scss
index aa25a900c..d293bb5ca 100644
--- a/src/client/views/collections/ParentDocumentSelector.scss
+++ b/src/client/views/collections/ParentDocumentSelector.scss
@@ -1,14 +1,25 @@
-.PDS-flyout {
- position: absolute;
+.parentDocumentSelector-linkFlyout {
+ div {
+ overflow: visible !important;
+ }
+ .metadataEntry-outerDiv {
+ overflow: hidden !important;
+ pointer-events: all;
+ }
+}
+.parentDocumentSelector-flyout {
+ position: relative;
z-index: 9999;
background-color: #eeeeee;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
- min-width: 150px;
color: black;
- top: 12px;
padding: 10px;
border-radius: 3px;
+ display: inline-block;
+ height: 100%;
+ width: 100%;
+ border-radius: 3px;
hr {
height: 1px;
@@ -21,7 +32,11 @@
}
}
.parentDocumentSelector-button {
- pointer-events: all;
+ pointer-events: all;
+ position: relative;
+ display: inline-block;
+ padding-left: 5px;
+ padding-right: 5px;
}
.parentDocumentSelector-metadata {
pointer-events: auto;
@@ -30,6 +45,9 @@
display: inline-block;
}
.buttonSelector {
+ div {
+ overflow: visible !important;
+ }
position: absolute;
display: inline-block;
padding-left: 5px;
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 4eb9e9d1e..24aa6ddfa 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -11,7 +11,7 @@ import { CollectionViewType } from "./CollectionView";
import { DocumentButtonBar } from "../DocumentButtonBar";
import { DocumentManager } from "../../util/DocumentManager";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faEdit } from "@fortawesome/free-solid-svg-icons";
+import { faEdit, faChevronCircleUp } from "@fortawesome/free-solid-svg-icons";
import { library } from "@fortawesome/fontawesome-svg-core";
import { MetadataEntryMenu } from "../MetadataEntryMenu";
import { DocumentView } from "../nodes/DocumentView";
@@ -34,7 +34,7 @@ export class SelectorContextMenu extends React.Component<SelectorProps> {
}
async fetchDocuments() {
- let aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document)).filter(doc => doc !== this.props.Document);
+ const aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document)).filter(doc => doc !== this.props.Document);
const { docs } = await SearchUtil.Search("", true, { fq: `data_l:"${this.props.Document[Id]}"` });
const map: Map<Doc, Doc> = new Map;
const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", true, { fq: `data_l:"${doc[Id]}"` }).then(result => result.docs)));
@@ -80,35 +80,20 @@ export class SelectorContextMenu extends React.Component<SelectorProps> {
@observer
export class ParentDocSelector extends React.Component<SelectorProps> {
- @observable hover = false;
-
- @action
- onMouseLeave = () => {
- this.hover = false;
- }
-
- @action
- onMouseEnter = () => {
- this.hover = true;
- }
-
render() {
- let flyout;
- if (this.hover) {
- flyout = (
- <div className="PDS-flyout" title=" ">
- <SelectorContextMenu {...this.props} />
- </div>
- );
- }
- return (
- <span className="parentDocumentSelector-button" style={{ position: "relative", display: "inline-block", paddingLeft: "5px", paddingRight: "5px" }}
- onMouseEnter={this.onMouseEnter}
- onMouseLeave={this.onMouseLeave}>
- <p>^</p>
- {flyout}
- </span>
+ const flyout = (
+ <div className="parentDocumentSelector-flyout" style={{}} title=" ">
+ <SelectorContextMenu {...this.props} />
+ </div>
);
+ return <div title="Tap to View Contexts/Metadata" onPointerDown={e => e.stopPropagation()} className="parentDocumentSelector-linkFlyout">
+ <Flyout anchorPoint={anchorPoints.LEFT_TOP}
+ content={flyout}>
+ <span className="parentDocumentSelector-button" >
+ <FontAwesomeIcon icon={faChevronCircleUp} size={"lg"} />
+ </span>
+ </Flyout>
+ </div>;
}
}
@@ -117,32 +102,31 @@ export class ButtonSelector extends React.Component<{ Document: Doc, Stack: any
@observable hover = false;
@action
- onMouseLeave = () => {
- this.hover = false;
+ onPointerDown = (e: React.PointerEvent) => {
+ this.hover = !this.hover;
+ e.stopPropagation();
}
-
- @action
- onMouseEnter = () => {
- this.hover = true;
+ customStylesheet(styles: any) {
+ return {
+ ...styles,
+ panel: {
+ ...styles.panel,
+ minWidth: "100px"
+ },
+ };
}
render() {
- let flyout;
- if (this.hover) {
- let view = DocumentManager.Instance.getDocumentView(this.props.Document);
- flyout = !view ? (null) : (
- <div className="PDS-flyout" title=" " onMouseLeave={this.onMouseLeave}>
- <DocumentButtonBar views={[view]} stack={this.props.Stack} />
- </div>
- );
- }
- return (
- <span className="buttonSelector"
- onMouseEnter={this.onMouseEnter}
- onMouseLeave={this.onMouseLeave}>
- {this.hover ? (null) : <FontAwesomeIcon icon={faEdit} size={"sm"} />}
- {flyout}
- </span>
+ const view = DocumentManager.Instance.getDocumentView(this.props.Document);
+ const flyout = (
+ <div className="ParentDocumentSelector-flyout" title=" ">
+ <DocumentButtonBar views={[view]} stack={this.props.Stack} />
+ </div>
);
+ return <span title="Tap for menu" onPointerDown={e => e.stopPropagation()} className="buttonSelector">
+ <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout} stylesheet={this.customStylesheet}>
+ <FontAwesomeIcon icon={faEdit} size={"sm"} />
+ </Flyout>
+ </span>;
}
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index e1d23ddcb..012115b1f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,5 +1,5 @@
import { Doc, Field, FieldResult } from "../../../../new_fields/Doc";
-import { NumCast, StrCast, Cast } from "../../../../new_fields/Types";
+import { NumCast, StrCast, Cast, DateCast } from "../../../../new_fields/Types";
import { ScriptBox } from "../../ScriptBox";
import { CompileScript } from "../../../util/Scripting";
import { ScriptField } from "../../../../new_fields/ScriptField";
@@ -8,6 +8,7 @@ import { emptyFunction } from "../../../../Utils";
import React = require("react");
import { ObservableMap, runInAction } from "mobx";
import { Id } from "../../../../new_fields/FieldSymbols";
+import { DateField } from "../../../../new_fields/DateField";
interface PivotData {
type: string;
@@ -33,6 +34,16 @@ export interface ViewDefResult {
bounds?: ViewDefBounds;
}
+function toLabel(target: FieldResult<Field>) {
+ if (target instanceof DateField) {
+ const date = DateCast(target).date;
+ if (date) {
+ return `${date.toDateString()} ${date.toTimeString()}`;
+ }
+ }
+ return String(target);
+}
+
export function computePivotLayout(poolData: ObservableMap<string, any>, pivotDoc: Doc, childDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], viewDefsToJSX: (views: any) => ViewDefResult[]) {
const pivotAxisWidth = NumCast(pivotDoc.pivotWidth, 200);
const pivotColumnGroups = new Map<FieldResult<Field>, Doc[]>();
@@ -58,7 +69,7 @@ export function computePivotLayout(poolData: ObservableMap<string, any>, pivotDo
let xCount = 0;
groupNames.push({
type: "text",
- text: String(key),
+ text: toLabel(key),
x,
y: pivotAxisWidth + 50,
width: pivotAxisWidth * expander * numCols,
@@ -66,7 +77,7 @@ export function computePivotLayout(poolData: ObservableMap<string, any>, pivotDo
fontSize: NumCast(pivotDoc.pivotFontSize, 10)
});
for (const doc of val) {
- let layoutDoc = Doc.Layout(doc);
+ const layoutDoc = Doc.Layout(doc);
let wid = pivotAxisWidth;
let hgt = layoutDoc.nativeWidth ? (NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth)) * pivotAxisWidth : pivotAxisWidth;
if (hgt > pivotAxisWidth) {
@@ -89,7 +100,7 @@ export function computePivotLayout(poolData: ObservableMap<string, any>, pivotDo
});
childPairs.map(pair => {
- let defaultPosition = {
+ const defaultPosition = {
x: NumCast(pair.layout.x),
y: NumCast(pair.layout.y),
z: NumCast(pair.layout.z),
@@ -97,7 +108,7 @@ export function computePivotLayout(poolData: ObservableMap<string, any>, pivotDo
height: NumCast(pair.layout.height)
};
const pos = docMap.get(pair.layout) || defaultPosition;
- let data = poolData.get(pair.layout[Id]);
+ const data = poolData.get(pair.layout[Id]);
if (!data || pos.x !== data.x || pos.y !== data.y || pos.z !== data.z || pos.width !== data.width || pos.height !== data.height) {
runInAction(() => poolData.set(pair.layout[Id], { transition: "transform 1s", ...pos }));
}
@@ -107,10 +118,10 @@ export function computePivotLayout(poolData: ObservableMap<string, any>, pivotDo
export function AddCustomFreeFormLayout(doc: Doc, dataKey: string): () => void {
return () => {
- let addOverlay = (key: "arrangeScript" | "arrangeInit", options: OverlayElementOptions, params?: Record<string, string>, requiredType?: string) => {
+ const addOverlay = (key: "arrangeScript" | "arrangeInit", options: OverlayElementOptions, params?: Record<string, string>, requiredType?: string) => {
let overlayDisposer: () => void = emptyFunction; // filled in below after we have a reference to the scriptingBox
const scriptField = Cast(doc[key], ScriptField);
- let scriptingBox = <ScriptBox initialText={scriptField && scriptField.script.originalScript}
+ const scriptingBox = <ScriptBox initialText={scriptField && scriptField.script.originalScript}
// tslint:disable-next-line: no-unnecessary-callback-wrapper
onCancel={() => overlayDisposer()} // don't get rid of the function wrapper-- we don't want to use the current value of overlayDiposer, but the one set below
onSave={(text, onError) => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 73b45edc6..178a5bcdc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -7,7 +7,8 @@ import React = require("react");
import v5 = require("uuid/v5");
import { DocumentType } from "../../../documents/DocumentTypes";
import { observable, action, reaction, IReactionDisposer } from "mobx";
-import { StrCast, Cast } from "../../../../new_fields/Types";
+import { StrCast } from "../../../../new_fields/Types";
+import { Id } from "../../../../new_fields/FieldSymbols";
export interface CollectionFreeFormLinkViewProps {
A: DocumentView;
@@ -17,36 +18,61 @@ export interface CollectionFreeFormLinkViewProps {
@observer
export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
- @observable _opacity: number = 1;
- @observable _update: number = 0;
+ @observable _opacity: number = 0;
_anchorDisposer: IReactionDisposer | undefined;
@action
componentDidMount() {
- setTimeout(action(() => this._opacity = 0.05), 750);
- this._anchorDisposer = reaction(() => [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform()],
- () => {
- let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
- let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
- let adiv = (acont.length ? acont[0] : this.props.A.ContentDiv!);
- let bdiv = (bcont.length ? bcont[0] : this.props.B.ContentDiv!);
- let a = adiv.getBoundingClientRect();
- let b = bdiv.getBoundingClientRect();
- let abounds = adiv.parentElement!.getBoundingClientRect();
- let bbounds = bdiv.parentElement!.getBoundingClientRect();
- let apt = Utils.closestPtBetweenRectangles(abounds.left, abounds.top, abounds.width, abounds.height,
+ this._anchorDisposer = reaction(() => [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform(), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document)],
+ action(() => {
+ setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
+ setTimeout(action(() => this._opacity = 0.05), 750); // this will unhighlight the link line.
+ const acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+ const bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+ const adiv = (acont.length ? acont[0] : this.props.A.ContentDiv!);
+ const bdiv = (bcont.length ? bcont[0] : this.props.B.ContentDiv!);
+ const a = adiv.getBoundingClientRect();
+ const b = bdiv.getBoundingClientRect();
+ const abounds = adiv.parentElement!.getBoundingClientRect();
+ const bbounds = bdiv.parentElement!.getBoundingClientRect();
+ const apt = Utils.closestPtBetweenRectangles(abounds.left, abounds.top, abounds.width, abounds.height,
bbounds.left, bbounds.top, bbounds.width, bbounds.height,
a.left + a.width / 2, a.top + a.height / 2);
- let bpt = Utils.closestPtBetweenRectangles(bbounds.left, bbounds.top, bbounds.width, bbounds.height,
+ const bpt = Utils.closestPtBetweenRectangles(bbounds.left, bbounds.top, bbounds.width, bbounds.height,
abounds.left, abounds.top, abounds.width, abounds.height,
apt.point.x, apt.point.y);
- let afield = StrCast(this.props.A.props.Document[StrCast(this.props.A.props.layoutKey, "layout")]).indexOf("anchor1") === -1 ? "anchor2" : "anchor1";
- let bfield = afield === "anchor1" ? "anchor2" : "anchor1";
- this.props.A.props.Document[afield + "_x"] = (apt.point.x - abounds.left) / abounds.width * 100;
- this.props.A.props.Document[afield + "_y"] = (apt.point.y - abounds.top) / abounds.height * 100;
- this.props.A.props.Document[bfield + "_x"] = (bpt.point.x - bbounds.left) / bbounds.width * 100;
- this.props.A.props.Document[bfield + "_y"] = (bpt.point.y - bbounds.top) / bbounds.height * 100;
- this._update++;
- }
+ const afield = StrCast(this.props.A.props.Document[StrCast(this.props.A.props.layoutKey, "layout")]).indexOf("anchor1") === -1 ? "anchor2" : "anchor1";
+ const bfield = afield === "anchor1" ? "anchor2" : "anchor1";
+
+ // really hacky stuff to make the DocuLinkBox display where we want it to:
+ // if there's an element in the DOM with the id of the opposite anchor, then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right
+ // otherwise, we just use the computed nearest point on the document boundary to the target Document
+ const targetAhyperlink = window.document.getElementById((this.props.LinkDocs[0][afield] as Doc)[Id]);
+ const targetBhyperlink = window.document.getElementById((this.props.LinkDocs[0][bfield] as Doc)[Id]);
+ if (!targetBhyperlink) {
+ this.props.A.props.Document[afield + "_x"] = (apt.point.x - abounds.left) / abounds.width * 100;
+ this.props.A.props.Document[afield + "_y"] = (apt.point.y - abounds.top) / abounds.height * 100;
+ } else {
+ setTimeout(() => {
+ (this.props.A.props.Document[(this.props.A.props as any).fieldKey] as Doc);
+ let m = targetBhyperlink.getBoundingClientRect();
+ let mp = this.props.A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
+ this.props.A.props.Document[afield + "_x"] = mp[0] / this.props.A.props.PanelWidth() * 100;
+ this.props.A.props.Document[afield + "_y"] = mp[1] / this.props.A.props.PanelHeight() * 100;
+ }, 0);
+ }
+ if (!targetAhyperlink) {
+ this.props.A.props.Document[bfield + "_x"] = (bpt.point.x - bbounds.left) / bbounds.width * 100;
+ this.props.A.props.Document[bfield + "_y"] = (bpt.point.y - bbounds.top) / bbounds.height * 100;
+ } else {
+ setTimeout(() => {
+ (this.props.B.props.Document[(this.props.B.props as any).fieldKey] as Doc);
+ let m = targetAhyperlink.getBoundingClientRect();
+ let mp = this.props.B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
+ this.props.B.props.Document[afield + "_x"] = mp[0] / this.props.B.props.PanelWidth() * 100;
+ this.props.B.props.Document[afield + "_y"] = mp[1] / this.props.B.props.PanelHeight() * 100;
+ }, 0);
+ }
+ })
, { fireImmediately: true });
}
@action
@@ -55,22 +81,24 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
}
render() {
- let y = this._update;
- let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
- let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
- let a = (acont.length ? acont[0] : this.props.A.ContentDiv!).getBoundingClientRect();
- let b = (bcont.length ? bcont[0] : this.props.B.ContentDiv!).getBoundingClientRect();
- let apt = Utils.closestPtBetweenRectangles(a.left, a.top, a.width, a.height,
+ const acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+ const bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+ const a = (acont.length ? acont[0] : this.props.A.ContentDiv!).getBoundingClientRect();
+ const b = (bcont.length ? bcont[0] : this.props.B.ContentDiv!).getBoundingClientRect();
+ const apt = Utils.closestPtBetweenRectangles(a.left, a.top, a.width, a.height,
b.left, b.top, b.width, b.height,
a.left + a.width / 2, a.top + a.height / 2);
- let bpt = Utils.closestPtBetweenRectangles(b.left, b.top, b.width, b.height,
+ const bpt = Utils.closestPtBetweenRectangles(b.left, b.top, b.width, b.height,
a.left, a.top, a.width, a.height,
apt.point.x, apt.point.y);
- let pt1 = [apt.point.x, apt.point.y];
- let pt2 = [bpt.point.x, bpt.point.y];
- return (<line key="linkLine" className="collectionfreeformlinkview-linkLine"
- style={{ opacity: this._opacity }}
- x1={`${pt1[0]}`} y1={`${pt1[1]}`}
- x2={`${pt2[0]}`} y2={`${pt2[1]}`} />);
+ const pt1 = [apt.point.x, apt.point.y];
+ const pt2 = [bpt.point.x, bpt.point.y];
+ let aActive = this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document);
+ let bActive = this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document);
+ return !aActive && !bActive ? (null) :
+ <line key="linkLine" className="collectionfreeformlinkview-linkLine"
+ style={{ opacity: this._opacity, strokeDasharray: "2 2" }}
+ x1={`${pt1[0]}`} y1={`${pt1[1]}`}
+ x2={`${pt2[0]}`} y2={`${pt2[1]}`} />;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index e9191c176..044d35eca 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -72,11 +72,11 @@ export class CollectionFreeFormLinksView extends React.Component {
}
@computed
get uniqueConnections() {
- let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => {
+ const connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => {
if (!drawnPairs.reduce((found, drawnPair) => {
- let match1 = (connection.a === drawnPair.a && connection.b === drawnPair.b);
- let match2 = (connection.a === drawnPair.b && connection.b === drawnPair.a);
- let match = match1 || match2;
+ const match1 = (connection.a === drawnPair.a && connection.b === drawnPair.b);
+ const match2 = (connection.a === drawnPair.b && connection.b === drawnPair.a);
+ const match = match1 || match2;
if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) {
drawnPair.l.push(connection.l);
}
@@ -91,13 +91,11 @@ export class CollectionFreeFormLinksView extends React.Component {
}
render() {
- return (
- <div className="collectionfreeformlinksview-container">
- <svg className="collectionfreeformlinksview-svgCanvas">
- {SelectionManager.GetIsDragging() ? (null) : this.uniqueConnections}
- </svg>
- {this.props.children}
- </div>
- );
+ return <div className="collectionfreeformlinksview-container">
+ <svg className="collectionfreeformlinksview-svgCanvas">
+ {SelectionManager.GetIsDragging() ? (null) : this.uniqueConnections}
+ </svg>
+ {this.props.children}
+ </div>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index b8148852d..bb9ae4326 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -13,14 +13,14 @@ import v5 = require("uuid/v5");
export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
protected getCursors(): CursorField[] {
- let doc = this.props.Document;
+ const doc = this.props.Document;
- let id = CurrentUserUtils.id;
+ const id = CurrentUserUtils.id;
if (!id) {
return [];
}
- let cursors = Cast(doc.cursors, listSpec(CursorField));
+ const cursors = Cast(doc.cursors, listSpec(CursorField));
const now = mobxUtils.now();
// const now = Date.now();
@@ -30,7 +30,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
private crosshairs?: HTMLCanvasElement;
drawCrosshairs = (backgroundColor: string) => {
if (this.crosshairs) {
- let ctx = this.crosshairs.getContext('2d');
+ const ctx = this.crosshairs.getContext('2d');
if (ctx) {
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, 20, 20);
@@ -62,8 +62,8 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
get sharedCursors() {
return this.getCursors().map(c => {
- let m = c.data.metadata;
- let l = c.data.position;
+ const m = c.data.metadata;
+ const l = c.data.position;
this.drawCrosshairs("#" + v5(m.id, v5.URL).substring(0, 6).toUpperCase() + "22");
return (
<div key={m.id} className="collectionFreeFormRemoteCursors-cont"
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 070d4aa65..58fb81453 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -28,6 +28,21 @@
// touch action none means that the browser will handle none of the touch actions. this allows us to implement our own actions.
touch-action: none;
+ .collectionfreeformview-placeholder {
+ background: gray;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ .collectionfreeformview-placeholderSpan {
+ font-size: 32;
+ display: flex;
+ text-align: center;
+ margin: auto;
+ background: #80808069;
+ }
+ }
+
.collectionfreeformview>.jsx-parser {
position: inherit;
height: 100%;
@@ -52,6 +67,8 @@
left: 0;
width: 100%;
height: 100%;
+ align-items: center;
+ display: flex;
}
// selection border...?
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 75690ab2c..eb5a074bb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,12 +1,12 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faEye } from "@fortawesome/free-regular-svg-icons";
import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
-import { action, computed, observable, trace, ObservableMap, untracked, reaction, runInAction, IReactionDisposer } from "mobx";
+import { action, computed, observable, ObservableMap, reaction, runInAction, IReactionDisposer } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../../new_fields/Doc";
import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
import { Id } from "../../../../new_fields/FieldSymbols";
-import { InkTool } from "../../../../new_fields/InkField";
+import { InkTool, InkField, InkData } from "../../../../new_fields/InkField";
import { createSchema, makeInterface } from "../../../../new_fields/Schema";
import { ScriptField } from "../../../../new_fields/ScriptField";
import { BoolCast, Cast, DateCast, NumCast, StrCast } from "../../../../new_fields/Types";
@@ -26,7 +26,7 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"
import { ContextMenu } from "../../ContextMenu";
import { ContextMenuProps } from "../../ContextMenuItem";
import { InkingControl } from "../../InkingControl";
-import { CreatePolyline, InkingStroke } from "../../InkingStroke";
+import { CreatePolyline } from "../../InkingStroke";
import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
import { DocumentViewProps } from "../../nodes/DocumentView";
import { FormattedTextBox } from "../../nodes/FormattedTextBox";
@@ -39,10 +39,11 @@ import "./CollectionFreeFormView.scss";
import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
-import { computedFn, keepAlive } from "mobx-utils";
+import { computedFn } from "mobx-utils";
import { TraceMobx } from "../../../../new_fields/util";
import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
import { LinkManager } from "../../../util/LinkManager";
+import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
@@ -100,10 +101,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
private addLiveTextBox = (newBox: Doc) => {
FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
- let maxHeading = this.childDocs.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
+ const maxHeading = this.childDocs.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
let heading = maxHeading === 0 || this.childDocs.length === 0 ? 1 : maxHeading === 1 ? 2 : 0;
if (heading === 0) {
- let sorted = this.childDocs.filter(d => d.type === DocumentType.TEXT && d.data_ext instanceof Doc && d.data_ext.lastModified).sort((a, b) => DateCast((Cast(a.data_ext, Doc) as Doc).lastModified).date > DateCast((Cast(b.data_ext, Doc) as Doc).lastModified).date ? 1 :
+ const sorted = this.childDocs.filter(d => d.type === DocumentType.TEXT && d.data_ext instanceof Doc && d.data_ext.lastModified).sort((a, b) => DateCast((Cast(a.data_ext, Doc) as Doc).lastModified).date > DateCast((Cast(b.data_ext, Doc) as Doc).lastModified).date ? 1 :
DateCast((Cast(a.data_ext, Doc) as Doc).lastModified).date < DateCast((Cast(b.data_ext, Doc) as Doc).lastModified).date ? -1 : 0);
heading = !sorted.length ? Math.max(1, maxHeading) : NumCast(sorted[sorted.length - 1].heading) === 1 ? 2 : NumCast(sorted[sorted.length - 1].heading);
}
@@ -111,7 +112,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
this.addDocument(newBox);
}
private addDocument = (newBox: Doc) => {
- let added = this.props.addDocument(newBox);
+ const added = this.props.addDocument(newBox);
added && this.bringToFront(newBox);
added && this.updateCluster(newBox);
return added;
@@ -128,54 +129,54 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
onDrop = (e: React.DragEvent): Promise<void> => {
- var pt = this.getTransform().transformPoint(e.pageX, e.pageY);
+ const pt = this.getTransform().transformPoint(e.pageX, e.pageY);
return super.onDrop(e, { x: pt[0], y: pt[1] });
}
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
- let xf = this.getTransform();
- let xfo = this.getTransformOverlay();
- let [xp, yp] = xf.transformPoint(de.x, de.y);
- let [xpo, ypo] = xfo.transformPoint(de.x, de.y);
+ const xf = this.getTransform();
+ const xfo = this.getTransformOverlay();
+ const [xp, yp] = xf.transformPoint(de.x, de.y);
+ const [xpo, ypo] = xfo.transformPoint(de.x, de.y);
if (super.drop(e, de)) {
- if (de.data instanceof DragManager.DocumentDragData) {
- if (de.data.droppedDocuments.length) {
- let firstDoc = de.data.droppedDocuments[0];
- let z = NumCast(firstDoc.z);
- let x = (z ? xpo : xp) - de.data.offset[0];
- let y = (z ? ypo : yp) - de.data.offset[1];
- let dropX = NumCast(firstDoc.x);
- let dropY = NumCast(firstDoc.y);
- de.data.droppedDocuments.forEach(action((d: Doc) => {
- let layoutDoc = Doc.Layout(d);
+ if (de.complete.docDragData) {
+ if (de.complete.docDragData.droppedDocuments.length) {
+ const firstDoc = de.complete.docDragData.droppedDocuments[0];
+ const z = NumCast(firstDoc.z);
+ const x = (z ? xpo : xp) - de.complete.docDragData.offset[0];
+ const y = (z ? ypo : yp) - de.complete.docDragData.offset[1];
+ const dropX = NumCast(firstDoc.x);
+ const dropY = NumCast(firstDoc.y);
+ de.complete.docDragData.droppedDocuments.forEach(action((d: Doc) => {
+ const layoutDoc = Doc.Layout(d);
d.x = x + NumCast(d.x) - dropX;
d.y = y + NumCast(d.y) - dropY;
if (!NumCast(layoutDoc.width)) {
layoutDoc.width = 300;
}
if (!NumCast(layoutDoc.height)) {
- let nw = NumCast(layoutDoc.nativeWidth);
- let nh = NumCast(layoutDoc.nativeHeight);
+ const nw = NumCast(layoutDoc.nativeWidth);
+ const nh = NumCast(layoutDoc.nativeHeight);
layoutDoc.height = nw && nh ? nh / nw * NumCast(layoutDoc.width) : 300;
}
this.bringToFront(d);
}));
- de.data.droppedDocuments.length === 1 && this.updateCluster(de.data.droppedDocuments[0]);
+ de.complete.docDragData.droppedDocuments.length === 1 && this.updateCluster(de.complete.docDragData.droppedDocuments[0]);
}
}
- else if (de.data instanceof DragManager.AnnotationDragData) {
- if (de.data.dropDocument) {
- let dragDoc = de.data.dropDocument;
- let x = xp - de.data.offset[0];
- let y = yp - de.data.offset[1];
- let dropX = NumCast(dragDoc.x);
- let dropY = NumCast(dragDoc.y);
+ else if (de.complete.annoDragData) {
+ if (de.complete.annoDragData.dropDocument) {
+ const dragDoc = de.complete.annoDragData.dropDocument;
+ const x = xp - de.complete.annoDragData.offset[0];
+ const y = yp - de.complete.annoDragData.offset[1];
+ const dropX = NumCast(dragDoc.x);
+ const dropY = NumCast(dragDoc.y);
dragDoc.x = x + NumCast(dragDoc.x) - dropX;
dragDoc.y = y + NumCast(dragDoc.y) - dropY;
- de.data.targetContext = this.props.Document; // dropped a PDF annotation, so we need to set the targetContext on the dragData which the PDF view uses at the end of the drop operation
+ de.complete.annoDragData.targetContext = this.props.Document; // dropped a PDF annotation, so we need to set the targetContext on the dragData which the PDF view uses at the end of the drop operation
this.bringToFront(dragDoc);
}
}
@@ -185,31 +186,28 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
pickCluster(probe: number[]) {
return this.childLayoutPairs.map(pair => pair.layout).reduce((cluster, cd) => {
- let layoutDoc = Doc.Layout(cd);
- let cx = NumCast(cd.x) - this._clusterDistance;
- let cy = NumCast(cd.y) - this._clusterDistance;
- let cw = NumCast(layoutDoc.width) + 2 * this._clusterDistance;
- let ch = NumCast(layoutDoc.height) + 2 * this._clusterDistance;
+ const layoutDoc = Doc.Layout(cd);
+ const cx = NumCast(cd.x) - this._clusterDistance;
+ const cy = NumCast(cd.y) - this._clusterDistance;
+ const cw = NumCast(layoutDoc.width) + 2 * this._clusterDistance;
+ const ch = NumCast(layoutDoc.height) + 2 * this._clusterDistance;
return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ?
NumCast(cd.cluster) : cluster;
}, -1);
}
tryDragCluster(e: PointerEvent | TouchEvent) {
- let ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0);
+ const ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0);
if (ptsParent) {
- let cluster = this.pickCluster(this.getTransform().transformPoint(ptsParent.clientX, ptsParent.clientY));
+ const cluster = this.pickCluster(this.getTransform().transformPoint(ptsParent.clientX, ptsParent.clientY));
if (cluster !== -1) {
- let eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => NumCast(cd.cluster) === cluster);
- let clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.CollectionView)!);
- let de = new DragManager.DocumentDragData(eles);
+ const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => NumCast(cd.cluster) === cluster);
+ const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.CollectionView)!);
+ const de = new DragManager.DocumentDragData(eles);
de.moveDocument = this.props.moveDocument;
const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0);
de.offset = this.getTransform().transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
de.dropAction = e.ctrlKey || e.altKey ? "alias" : undefined;
- DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, ptsParent.clientX, ptsParent.clientY, {
- handlers: { dragComplete: action(emptyFunction) },
- hideSource: !de.dropAction
- });
+ DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, ptsParent.clientX, ptsParent.clientY, { hideSource: !de.dropAction });
return true;
}
}
@@ -227,10 +225,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@undoBatch
@action
updateCluster(doc: Doc) {
- let childLayouts = this.childLayoutPairs.map(pair => pair.layout);
+ const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
if (this.props.Document.useClusters) {
this._clusterSets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
- let preferredInd = NumCast(doc.cluster);
+ const preferredInd = NumCast(doc.cluster);
doc.cluster = -1;
this._clusterSets.map((set, i) => set.map(member => {
if (doc.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
@@ -257,15 +255,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
getClusterColor = (doc: Doc) => {
let clusterColor = "";
- let cluster = NumCast(doc.cluster);
+ const cluster = NumCast(doc.cluster);
if (this.Document.useClusters) {
if (this._clusterSets.length <= cluster) {
setTimeout(() => this.updateCluster(doc), 0);
} else {
// choose a cluster color from a palette
- let colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"];
+ const colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"];
clusterColor = colors[cluster % colors.length];
- let set = this._clusterSets[cluster] && this._clusterSets[cluster].filter(s => s.backgroundColor && (s.backgroundColor !== s.defaultBackgroundColor));
+ const set = this._clusterSets[cluster] && this._clusterSets[cluster].filter(s => s.backgroundColor && (s.backgroundColor !== s.defaultBackgroundColor));
// override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document
set && set.filter(s => !s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
set && set.filter(s => s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
@@ -289,7 +287,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) {
e.stopPropagation();
e.preventDefault();
- let point = this.getTransform().transformPoint(e.pageX, e.pageY);
+ const point = this.getTransform().transformPoint(e.pageX, e.pageY);
this._points.push({ X: point[0], Y: point[1] });
}
// if not using a pen and in no ink mode
@@ -326,8 +324,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
handle1PointerDown = (e: React.TouchEvent) => {
- if (e.nativeEvent.cancelBubble) return;
- let pt = e.targetTouches.item(0);
+ const pt = e.targetTouches.item(0);
if (pt) {
this._hitCluster = this.props.Document.useCluster ? this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)) !== -1 : false;
if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) {
@@ -338,12 +335,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen) {
e.stopPropagation();
e.preventDefault();
- let point = this.getTransform().transformPoint(pt.pageX, pt.pageY);
+ const point = this.getTransform().transformPoint(pt.pageX, pt.pageY);
this._points.push({ X: point[0], Y: point[1] });
}
else if (InkingControl.Instance.selectedTool === InkTool.None) {
this._lastX = pt.pageX;
this._lastY = pt.pageY;
+ e.stopPropagation();
+ e.preventDefault();
}
else {
e.stopPropagation();
@@ -358,21 +357,21 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && this._points.length <= 1) return;
if (this._points.length > 1) {
- let B = this.svgBounds;
- let points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top }));
+ const B = this.svgBounds;
+ const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top }));
- let result = GestureUtils.GestureRecognizer.Recognize(new Array(points));
+ const result = GestureUtils.GestureRecognizer.Recognize(new Array(points));
let actionPerformed = false;
if (result && result.Score > 0.7) {
switch (result.Name) {
case GestureUtils.Gestures.Box:
- let bounds = { x: Math.min(...this._points.map(p => p.X)), r: Math.max(...this._points.map(p => p.X)), y: Math.min(...this._points.map(p => p.y)), b: Math.max(...this._points.map(p => p.Y)) };
- let sel = this.getActiveDocuments().filter(doc => {
- let l = NumCast(doc.x);
- let r = l + doc[WidthSym]();
- let t = NumCast(doc.y);
- let b = t + doc[HeightSym]();
- let pass = !(bounds.x > r || bounds.r < l || bounds.y > b || bounds.b < t);
+ const bounds = { x: Math.min(...this._points.map(p => p.X)), r: Math.max(...this._points.map(p => p.X)), y: Math.min(...this._points.map(p => p.Y)), b: Math.max(...this._points.map(p => p.Y)) };
+ const sel = this.getActiveDocuments().filter(doc => {
+ const l = NumCast(doc.x);
+ const r = l + doc[WidthSym]();
+ const t = NumCast(doc.y);
+ const b = t + doc[HeightSym]();
+ const pass = !(bounds.x > r || bounds.r < l || bounds.y > b || bounds.b < t);
if (pass) {
doc.x = l - B.left - B.width / 2;
doc.y = t - B.top - B.height / 2;
@@ -384,15 +383,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
actionPerformed = true;
break;
case GestureUtils.Gestures.Line:
- let ep1 = this._points[0];
- let ep2 = this._points[this._points.length - 1];
+ const ep1 = this._points[0];
+ const ep2 = this._points[this._points.length - 1];
let d1: Doc | undefined;
let d2: Doc | undefined;
this.getActiveDocuments().map(doc => {
- let l = NumCast(doc.x);
- let r = l + doc[WidthSym]();
- let t = NumCast(doc.y);
- let b = t + doc[HeightSym]();
+ const l = NumCast(doc.x);
+ const r = l + doc[WidthSym]();
+ const t = NumCast(doc.y);
+ const b = t + doc[HeightSym]();
if (!d1 && l < ep1.X && r > ep1.X && t < ep1.Y && b > ep1.Y) {
d1 = doc;
}
@@ -414,7 +413,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
if (!actionPerformed) {
- let inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { width: B.width, height: B.height, x: B.left, y: B.top });
+ const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { width: B.width, height: B.height, x: B.left, y: B.top });
this.addDocument(inkDoc);
this._points = [];
}
@@ -433,26 +432,26 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let x = this.Document.panX || 0;
let y = this.Document.panY || 0;
- let docs = this.childLayoutPairs.map(pair => pair.layout);
- let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
+ const docs = this.childLayoutPairs.map(pair => pair.layout);
+ const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
if (!this.isAnnotationOverlay) {
PDFMenu.Instance.fadeOut(true);
- let minx = docs.length ? NumCast(docs[0].x) : 0;
- let maxx = docs.length ? NumCast(docs[0].width) + minx : minx;
- let miny = docs.length ? NumCast(docs[0].y) : 0;
- let maxy = docs.length ? NumCast(docs[0].height) + miny : miny;
- let ranges = docs.filter(doc => doc).reduce((range, doc) => {
- let layoutDoc = Doc.Layout(doc);
- let x = NumCast(doc.x);
- let xe = x + NumCast(layoutDoc.width);
- let y = NumCast(doc.y);
- let ye = y + NumCast(layoutDoc.height);
+ const minx = docs.length ? NumCast(docs[0].x) : 0;
+ const maxx = docs.length ? NumCast(docs[0].width) + minx : minx;
+ const miny = docs.length ? NumCast(docs[0].y) : 0;
+ const maxy = docs.length ? NumCast(docs[0].height) + miny : miny;
+ const ranges = docs.filter(doc => doc).reduce((range, doc) => {
+ const layoutDoc = Doc.Layout(doc);
+ const x = NumCast(doc.x);
+ const xe = x + NumCast(layoutDoc.width);
+ const y = NumCast(doc.y);
+ const ye = y + NumCast(layoutDoc.height);
return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]],
[range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]];
}, [[minx, maxx], [miny, maxy]]);
- let cscale = this.props.ContainingCollectionDoc ? NumCast(this.props.ContainingCollectionDoc.scale) : 1;
- let panelDim = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth() / this.zoomScaling() * cscale,
+ const cscale = this.props.ContainingCollectionDoc ? NumCast(this.props.ContainingCollectionDoc.scale) : 1;
+ const panelDim = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth() / this.zoomScaling() * cscale,
this.props.PanelHeight() / this.zoomScaling() * cscale);
if (ranges[0][0] - dx > (this.panX() + panelDim[0] / 2)) x = ranges[0][1] + panelDim[0] / 2;
if (ranges[0][1] - dx < (this.panX() - panelDim[0] / 2)) x = ranges[0][0] - panelDim[0] / 2;
@@ -475,7 +474,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (!e.cancelBubble) {
const selectedTool = InkingControl.Instance.selectedTool;
if (selectedTool === InkTool.Highlighter || selectedTool === InkTool.Pen || InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
- let point = this.getTransform().transformPoint(e.clientX, e.clientY);
+ const point = this.getTransform().transformPoint(e.clientX, e.clientY);
this._points.push({ X: point[0], Y: point[1] });
}
else if (selectedTool === InkTool.None) {
@@ -496,8 +495,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
handle1PointerMove = (e: TouchEvent) => {
// panning a workspace
if (!e.cancelBubble) {
- let myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);
- let pt = myTouches[0];
+ const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);
+ const pt = myTouches[0];
if (pt) {
if (InkingControl.Instance.selectedTool === InkTool.None) {
if (this._hitCluster && this.tryDragCluster(e)) {
@@ -510,7 +509,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
this.pan(pt);
}
else if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
- let point = this.getTransform().transformPoint(pt.clientX, pt.clientY);
+ const point = this.getTransform().transformPoint(pt.clientX, pt.clientY);
this._points.push({ X: point[0], Y: point[1] });
}
}
@@ -522,28 +521,28 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
handle2PointersMove = (e: TouchEvent) => {
// pinch zooming
if (!e.cancelBubble) {
- let myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);
- let pt1 = myTouches[0];
- let pt2 = myTouches[1];
+ const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);
+ const pt1 = myTouches[0];
+ const pt2 = myTouches[1];
if (this.prevPoints.size === 2) {
- let oldPoint1 = this.prevPoints.get(pt1.identifier);
- let oldPoint2 = this.prevPoints.get(pt2.identifier);
+ const oldPoint1 = this.prevPoints.get(pt1.identifier);
+ const oldPoint2 = this.prevPoints.get(pt2.identifier);
if (oldPoint1 && oldPoint2) {
- let dir = InteractionUtils.Pinching(pt1, pt2, oldPoint1, oldPoint2);
+ const dir = InteractionUtils.Pinching(pt1, pt2, oldPoint1, oldPoint2);
// if zooming, zoom
if (dir !== 0) {
- let d1 = Math.sqrt(Math.pow(pt1.clientX - oldPoint1.clientX, 2) + Math.pow(pt1.clientY - oldPoint1.clientY, 2));
- let d2 = Math.sqrt(Math.pow(pt2.clientX - oldPoint2.clientX, 2) + Math.pow(pt2.clientY - oldPoint2.clientY, 2));
- let centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
- let centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
+ const d1 = Math.sqrt(Math.pow(pt1.clientX - oldPoint1.clientX, 2) + Math.pow(pt1.clientY - oldPoint1.clientY, 2));
+ const d2 = Math.sqrt(Math.pow(pt2.clientX - oldPoint2.clientX, 2) + Math.pow(pt2.clientY - oldPoint2.clientY, 2));
+ const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
+ const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
// calculate the raw delta value
- let rawDelta = (dir * (d1 + d2));
+ const rawDelta = (dir * (d1 + d2));
// this floors and ceils the delta value to prevent jitteriness
- let delta = Math.sign(rawDelta) * Math.min(Math.abs(rawDelta), 8);
+ const delta = Math.sign(rawDelta) * Math.min(Math.abs(rawDelta), 8);
this.zoom(centerX, centerY, delta * window.devicePixelRatio);
this.prevPoints.set(pt1.identifier, pt1);
this.prevPoints.set(pt2.identifier, pt2);
@@ -551,27 +550,28 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// this is not zooming. derive some form of panning from it.
else {
// use the centerx and centery as the "new mouse position"
- let centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
- let centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
+ const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
+ const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
this.pan({ clientX: centerX, clientY: centerY });
this._lastX = centerX;
this._lastY = centerY;
}
}
}
+ e.stopPropagation();
+ e.preventDefault();
}
- e.stopPropagation();
- e.preventDefault();
}
+ @action
handle2PointersDown = (e: React.TouchEvent) => {
if (!e.nativeEvent.cancelBubble && this.props.active(true)) {
- let pt1: React.Touch | null = e.targetTouches.item(0);
- let pt2: React.Touch | null = e.targetTouches.item(1);
+ const pt1: React.Touch | null = e.targetTouches.item(0);
+ const pt2: React.Touch | null = e.targetTouches.item(1);
if (!pt1 || !pt2) return;
- let centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
- let centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
+ const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
+ const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
this._lastX = centerX;
this._lastY = centerY;
document.removeEventListener("touchmove", this.onTouch);
@@ -596,11 +596,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
deltaScale = 1 / this.zoomScaling();
}
if (deltaScale < 0) deltaScale = -deltaScale;
- let [x, y] = this.getTransform().transformPoint(pointX, pointY);
- let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
+ const [x, y] = this.getTransform().transformPoint(pointX, pointY);
+ const localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
if (localTransform.Scale >= 0.15) {
- let safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
+ const safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
this.props.Document.scale = Math.abs(safeScale);
this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
}
@@ -622,7 +622,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
setPan(panX: number, panY: number, panType: string = "None") {
if (!this.Document.lockedTransform || this.Document.inOverlay) {
this.Document.panTransformType = panType;
- var scale = this.getLocalTransform().inverse().Scale;
+ const scale = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) : (1 - 1 / scale) * this.nativeHeight), Math.max(0, panY));
this.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
@@ -647,6 +647,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
focusDocument = (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => boolean) => {
const state = HistoryUtil.getState();
+
// TODO This technically isn't correct if type !== "doc", as
// currently nothing is done, but we should probably push a new state
if (state.type === "doc" && this.Document.panX !== undefined && this.Document.panY !== undefined) {
@@ -662,28 +663,29 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
SelectionManager.DeselectAll();
if (this.props.Document.scrollHeight) {
- let annotOn = Cast(doc.annotationOn, Doc) as Doc;
+ const annotOn = Cast(doc.annotationOn, Doc) as Doc;
if (!annotOn) {
this.props.focus(doc);
} else {
- let contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn.height);
- let offset = annotOn && (contextHgt / 2 * 96 / 72);
+ const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn.height);
+ const offset = annotOn && (contextHgt / 2 * 96 / 72);
this.props.Document.scrollY = NumCast(doc.y) - offset;
}
} else {
- let layoutdoc = Doc.Layout(doc);
+ const layoutdoc = Doc.Layout(doc);
const newPanX = NumCast(doc.x) + NumCast(layoutdoc.width) / 2;
const newPanY = NumCast(doc.y) + NumCast(layoutdoc.height) / 2;
const newState = HistoryUtil.getState();
newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
HistoryUtil.pushState(newState);
- let savedState = { px: this.Document.panX, py: this.Document.panY, s: this.Document.scale, pt: this.Document.panTransformType };
+ const savedState = { px: this.Document.panX, py: this.Document.panY, s: this.Document.scale, pt: this.Document.panTransformType };
- this.setPan(newPanX, newPanY, "Ease");
+ if (!doc.z) this.setPan(newPanX, newPanY, "Ease"); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
Doc.BrushDoc(this.props.Document);
this.props.focus(this.props.Document);
willZoom && this.setScaleToZoom(layoutdoc, scale);
+ Doc.linkFollowHighlight(doc);
afterFocus && setTimeout(() => {
if (afterFocus && afterFocus()) {
@@ -707,11 +709,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
getScale = () => this.Document.scale || 1;
+ @computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
+
getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
return {
...this.props,
DataDoc: childData,
Document: childLayout,
+ LibraryPath: this.libraryPath,
layoutKey: undefined,
ruleProvider: this.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider, //bcz: hack! - currently ruleProviders apply to documents in nested colleciton, not direct children of themselves
onClick: undefined, // this.props.onClick, // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them
@@ -763,7 +768,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
}
- childDataProvider = computedFn(function childDataProvider(doc: Doc) { return (this as any)._layoutPoolData.get(doc[Id]); }.bind(this));
+ childDataProvider = computedFn(function childDataProvider(this: any, doc: Doc) { return this._layoutPoolData.get(doc[Id]); }.bind(this));
doPivotLayout(poolData: ObservableMap<string, any>) {
return computePivotLayout(poolData, this.props.Document, this.childDocs,
@@ -771,10 +776,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
doFreeformLayout(poolData: ObservableMap<string, any>) {
- let layoutDocs = this.childLayoutPairs.map(pair => pair.layout);
+ const layoutDocs = this.childLayoutPairs.map(pair => pair.layout);
const initResult = this.Document.arrangeInit && this.Document.arrangeInit.script.run({ docs: layoutDocs, collection: this.Document }, console.log);
let state = initResult && initResult.success ? initResult.result.scriptState : undefined;
- let elements = initResult && initResult.success ? this.viewDefsToJSX(initResult.result.views) : [];
+ const elements = initResult && initResult.success ? this.viewDefsToJSX(initResult.result.views) : [];
this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => {
const data = poolData.get(pair.layout[Id]);
@@ -793,7 +798,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
case "pivot": computedElementData = this.doPivotLayout(this._layoutPoolData); break;
default: computedElementData = this.doFreeformLayout(this._layoutPoolData); break;
}
- this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).forEach(pair =>
+ this.childLayoutPairs.filter((pair, i) => this.isCurrent(pair.layout)).forEach(pair =>
computedElementData.elements.push({
ele: <CollectionFreeFormDocumentView key={pair.layout[Id]} dataProvider={this.childDataProvider}
ruleProvider={this.Document.isRuleProvider ? this.props.Document : this.props.ruleProvider}
@@ -805,6 +810,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
componentDidMount() {
+ super.componentDidMount();
this._layoutComputeReaction = reaction(() => { TraceMobx(); return this.doLayoutComputation; },
action((computation: { elements: ViewDefResult[] }) => computation && (this._layoutElements = computation.elements)),
{ fireImmediately: true, name: "doLayout" });
@@ -823,7 +829,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
layoutDocsInGrid = () => {
UndoManager.RunInBatch(() => {
const docs = DocListCast(this.Document[this.props.fieldKey]);
- let startX = this.Document.panX || 0;
+ const startX = this.Document.panX || 0;
let x = startX;
let y = this.Document.panY || 0;
let i = 0;
@@ -848,8 +854,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
this.Document.isRuleProvider && this.childLayoutPairs.map(pair =>
// iterate over the children of a displayed document (or if the displayed document is a template, iterate over the children of that template)
DocListCast(Doc.Layout(pair.layout).data).map(heading => {
- let headingPair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, heading);
- let headingLayout = headingPair.layout && (pair.layout.data_ext instanceof Doc) && (pair.layout.data_ext[`Layout[${headingPair.layout[Id]}]`] as Doc) || headingPair.layout;
+ const headingPair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, heading);
+ const headingLayout = headingPair.layout && (pair.layout.data_ext instanceof Doc) && (pair.layout.data_ext[`Layout[${headingPair.layout[Id]}]`] as Doc) || headingPair.layout;
if (headingLayout && NumCast(headingLayout.heading) > 0 && headingLayout.backgroundColor !== headingLayout.defaultBackgroundColor) {
Doc.GetProto(this.props.Document)["ruleColor_" + NumCast(headingLayout.heading)] = headingLayout.backgroundColor;
}
@@ -858,17 +864,30 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
analyzeStrokes = async () => {
- // CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], data.inkData);
+ const children = await DocListCastAsync(this.dataDoc.data);
+ if (!children) {
+ return;
+ }
+ const inkData: InkData[] = [];
+ for (const doc of children) {
+ const data = Cast(doc.data, InkField)?.inkData;
+ data && inkData.push(data);
+ }
+ if (!inkData.length) {
+ return;
+ }
+ CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], inkData);
}
onContextMenu = (e: React.MouseEvent) => {
- let layoutItems: ContextMenuProps[] = [];
+ const layoutItems: ContextMenuProps[] = [];
if (this.childDocs.some(d => BoolCast(d.isTemplateDoc))) {
layoutItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
}
layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
- layoutItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: async () => this.Document.fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
+ layoutItems.push({ description: `${this.Document.LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document.LODdisable = !this.Document.LODdisable, icon: "table" });
+ layoutItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document.fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
layoutItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
layoutItems.push({ description: `${this.Document.isRuleProvider ? "Stop Auto Format" : "Auto Format"}`, event: this.autoFormat, icon: "chalkboard" });
layoutItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
@@ -881,7 +900,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
input.accept = ".zip";
input.onchange = async _e => {
const upload = Utils.prepend("/uploadDoc");
- let formData = new FormData();
+ const formData = new FormData();
const file = input.files && input.files[0];
if (file) {
formData.append('file', file);
@@ -916,7 +935,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
private childViews = () => {
- let children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
+ const children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
return [
...children,
...this.views,
@@ -924,12 +943,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
@computed get svgBounds() {
- let xs = this._points.map(p => p.X);
- let ys = this._points.map(p => p.Y);
- let right = Math.max(...xs);
- let left = Math.min(...xs);
- let bottom = Math.max(...ys);
- let top = Math.min(...ys);
+ const xs = this._points.map(p => p.X);
+ const ys = this._points.map(p => p.Y);
+ const right = Math.max(...xs);
+ const left = Math.min(...xs);
+ const bottom = Math.max(...ys);
+ const top = Math.min(...ys);
return { right: right, left: left, bottom: bottom, top: top, width: right - left, height: bottom - top };
}
@@ -938,7 +957,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return (null);
}
- let B = this.svgBounds;
+ const B = this.svgBounds;
return (
<svg width={B.width} height={B.height} style={{ transform: `translate(${B.left}px, ${B.top}px)`, position: "absolute", zIndex: 30000 }}>
@@ -948,12 +967,26 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
children = () => {
- let eles: JSX.Element[] = [];
+ const eles: JSX.Element[] = [];
this.extensionDoc && (eles.push(...this.childViews()));
this.currentStroke && (eles.push(this.currentStroke));
eles.push(<CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />);
return eles;
}
+ @computed get placeholder() {
+ return <div className="collectionfreeformview-placeholder" style={{ background: this.Document.backgroundColor }}>
+ <span className="collectionfreeformview-placeholderSpan">{this.props.Document.title}</span>
+ </div>;
+ }
+ @computed get marqueeView() {
+ return <MarqueeView {...this.props} extensionDoc={this.extensionDoc!} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} addDocument={this.addDocument}
+ addLiveTextDocument={this.addLiveTextBox} getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}>
+ <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
+ easing={this.easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
+ {this.children}
+ </CollectionFreeFormViewPannableContents>
+ </MarqueeView>;
+ }
render() {
TraceMobx();
// update the actual dimensions of the collection so that they can inquired (e.g., by a minimap)
@@ -963,19 +996,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// this.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y);
// if isAnnotationOverlay is set, then children will be stored in the extension document for the fieldKey.
// otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document
- return !this.extensionDoc ? (null) :
- <div className={"collectionfreeformview-container"} ref={this.createDropTarget} onWheel={this.onPointerWheel}//pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
- style={{ height: this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}
- onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onTouchStart={this.onTouchStart}>
- <MarqueeView {...this.props} extensionDoc={this.extensionDoc} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} addDocument={this.addDocument}
- addLiveTextDocument={this.addLiveTextBox} getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}>
- <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
- easing={this.easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
- {this.children}
- </CollectionFreeFormViewPannableContents>
- </MarqueeView>
- <CollectionFreeFormOverlayView elements={this.elementFunc} />
- </div>;
+ if (!this.extensionDoc) return (null);
+ // let lodarea = this.Document[WidthSym]() * this.Document[HeightSym]() / this.props.ScreenToLocalTransform().Scale / this.props.ScreenToLocalTransform().Scale;
+ return <div className={"collectionfreeformview-container"} ref={this.createDropTarget} onWheel={this.onPointerWheel}//pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
+ style={{ pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined, height: this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}
+ onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onTouchStart={this.onTouchStart}>
+ {!this.Document.LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? // && this.props.CollectionView && lodarea < NumCast(this.Document.LODarea, 100000) ?
+ this.placeholder : this.marqueeView}
+ <CollectionFreeFormOverlayView elements={this.elementFunc} />
+ </div>;
}
}
@@ -1003,7 +1032,7 @@ interface CollectionFreeFormViewPannableContentsProps {
@observer
class CollectionFreeFormViewPannableContents extends React.Component<CollectionFreeFormViewPannableContentsProps>{
render() {
- let freeformclass = "collectionfreeformview" + (this.props.easing() ? "-ease" : "-none");
+ const freeformclass = "collectionfreeformview" + (this.props.easing() ? "-ease" : "-none");
const cenx = this.props.centeringShiftX();
const ceny = this.props.centeringShiftY();
const panx = -this.props.panX();
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 28ddc19d7..32e39d25e 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -21,7 +21,7 @@ export default class MarqueeOptionsMenu extends AntimodeMenu {
}
render() {
- let buttons = [
+ const buttons = [
<button
className="antimodeMenu-button"
title="Create a Collection"
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index d14495626..18d6da0da 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -5,10 +5,9 @@
left:0;
width:100%;
height:100%;
-}
-.marqueeView {
overflow: hidden;
pointer-events: inherit;
+ border-radius: inherit;
}
.marqueeView:focus-within {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5ed3fecb5..523edb918 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,7 +1,7 @@
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast } from "../../../../new_fields/Doc";
-import { InkField, PointData } from "../../../../new_fields/InkField";
+import { InkField } from "../../../../new_fields/InkField";
import { List } from "../../../../new_fields/List";
import { listSpec } from "../../../../new_fields/Schema";
import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
@@ -15,11 +15,9 @@ import { Transform } from "../../../util/Transform";
import { undoBatch } from "../../../util/UndoManager";
import { PreviewCursor } from "../../PreviewCursor";
import { CollectionViewType } from "../CollectionView";
-import { CollectionFreeFormView } from "./CollectionFreeFormView";
import "./MarqueeView.scss";
import React = require("react");
import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
-import InkSelectDecorations from "../../InkSelectDecorations";
import { SubCollectionViewProps } from "../CollectionSubView";
interface MarqueeViewProps {
@@ -67,26 +65,27 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@action
onKeyPress = (e: KeyboardEvent) => {
//make textbox and add it to this collection
+ // tslint:disable-next-line:prefer-const
let [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
if (e.key === "q" && e.ctrlKey) {
e.preventDefault();
(async () => {
- let text: string = await navigator.clipboard.readText();
- let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
+ const text: string = await navigator.clipboard.readText();
+ const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
for (let i = 0; i < ns.length - 1; i++) {
while (!(ns[i].trim() === "" || ns[i].endsWith("-\r") || ns[i].endsWith("-") ||
ns[i].endsWith(";\r") || ns[i].endsWith(";") ||
ns[i].endsWith(".\r") || ns[i].endsWith(".") ||
ns[i].endsWith(":\r") || ns[i].endsWith(":")) && i < ns.length - 1) {
- let sub = ns[i].endsWith("\r") ? 1 : 0;
- let br = ns[i + 1].trim() === "";
+ const sub = ns[i].endsWith("\r") ? 1 : 0;
+ const br = ns[i + 1].trim() === "";
ns.splice(i, 2, ns[i].substr(0, ns[i].length - sub) + ns[i + 1].trimLeft());
if (br) break;
}
}
ns.map(line => {
- let indent = line.search(/\S|$/);
- let newBox = Docs.Create.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line });
+ const indent = line.search(/\S|$/);
+ const newBox = Docs.Create.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line });
this.props.addDocument(newBox);
y += 40 * this.props.getTransform().Scale;
});
@@ -94,7 +93,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
} else if (e.key === "b" && e.ctrlKey) {
e.preventDefault();
navigator.clipboard.readText().then(text => {
- let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
+ 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 {
@@ -105,8 +104,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.props.addLiveTextDocument(
Docs.Create.TextDocument({ width: 200, height: 100, x: x, y: y, autoHeight: true, title: "-typed text-" }));
} else if (e.keyCode > 48 && e.keyCode <= 57) {
- let notes = DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data);
- let text = Docs.Create.TextDocument({ width: 200, height: 100, x: x, y: y, autoHeight: true, title: "-typed text-" });
+ const notes = DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data);
+ const text = Docs.Create.TextDocument({ width: 200, height: 100, x: x, y: y, autoHeight: true, title: "-typed text-" });
text.layout = notes[(e.keyCode - 49) % notes.length];
this.props.addLiveTextDocument(text);
}
@@ -124,31 +123,31 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
ns.splice(0, 1);
}
if (ns.length > 0) {
- let columns = ns[0].split("\t");
- let docList: Doc[] = [];
+ const columns = ns[0].split("\t");
+ const docList: Doc[] = [];
let groupAttr: string | number = "";
- let rowProto = new Doc();
+ const rowProto = new Doc();
rowProto.title = rowProto.Id;
rowProto.width = 200;
rowProto.isPrototype = true;
for (let i = 1; i < ns.length - 1; i++) {
- let values = ns[i].split("\t");
+ const values = ns[i].split("\t");
if (values.length === 1 && columns.length > 1) {
groupAttr = values[0];
continue;
}
- let docDataProto = Doc.MakeDelegate(rowProto);
+ const docDataProto = Doc.MakeDelegate(rowProto);
docDataProto.isPrototype = true;
columns.forEach((col, i) => docDataProto[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined));
if (groupAttr) {
docDataProto._group = groupAttr;
}
docDataProto.title = i.toString();
- let doc = Doc.MakeDelegate(docDataProto);
+ const doc = Doc.MakeDelegate(docDataProto);
doc.width = 200;
docList.push(doc);
}
- let newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 });
+ const newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 });
this.props.addDocument(newCol);
}
@@ -193,13 +192,13 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
onPointerUp = (e: PointerEvent): void => {
if (!this.props.active(true)) this.props.selectDocuments([this.props.Document], []);
if (this._visible) {
- let mselect = this.marqueeSelect();
+ const mselect = this.marqueeSelect();
if (!e.shiftKey) {
SelectionManager.DeselectAll(mselect.length ? undefined : this.props.Document);
}
// let inkselect = this.ink ? this.marqueeInkSelect(this.ink.inkData) : new Map();
// let inks = inkselect.size ? [{ Document: this.inkDoc, Ink: inkselect }] : [];
- let docs = mselect.length ? mselect : [this.props.Document];
+ const docs = mselect.length ? mselect : [this.props.Document];
this.props.selectDocuments(docs, []);
}
if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100)) {
@@ -212,7 +211,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
this.cleanupInteractions(true, this._commandExecuted);
- let hideMarquee = () => {
+ const hideMarquee = () => {
this.hideMarquee();
MarqueeOptionsMenu.Instance.fadeOut(true);
document.removeEventListener("pointerdown", hideMarquee);
@@ -260,10 +259,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@computed
get Bounds() {
- let left = this._downX < this._lastX ? this._downX : this._lastX;
- let top = this._downY < this._lastY ? this._downY : this._lastY;
- let topLeft = this.props.getTransform().transformPoint(left, top);
- let size = this.props.getTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+ const left = this._downX < this._lastX ? this._downX : this._lastX;
+ const top = this._downY < this._lastY ? this._downY : this._lastY;
+ const topLeft = this.props.getTransform().transformPoint(left, top);
+ const size = this.props.getTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
}
@@ -302,15 +301,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
getCollection = (selected: Doc[]) => {
- let bounds = this.Bounds;
- let defaultPalette = ["rgb(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
+ const bounds = this.Bounds;
+ const defaultPalette = ["rgb(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
"rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
- let colorPalette = Cast(this.props.Document.colorPalette, listSpec("string"));
+ const colorPalette = Cast(this.props.Document.colorPalette, listSpec("string"));
if (!colorPalette) this.props.Document.colorPalette = new List<string>(defaultPalette);
- let palette = Array.from(Cast(this.props.Document.colorPalette, listSpec("string")) as string[]);
- let usedPaletted = new Map<string, number>();
+ const palette = Array.from(Cast(this.props.Document.colorPalette, listSpec("string")) as string[]);
+ const usedPaletted = new Map<string, number>();
[...this.props.activeDocuments(), this.props.Document].map(child => {
- let bg = StrCast(Doc.Layout(child).backgroundColor);
+ const bg = StrCast(Doc.Layout(child).backgroundColor);
if (palette.indexOf(bg) !== -1) {
palette.splice(palette.indexOf(bg), 1);
if (usedPaletted.get(bg)) usedPaletted.set(bg, usedPaletted.get(bg)! + 1);
@@ -320,10 +319,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
usedPaletted.delete("#f1efeb");
usedPaletted.delete("white");
usedPaletted.delete("rgba(255,255,255,1)");
- let usedSequnce = Array.from(usedPaletted.keys()).sort((a, b) => usedPaletted.get(a)! < usedPaletted.get(b)! ? -1 : usedPaletted.get(a)! > usedPaletted.get(b)! ? 1 : 0);
- let chosenColor = (usedPaletted.size === 0) ? "white" : palette.length ? palette[0] : usedSequnce[0];
- let inkData = this.ink ? this.ink.inkData : undefined;
- let newCollection = Docs.Create.FreeformDocument(selected, {
+ const usedSequnce = Array.from(usedPaletted.keys()).sort((a, b) => usedPaletted.get(a)! < usedPaletted.get(b)! ? -1 : usedPaletted.get(a)! > usedPaletted.get(b)! ? 1 : 0);
+ const chosenColor = (usedPaletted.size === 0) ? "white" : palette.length ? palette[0] : usedSequnce[0];
+ // const inkData = this.ink ? this.ink.inkData : undefined;
+ const newCollection = Docs.Create.FreeformDocument(selected, {
x: bounds.left,
y: bounds.top,
panX: 0,
@@ -334,7 +333,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
height: bounds.height,
title: "a nested collection",
});
- let dataExtensionField = Doc.CreateDocumentExtensionForField(newCollection, "data");
+ // const dataExtensionField = Doc.CreateDocumentExtensionForField(newCollection, "data");
// dataExtensionField.ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined;
// this.marqueeInkDelete(inkData);
this.hideMarquee();
@@ -343,8 +342,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@action
collection = (e: KeyboardEvent | React.PointerEvent | undefined) => {
- let bounds = this.Bounds;
- let selected = this.marqueeSelect(false);
+ const bounds = this.Bounds;
+ const selected = this.marqueeSelect(false);
if (e instanceof KeyboardEvent ? e.key === "c" : true) {
selected.map(d => {
this.props.removeDocument(d);
@@ -354,7 +353,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
return d;
});
}
- let newCollection = this.getCollection(selected);
+ const newCollection = this.getCollection(selected);
this.props.addDocument(newCollection);
this.props.selectDocuments([newCollection], []);
MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -363,9 +362,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@action
summary = (e: KeyboardEvent | React.PointerEvent | undefined) => {
- let bounds = this.Bounds;
- let selected = this.marqueeSelect(false);
- let newCollection = this.getCollection(selected);
+ const bounds = this.Bounds;
+ const selected = this.marqueeSelect(false);
+ const newCollection = this.getCollection(selected);
selected.map(d => {
this.props.removeDocument(d);
@@ -375,13 +374,13 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
return d;
});
newCollection.chromeStatus = "disabled";
- let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, autoHeight: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
+ const summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, autoHeight: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
Doc.GetProto(summary).summarizedDocs = new List<Doc>([newCollection]);
newCollection.x = bounds.left + bounds.width;
Doc.GetProto(newCollection).summaryDoc = summary;
Doc.GetProto(newCollection).title = ComputedField.MakeFunction(`summaryTitle(this);`);
if (e instanceof KeyboardEvent ? e.key === "s" : true) { // summary is wrapped in an expand/collapse container that also contains the summarized documents in a free form view.
- let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
+ const container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
container.viewType = CollectionViewType.Stacking;
container.autoHeight = true;
Doc.GetProto(summary).maximizeLocation = "inPlace"; // or "onRight"
@@ -462,42 +461,42 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// }
marqueeSelect(selectBackgrounds: boolean = true) {
- let selRect = this.Bounds;
- let selection: Doc[] = [];
+ const selRect = this.Bounds;
+ const selection: Doc[] = [];
this.props.activeDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => {
- let layoutDoc = Doc.Layout(doc);
- var x = NumCast(doc.x);
- var y = NumCast(doc.y);
- var w = NumCast(layoutDoc.width);
- var h = NumCast(layoutDoc.height);
+ const layoutDoc = Doc.Layout(doc);
+ const x = NumCast(doc.x);
+ const y = NumCast(doc.y);
+ const w = NumCast(layoutDoc.width);
+ const h = NumCast(layoutDoc.height);
if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
selection.push(doc);
}
});
if (!selection.length && selectBackgrounds) {
this.props.activeDocuments().filter(doc => doc.z === undefined).map(doc => {
- let layoutDoc = Doc.Layout(doc);
- var x = NumCast(doc.x);
- var y = NumCast(doc.y);
- var w = NumCast(layoutDoc.width);
- var h = NumCast(layoutDoc.height);
+ const layoutDoc = Doc.Layout(doc);
+ const x = NumCast(doc.x);
+ const y = NumCast(doc.y);
+ const w = NumCast(layoutDoc.width);
+ const h = NumCast(layoutDoc.height);
if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
selection.push(doc);
}
});
}
if (!selection.length) {
- let left = this._downX < this._lastX ? this._downX : this._lastX;
- let top = this._downY < this._lastY ? this._downY : this._lastY;
- let topLeft = this.props.getContainerTransform().transformPoint(left, top);
- let size = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
- let otherBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
+ const left = this._downX < this._lastX ? this._downX : this._lastX;
+ const top = this._downY < this._lastY ? this._downY : this._lastY;
+ const topLeft = this.props.getContainerTransform().transformPoint(left, top);
+ const size = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+ const otherBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
this.props.activeDocuments().filter(doc => doc.z !== undefined).map(doc => {
- let layoutDoc = Doc.Layout(doc);
- var x = NumCast(doc.x);
- var y = NumCast(doc.y);
- var w = NumCast(layoutDoc.width);
- var h = NumCast(layoutDoc.height);
+ const layoutDoc = Doc.Layout(doc);
+ const x = NumCast(doc.x);
+ const y = NumCast(doc.y);
+ const w = NumCast(layoutDoc.width);
+ const h = NumCast(layoutDoc.height);
if (this.intersectRect({ left: x, top: y, width: w, height: h }, otherBounds)) {
selection.push(doc);
}
@@ -508,8 +507,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@computed
get marqueeDiv() {
- let p: [number, number] = this._visible ? this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY) : [0, 0];
- let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+ const p: [number, number] = this._visible ? this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY) : [0, 0];
+ const v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
/**
* @RE - The commented out span below
* This contains the "C for collection, ..." text on marquees.
@@ -521,7 +520,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
render() {
- return <div className="marqueeView" onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} style={{ borderRadius: "inherit" }} onClick={this.onClick} onPointerDown={this.onPointerDown}>
+ return <div className="marqueeView" onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}>
{this._visible ? this.marqueeDiv : null}
{this.props.children}
</div>;
diff --git a/src/client/views/document_templates/caption_toggle/DetailedCaptionToggle.tsx b/src/client/views/document_templates/caption_toggle/DetailedCaptionToggle.tsx
index f8104cef3..3aaf4120c 100644
--- a/src/client/views/document_templates/caption_toggle/DetailedCaptionToggle.tsx
+++ b/src/client/views/document_templates/caption_toggle/DetailedCaptionToggle.tsx
@@ -1,8 +1,8 @@
import * as React from 'react';
-import { FontWeightProperty, FontStyleProperty, FontSizeProperty, ColorProperty } from 'csstype';
+import { FontStyleProperty, ColorProperty } from 'csstype';
import { observer } from 'mobx-react';
import { observable, action, runInAction } from 'mobx';
-import { FormattedTextBox, FormattedTextBoxProps } from '../../nodes/FormattedTextBox';
+import { FormattedTextBox } from '../../nodes/FormattedTextBox';
import { FieldViewProps } from '../../nodes/FieldView';
interface DetailedCaptionDataProps {
@@ -33,7 +33,7 @@ export default class DetailedCaptionToggle extends React.Component<DetailedCapti
}
render() {
- let size = this.props.toggleSize || 20;
+ const size = this.props.toggleSize || 20;
return (
<div style={{
transition: "0.5s opacity ease",
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index ecb3e9db4..bb8a8b47b 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -43,12 +43,12 @@ class GroupTypesDropdown extends React.Component<GroupTypesDropdownProps> {
@action
onKeyDown = (e: React.KeyboardEvent): void => {
if (e.key === "Enter") {
- let allGroupTypes = Array.from(LinkManager.Instance.getAllGroupTypes());
- let groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
- let exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase());
+ const allGroupTypes = Array.from(LinkManager.Instance.getAllGroupTypes());
+ const groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+ const exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase());
if (exactFound > -1) {
- let groupType = groupOptions[exactFound];
+ const groupType = groupOptions[exactFound];
this.props.setGroupType(groupType);
this._groupType = groupType;
} else {
@@ -84,19 +84,19 @@ class GroupTypesDropdown extends React.Component<GroupTypesDropdownProps> {
renderOptions = (): JSX.Element[] | JSX.Element => {
if (this._searchTerm === "") return <></>;
- let allGroupTypes = Array.from(LinkManager.Instance.getAllGroupTypes());
- let groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
- let exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
+ const allGroupTypes = Array.from(LinkManager.Instance.getAllGroupTypes());
+ const groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+ const exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
- let options = groupOptions.map(groupType => {
- let ref = React.createRef<HTMLDivElement>();
+ const options = groupOptions.map(groupType => {
+ const ref = React.createRef<HTMLDivElement>();
return <div key={groupType} ref={ref} className="linkEditor-option"
onClick={() => this.onOptionClick(groupType, false)}>{groupType}</div>;
});
// if search term does not already exist as a group type, give option to create new group type
if (!exactFound && this._searchTerm !== "") {
- let ref = React.createRef<HTMLDivElement>();
+ const ref = React.createRef<HTMLDivElement>();
options.push(<div key={""} ref={ref} className="linkEditor-option"
onClick={() => this.onOptionClick(this._searchTerm, true)}>Define new "{this._searchTerm}" relationship</div>);
}
@@ -138,10 +138,10 @@ class LinkMetadataEditor extends React.Component<LinkMetadataEditorProps> {
@action
setMetadataKey = (value: string): void => {
- let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);
+ const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);
// don't allow user to create existing key
- let newIndex = groupMdKeys.findIndex(key => key.toUpperCase() === value.toUpperCase());
+ const newIndex = groupMdKeys.findIndex(key => key.toUpperCase() === value.toUpperCase());
if (newIndex > -1) {
this._keyError = true;
this._key = value;
@@ -151,7 +151,7 @@ class LinkMetadataEditor extends React.Component<LinkMetadataEditorProps> {
}
// set new value for key
- let currIndex = groupMdKeys.findIndex(key => {
+ const currIndex = groupMdKeys.findIndex(key => {
return StrCast(key).toUpperCase() === this._key.toUpperCase();
});
if (currIndex === -1) console.error("LinkMetadataEditor: key was not found");
@@ -172,9 +172,9 @@ class LinkMetadataEditor extends React.Component<LinkMetadataEditorProps> {
@action
removeMetadata = (): void => {
- let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);
+ const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);
- let index = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase());
+ const index = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase());
if (index === -1) console.error("LinkMetadataEditor: key was not found");
groupMdKeys.splice(index, 1);
@@ -206,7 +206,7 @@ export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> {
constructor(props: LinkGroupEditorProps) {
super(props);
- let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(StrCast(props.groupDoc.type));
+ const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(StrCast(props.groupDoc.type));
groupMdKeys.forEach(key => {
this._metadataIds.set(key, Utils.GenerateGuid());
});
@@ -226,25 +226,25 @@ export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> {
}
copyGroup = async (groupType: string): Promise<void> => {
- let sourceGroupDoc = this.props.groupDoc;
+ const sourceGroupDoc = this.props.groupDoc;
const sourceMdDoc = await Cast(sourceGroupDoc.metadata, Doc);
if (!sourceMdDoc) return;
- let destDoc = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
+ const destDoc = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
// let destGroupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, destDoc);
- let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
+ const keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
// create new metadata doc with copied kvp
- let destMdDoc = new Doc();
+ const destMdDoc = new Doc();
destMdDoc.anchor1 = StrCast(sourceMdDoc.anchor2);
destMdDoc.anchor2 = StrCast(sourceMdDoc.anchor1);
keys.forEach(key => {
- let val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]);
+ const val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]);
destMdDoc[key] = val;
});
// create new group doc with new metadata doc
- let destGroupDoc = new Doc();
+ const destGroupDoc = new Doc();
destGroupDoc.type = groupType;
destGroupDoc.metadata = destMdDoc;
@@ -256,7 +256,7 @@ export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> {
@action
addMetadata = (groupType: string): void => {
this._metadataIds.set("new key", Utils.GenerateGuid());
- let mdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
+ const mdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
// only add "new key" if there is no other key with value "new key"; prevents spamming
if (mdKeys.indexOf("new key") === -1) mdKeys.push("new key");
LinkManager.Instance.setMetadataKeysForGroup(groupType, mdKeys);
@@ -268,17 +268,17 @@ export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> {
}
renderMetadata = (): JSX.Element[] => {
- let metadata: Array<JSX.Element> = [];
- let groupDoc = this.props.groupDoc;
+ const metadata: Array<JSX.Element> = [];
+ const groupDoc = this.props.groupDoc;
const mdDoc = FieldValue(Cast(groupDoc.metadata, Doc));
if (!mdDoc) {
return [];
}
- let groupType = StrCast(groupDoc.type);
- let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
+ const groupType = StrCast(groupDoc.type);
+ const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
groupMdKeys.forEach((key) => {
- let val = StrCast(mdDoc[key]);
+ const val = StrCast(mdDoc[key]);
metadata.push(
<LinkMetadataEditor key={"mded-" + this._metadataIds.get(key)} id={this._metadataIds.get(key)!} groupType={groupType} mdDoc={mdDoc} mdKey={key} mdValue={val} changeMdIdKey={this.changeMdIdKey} />
);
@@ -287,18 +287,18 @@ export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> {
}
viewGroupAsTable = (groupType: string): JSX.Element => {
- let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
- let index = keys.indexOf("");
+ const keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
+ const index = keys.indexOf("");
if (index > -1) keys.splice(index, 1);
- let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb"));
- let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType);
- let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" }));
- let ref = React.createRef<HTMLDivElement>();
+ const cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb"));
+ const docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType);
+ const createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" }));
+ const ref = React.createRef<HTMLDivElement>();
return <div ref={ref}><button className="linkEditor-button" onPointerDown={SetupDrag(ref, createTable)} title="Drag to view relationship table"><FontAwesomeIcon icon="table" size="sm" /></button></div>;
}
render() {
- let groupType = StrCast(this.props.groupDoc.type);
+ const groupType = StrCast(this.props.groupDoc.type);
// if ((groupType && LinkManager.Instance.getMetadataKeysInGroup(groupType).length > 0) || groupType === "") {
let buttons;
if (groupType === "") {
@@ -356,15 +356,15 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
@action
addGroup = (): void => {
// create new metadata document for group
- let mdDoc = new Doc();
+ const mdDoc = new Doc();
mdDoc.anchor1 = this.props.sourceDoc.title;
- let opp = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
+ const opp = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
if (opp) {
mdDoc.anchor2 = opp.title;
}
// create new group document
- let groupDoc = new Doc();
+ const groupDoc = new Doc();
groupDoc.type = "";
groupDoc.metadata = mdDoc;
@@ -372,10 +372,10 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
}
render() {
- let destination = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
+ const destination = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
- let groupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc);
- let groups = groupList.map(groupDoc => {
+ const groupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc);
+ const groups = groupList.map(groupDoc => {
return <LinkGroupEditor key={"gred-" + StrCast(groupDoc.type)} linkDoc={this.props.linkDoc} sourceDoc={this.props.sourceDoc} groupDoc={groupDoc} />;
});
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
index efe2c7f2a..29e167ff7 100644
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -68,14 +68,14 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
this._contextDisposer = reaction(
() => this.selectedContextString,
async () => {
- let ref = await DocServer.GetRefField(this.selectedContextString);
+ const ref = await DocServer.GetRefField(this.selectedContextString);
runInAction(() => {
if (ref instanceof Doc) {
this.selectedContext = ref;
}
});
if (this.selectedContext instanceof Doc) {
- let aliases = await SearchUtil.GetViewsOfDocument(this.selectedContext);
+ const aliases = await SearchUtil.GetViewsOfDocument(this.selectedContext);
runInAction(() => { this.selectedContextAliases = aliases; });
}
}
@@ -90,8 +90,8 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
if (LinkFollowBox.destinationDoc && this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
runInAction(() => this.canPan = false);
if (this.sourceView.props.ContainingCollectionDoc.viewType === CollectionViewType.Freeform) {
- let docs = Cast(this.sourceView.props.ContainingCollectionDoc.data, listSpec(Doc), []);
- let aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(LinkFollowBox.destinationDoc));
+ const docs = Cast(this.sourceView.props.ContainingCollectionDoc.data, listSpec(Doc), []);
+ const aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(LinkFollowBox.destinationDoc));
aliases.forEach(alias => {
if (docs.filter(doc => doc === alias).length > 0) {
@@ -118,8 +118,8 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
async fetchDocuments() {
if (LinkFollowBox.destinationDoc) {
- let dest: Doc = LinkFollowBox.destinationDoc;
- let aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(dest));
+ const dest: Doc = LinkFollowBox.destinationDoc;
+ const aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(dest));
const { docs } = await SearchUtil.Search("", true, { fq: `data_l:"${dest[Id]}"` });
const map: Map<Doc, Doc> = new Map;
const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", true, { fq: `data_l:"${doc[Id]}"` }).then(result => result.docs)));
@@ -128,7 +128,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
runInAction(async () => {
this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).map(doc => ({ col: doc, target: dest }));
this._otherDocs = Array.from(map.entries()).filter(entry => !Doc.AreProtosEqual(entry[0], CollectionDockingView.Instance.props.Document)).map(([col, target]) => ({ col, target }));
- let tcontext = LinkFollowBox.linkDoc && (await Cast(LinkFollowBox.linkDoc.anchor2Context, Doc)) as Doc;
+ const tcontext = LinkFollowBox.linkDoc && (await Cast(LinkFollowBox.linkDoc.anchor2Context, Doc)) as Doc;
runInAction(() => tcontext && this._docs.splice(0, 0, { col: tcontext, target: dest }));
});
}
@@ -157,7 +157,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
@undoBatch
openFullScreen = () => {
if (LinkFollowBox.destinationDoc) {
- let view = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc);
+ const view = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc);
view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view);
}
}
@@ -171,7 +171,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
options.context.panX = newPanX;
options.context.panY = newPanY;
}
- let view = DocumentManager.Instance.getDocumentView(options.context);
+ const view = DocumentManager.Instance.getDocumentView(options.context);
view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view);
this.highlightDoc();
}
@@ -211,7 +211,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
@undoBatch
openLinkRight = () => {
if (LinkFollowBox.destinationDoc) {
- let alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
+ const alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
(LinkFollowBox._addDocTab || this.props.addDocTab)(alias, undefined, "onRight");
this.highlightDoc();
SelectionManager.DeselectAll();
@@ -222,7 +222,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
@undoBatch
jumpToLink = async (options: { shouldZoom: boolean }) => {
if (LinkFollowBox.sourceDoc && LinkFollowBox.linkDoc) {
- let focus = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
+ const focus = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
//let focus = (doc: Doc, maxLocation: string) => this.props.focus(docthis.props.focus(LinkFollowBox.destinationDoc, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation));
DocumentManager.Instance.FollowLink(LinkFollowBox.linkDoc, LinkFollowBox.sourceDoc, focus, options && options.shouldZoom, false, undefined);
@@ -232,7 +232,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
@undoBatch
openLinkTab = () => {
if (LinkFollowBox.destinationDoc) {
- let fullScreenAlias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
+ const fullScreenAlias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
// this.prosp.addDocTab is empty -- use the link source's addDocTab
(LinkFollowBox._addDocTab || this.props.addDocTab)(fullScreenAlias, undefined, "inTab");
@@ -264,14 +264,14 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
if (LinkFollowBox.destinationDoc && LinkFollowBox.sourceDoc) {
if (this.sourceView && this.sourceView.props.addDocument) {
- let destViews = DocumentManager.Instance.getDocumentViews(LinkFollowBox.destinationDoc);
+ const destViews = DocumentManager.Instance.getDocumentViews(LinkFollowBox.destinationDoc);
if (!destViews.find(dv => dv.props.ContainingCollectionView === this.sourceView!.props.ContainingCollectionView)) {
- let alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
- let y = NumCast(LinkFollowBox.sourceDoc.y);
- let x = NumCast(LinkFollowBox.sourceDoc.x);
+ const alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
+ const y = NumCast(LinkFollowBox.sourceDoc.y);
+ const x = NumCast(LinkFollowBox.sourceDoc.x);
- let width = NumCast(LinkFollowBox.sourceDoc.width);
- let height = NumCast(LinkFollowBox.sourceDoc.height);
+ const width = NumCast(LinkFollowBox.sourceDoc.width);
+ const height = NumCast(LinkFollowBox.sourceDoc.height);
alias.x = x + width + 30;
alias.y = y;
@@ -301,8 +301,8 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
this.selectedContext = LinkFollowBox.destinationDoc;
}
if (this.selectedOption === "") this.selectedOption = FollowOptions.NOZOOM;
- let shouldZoom: boolean = this.selectedOption === FollowOptions.NOZOOM ? false : true;
- let notOpenInContext: boolean = this.selectedContextString === "self" || this.selectedContextString === LinkFollowBox.destinationDoc[Id];
+ const shouldZoom: boolean = this.selectedOption === FollowOptions.NOZOOM ? false : true;
+ const notOpenInContext: boolean = this.selectedContextString === "self" || this.selectedContextString === LinkFollowBox.destinationDoc[Id];
if (this.selectedMode === FollowModes.INPLACE) {
if (shouldZoom !== undefined) this.openLinkInPlace({ shouldZoom: shouldZoom });
@@ -328,7 +328,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
@action
handleModeChange = (e: React.ChangeEvent) => {
- let target = e.target as HTMLInputElement;
+ const target = e.target as HTMLInputElement;
this.selectedMode = target.value;
this.selectedContext = undefined;
this.selectedContextString = "";
@@ -345,13 +345,13 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
@action
handleOptionChange = (e: React.ChangeEvent) => {
- let target = e.target as HTMLInputElement;
+ const target = e.target as HTMLInputElement;
this.selectedOption = target.value;
}
@action
handleContextChange = (e: React.ChangeEvent) => {
- let target = e.target as HTMLInputElement;
+ const target = e.target as HTMLInputElement;
this.selectedContextString = target.value;
// selectedContext is updated in reaction
this.selectedOption = "";
@@ -360,7 +360,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
@computed
get canOpenInPlace() {
if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
- let colDoc = this.sourceView.props.ContainingCollectionDoc;
+ const colDoc = this.sourceView.props.ContainingCollectionDoc;
if (colDoc.viewType && colDoc.viewType === CollectionViewType.Freeform) return true;
}
return false;
diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss
index a4018bd2d..7dee22f66 100644
--- a/src/client/views/linking/LinkMenu.scss
+++ b/src/client/views/linking/LinkMenu.scss
@@ -48,90 +48,5 @@
}
}
-.linkMenu-item {
- // border-top: 0.5px solid $main-accent;
- position: relative;
- display: flex;
- font-size: 12px;
-
-
- .link-name {
- position: relative;
-
- p {
- padding: 4px 6px;
- line-height: 12px;
- border-radius: 5px;
- overflow-wrap: break-word;
- }
- }
-
- .linkMenu-item-content {
- width: 100%;
- }
-
- .link-metadata {
- padding: 0 10px 0 16px;
- margin-bottom: 4px;
- color: $main-accent;
- font-style: italic;
- font-size: 10.5px;
- }
-
- &:hover {
- .linkMenu-item-buttons {
- display: flex;
- }
- .linkMenu-item-content {
- &.expand-two p {
- width: calc(100% - 52px);
- background-color: lightgray;
- }
- &.expand-three p {
- width: calc(100% - 84px);
- background-color: lightgray;
- }
- }
- }
-}
-
-.linkMenu-item-buttons {
- display: none;
- position: absolute;
- top: 50%;
- right: 0;
- transform: translateY(-50%);
-
- .button {
- width: 20px;
- height: 20px;
- margin: 0;
- margin-right: 6px;
- border-radius: 50%;
- cursor: pointer;
- pointer-events: auto;
- background-color: $dark-color;
- color: $light-color;
- font-size: 65%;
- transition: transform 0.2s;
- text-align: center;
- position: relative;
-
- .fa-icon {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- }
-
- &:last-child {
- margin-right: 0;
- }
- &:hover {
- background: $main-accent;
- }
- }
-}
-
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 27af873b5..52628ba4c 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -34,7 +34,7 @@ export class LinkMenu extends React.Component<Props> {
}
renderAllGroups = (groups: Map<string, Array<Doc>>): Array<JSX.Element> => {
- let linkItems: Array<JSX.Element> = [];
+ const linkItems: Array<JSX.Element> = [];
groups.forEach((group, groupType) => {
linkItems.push(
<LinkMenuGroup
@@ -55,8 +55,8 @@ export class LinkMenu extends React.Component<Props> {
}
render() {
- let sourceDoc = this.props.docView.props.Document;
- let groups: Map<string, Doc[]> = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc);
+ const sourceDoc = this.props.docView.props.Document;
+ const groups: Map<string, Doc[]> = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc);
if (this._editingLink === undefined) {
return (
<div className="linkMenu">
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index 1891919ce..abd17ec4d 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -4,11 +4,9 @@ import { observer } from "mobx-react";
import { Doc } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { emptyFunction } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DragManager, SetupDrag } from "../../util/DragManager";
import { LinkManager } from "../../util/LinkManager";
-import { UndoManager } from "../../util/UndoManager";
import { DocumentView } from "../nodes/DocumentView";
import './LinkMenu.scss';
import { LinkMenuItem } from "./LinkMenuItem";
@@ -21,7 +19,6 @@ interface LinkMenuGroupProps {
showEditor: (linkDoc: Doc) => void;
addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
docView: DocumentView;
-
}
@observer
@@ -44,44 +41,31 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
e.stopPropagation();
}
-
onLinkButtonMoved = async (e: PointerEvent) => {
- UndoManager.RunInBatch(() => {
- if (this._drag.current !== null && (e.movementX > 1 || e.movementY > 1)) {
- document.removeEventListener("pointermove", this.onLinkButtonMoved);
- document.removeEventListener("pointerup", this.onLinkButtonUp);
+ if (this._drag.current && (e.movementX > 1 || e.movementY > 1)) {
+ document.removeEventListener("pointermove", this.onLinkButtonMoved);
+ document.removeEventListener("pointerup", this.onLinkButtonUp);
- let draggedDocs = this.props.group.map(linkDoc => {
- let opp = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
- if (opp) return opp;
- }) as Doc[];
- let dragData = new DragManager.DocumentDragData(draggedDocs);
-
- DragManager.StartLinkedDocumentDrag([this._drag.current], dragData, e.x, e.y, {
- handlers: {
- dragComplete: action(emptyFunction),
- },
- hideSource: false
- });
- }
- }, "drag links");
+ const targets = this.props.group.map(l => LinkManager.Instance.getOppositeAnchor(l, this.props.sourceDoc)).filter(d => d) as Doc[];
+ DragManager.StartLinkTargetsDrag(this._drag.current!, e.x, e.y, this.props.sourceDoc, targets);
+ }
e.stopPropagation();
}
viewGroupAsTable = (groupType: string): JSX.Element => {
- let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
- let index = keys.indexOf("");
+ const keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
+ const index = keys.indexOf("");
if (index > -1) keys.splice(index, 1);
- let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb"));
- let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType);
- let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" }));
- let ref = React.createRef<HTMLDivElement>();
+ const cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb"));
+ const docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType);
+ const createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" }));
+ const ref = React.createRef<HTMLDivElement>();
return <div ref={ref}><button className="linkEditor-button linkEditor-tableButton" onPointerDown={SetupDrag(ref, createTable)} title="Drag to view relationship table"><FontAwesomeIcon icon="table" size="sm" /></button></div>;
}
render() {
- let groupItems = this.props.group.map(linkDoc => {
- let destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
+ const groupItems = this.props.group.map(linkDoc => {
+ const destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
if (destination && this.props.sourceDoc) {
return <LinkMenuItem key={destination[Id] + this.props.sourceDoc[Id]}
groupType={this.props.groupType}
diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss
new file mode 100644
index 000000000..fd0954f65
--- /dev/null
+++ b/src/client/views/linking/LinkMenuItem.scss
@@ -0,0 +1,87 @@
+@import "../globalCssVariables";
+
+.linkMenu-item {
+ // border-top: 0.5px solid $main-accent;
+ position: relative;
+ display: flex;
+ font-size: 12px;
+
+
+ .linkMenu-name {
+ position: relative;
+
+ p {
+ padding: 4px 6px;
+ line-height: 12px;
+ border-radius: 5px;
+ overflow-wrap: break-word;
+ user-select: none;
+ }
+ }
+
+ .linkMenu-item-content {
+ width: 100%;
+ }
+
+ .link-metadata {
+ padding: 0 10px 0 16px;
+ margin-bottom: 4px;
+ color: $main-accent;
+ font-style: italic;
+ font-size: 10.5px;
+ }
+
+ &:hover {
+ .linkMenu-item-buttons {
+ display: flex;
+ }
+ .linkMenu-item-content {
+ &.expand-two p {
+ width: calc(100% - 52px);
+ background-color: lightgray;
+ }
+ &.expand-three p {
+ width: calc(100% - 84px);
+ background-color: lightgray;
+ }
+ }
+ }
+}
+
+.linkMenu-item-buttons {
+ display: none;
+ position: absolute;
+ top: 50%;
+ right: 0;
+ transform: translateY(-50%);
+
+ .button {
+ width: 20px;
+ height: 20px;
+ margin: 0;
+ margin-right: 6px;
+ border-radius: 50%;
+ cursor: pointer;
+ pointer-events: auto;
+ background-color: $dark-color;
+ color: $light-color;
+ font-size: 65%;
+ transition: transform 0.2s;
+ text-align: center;
+ position: relative;
+
+ .fa-icon {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+
+ &:last-child {
+ margin-right: 0;
+ }
+ &:hover {
+ background: $main-accent;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 238660de3..b7d27ee30 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -5,11 +5,11 @@ import { action, observable } from 'mobx';
import { observer } from "mobx-react";
import { Doc } from '../../../new_fields/Doc';
import { Cast, StrCast } from '../../../new_fields/Types';
-import { DragLinkAsDocument } from '../../util/DragManager';
+import { DragManager } from '../../util/DragManager';
import { LinkManager } from '../../util/LinkManager';
import { ContextMenu } from '../ContextMenu';
import { LinkFollowBox } from './LinkFollowBox';
-import './LinkMenu.scss';
+import './LinkMenuItem.scss';
import React = require("react");
library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp);
@@ -26,6 +26,9 @@ interface LinkMenuItemProps {
@observer
export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
private _drag = React.createRef<HTMLDivElement>();
+ private _downX = 0;
+ private _downY = 0;
+ private _eleClone: any;
@observable private _showMore: boolean = false;
@action toggleShowMore() { this._showMore = !this._showMore; }
@@ -36,15 +39,15 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
}
renderMetadata = (): JSX.Element => {
- let groups = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc);
- let index = groups.findIndex(groupDoc => StrCast(groupDoc.type).toUpperCase() === this.props.groupType.toUpperCase());
- let groupDoc = index > -1 ? groups[index] : undefined;
+ const groups = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc);
+ const index = groups.findIndex(groupDoc => StrCast(groupDoc.type).toUpperCase() === this.props.groupType.toUpperCase());
+ const groupDoc = index > -1 ? groups[index] : undefined;
let mdRows: Array<JSX.Element> = [];
if (groupDoc) {
- let mdDoc = Cast(groupDoc.metadata, Doc, null);
+ const mdDoc = Cast(groupDoc.metadata, Doc, null);
if (mdDoc) {
- let keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
+ const keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
mdRows = keys.map(key => {
return (<div key={key} className="link-metadata-row"><b>{key}</b>: {StrCast(mdDoc[key])}</div>);
});
@@ -55,6 +58,9 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
}
onLinkButtonDown = (e: React.PointerEvent): void => {
+ this._downX = e.clientX;
+ this._downY = e.clientY;
+ this._eleClone = this._drag.current!.cloneNode(true);
e.stopPropagation();
document.removeEventListener("pointermove", this.onLinkButtonMoved);
document.addEventListener("pointermove", this.onLinkButtonMoved);
@@ -75,11 +81,12 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
}
onLinkButtonMoved = async (e: PointerEvent) => {
- if (this._drag.current !== null && (e.movementX > 1 || e.movementY > 1)) {
+ if (this._drag.current !== null && Math.abs((e.clientX - this._downX) * (e.clientX - this._downX) + (e.clientY - this._downY) * (e.clientY - this._downY)) > 5) {
document.removeEventListener("pointermove", this.onLinkButtonMoved);
document.removeEventListener("pointerup", this.onLinkButtonUp);
- DragLinkAsDocument(this._drag.current, e.x, e.y, this.props.linkDoc, this.props.sourceDoc);
+ this._eleClone.style.transform = `translate(${e.x}px, ${e.y}px)`;
+ DragManager.StartLinkTargetsDrag(this._eleClone, e.x, e.y, this.props.sourceDoc, [this.props.linkDoc]);
}
e.stopPropagation();
}
@@ -109,20 +116,21 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
}
render() {
-
- let keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
- let canExpand = keys ? keys.length > 0 : false;
+ const keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
+ const canExpand = keys ? keys.length > 0 : false;
return (
<div className="linkMenu-item">
<div className={canExpand ? "linkMenu-item-content expand-three" : "linkMenu-item-content expand-two"}>
- <div className="link-name">
- <p ref={this._drag} onPointerDown={this.onLinkButtonDown}>{StrCast(this.props.destinationDoc.title)}</p>
+ <div ref={this._drag} className="linkMenu-name" title="drag to view target. click to customize." onPointerDown={this.onLinkButtonDown}>
+ <p >{StrCast(this.props.destinationDoc.title)}</p>
<div className="linkMenu-item-buttons">
{canExpand ? <div title="Show more" className="button" onPointerDown={() => this.toggleShowMore()}>
<FontAwesomeIcon className="fa-icon" icon={this._showMore ? "chevron-up" : "chevron-down"} size="sm" /></div> : <></>}
<div title="Edit link" className="button" onPointerDown={this.onEdit}><FontAwesomeIcon className="fa-icon" icon="edit" size="sm" /></div>
- <div title="Follow link" className="button" onClick={this.followDefault} onContextMenu={this.onContextMenu}><FontAwesomeIcon className="fa-icon" icon="arrow-right" size="sm" /></div>
+ <div title="Follow link" className="button" onClick={this.followDefault} onContextMenu={this.onContextMenu}>
+ <FontAwesomeIcon className="fa-icon" icon="arrow-right" size="sm" />
+ </div>
</div>
</div>
{this._showMore ? this.renderMetadata() : <></>}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 86bd23b67..95c765e8a 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -8,7 +8,6 @@ import { DocExtendableComponent } from "../DocComponent";
import { makeInterface, createSchema } from "../../../new_fields/Schema";
import { documentSchema } from "../../../new_fields/documentSchemas";
import { Utils, returnTrue, emptyFunction, returnOne, returnTransparent } from "../../../Utils";
-import { RouteStore } from "../../../server/RouteStore";
import { runInAction, observable, reaction, IReactionDisposer, computed, action } from "mobx";
import { DateField } from "../../../new_fields/DateField";
import { SelectionManager } from "../../util/SelectionManager";
@@ -57,19 +56,19 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
this._linkPlayDisposer = reaction(() => this.layoutDoc.scrollToLinkID,
scrollLinkId => {
scrollLinkId && DocListCast(this.dataDoc.links).filter(l => l[Id] === scrollLinkId).map(l => {
- let la1 = l.anchor1 as Doc;
- let linkTime = Doc.AreProtosEqual(la1, this.dataDoc) ? NumCast(l.anchor1Timecode) : NumCast(l.anchor2Timecode);
+ const la1 = l.anchor1 as Doc;
+ const linkTime = Doc.AreProtosEqual(la1, this.dataDoc) ? NumCast(l.anchor1Timecode) : NumCast(l.anchor2Timecode);
setTimeout(() => { this.playFrom(linkTime); Doc.linkFollowHighlight(l); }, 250);
});
scrollLinkId && Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false);
}, { fireImmediately: true });
this._reactionDisposer = reaction(() => SelectionManager.SelectedDocuments(),
selected => {
- let sel = selected.length ? selected[0].props.Document : undefined;
+ const sel = selected.length ? selected[0].props.Document : undefined;
this.Document.playOnSelect && sel && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFrom(DateCast(sel.creationTime).date.getTime());
});
this._scrubbingDisposer = reaction(() => AudioBox._scrubTime, timeInMillisecondsFrom1970 => {
- let start = this.extensionDoc && DateCast(this.extensionDoc.recordingStart);
+ const start = this.extensionDoc && DateCast(this.extensionDoc.recordingStart);
start && this.playFrom((timeInMillisecondsFrom1970 - start.date.getTime()) / 1000);
});
}
@@ -128,7 +127,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
recordAudioAnnotation = () => {
let gumStream: any;
- let self = this;
+ const self = this;
const extensionDoc = this.extensionDoc;
extensionDoc && navigator.mediaDevices.getUserMedia({
audio: true
@@ -140,7 +139,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
self._recorder.ondataavailable = async function (e: any) {
const formData = new FormData();
formData.append("file", e.data);
- const res = await fetch(Utils.prepend(RouteStore.upload), {
+ const res = await fetch(Utils.prepend("/upload"), {
method: 'POST',
body: formData
});
@@ -161,7 +160,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
}
specificContextMenu = (e: React.MouseEvent): void => {
- let funcs: ContextMenuProps[] = [];
+ const funcs: ContextMenuProps[] = [];
funcs.push({ description: (this.Document.playOnSelect ? "Don't play" : "Play") + " when document selected", event: () => this.Document.playOnSelect = !this.Document.playOnSelect, icon: "expand-arrows-alt" });
ContextMenu.Instance.addItem({ description: "Audio Funcs...", subitems: funcs, icon: "asterisk" });
@@ -171,7 +170,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
this._recorder.stop();
this.dataDoc.duration = (new Date().getTime() - this._recordStart) / 1000;
this._audioState = "recorded";
- let ind = AudioBox.ActiveRecordings.indexOf(this.props.Document);
+ const ind = AudioBox.ActiveRecordings.indexOf(this.props.Document);
ind !== -1 && (AudioBox.ActiveRecordings.splice(ind, 1));
});
@@ -199,13 +198,13 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
}
@computed get path() {
- let field = Cast(this.props.Document[this.props.fieldKey], AudioField);
- let path = (field instanceof AudioField) ? field.url.href : "";
+ const field = Cast(this.props.Document[this.props.fieldKey], AudioField);
+ const path = (field instanceof AudioField) ? field.url.href : "";
return path === nullAudio ? "" : path;
}
@computed get audio() {
- let interactive = this.active() ? "-interactive" : "";
+ const interactive = this.active() ? "-interactive" : "";
return <audio ref={this.setRef} className={`audiobox-control${interactive}`}>
<source src={this.path} type="audio/mpeg" />
Not supported.
@@ -213,7 +212,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
}
render() {
- let interactive = this.active() ? "-interactive" : "";
+ const interactive = this.active() ? "-interactive" : "";
return (!this.extensionDoc ? (null) :
<div className={`audiobox-container`} onContextMenu={this.specificContextMenu}
onClick={!this.path ? this.recordClick : undefined}>
@@ -229,7 +228,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
<div className="audiobox-timeline" onClick={e => e.stopPropagation()}
onPointerDown={e => {
if (e.button === 0 && !e.ctrlKey) {
- let rect = (e.target as any).getBoundingClientRect();
+ const rect = (e.target as any).getBoundingClientRect();
this._ele!.currentTime = this.Document.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration);
this.pause();
e.stopPropagation();
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index 659ba154a..d1272c266 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -46,15 +46,15 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
this.dropDisposer();
}
if (ele) {
- this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+ this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
}
}
specificContextMenu = (e: React.MouseEvent): void => {
- let funcs: ContextMenuProps[] = [];
+ const funcs: ContextMenuProps[] = [];
funcs.push({
description: "Clear Script Params", event: () => {
- let params = FieldValue(this.Document.buttonParams);
+ const params = FieldValue(this.Document.buttonParams);
params && params.map(p => this.props.Document[p] = undefined);
}, icon: "trash"
});
@@ -65,16 +65,17 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
- if (de.data instanceof DragManager.DocumentDragData && e.target) {
- this.props.Document[(e.target as any).textContent] = new List<Doc>(de.data.droppedDocuments.map((d, i) =>
- d.onDragStart ? de.data.draggedDocuments[i] : d));
+ const docDragData = de.complete.docDragData;
+ if (docDragData && e.target) {
+ this.props.Document[(e.target as any).textContent] = new List<Doc>(docDragData.droppedDocuments.map((d, i) =>
+ d.onDragStart ? docDragData.draggedDocuments[i] : d));
e.stopPropagation();
}
}
// (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")")
render() {
- let params = this.Document.buttonParams;
- let missingParams = params && params.filter(p => this.props.Document[p] === undefined);
+ const params = this.Document.buttonParams;
+ const missingParams = params && params.filter(p => this.props.Document[p] === undefined);
params && params.map(p => DocListCast(this.props.Document[p])); // bcz: really hacky form of prefetching ...
return (
<div className="buttonBox-outerDiv" ref={this.createDropTarget} onContextMenu={this.specificContextMenu}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index c85b59488..261a88deb 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -31,7 +31,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
get Y() { return this._animPos !== undefined ? this._animPos[1] : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); }
get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.dataProvider && this.dataProvider ? this.dataProvider.width : this.layoutDoc[WidthSym](); }
get height() {
- let hgt = this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.dataProvider && this.dataProvider ? this.dataProvider.height : this.layoutDoc[HeightSym]();
+ const hgt = this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.dataProvider && this.dataProvider ? this.dataProvider.height : this.layoutDoc[HeightSym]();
return (hgt === undefined && this.nativeWidth && this.nativeHeight) ? this.width * this.nativeHeight / this.nativeWidth : hgt;
}
@computed get dataProvider() { return this.props.dataProvider && this.props.dataProvider(this.props.Document) ? this.props.dataProvider(this.props.Document) : undefined; }
@@ -40,13 +40,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
@computed get renderScriptDim() {
if (this.Document.renderScript) {
- let someView = Cast(this.props.Document.someView, Doc);
- let minimap = Cast(this.props.Document.minimap, Doc);
+ const someView = Cast(this.props.Document.someView, Doc);
+ const minimap = Cast(this.props.Document.minimap, Doc);
if (someView instanceof Doc && minimap instanceof Doc) {
- let x = (NumCast(someView.panX) - NumCast(someView.width) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap.width) - NumCast(minimap.width) / 2;
- let y = (NumCast(someView.panY) - NumCast(someView.height) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap.height) - NumCast(minimap.height) / 2;
- let w = NumCast(someView.width) / NumCast(someView.scale) / NumCast(minimap.fitW) * NumCast(minimap.width);
- let h = NumCast(someView.height) / NumCast(someView.scale) / NumCast(minimap.fitH) * NumCast(minimap.height);
+ const x = (NumCast(someView.panX) - NumCast(someView.width) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap.width) - NumCast(minimap.width) / 2;
+ const y = (NumCast(someView.panY) - NumCast(someView.height) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap.height) - NumCast(minimap.height) / 2;
+ const w = NumCast(someView.width) / NumCast(someView.scale) / NumCast(minimap.fitW) * NumCast(minimap.width);
+ const h = NumCast(someView.height) / NumCast(someView.scale) / NumCast(minimap.fitH) * NumCast(minimap.height);
return { x: x, y: y, width: w, height: h };
}
}
@@ -70,9 +70,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
.scale(1 / this.contentScaling())
borderRounding = () => {
- let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
- let ld = this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")] instanceof Doc ? this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")] as Doc : undefined;
- let br = StrCast((ld || this.props.Document).borderRounding);
+ const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
+ const ld = this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")] instanceof Doc ? this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")] as Doc : undefined;
+ const br = StrCast((ld || this.props.Document).borderRounding);
return !br && ruleRounding ? ruleRounding : br;
}
@@ -94,7 +94,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
this.layoutDoc.opacity === 0 ? undefined : // if it's not visible, then no shadow
this.layoutDoc.z ? `#9c9396 ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow
this.clusterColor ? (`${this.clusterColor} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc.isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
- this.layoutDoc.isBackground ? `1px 1px 1px ${this.clusterColor}` : // if it's a background & has a cluster color, make the shadow spread really big
+ this.layoutDoc.isBackground ? undefined : // if it's a background & has a cluster color, make the shadow spread really big
StrCast(this.layoutDoc.boxShadow, ""),
borderRadius: this.borderRounding(),
transform: this.transform,
@@ -104,6 +104,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
zIndex: this.Document.zIndex || 0,
}} >
<DocumentView {...this.props}
+ dragDivName={"collectionFreeFormDocumentView-container"}
ContentScaling={this.contentScaling}
ScreenToLocalTransform={this.getTransform}
backgroundColor={this.clusterColorFunc}
diff --git a/src/client/views/nodes/ContentFittingDocumentView.scss b/src/client/views/nodes/ContentFittingDocumentView.scss
index 796e67269..2801af441 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.scss
+++ b/src/client/views/nodes/ContentFittingDocumentView.scss
@@ -2,10 +2,11 @@
.contentFittingDocumentView {
position: relative;
- height: auto !important;
+ display: flex;
+ align-items: center;
.contentFittingDocumentView-previewDoc {
- position: absolute;
+ position: relative;
display: inline;
}
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 573a55710..2f8142a44 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -13,10 +13,12 @@ import '../DocumentDecorations.scss';
import { DocumentView } from "../nodes/DocumentView";
import "./ContentFittingDocumentView.scss";
import { CollectionView } from "../collections/CollectionView";
+import { TraceMobx } from "../../../new_fields/util";
interface ContentFittingDocumentViewProps {
Document?: Doc;
DataDocument?: Doc;
+ LibraryPath: Doc[];
childDocs?: Doc[];
renderDepth: number;
fitToBox?: boolean;
@@ -29,9 +31,9 @@ interface ContentFittingDocumentViewProps {
CollectionDoc?: Doc;
onClick?: ScriptField;
getTransform: () => Transform;
- addDocument: (document: Doc) => boolean;
- moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean;
- removeDocument: (document: Doc) => boolean;
+ addDocument?: (document: Doc) => boolean;
+ moveDocument?: (document: Doc, target: Doc | undefined, addDoc: ((doc: Doc) => boolean)) => boolean;
+ removeDocument?: (document: Doc) => boolean;
active: (outsideReaction: boolean) => boolean;
whenActiveChanged: (isActive: boolean) => void;
addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
@@ -43,11 +45,12 @@ interface ContentFittingDocumentViewProps {
@observer
export class ContentFittingDocumentView extends React.Component<ContentFittingDocumentViewProps>{
+ public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
private get layoutDoc() { return this.props.Document && Doc.Layout(this.props.Document); }
- private get nativeWidth() { return NumCast(this.layoutDoc!.nativeWidth, this.props.PanelWidth()); }
- private get nativeHeight() { return NumCast(this.layoutDoc!.nativeHeight, this.props.PanelHeight()); }
+ private get nativeWidth() { return NumCast(this.layoutDoc?.nativeWidth, this.props.PanelWidth()); }
+ private get nativeHeight() { return NumCast(this.layoutDoc?.nativeHeight, this.props.PanelHeight()); }
private contentScaling = () => {
- let wscale = this.props.PanelWidth() / (this.nativeWidth ? this.nativeWidth : this.props.PanelWidth());
+ const wscale = this.props.PanelWidth() / (this.nativeWidth ? this.nativeWidth : this.props.PanelWidth());
if (wscale * this.nativeHeight > this.props.PanelHeight()) {
return this.props.PanelHeight() / (this.nativeHeight ? this.nativeHeight : this.props.PanelHeight());
}
@@ -57,11 +60,12 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
- if (de.data instanceof DragManager.DocumentDragData) {
+ const docDragData = de.complete.docDragData;
+ if (docDragData) {
this.props.childDocs && this.props.childDocs.map(otherdoc => {
- let target = Doc.GetProto(otherdoc);
+ const target = Doc.GetProto(otherdoc);
target.layout = ComputedField.MakeFunction("this.image_data[0]");
- target.layoutCustom = Doc.MakeDelegate(de.data.draggedDocuments[0]);
+ target.layoutCustom = Doc.MakeDelegate(docDragData.draggedDocuments[0]);
});
e.stopPropagation();
}
@@ -69,24 +73,30 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
}
private PanelWidth = () => this.nativeWidth && (!this.props.Document || !this.props.Document.fitWidth) ? this.nativeWidth * this.contentScaling() : this.props.PanelWidth();
private PanelHeight = () => this.nativeHeight && (!this.props.Document || !this.props.Document.fitWidth) ? this.nativeHeight * this.contentScaling() : this.props.PanelHeight();
- private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling());
+ private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling());
private get centeringOffset() { return this.nativeWidth && (!this.props.Document || !this.props.Document.fitWidth) ? (this.props.PanelWidth() - this.nativeWidth * this.contentScaling()) / 2 : 0; }
+ private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 ? (this.props.PanelHeight() - this.nativeHeight * this.contentScaling()) / 2 : 0; }
- @computed get borderRounding() { return StrCast(this.props.Document!.borderRounding); }
+ @computed get borderRounding() { return StrCast(this.props.Document?.borderRounding); }
render() {
- return (<div className="contentFittingDocumentView" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
+ TraceMobx();
+ return (<div className="contentFittingDocumentView" style={{
+ width: Math.abs(this.centeringYOffset) > 0.001 ? "auto" : this.props.PanelWidth(),
+ height: Math.abs(this.centeringOffset) > 0.0001 ? "auto" : this.props.PanelHeight()
+ }}>
{!this.props.Document || !this.props.PanelWidth ? (null) : (
<div className="contentFittingDocumentView-previewDoc"
style={{
transform: `translate(${this.centeringOffset}px, 0px)`,
borderRadius: this.borderRounding,
- height: this.props.PanelHeight(),
- width: this.props.PanelWidth()
+ height: Math.abs(this.centeringYOffset) > 0.001 ? `${100 * this.nativeHeight / this.nativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%` : this.props.PanelHeight(),
+ width: Math.abs(this.centeringOffset) > 0.001 ? `${100 * (this.props.PanelWidth() - this.centeringOffset * 2) / this.props.PanelWidth()}%` : this.props.PanelWidth()
}}>
<DocumentView {...this.props}
- DataDoc={this.props.DataDocument}
Document={this.props.Document}
+ DataDoc={this.props.DataDocument}
+ LibraryPath={this.props.LibraryPath}
fitToBox={this.props.fitToBox}
onClick={this.props.onClick}
ruleProvider={this.props.ruleProvider}
@@ -101,7 +111,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
pinToPres={this.props.pinToPres}
parentActive={this.props.active}
ScreenToLocalTransform={this.getTransform}
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={this.props.renderDepth}
ContentScaling={this.contentScaling}
PanelWidth={this.PanelWidth}
PanelHeight={this.PanelHeight}
diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx
index d73407903..0d4d50c59 100644
--- a/src/client/views/nodes/DocuLinkBox.tsx
+++ b/src/client/views/nodes/DocuLinkBox.tsx
@@ -1,17 +1,18 @@
import { action, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, WidthSym, HeightSym } from "../../../new_fields/Doc";
import { makeInterface } from "../../../new_fields/Schema";
import { NumCast, StrCast, Cast } from "../../../new_fields/Types";
import { Utils } from '../../../Utils';
import { DocumentManager } from "../../util/DocumentManager";
-import { DragLinksAsDocuments } from "../../util/DragManager";
+import { DragManager } from "../../util/DragManager";
import { DocComponent } from "../DocComponent";
import "./DocuLinkBox.scss";
import { FieldView, FieldViewProps } from "./FieldView";
import React = require("react");
import { DocumentType } from "../../documents/DocumentTypes";
import { documentSchema } from "../../../new_fields/documentSchemas";
+import { Id } from "../../../new_fields/FieldSymbols";
type DocLinkSchema = makeInterface<[typeof documentSchema]>;
const DocLinkDocument = makeInterface(documentSchema);
@@ -36,14 +37,14 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
(e.button === 0 && !e.ctrlKey) && e.stopPropagation();
}
onPointerMove = action((e: PointerEvent) => {
- let cdiv = this._ref && this._ref.current && this._ref.current.parentElement;
+ const cdiv = this._ref && this._ref.current && this._ref.current.parentElement;
if (cdiv && (Math.abs(e.clientX - this._downx) > 5 || Math.abs(e.clientY - this._downy) > 5)) {
- let bounds = cdiv.getBoundingClientRect();
- let pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY);
- let separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
- let dragdist = Math.sqrt((pt[0] - this._downx) * (pt[0] - this._downx) + (pt[1] - this._downy) * (pt[1] - this._downy));
+ const bounds = cdiv.getBoundingClientRect();
+ const pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY);
+ const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
+ const dragdist = Math.sqrt((pt[0] - this._downx) * (pt[0] - this._downx) + (pt[1] - this._downy) * (pt[1] - this._downy));
if (separation > 100) {
- DragLinksAsDocuments(this._ref.current!, pt[0], pt[1], Cast(this.props.Document[this.props.fieldKey], Doc) as Doc, this.props.Document); // Containging collection is the document, not a collection... hack.
+ DragManager.StartLinkTargetsDrag(this._ref.current!, pt[0], pt[1], Cast(this.props.Document[this.props.fieldKey], Doc) as Doc, [this.props.Document]); // Containging collection is the document, not a collection... hack.
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
} else if (dragdist > separation) {
@@ -67,18 +68,18 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
}
render() {
- let anchorDoc = Cast(this.props.Document[this.props.fieldKey], Doc);
- let hasAnchor = anchorDoc instanceof Doc && anchorDoc.type === DocumentType.PDFANNO;
- let y = NumCast(this.props.Document[this.props.fieldKey + "_y"], 100);
- let x = NumCast(this.props.Document[this.props.fieldKey + "_x"], 100);
- let c = StrCast(this.props.Document.backgroundColor, "lightblue");
- let anchor = this.props.fieldKey === "anchor1" ? "anchor2" : "anchor1";
- let timecode = this.props.Document[anchor + "Timecode"];
- let targetTitle = StrCast((this.props.Document[anchor]! as Doc).title) + (timecode !== undefined ? ":" + timecode : "");
+ const x = NumCast(this.props.Document[this.props.fieldKey + "_x"], 100);
+ const y = NumCast(this.props.Document[this.props.fieldKey + "_y"], 100);
+ const c = StrCast(this.props.Document.backgroundColor, "lightblue");
+ const anchor = this.props.fieldKey === "anchor1" ? "anchor2" : "anchor1";
+ const anchorScale = (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : .15;
+
+ const timecode = this.props.Document[anchor + "Timecode"];
+ const targetTitle = StrCast((this.props.Document[anchor]! as Doc).title) + (timecode !== undefined ? ":" + timecode : "");
return <div className="docuLinkBox-cont" onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle}
ref={this._ref} style={{
background: c, left: `calc(${x}% - 12.5px)`, top: `calc(${y}% - 12.5px)`,
- transform: `scale(${hasAnchor ? 0.333 : 1 / this.props.ContentScaling()})`
+ transform: `scale(${anchorScale / this.props.ContentScaling()})`
}} />;
}
}
diff --git a/src/client/views/nodes/DocumentBox.scss b/src/client/views/nodes/DocumentBox.scss
new file mode 100644
index 000000000..b7d06b364
--- /dev/null
+++ b/src/client/views/nodes/DocumentBox.scss
@@ -0,0 +1,15 @@
+.documentBox-container {
+ width: 100%;
+ height: 100%;
+ pointer-events: all;
+ background: gray;
+ border: #00000021 solid 15px;
+ border-top: #0000005e inset 15px;
+ border-bottom: #0000005e outset 15px;
+ .documentBox-lock {
+ margin: auto;
+ color: white;
+ margin-top: -15px;
+ position: absolute;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
new file mode 100644
index 000000000..94755afec
--- /dev/null
+++ b/src/client/views/nodes/DocumentBox.tsx
@@ -0,0 +1,114 @@
+import { IReactionDisposer, reaction } from "mobx";
+import { observer } from "mobx-react";
+import { Doc, Field } from "../../../new_fields/Doc";
+import { documentSchema } from "../../../new_fields/documentSchemas";
+import { List } from "../../../new_fields/List";
+import { makeInterface } from "../../../new_fields/Schema";
+import { ComputedField } from "../../../new_fields/ScriptField";
+import { Cast, StrCast, BoolCast } from "../../../new_fields/Types";
+import { emptyFunction, emptyPath } from "../../../Utils";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
+import { DocComponent } from "../DocComponent";
+import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import "./DocumentBox.scss";
+import { FieldView, FieldViewProps } from "./FieldView";
+import React = require("react");
+
+type DocBoxSchema = makeInterface<[typeof documentSchema]>;
+const DocBoxDocument = makeInterface(documentSchema);
+
+@observer
+export class DocumentBox extends DocComponent<FieldViewProps, DocBoxSchema>(DocBoxDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocumentBox, fieldKey); }
+ _prevSelectionDisposer: IReactionDisposer | undefined;
+ _selections: Doc[] = [];
+ _curSelection = -1;
+ componentDidMount() {
+ this._prevSelectionDisposer = reaction(() => Cast(this.props.Document[this.props.fieldKey], Doc) as Doc, (data) => {
+ if (data && !this._selections.includes(data)) {
+ this._selections.length = ++this._curSelection;
+ this._selections.push(data);
+ }
+ });
+ }
+ componentWillUnmount() {
+ this._prevSelectionDisposer && this._prevSelectionDisposer();
+ }
+ specificContextMenu = (e: React.MouseEvent): void => {
+ const funcs: ContextMenuProps[] = [];
+ funcs.push({ description: (this.isSelectionLocked() ? "Show" : "Lock") + " Selection", event: () => this.toggleLockSelection, icon: "expand-arrows-alt" });
+ funcs.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
+
+ ContextMenu.Instance.addItem({ description: "DocumentBox Funcs...", subitems: funcs, icon: "asterisk" });
+ }
+ lockSelection = () => {
+ Doc.GetProto(this.props.Document)[this.props.fieldKey] = this.props.Document[this.props.fieldKey];
+ }
+ showSelection = () => {
+ Doc.GetProto(this.props.Document)[this.props.fieldKey] = ComputedField.MakeFunction("selectedDocs(this,true,[_last_])?.[0]");
+ }
+ isSelectionLocked = () => {
+ const kvpstring = Field.toKeyValueString(this.props.Document, this.props.fieldKey);
+ return !(kvpstring.startsWith("=") || kvpstring.startsWith(":="));
+ }
+ toggleLockSelection = () => {
+ !this.isSelectionLocked() ? this.lockSelection() : this.showSelection();
+ }
+ prevSelection = () => {
+ if (this._curSelection > 0) {
+ Doc.UserDoc().SelectedDocs = new List([this._selections[--this._curSelection]]);
+ }
+ }
+ nextSelection = () => {
+ if (this._curSelection < this._selections.length - 1 && this._selections.length) {
+ Doc.UserDoc().SelectedDocs = new List([this._selections[++this._curSelection]]);
+ }
+ }
+ onPointerDown = (e: React.PointerEvent) => {
+ }
+ onClick = (e: React.MouseEvent) => {
+ if (this._contRef.current!.getBoundingClientRect().top + 15 > e.clientY) this.toggleLockSelection();
+ else {
+ if (this._contRef.current!.getBoundingClientRect().left + 15 > e.clientX) this.prevSelection();
+ if (this._contRef.current!.getBoundingClientRect().right - 15 < e.clientX) this.nextSelection();
+ }
+ }
+ _contRef = React.createRef<HTMLDivElement>();
+ pwidth = () => this.props.PanelWidth() - 30;
+ pheight = () => this.props.PanelHeight() - 30;
+ getTransform = () => this.props.ScreenToLocalTransform().translate(-15, -15);
+ render() {
+ const containedDoc = this.props.Document[this.props.fieldKey] as Doc;
+ return <div className="documentBox-container" ref={this._contRef}
+ onContextMenu={this.specificContextMenu}
+ onPointerDown={this.onPointerDown} onClick={this.onClick}
+ style={{ background: StrCast(this.props.Document.backgroundColor) }}>
+ <div className="documentBox-lock">
+ <FontAwesomeIcon icon={this.isSelectionLocked() ? "lock" : "unlock"} size="sm" />
+ </div>
+ {!(containedDoc instanceof Doc) ? (null) : <ContentFittingDocumentView
+ Document={containedDoc}
+ DataDocument={undefined}
+ LibraryPath={emptyPath}
+ fitToBox={this.props.fitToBox}
+ addDocument={this.props.addDocument}
+ moveDocument={this.props.moveDocument}
+ removeDocument={this.props.removeDocument}
+ ruleProvider={this.props.ruleProvider}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ getTransform={this.getTransform}
+ renderDepth={this.props.Document.forceActive ? 0 : this.props.renderDepth + 1} // bcz: really need to have an 'alwaysSelected' prop that's not conflated with renderDepth
+ PanelWidth={this.pwidth}
+ PanelHeight={this.pheight}
+ focus={this.props.focus}
+ active={this.props.active}
+ whenActiveChanged={this.props.whenActiveChanged}
+ setPreviewScript={emptyFunction}
+ previewScript={undefined}
+ />}
+ </div>;
+ }
+}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index b9b84d5ce..8f6bfc8e1 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -14,6 +14,7 @@ import { LinkFollowBox } from "../linking/LinkFollowBox";
import { YoutubeBox } from "./../../apis/youtube/YoutubeBox";
import { AudioBox } from "./AudioBox";
import { ButtonBox } from "./ButtonBox";
+import { DocumentBox } from "./DocumentBox";
import { DocumentViewProps } from "./DocumentView";
import "./DocumentView.scss";
import { FontIconBox } from "./FontIconBox";
@@ -32,6 +33,7 @@ import { VideoBox } from "./VideoBox";
import { WebBox } from "./WebBox";
import { InkingStroke } from "../InkingStroke";
import React = require("react");
+import { TraceMobx } from "../../../new_fields/util";
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -57,6 +59,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
hideOnLeave?: boolean
}> {
@computed get layout(): string {
+ TraceMobx();
if (!this.layoutDoc) return "<p>awaiting layout</p>";
const layout = Cast(this.layoutDoc[this.props.layoutKey], "string");
if (layout === undefined) {
@@ -83,7 +86,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
}
CreateBindings(): JsxBindings {
- let list = {
+ const list = {
...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit,
Document: this.layoutDoc,
DataDoc: this.dataDoc,
@@ -92,6 +95,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
}
render() {
+ TraceMobx();
return (this.props.renderDepth > 7 || !this.layout) ? (null) :
<ObserverJsxParser
blacklistedAttrs={[]}
@@ -99,7 +103,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, FontIconBox: FontIconBox, ButtonBox, FieldView,
CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox, PresElementBox, QueryBox,
- ColorBox, DocuLinkBox, InkingStroke
+ ColorBox, DocuLinkBox, InkingStroke, DocumentBox
}}
bindings={this.CreateBindings()}
jsx={this.layout}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index dfb84ed5c..f44c6dd3b 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -39,6 +39,7 @@
transform-origin: top left;
width: 100%;
height: 100%;
+ z-index: 1;
}
.documentView-styleWrapper {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1780d9789..a01e77c4e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -19,7 +19,6 @@ import { DocumentType } from '../../documents/DocumentTypes';
import { ClientUtils } from '../../util/ClientUtils';
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager, dropActionType } from "../../util/DragManager";
-import { LinkManager } from '../../util/LinkManager';
import { Scripting } from '../../util/Scripting';
import { SelectionManager } from "../../util/SelectionManager";
import SharingManager from '../../util/SharingManager';
@@ -44,6 +43,8 @@ import { InteractionUtils } from '../../util/InteractionUtils';
import { InkingControl } from '../InkingControl';
import { InkTool } from '../../../new_fields/InkField';
import { TraceMobx } from '../../../new_fields/util';
+import { List } from '../../../new_fields/List';
+import { FormattedTextBoxComment } from './FormattedTextBoxComment';
library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight,
fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
@@ -54,11 +55,13 @@ export interface DocumentViewProps {
ContainingCollectionDoc: Opt<Doc>;
Document: Doc;
DataDoc?: Doc;
+ LibraryPath: Doc[];
fitToBox?: boolean;
onClick?: ScriptField;
+ dragDivName?: string;
addDocument?: (doc: Doc) => boolean;
removeDocument?: (doc: Doc) => boolean;
- moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+ moveDocument?: (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
renderDepth: number;
showOverlays?: (doc: Doc) => { title?: string, caption?: string };
@@ -70,7 +73,7 @@ export interface DocumentViewProps {
parentActive: (outsideReaction: boolean) => boolean;
whenActiveChanged: (isActive: boolean) => void;
bringToFront: (doc: Doc, sendToBack?: boolean) => void;
- addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+ addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string, libraryPath?: Doc[]) => boolean;
pinToPres: (document: Doc) => void;
zoomToScale: (scale: number) => void;
backgroundColor: (doc: Doc) => string | undefined;
@@ -91,6 +94,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
private _hitTemplateDrag = false;
private _mainCont = React.createRef<HTMLDivElement>();
private _dropDisposer?: DragManager.DragDropDisposer;
+ private _titleRef = React.createRef<EditableView>();
public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
public get ContentDiv() { return this._mainCont.current; }
@@ -102,7 +106,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@action
componentDidMount() {
- this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }));
+ this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this)));
!this.props.dontRegisterView && DocumentManager.Instance.DocumentViews.push(this);
}
@@ -110,7 +114,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@action
componentDidUpdate() {
this._dropDisposer && this._dropDisposer();
- this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }));
+ this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this)));
}
@action
@@ -122,18 +126,54 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
startDragging(x: number, y: number, dropAction: dropActionType, applyAsTemplate?: boolean) {
if (this._mainCont.current) {
- let dragData = new DragManager.DocumentDragData([this.props.Document]);
+ const dragData = new DragManager.DocumentDragData([this.props.Document]);
const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
dragData.dropAction = dropAction;
- dragData.moveDocument = this.Document.onDragStart ? undefined : this.props.moveDocument;
+ dragData.moveDocument = this.props.moveDocument;// this.Document.onDragStart ? undefined : this.props.moveDocument;
dragData.applyAsTemplate = applyAsTemplate;
- DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
- handlers: {
- dragComplete: action((emptyFunction))
- },
- hideSource: !dropAction && !this.Document.onDragStart
- });
+ dragData.dragDivName = this.props.dragDivName;
+ DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.Document.onDragStart });
+ }
+ }
+
+ public static FloatDoc(topDocView: DocumentView, x: number, y: number) {
+ const topDoc = topDocView.props.Document;
+ const de = new DragManager.DocumentDragData([topDoc]);
+ de.dragDivName = topDocView.props.dragDivName;
+ de.moveDocument = topDocView.props.moveDocument;
+ undoBatch(action(() => topDoc.z = topDoc.z ? 0 : 1))();
+ setTimeout(() => {
+ const newDocView = DocumentManager.Instance.getDocumentView(topDoc);
+ if (newDocView) {
+ const contentDiv = newDocView.ContentDiv!;
+ const xf = contentDiv.getBoundingClientRect();
+ DragManager.StartDocumentDrag([contentDiv], de, x, y, { offsetX: x - xf.left, offsetY: y - xf.top, hideSource: true });
+ }
+ }, 0);
+ }
+
+ onKeyDown = (e: React.KeyboardEvent) => {
+ if (e.altKey && !(e.nativeEvent as any).StopPropagationForReal) {
+ (e.nativeEvent as any).StopPropagationForReal = true; // e.stopPropagation() doesn't seem to work...
+ e.stopPropagation();
+ e.preventDefault();
+ if (e.key === "†" || e.key === "t") {
+ if (!StrCast(this.layoutDoc.showTitle)) this.layoutDoc.showTitle = "title";
+ if (!this._titleRef.current) setTimeout(() => this._titleRef.current?.setIsFocused(true), 0);
+ else if (!this._titleRef.current.setIsFocused(true)) { // if focus didn't change, focus on interior text...
+ {
+ this._titleRef.current?.setIsFocused(false);
+ const any = (this._mainCont.current?.getElementsByClassName("ProseMirror")?.[0] as any);
+ any.keeplocation = true;
+ any?.focus();
+ }
+ }
+ } else if (e.key === "f") {
+ const ex = (e.nativeEvent.target! as any).getBoundingClientRect().left;
+ const ey = (e.nativeEvent.target! as any).getBoundingClientRect().top;
+ DocumentView.FloatDoc(this, ex, ey);
+ }
}
}
@@ -143,7 +183,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
e.stopPropagation();
let preventDefault = true;
if (this._doubleTap && this.props.renderDepth && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
- let fullScreenAlias = Doc.MakeAlias(this.props.Document);
+ const fullScreenAlias = Doc.MakeAlias(this.props.Document);
if (StrCast(fullScreenAlias.layoutKey) !== "layoutCustom" && fullScreenAlias.layoutCustom !== undefined) {
fullScreenAlias.layoutKey = "layoutCustom";
}
@@ -154,6 +194,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.onClickHandler.script.run({ this: this.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log);
} else if (this.Document.type === DocumentType.BUTTON) {
ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY);
+ } else if (this.props.Document.isButton === "Selector") { // this should be moved to an OnClick script
+ FormattedTextBoxComment.Hide();
+ this.Document.links?.[0] instanceof Doc && (Doc.UserDoc().SelectedDocs = new List([Doc.LinkOtherAnchor(this.Document.links[0]!, this.props.Document)]));
} else if (this.Document.isButton) {
SelectionManager.SelectDoc(this, e.ctrlKey); // don't think this should happen if a button action is actually triggered.
this.buttonClick(e.altKey, e.ctrlKey);
@@ -166,9 +209,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
buttonClick = async (altKey: boolean, ctrlKey: boolean) => {
- let maximizedDocs = await DocListCastAsync(this.Document.maximizedDocs);
- let summarizedDocs = await DocListCastAsync(this.Document.summarizedDocs);
- let linkDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document);
+ const maximizedDocs = await DocListCastAsync(this.Document.maximizedDocs);
+ const summarizedDocs = await DocListCastAsync(this.Document.summarizedDocs);
+ const linkDocs = DocListCast(this.props.Document.links);
let expandedDocs: Doc[] = [];
expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
@@ -179,7 +222,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
maxLocation = this.Document.maximizeLocation = (!ctrlKey ? !altKey ? maxLocation : (maxLocation !== "inPlace" ? "inPlace" : "onRight") : (maxLocation !== "inPlace" ? "inPlace" : "inTab"));
if (maxLocation === "inPlace") {
expandedDocs.forEach(maxDoc => this.props.addDocument && this.props.addDocument(maxDoc));
- let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.layoutDoc.width) / 2, NumCast(this.layoutDoc.height) / 2);
+ const scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.layoutDoc.width) / 2, NumCast(this.layoutDoc.height) / 2);
DocumentManager.Instance.animateBetweenPoint(scrpt, expandedDocs);
} else {
expandedDocs.forEach(maxDoc => (!this.props.addDocTab(maxDoc, undefined, "close") && this.props.addDocTab(maxDoc, undefined, maxLocation)));
@@ -195,7 +238,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
handle1PointerDown = (e: React.TouchEvent) => {
if (!e.nativeEvent.cancelBubble) {
- let touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0];
+ const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0];
this._downX = touch.clientX;
this._downY = touch.clientY;
this._hitTemplateDrag = false;
@@ -220,7 +263,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
document.removeEventListener("touchmove", this.onTouch);
}
else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) {
- let touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0];
+ const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0];
if (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3) {
if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick)) {
document.removeEventListener("touchmove", this.onTouch);
@@ -248,12 +291,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@action
handle2PointersMove = (e: TouchEvent) => {
- let myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);
- let pt1 = myTouches[0];
- let pt2 = myTouches[1];
- let oldPoint1 = this.prevPoints.get(pt1.identifier);
- let oldPoint2 = this.prevPoints.get(pt2.identifier);
- let pinching = InteractionUtils.Pinning(pt1, pt2, oldPoint1!, oldPoint2!);
+ const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);
+ const pt1 = myTouches[0];
+ const pt2 = myTouches[1];
+ const oldPoint1 = this.prevPoints.get(pt1.identifier);
+ const oldPoint2 = this.prevPoints.get(pt2.identifier);
+ const pinching = InteractionUtils.Pinning(pt1, pt2, oldPoint1!, oldPoint2!);
if (pinching !== 0 && oldPoint1 && oldPoint2) {
// let dX = (Math.min(pt1.clientX, pt2.clientX) - Math.min(oldPoint1.clientX, oldPoint2.clientX));
// let dY = (Math.min(pt1.clientY, pt2.clientY) - Math.min(oldPoint1.clientY, oldPoint2.clientY));
@@ -261,24 +304,24 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
// let dY = Math.sign(Math.abs(pt1.clientY - oldPoint1.clientY) - Math.abs(pt2.clientY - oldPoint2.clientY));
// let dW = -dX;
// let dH = -dY;
- let dW = (Math.abs(pt1.clientX - pt2.clientX) - Math.abs(oldPoint1.clientX - oldPoint2.clientX));
- let dH = (Math.abs(pt1.clientY - pt2.clientY) - Math.abs(oldPoint1.clientY - oldPoint2.clientY));
- let dX = -1 * Math.sign(dW);
- let dY = -1 * Math.sign(dH);
+ const dW = (Math.abs(pt1.clientX - pt2.clientX) - Math.abs(oldPoint1.clientX - oldPoint2.clientX));
+ const dH = (Math.abs(pt1.clientY - pt2.clientY) - Math.abs(oldPoint1.clientY - oldPoint2.clientY));
+ const dX = -1 * Math.sign(dW);
+ const dY = -1 * Math.sign(dH);
if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
- let doc = PositionDocument(this.props.Document);
- let layoutDoc = PositionDocument(Doc.Layout(this.props.Document));
+ const doc = PositionDocument(this.props.Document);
+ const layoutDoc = PositionDocument(Doc.Layout(this.props.Document));
let nwidth = layoutDoc.nativeWidth || 0;
let nheight = layoutDoc.nativeHeight || 0;
- let width = (layoutDoc.width || 0);
- let height = (layoutDoc.height || (nheight / nwidth * width));
- let scale = this.props.ScreenToLocalTransform().Scale * this.props.ContentScaling();
- let actualdW = Math.max(width + (dW * scale), 20);
- let actualdH = Math.max(height + (dH * scale), 20);
+ const width = (layoutDoc.width || 0);
+ const height = (layoutDoc.height || (nheight / nwidth * width));
+ const scale = this.props.ScreenToLocalTransform().Scale * this.props.ContentScaling();
+ const actualdW = Math.max(width + (dW * scale), 20);
+ const actualdH = Math.max(height + (dH * scale), 20);
doc.x = (doc.x || 0) + dX * (actualdW - width);
doc.y = (doc.y || 0) + dY * (actualdH - height);
- let fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight);
+ const fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight);
if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) {
layoutDoc.ignoreAspect = false;
layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0;
@@ -323,7 +366,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
// console.log(e.nativeEvent)
// continue if the event hasn't been canceled AND we are using a moues or this is has an onClick or onDragStart function (meaning it is a button document)
if (!InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE)) {
- e.stopPropagation();
+ if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
+ e.stopPropagation();
+ }
return;
}
if ((!e.nativeEvent.cancelBubble || this.Document.onClick || this.Document.onDragStart)) {
@@ -415,7 +460,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
fieldTemplate.heading = 1;
fieldTemplate.autoHeight = true;
- let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: doc.title + "_layout", width: width + 20, height: Math.max(100, height + 45) });
+ const docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: doc.title + "_layout", width: width + 20, height: Math.max(100, height + 45) });
Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate), true);
Doc.ApplyTemplateTo(docTemplate, dataDoc || doc, "layoutCustom", undefined);
@@ -437,34 +482,46 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@undoBatch
+ makeSelBtnClicked = (): void => {
+ if (this.Document.isButton || this.Document.onClick || this.Document.ignoreClick) {
+ this.Document.isButton = false;
+ this.Document.ignoreClick = false;
+ this.Document.onClick = undefined;
+ } else {
+ this.props.Document.isButton = "Selector";
+ }
+ }
+
+ @undoBatch
@action
drop = async (e: Event, de: DragManager.DropEvent) => {
- if (de.data instanceof DragManager.AnnotationDragData) {
+ if (de.complete.annoDragData) {
/// this whole section for handling PDF annotations looks weird. Need to rethink this to make it cleaner
e.stopPropagation();
- (de.data as any).linkedToDoc = true;
+ de.complete.annoDragData.linkedToDoc = true;
- DocUtils.MakeLink({ doc: de.data.annotationDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, `Link from ${StrCast(de.data.annotationDocument.title)}`);
+ DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc },
+ `Link from ${StrCast(de.complete.annoDragData.annotationDocument.title)}`);
}
- if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) {
- Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document, "layoutCustom");
+ if (de.complete.docDragData && de.complete.docDragData.applyAsTemplate) {
+ Doc.ApplyTemplateTo(de.complete.docDragData.draggedDocuments[0], this.props.Document, "layoutCustom");
e.stopPropagation();
}
- if (de.data instanceof DragManager.LinkDragData) {
+ if (de.complete.linkDragData) {
e.stopPropagation();
// const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
// const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
- de.data.linkSourceDocument !== this.props.Document &&
- (de.data.linkDocument = DocUtils.MakeLink({ doc: de.data.linkSourceDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, "in-text link being created")); // TODODO this is where in text links get passed
+ de.complete.linkDragData.linkSourceDocument !== this.props.Document &&
+ (de.complete.linkDragData.linkDocument = DocUtils.MakeLink({ doc: de.complete.linkDragData.linkSourceDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, "in-text link being created")); // TODODO this is where in text links get passed
}
}
@action
onDrop = (e: React.DragEvent) => {
- let text = e.dataTransfer.getData("text/plain");
+ const text = e.dataTransfer.getData("text/plain");
if (!e.isDefaultPrevented() && text && text.startsWith("<div")) {
- let oldLayout = this.Document.layout || "";
- let layout = text.replace("{layout}", oldLayout);
+ const oldLayout = this.Document.layout || "";
+ const layout = text.replace("{layout}", oldLayout);
this.Document.layout = layout;
e.stopPropagation();
e.preventDefault();
@@ -485,11 +542,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch
@action
makeIntoPortal = async () => {
- let anchors = await Promise.all(DocListCast(this.Document.links).map(async (d: Doc) => Cast(d.anchor2, Doc)));
+ const anchors = await Promise.all(DocListCast(this.Document.links).map(async (d: Doc) => Cast(d.anchor2, Doc)));
if (!anchors.find(anchor2 => anchor2 && anchor2.title === this.Document.title + ".portal" ? true : false)) {
- let portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, "");
+ const portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, "");
DocServer.GetRefField(portalID).then(existingPortal => {
- let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.layoutDoc.width || 0) + 10, height: this.layoutDoc.height || 0, title: portalID });
+ const portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.layoutDoc.width || 0) + 10, height: this.layoutDoc.height || 0, title: portalID });
DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: portal }, portalID, "portal link");
this.Document.isButton = true;
});
@@ -537,32 +594,27 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
e.preventDefault();
const cm = ContextMenu.Instance;
- let subitems: ContextMenuProps[] = [];
- subitems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this), icon: "desktop" });
- subitems.push({ description: "Open Tab ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab"), icon: "folder" });
- subitems.push({ description: "Open Right ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight"), icon: "caret-square-right" });
+ const subitems: ContextMenuProps[] = [];
+ subitems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this, this.props.LibraryPath), icon: "desktop" });
+ subitems.push({ description: "Open Tab ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab", this.props.LibraryPath), icon: "folder" });
+ subitems.push({ description: "Open Right ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight", this.props.LibraryPath), icon: "caret-square-right" });
subitems.push({ description: "Open Alias Tab ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "inTab"), icon: "folder" });
subitems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "onRight"), icon: "caret-square-right" });
subitems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" });
cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" });
- let existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
- let onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
+ const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
+ const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript("toggleDetail(this)"), icon: "window-restore" });
onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
onClicks.push({ description: this.Document.isButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.makeBtnClicked, icon: "concierge-bell" });
+ onClicks.push({ description: this.props.Document.isButton ? "Remove Select Link Behavior" : "Select Link", event: this.makeSelBtnClicked, icon: "concierge-bell" });
onClicks.push({ description: "Edit onClick Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", obj.x, obj.y) });
- onClicks.push({
- description: "Edit onClick Foreach Doc Script", icon: "edit", event: (obj: any) => {
- this.props.Document.collectionContext = this.props.ContainingCollectionDoc;
- ScriptBox.EditButtonScript("Foreach Collection Doc (d) => ", this.props.Document, "onClick", obj.x, obj.y, "docList(this.collectionContext.data).map(d => {", "});\n");
- }
- });
!existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
- let funcs: ContextMenuProps[] = [];
+ const funcs: ContextMenuProps[] = [];
if (this.Document.onDragStart) {
funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.Document.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.Document.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
@@ -570,8 +622,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
ContextMenu.Instance.addItem({ description: "OnDrag...", subitems: funcs, icon: "asterisk" });
}
- let existing = ContextMenu.Instance.findByDescription("Layout...");
- let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
+ const existing = ContextMenu.Instance.findByDescription("Layout...");
+ const layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.Document.lockedPosition ? "unlock" : "lock" });
if (this.props.DataDoc) {
layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc!), icon: "concierge-bell" });
@@ -590,8 +642,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
!existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" });
- let more = ContextMenu.Instance.findByDescription("More...");
- let moreItems: ContextMenuProps[] = more && "subitems" in more ? more.subitems : [];
+ const more = ContextMenu.Instance.findByDescription("More...");
+ const moreItems: ContextMenuProps[] = more && "subitems" in more ? more.subitems : [];
if (!ClientUtils.RELEASE) {
// let copies: ContextMenuProps[] = [];
@@ -626,7 +678,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
!more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
runInAction(() => {
if (!ClientUtils.RELEASE) {
- let setWriteMode = (mode: DocServer.WriteMode) => {
+ const setWriteMode = (mode: DocServer.WriteMode) => {
DocServer.AclsMode = mode;
const mode1 = mode;
const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground;
@@ -640,7 +692,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
DocServer.setFieldWriteMode("scale", mode2);
DocServer.setFieldWriteMode("viewType", mode2);
};
- let aclsMenu: ContextMenuProps[] = [];
+ const aclsMenu: ContextMenuProps[] = [];
aclsMenu.push({ description: "Default (write/read all)", event: () => setWriteMode(DocServer.WriteMode.Default), icon: DocServer.AclsMode === DocServer.WriteMode.Default ? "check" : "exclamation" });
aclsMenu.push({ description: "Playground (write own/no read)", event: () => setWriteMode(DocServer.WriteMode.Playground), icon: DocServer.AclsMode === DocServer.WriteMode.Playground ? "check" : "exclamation" });
aclsMenu.push({ description: "Live Playground (write own/read others)", event: () => setWriteMode(DocServer.WriteMode.LivePlayground), icon: DocServer.AclsMode === DocServer.WriteMode.LivePlayground ? "check" : "exclamation" });
@@ -664,10 +716,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
SelectionManager.SelectDoc(this, false);
}
});
+ const path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc().LibraryBtn as Doc).sourcePanel as Doc) ? "" : d.title), "");
+ cm.addItem({
+ description: `path: ${path}`, event: () => {
+ this.props.LibraryPath.map(lp => Doc.GetProto(lp).treeViewOpen = lp.treeViewOpen = true);
+ Doc.linkFollowHighlight(this.props.Document);
+ }, icon: "check"
+ });
}
// does Document set a layout prop
- setsLayoutProp = (prop: string) => this.props.Document[prop] !== this.props.Document["default" + prop[0].toUpperCase() + prop.slice(1)];
+ setsLayoutProp = (prop: string) => this.props.Document[prop] !== this.props.Document["default" + prop[0].toUpperCase() + prop.slice(1)] && this.props.Document["default" + prop[0].toUpperCase() + prop.slice(1)];
// get the a layout prop by first choosing the prop from Document, then falling back to the layout doc otherwise.
getLayoutPropStr = (prop: string) => StrCast(this.setsLayoutProp(prop) ? this.props.Document[prop] : this.layoutDoc[prop]);
getLayoutPropNum = (prop: string) => NumCast(this.setsLayoutProp(prop) ? this.props.Document[prop] : this.layoutDoc[prop]);
@@ -676,8 +735,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
chromeHeight = () => {
- let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined;
- let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.Document.showTitle);
+ const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined;
+ const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle);
return (showTitle ? 25 : 0) + 1;
}
@@ -689,6 +748,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
ContainingCollectionDoc={this.props.ContainingCollectionDoc}
Document={this.props.Document}
fitToBox={this.props.fitToBox}
+ LibraryPath={this.props.LibraryPath}
addDocument={this.props.addDocument}
removeDocument={this.props.removeDocument}
moveDocument={this.props.moveDocument}
@@ -722,17 +782,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
// if it's a tempoarl link (currently just for Audio), then the audioBox will display the anchor and we don't want to display it here.
// would be good to generalize this some way.
isNonTemporalLink = (linkDoc: Doc) => {
- let anchor = Cast(Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1 : linkDoc.anchor2, Doc) as Doc;
- let ept = Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1Timecode : linkDoc.anchor2Timecode;
+ const anchor = Cast(Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1 : linkDoc.anchor2, Doc) as Doc;
+ const ept = Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1Timecode : linkDoc.anchor2Timecode;
return anchor.type === DocumentType.AUDIO && NumCast(ept) ? false : true;
}
@computed get innards() {
TraceMobx();
const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined;
- const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.getLayoutPropStr("showTitle");
+ const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.getLayoutPropStr("showTitle"));
const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.getLayoutPropStr("showCaption");
- const showTextTitle = showTitle && StrCast(this.Document.layout).indexOf("FormattedTextBox") !== -1 ? showTitle : undefined;
+ const showTextTitle = showTitle && StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1 ? showTitle : undefined;
const searchHighlight = (!this.Document.searchFields ? (null) :
<div className="documentView-searchHighlight">
{this.Document.searchFields}
@@ -750,11 +810,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
position: showTextTitle ? "relative" : "absolute",
pointerEvents: SelectionManager.GetIsDragging() ? "none" : "all",
}}>
- <EditableView
+ <EditableView ref={this._titleRef}
contents={this.Document[showTitle]}
display={"block"} height={72} fontSize={12}
GetValue={() => StrCast(this.Document[showTitle])}
- SetValue={(value: string) => (Doc.GetProto(this.Document)[showTitle] = value) ? true : true}
+ SetValue={undoBatch((value: string) => (Doc.GetProto(this.Document)[showTitle] = value) ? true : true)}
/>
</div>);
return <>
@@ -787,7 +847,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
render() {
- if (!this.props.Document) return (null);
+ if (!(this.props.Document instanceof Doc)) return (null);
const ruleColor = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleColor_" + this.Document.heading]) : undefined;
const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
const colorSet = this.setsLayoutProp("backgroundColor");
@@ -801,22 +861,16 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const localScale = fullDegree;
const animDims = this.Document.animateToDimensions ? Array.from(this.Document.animateToDimensions) : undefined;
- let animheight = animDims ? animDims[1] : "100%";
- let animwidth = animDims ? animDims[0] : "100%";
+ const animheight = animDims ? animDims[1] : "100%";
+ const animwidth = animDims ? animDims[0] : "100%";
const highlightColors = ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear;
- return <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} ref={this._mainCont}
+ highlighting = highlighting && this.props.focus !== emptyFunction; // bcz: hack to turn off highlighting onsidebar panel documents. need to flag a document as not highlightable in a more direct way
+ return <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} ref={this._mainCont} onKeyDown={this.onKeyDown}
onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
- onPointerEnter={e => {
- // console.log("Brush" + this.props.Document.title);
- Doc.BrushDoc(this.props.Document);
- }} onPointerLeave={e => {
- // console.log("UnBrush" + this.props.Document.title);
- Doc.UnBrushDoc(this.props.Document);
-
- }}
+ onPointerEnter={e => Doc.BrushDoc(this.props.Document)} onPointerLeave={e => Doc.UnBrushDoc(this.props.Document)}
style={{
transition: this.Document.isAnimating ? ".5s linear" : StrCast(this.Document.transition),
pointerEvents: this.ignorePointerEvents ? "none" : "all",
diff --git a/src/client/views/nodes/FaceRectangle.tsx b/src/client/views/nodes/FaceRectangle.tsx
index 887efc0d5..20afa4565 100644
--- a/src/client/views/nodes/FaceRectangle.tsx
+++ b/src/client/views/nodes/FaceRectangle.tsx
@@ -12,7 +12,7 @@ export default class FaceRectangle extends React.Component<{ rectangle: Rectangl
}
render() {
- let rectangle = this.props.rectangle;
+ const rectangle = this.props.rectangle;
return (
<div
style={{
diff --git a/src/client/views/nodes/FaceRectangles.tsx b/src/client/views/nodes/FaceRectangles.tsx
index acf1aced3..3c7f1f206 100644
--- a/src/client/views/nodes/FaceRectangles.tsx
+++ b/src/client/views/nodes/FaceRectangles.tsx
@@ -20,10 +20,10 @@ export interface RectangleTemplate {
export default class FaceRectangles extends React.Component<FaceRectanglesProps> {
render() {
- let faces = DocListCast(this.props.document.faces);
- let templates: RectangleTemplate[] = faces.map(faceDoc => {
- let rectangle = Cast(faceDoc.faceRectangle, Doc) as Doc;
- let style = {
+ const faces = DocListCast(this.props.document.faces);
+ const templates: RectangleTemplate[] = faces.map(faceDoc => {
+ const rectangle = Cast(faceDoc.faceRectangle, Doc) as Doc;
+ const style = {
top: NumCast(rectangle.top),
left: NumCast(rectangle.left),
width: NumCast(rectangle.width),
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index c93746773..c56fde186 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -30,6 +30,7 @@ export interface FieldViewProps {
ruleProvider: Doc | undefined;
Document: Doc;
DataDoc?: Doc;
+ LibraryPath: Doc[];
onClick?: ScriptField;
isSelected: (outsideReaction?: boolean) => boolean;
select: (isCtrlPressed: boolean) => void;
@@ -38,7 +39,7 @@ export interface FieldViewProps {
addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
pinToPres: (document: Doc) => void;
removeDocument?: (document: Doc) => boolean;
- moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+ moveDocument?: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
active: (outsideReaction?: boolean) => boolean;
whenActiveChanged: (isActive: boolean) => void;
@@ -53,7 +54,7 @@ export interface FieldViewProps {
@observer
export class FieldView extends React.Component<FieldViewProps> {
public static LayoutString(fieldType: { name: string }, fieldStr: string) {
- return `<${fieldType.name} {...props} fieldKey={"${fieldStr}"}/>`; //e.g., "<ImageBox {...props} fieldKey={"dada} />"
+ return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "<ImageBox {...props} fieldKey={"dada} />"
}
@computed
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 960b55e3e..2433251b3 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -25,8 +25,8 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
this._backgroundReaction = reaction(() => this.props.Document.backgroundColor,
() => {
if (this._ref && this._ref.current) {
- let col = Utils.fromRGBAstr(getComputedStyle(this._ref.current).backgroundColor);
- let colsum = (col.r + col.g + col.b);
+ const col = Utils.fromRGBAstr(getComputedStyle(this._ref.current).backgroundColor);
+ const colsum = (col.r + col.g + col.b);
if (colsum / col.a > 600 || col.a < 0.25) runInAction(() => this._foregroundColor = "black");
else if (colsum / col.a <= 600 || col.a >= .25) runInAction(() => this._foregroundColor = "white");
}
@@ -36,8 +36,8 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
this._backgroundReaction && this._backgroundReaction();
}
render() {
- let referenceDoc = (this.props.Document.dragFactory instanceof Doc ? this.props.Document.dragFactory : this.props.Document);
- let referenceLayout = Doc.Layout(referenceDoc);
+ const referenceDoc = (this.props.Document.dragFactory instanceof Doc ? this.props.Document.dragFactory : this.props.Document);
+ const referenceLayout = Doc.Layout(referenceDoc);
return <button className="fontIconBox-outerDiv" title={StrCast(this.props.Document.title)} ref={this._ref}
style={{
background: StrCast(referenceLayout.backgroundColor),
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 2e5848db4..c203ca0c3 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -26,14 +26,13 @@
color: initial;
height: 100%;
pointer-events: all;
- overflow-y: auto;
max-height: 100%;
display: flex;
flex-direction: row;
.formattedTextBox-dictation {
- height: 20px;
- width: 20px;
+ height: 12px;
+ width: 10px;
top: 0px;
left: 0px;
position: absolute;
@@ -59,6 +58,7 @@
height: 35px;
background: lightgray;
border-radius: 20px;
+ cursor:grabbing;
}
.formattedTextBox-cont>.formattedTextBox-sidebar-handle {
@@ -190,15 +190,27 @@ footnote::after {
width: 0;
}
-.formattedTextBox-summarizer {
- opacity: 0.5;
+
+.formattedTextBox-inlineComment {
position: relative;
width: 40px;
height: 20px;
+ &::before {
+ content: "→";
+ }
+ &:hover {
+ background: orange;
+ }
}
-.formattedTextBox-summarizer::after {
- content: "←";
+.formattedTextBox-summarizer {
+ opacity: 0.5;
+ position: relative;
+ width: 40px;
+ height: 20px;
+ &::after {
+ content: "←";
+ }
}
.formattedTextBox-summarizer-collapsed {
@@ -206,232 +218,50 @@ footnote::after {
position: relative;
width: 40px;
height: 20px;
-}
-
-.formattedTextBox-summarizer-collapsed::after {
- content: "...";
+ &::after {
+ content: "...";
+ }
}
.ProseMirror {
touch-action: none;
-
- ol {
- counter-reset: deci1 0;
- padding-left: 0px;
+ span {
+ font-family: inherit;
}
- .decimal1-ol {
- counter-reset: deci1;
-
- p {
- display: inline
- }
-
- ;
- font-size: 24;
-
- ul,
- ol {
- padding-left: 30px;
- }
+ ol, ul {
+ counter-reset: deci1 0 multi1 0;
+ padding-left: 1em;
+ font-family: inherit;
}
-
- .decimal2-ol {
- counter-reset: deci2;
-
- p {
- display: inline
- }
-
- ;
- font-size: 18;
-
- ul,
- ol {
- padding-left: 30px;
- }
- }
-
- .decimal3-ol {
- counter-reset: deci3;
-
- p {
- display: inline
- }
-
- ;
- font-size: 14;
-
- ul,
- ol {
- padding-left: 30px;
- }
- }
-
- .decimal4-ol {
- counter-reset: deci4;
-
- p {
- display: inline
- }
-
- ;
- font-size: 10;
-
- ul,
- ol {
- padding-left: 30px;
- }
- }
-
- .decimal5-ol {
- counter-reset: deci5;
-
- p {
- display: inline
- }
-
- ;
- font-size: 10;
-
- ul,
- ol {
- padding-left: 30px;
- }
- }
-
- .decimal6-ol {
- counter-reset: deci6;
-
- p {
- display: inline
- }
-
- ;
- font-size: 10;
-
- ul,
- ol {
- padding-left: 30px;
- }
- }
-
- .decimal7-ol {
- counter-reset: deci7;
-
- p {
- display: inline
- }
-
- ;
- font-size: 10;
-
- ul,
- ol {
- padding-left: 30px;
- }
- }
-
- .upper-alpha-ol {
- counter-reset: ualph;
-
- p {
- display: inline
- }
-
- ;
- font-size: 18;
- }
-
- .lower-roman-ol {
- counter-reset: lroman;
-
- p {
- display: inline
- }
-
- ;
- font-size: 14;
- }
-
- .lower-alpha-ol {
- counter-reset: lalpha;
-
- p {
- display: inline
- }
-
- ;
- font-size: 10;
- }
-
- .decimal1:before {
- content: counter(deci1) ") ";
- counter-increment: deci1;
- display: inline-block;
- min-width: 30;
- }
-
- .decimal2:before {
- content: counter(deci1) "."counter(deci2) ") ";
- counter-increment: deci2;
- display: inline-block;
- min-width: 35
- }
-
- .decimal3:before {
- content: counter(deci1) "."counter(deci2) "."counter(deci3) ") ";
- counter-increment: deci3;
- display: inline-block;
- min-width: 35
- }
-
- .decimal4:before {
- content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) ") ";
- counter-increment: deci4;
- display: inline-block;
- min-width: 40
- }
-
- .decimal5:before {
- content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) ") ";
- counter-increment: deci5;
- display: inline-block;
- min-width: 40
- }
-
- .decimal6:before {
- content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) ") ";
- counter-increment: deci6;
- display: inline-block;
- min-width: 45
- }
-
- .decimal7:before {
- content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) "."counter(deci7) ") ";
- counter-increment: deci7;
- display: inline-block;
- min-width: 50
- }
-
- .upper-alpha:before {
- content: counter(deci1) "."counter(ualph, upper-alpha) ") ";
- counter-increment: ualph;
- display: inline-block;
- min-width: 35
- }
-
- .lower-roman:before {
- content: counter(deci1) "."counter(ualph, upper-alpha) "."counter(lroman, lower-roman) ") ";
- counter-increment: lroman;
- display: inline-block;
- min-width: 50
+ ol {
+ margin-left: 1em;
+ font-family: inherit;
}
- .lower-alpha:before {
- content: counter(deci1) "."counter(ualph, upper-alpha) "."counter(lroman, lower-roman) "."counter(lalpha, lower-alpha) ") ";
- counter-increment: lalpha;
- display: inline-block;
- min-width: 35
- }
+ .decimal1-ol { counter-reset: deci1; p {display: inline; font-family: inherit} margin-left: 0; }
+ .decimal2-ol { counter-reset: deci2; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 1em;}
+ .decimal3-ol { counter-reset: deci3; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 2em;}
+ .decimal4-ol { counter-reset: deci4; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 3em;}
+ .decimal5-ol { counter-reset: deci5; p {display: inline; font-family: inherit} font-size: smaller; }
+ .decimal6-ol { counter-reset: deci6; p {display: inline; font-family: inherit} font-size: smaller; }
+ .decimal7-ol { counter-reset: deci7; p {display: inline; font-family: inherit} font-size: smaller; }
+
+ .multi1-ol { counter-reset: multi1; p {display: inline; font-family: inherit} margin-left: 0; padding-left: 1.2em }
+ .multi2-ol { counter-reset: multi2; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 1.4em;}
+ .multi3-ol { counter-reset: multi3; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 2em;}
+ .multi4-ol { counter-reset: multi4; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 3.4em;}
+
+ .decimal1:before { transition: 0.5s;counter-increment: deci1; display: inline-block; margin-left: -1em; width: 1em; content: counter(deci1) ". "; }
+ .decimal2:before { transition: 0.5s;counter-increment: deci2; display: inline-block; margin-left: -2.1em; width: 2.1em; content: counter(deci1) "."counter(deci2) ". "; }
+ .decimal3:before { transition: 0.5s;counter-increment: deci3; display: inline-block; margin-left: -2.85em;width: 2.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) ". "; }
+ .decimal4:before { transition: 0.5s;counter-increment: deci4; display: inline-block; margin-left: -3.85em;width: 3.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) ". "; }
+ .decimal5:before { transition: 0.5s;counter-increment: deci5; display: inline-block; margin-left: -2em; width: 5em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) ". "; }
+ .decimal6:before { transition: 0.5s;counter-increment: deci6; display: inline-block; margin-left: -2em; width: 6em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) ". "; }
+ .decimal7:before { transition: 0.5s;counter-increment: deci7; display: inline-block; margin-left: -2em; width: 7em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) "."counter(deci7) ". "; }
+
+ .multi1:before { transition: 0.5s;counter-increment: multi1; display: inline-block; margin-left: -1em; width: 1.2em; content: counter(multi1, upper-alpha) ". "; }
+ .multi2:before { transition: 0.5s;counter-increment: multi2; display: inline-block; margin-left: -2em; width: 2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) ". "; }
+ .multi3:before { transition: 0.5s;counter-increment: multi3; display: inline-block; margin-left: -2.85em; width:2.85em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) ". "; }
+ .multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; margin-left: -4.2em; width: 4.2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; }
} \ No newline at end of file
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index d601e188d..7555a594b 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,6 +1,6 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faEdit, faSmile, faTextHeight, faUpload } from '@fortawesome/free-solid-svg-icons';
-import _ from "lodash";
+import { isEqual } from "lodash";
import { action, computed, IReactionDisposer, Lambda, observable, reaction, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
import { baseKeymap } from "prosemirror-commands";
@@ -27,14 +27,13 @@ import { DictationManager } from '../../util/DictationManager';
import { DragManager } from "../../util/DragManager";
import buildKeymap from "../../util/ProsemirrorExampleTransfer";
import { inpRules } from "../../util/RichTextRules";
-import { FootnoteView, ImageResizeView, DashDocView, OrderedListView, schema, SummarizedView } from "../../util/RichTextSchema";
+import { DashDocCommentView, FootnoteView, ImageResizeView, DashDocView, OrderedListView, schema, SummaryView } from "../../util/RichTextSchema";
import { SelectionManager } from "../../util/SelectionManager";
import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu";
import { TooltipTextMenu } from "../../util/TooltipTextMenu";
import { undoBatch, UndoManager } from "../../util/UndoManager";
import { DocAnnotatableComponent } from "../DocComponent";
import { DocumentButtonBar } from '../DocumentButtonBar';
-import { DocumentDecorations } from '../DocumentDecorations';
import { InkingControl } from "../InkingControl";
import { FieldView, FieldViewProps } from "./FieldView";
import "./FormattedTextBox.scss";
@@ -77,12 +76,15 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
public static blankState = () => EditorState.create(FormattedTextBox.Instance.config);
public static Instance: FormattedTextBox;
public static ToolTipTextMenu: TooltipTextMenu | undefined = undefined;
+ public ProseRef?: HTMLDivElement;
private _ref: React.RefObject<HTMLDivElement> = React.createRef();
- private _proseRef?: HTMLDivElement;
+ private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
private _editorView: Opt<EditorView>;
private _applyingChange: boolean = false;
- private _nodeClicked: any;
private _searchIndex = 0;
+ private _sidebarMovement = 0;
+ private _lastX = 0;
+ private _lastY = 0;
private _undoTyping?: UndoManager.Batch;
private _searchReactionDisposer?: Lambda;
private _scrollToRegionReactionDisposer: Opt<IReactionDisposer>;
@@ -92,19 +94,22 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
private _proxyReactionDisposer: Opt<IReactionDisposer>;
private _pullReactionDisposer: Opt<IReactionDisposer>;
private _pushReactionDisposer: Opt<IReactionDisposer>;
+ private _buttonBarReactionDisposer: Opt<IReactionDisposer>;
private dropDisposer?: DragManager.DragDropDisposer;
@observable private _ruleFontSize = 0;
@observable private _ruleFontFamily = "Arial";
@observable private _fontAlign = "";
@observable private _entered = false;
+
+ public static FocusedBox: FormattedTextBox | undefined;
public static SelectOnLoad = "";
public static IsFragment(html: string) {
return html.indexOf("data-pm-slice") !== -1;
}
public static GetHref(html: string): string {
- let parser = new DOMParser();
- let parsedHtml = parser.parseFromString(html, 'text/html');
+ const parser = new DOMParser();
+ const parsedHtml = parser.parseFromString(html, 'text/html');
if (parsedHtml.body.childNodes.length === 1 && parsedHtml.body.childNodes[0].childNodes.length === 1 &&
(parsedHtml.body.childNodes[0].childNodes[0] as any).href) {
return (parsedHtml.body.childNodes[0].childNodes[0] as any).href;
@@ -126,12 +131,12 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
@undoBatch
public setFontColor(color: string) {
- let view = this._editorView!;
+ const view = this._editorView!;
if (view.state.selection.from === view.state.selection.to) return false;
if (view.state.selection.to - view.state.selection.from > view.state.doc.nodeSize - 3) {
this.layoutDoc.color = color;
}
- let colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color: color });
+ const colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color: color });
view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, colorMark));
return true;
}
@@ -139,6 +144,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
constructor(props: any) {
super(props);
FormattedTextBox.Instance = this;
+ this.updateHighlights();
}
public get CurrentDiv(): HTMLDivElement { return this._ref.current!; }
@@ -147,9 +153,9 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
doLinkOnDeselect() {
Array.from(this.linkOnDeselect.entries()).map(entry => {
- let key = entry[0];
- let value = entry[1];
- let id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
+ const key = entry[0];
+ const value = entry[1];
+ const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
DocServer.GetRefField(value).then(doc => {
DocServer.GetRefField(id).then(linkDoc => {
this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value);
@@ -164,34 +170,36 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
dispatchTransaction = (tx: Transaction) => {
if (this._editorView) {
- let metadata = tx.selection.$from.marks().find((m: Mark) => m.type === schema.marks.metadata);
+ const metadata = tx.selection.$from.marks().find((m: Mark) => m.type === schema.marks.metadata);
if (metadata) {
- let range = tx.selection.$from.blockRange(tx.selection.$to);
+ const range = tx.selection.$from.blockRange(tx.selection.$to);
let text = range ? tx.doc.textBetween(range.start, range.end) : "";
let textEndSelection = tx.selection.to;
for (; textEndSelection < range!.end && text[textEndSelection - range!.start] !== " "; textEndSelection++) { }
text = text.substr(0, textEndSelection - range!.start);
text = text.split(" ")[text.split(" ").length - 1];
- let split = text.split("::");
+ const split = text.split("::");
if (split.length > 1 && split[1]) {
- let key = split[0];
- let value = split[split.length - 1];
+ const key = split[0];
+ const value = split[split.length - 1];
this.linkOnDeselect.set(key, value);
- let id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
- const link = this._editorView.state.schema.marks.link.create({ href: `http://localhost:1050/doc/${id}`, location: "onRight", title: value });
+ const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
+ const link = this._editorView.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + id), location: "onRight", title: value });
const mval = this._editorView.state.schema.marks.metadataVal.create();
- let offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
+ const offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
tx = tx.addMark(textEndSelection - value.length + offset, textEndSelection, link).addMark(textEndSelection - value.length + offset, textEndSelection, mval);
this.dataDoc[key] = value;
}
}
const state = this._editorView.state.apply(tx);
this._editorView.updateState(state);
+ (tx.storedMarks && !this._editorView.state.storedMarks) && (this._editorView.state.storedMarks = tx.storedMarks);
- let tsel = this._editorView.state.selection.$from;
+ const tsel = this._editorView.state.selection.$from;
tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 5000 - 1000)));
this._applyingChange = true;
+ this.extensionDoc && !this.extensionDoc.lastModified && (this.extensionDoc.backgroundColor = "lightGray");
this.extensionDoc && (this.extensionDoc.lastModified = new DateField(new Date(Date.now())));
this.dataDoc[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON()), state.doc.textBetween(0, state.doc.content.size, "\n\n"));
this._applyingChange = false;
@@ -202,21 +210,21 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
updateTitle = () => {
if (StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.Document.customTitle) {
- let str = this._editorView.state.doc.textContent;
- let titlestr = str.substr(0, Math.min(40, str.length));
+ const str = this._editorView.state.doc.textContent;
+ const titlestr = str.substr(0, Math.min(40, str.length));
this.dataDoc.title = "-" + titlestr + (str.length > 40 ? "..." : "");
}
}
public highlightSearchTerms = (terms: string[]) => {
- if (this._editorView && (this._editorView as any).docView) {
+ if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) {
const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
- let res = terms.map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term));
+ const res = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term));
let tr = this._editorView.state.tr;
- let flattened: TextSelection[] = [];
+ const flattened: TextSelection[] = [];
res.map(r => r.map(h => flattened.push(h)));
- let lastSel = Math.min(flattened.length - 1, this._searchIndex);
+ const lastSel = Math.min(flattened.length - 1, this._searchIndex);
flattened.forEach((h: TextSelection, ind: number) => tr = tr.addMark(h.from, h.to, ind === lastSel ? activeMark : mark));
this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView());
@@ -227,59 +235,59 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
if (this._editorView && (this._editorView as any).docView) {
const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
- let end = this._editorView.state.doc.nodeSize - 2;
+ const end = this._editorView.state.doc.nodeSize - 2;
this._editorView.dispatch(this._editorView.state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
}
}
- setAnnotation = (start: number, end: number, mark: Mark, opened: boolean, keep: boolean = false) => {
- let view = this._editorView!;
- let nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: keep ? Doc.CurrentUserEmail : mark.attrs.userid, opened: opened });
+ adoptAnnotation = (start: number, end: number, mark: Mark) => {
+ const view = this._editorView!;
+ const nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: Doc.CurrentUserEmail });
view.dispatch(view.state.tr.removeMark(start, end, nmark).addMark(start, end, nmark));
}
protected createDropTarget = (ele: HTMLDivElement) => {
- this._proseRef = ele;
+ this.ProseRef = ele;
this.dropDisposer && this.dropDisposer();
- ele && (this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }));
+ ele && (this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
}
@undoBatch
@action
drop = async (e: Event, de: DragManager.DropEvent) => {
- if (de.data instanceof DragManager.DocumentDragData) {
- const draggedDoc = de.data.draggedDocuments.length && de.data.draggedDocuments[0];
+ if (de.complete.docDragData) {
+ const draggedDoc = de.complete.docDragData.draggedDocuments.length && de.complete.docDragData.draggedDocuments[0];
// replace text contents whend dragging with Alt
- if (draggedDoc && draggedDoc.type === DocumentType.TEXT && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.mods === "AltKey") {
+ if (draggedDoc && draggedDoc.type === DocumentType.TEXT && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.altKey) {
if (draggedDoc.data instanceof RichTextField) {
Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new RichTextField(draggedDoc.data.Data, draggedDoc.data.Text);
e.stopPropagation();
}
// apply as template when dragging with Meta
- } else if (draggedDoc && draggedDoc.type === DocumentType.TEXT && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.mods === "MetaKey") {
+ } else if (draggedDoc && draggedDoc.type === DocumentType.TEXT && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.metaKey) {
draggedDoc.isTemplateDoc = true;
let newLayout = Doc.Layout(draggedDoc);
if (typeof (draggedDoc.layout) === "string") {
newLayout = Doc.MakeDelegate(draggedDoc);
- newLayout.layout = StrCast(newLayout.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${this.props.fieldKey}"}`);
+ newLayout.layout = StrCast(newLayout.layout).replace(/fieldKey={'[^']*'}/, `fieldKey={'${this.props.fieldKey}'}`);
}
this.Document.layoutCustom = newLayout;
this.Document.layoutKey = "layoutCustom";
e.stopPropagation();
// embed document when dragging with a userDropAction or an embedDoc flag set
- } else if (de.data.userDropAction || de.data.embedDoc) {
- let target = de.data.droppedDocuments[0];
- const link = DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "Embedded Doc:" + target.title);
- if (link) {
- target.fitToBox = true;
- let node = schema.nodes.dashDoc.create({
- width: target[WidthSym](), height: target[HeightSym](),
- title: "dashDoc", docid: target[Id],
- float: "right"
- });
- let view = this._editorView!;
- view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
- this.tryUpdateHeight();
- e.stopPropagation();
- }
+ } else if (de.complete.docDragData.userDropAction || de.complete.docDragData.embedDoc) {
+ const target = de.complete.docDragData.droppedDocuments[0];
+ // const link = DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "Embedded Doc:" + target.title);
+ // if (link) {
+ target.fitToBox = true;
+ const node = schema.nodes.dashDoc.create({
+ width: target[WidthSym](), height: target[HeightSym](),
+ title: "dashDoc", docid: target[Id],
+ float: "right"
+ });
+ const view = this._editorView!;
+ view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
+ this.tryUpdateHeight();
+ e.stopPropagation();
+ // }
} // otherwise, fall through to outer collection to handle drop
}
}
@@ -292,7 +300,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
if (node.isBlock) {
// tslint:disable-next-line: prefer-for-of
for (let i = 0; i < (context.content as any).content.length; i++) {
- let result = this.getNodeEndpoints((context.content as any).content[i], node);
+ const result = this.getNodeEndpoints((context.content as any).content[i], node);
if (result) {
return {
from: result.from + offset + (context.type.name === "doc" ? 0 : 1),
@@ -313,9 +321,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
let ret: TextSelection[] = [];
if (node.isTextblock) {
- let index = 0, foundAt, ep = this.getNodeEndpoints(pm.state.doc, node);
+ let index = 0, foundAt;
+ const ep = this.getNodeEndpoints(pm.state.doc, node);
while (ep && (foundAt = node.textContent.slice(index).search(RegExp(find, "i"))) > -1) {
- let sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
+ const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
ret.push(sel);
index = index + foundAt + find.length;
}
@@ -324,7 +333,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
return ret;
}
- static _highlights: string[] = [];
+ static _highlights: string[] = ["Text from Others", "Todo Items", "Important Items", "Disagree Items", "Ignore Items"];
updateHighlights = () => {
clearStyleSheetRules(FormattedTextBox._userStyleSheet);
@@ -344,25 +353,44 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
addStyleSheetRule(FormattedTextBox._userStyleSheet, "userTag-" + "disagree", { "text-decoration": "line-through" });
}
if (FormattedTextBox._highlights.indexOf("Ignore Items") !== -1) {
- addStyleSheetRule(FormattedTextBox._userStyleSheet, "userTag-" + "ignore", { "font-size": "0" });
+ addStyleSheetRule(FormattedTextBox._userStyleSheet, "userTag-" + "ignore", { "font-size": "1" });
}
if (FormattedTextBox._highlights.indexOf("By Recent Minute") !== -1) {
addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" });
- let min = Math.round(Date.now() / 1000 / 60);
+ const min = Math.round(Date.now() / 1000 / 60);
numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-min-" + (min - i), { opacity: ((10 - i - 1) / 10).toString() }));
setTimeout(() => this.updateHighlights());
}
if (FormattedTextBox._highlights.indexOf("By Recent Hour") !== -1) {
addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" });
- let hr = Math.round(Date.now() / 1000 / 60 / 60);
+ const hr = Math.round(Date.now() / 1000 / 60 / 60);
numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-hr-" + (hr - i), { opacity: ((10 - i - 1) / 10).toString() }));
}
}
- toggleSidebar = () => this.props.Document.sidebarWidthPercent = StrCast(this.props.Document.sidebarWidthPercent, "0%") === "0%" ? "25%" : "0%";
+ sidebarDown = (e: React.PointerEvent) => {
+ this._lastX = e.clientX;
+ this._lastY = e.clientY;
+ this._sidebarMovement = 0;
+ document.addEventListener("pointermove", this.sidebarMove);
+ document.addEventListener("pointerup", this.sidebarUp);
+ e.stopPropagation();
+ e.preventDefault(); // prevents text from being selected during drag
+ }
+ sidebarMove = (e: PointerEvent) => {
+ const bounds = this.CurrentDiv.getBoundingClientRect();
+ this._sidebarMovement += Math.sqrt((e.clientX - this._lastX) * (e.clientX - this._lastX) + (e.clientY - this._lastY) * (e.clientY - this._lastY));
+ this.props.Document.sidebarWidthPercent = "" + 100 * (1 - (e.clientX - bounds.left) / bounds.width) + "%";
+ }
+ sidebarUp = (e: PointerEvent) => {
+ document.removeEventListener("pointermove", this.sidebarMove);
+ document.removeEventListener("pointerup", this.sidebarUp);
+ }
+
+ toggleSidebar = () => this._sidebarMovement < 5 && (this.props.Document.sidebarWidthPercent = StrCast(this.props.Document.sidebarWidthPercent, "0%") === "0%" ? "25%" : "0%");
specificContextMenu = (e: React.MouseEvent): void => {
- let funcs: ContextMenuProps[] = [];
+ const funcs: ContextMenuProps[] = [];
funcs.push({ description: "Toggle Sidebar", event: () => { e.stopPropagation(); this.toggleSidebar(); }, icon: "expand-arrows-alt" });
funcs.push({ description: "Record Bullet", event: () => { e.stopPropagation(); this.recordBullet(); }, icon: "expand-arrows-alt" });
["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
@@ -403,8 +431,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
recordBullet = async () => {
- let completedCue = "end session";
- let results = await DictationManager.Controls.listen({
+ const completedCue = "end session";
+ const results = await DictationManager.Controls.listen({
interimHandler: this.setCurrentBulletContent,
continuous: { indefinite: false },
terminators: [completedCue, "bullet", "next"]
@@ -420,20 +448,20 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
setCurrentBulletContent = (value: string) => {
if (this._editorView) {
let state = this._editorView.state;
- let from = state.selection.from;
- let to = state.selection.to;
+ const from = state.selection.from;
+ const to = state.selection.to;
this._editorView.dispatch(state.tr.insertText(value, from, to));
state = this._editorView.state;
- let updated = TextSelection.create(state.doc, from, from + value.length);
+ const updated = TextSelection.create(state.doc, from, from + value.length);
this._editorView.dispatch(state.tr.setSelection(updated));
}
}
nextBullet = (pos: number) => {
if (this._editorView) {
- let frag = Fragment.fromArray(this.newListItems(2));
+ const frag = Fragment.fromArray(this.newListItems(2));
if (this._editorView.state.doc.resolve(pos).depth >= 2) {
- let slice = new Slice(frag, 2, 2);
+ const slice = new Slice(frag, 2, 2);
let state = this._editorView.state;
this._editorView.dispatch(state.tr.step(new ReplaceStep(pos, pos, slice)));
pos += 4;
@@ -471,8 +499,15 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
componentDidMount() {
- this.pullFromGoogleDoc(this.checkState);
- this.dataDoc[GoogleRef] && this.dataDoc.unchanged && runInAction(() => DocumentDecorations.Instance.isAnimatingFetch = true);
+ this._buttonBarReactionDisposer = reaction(
+ () => DocumentButtonBar.Instance,
+ instance => {
+ if (instance) {
+ this.pullFromGoogleDoc(this.checkState);
+ this.dataDoc[GoogleRef] && this.dataDoc.unchanged && runInAction(() => instance.isAnimatingFetch = true);
+ }
+ }
+ );
this._reactionDisposer = reaction(
() => {
@@ -481,7 +516,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
},
incomingValue => {
if (this._editorView && !this._applyingChange) {
- let updatedState = JSON.parse(incomingValue);
+ const updatedState = JSON.parse(incomingValue);
this._editorView.updateState(EditorState.fromJSON(this.config, updatedState));
this.tryUpdateHeight();
}
@@ -493,7 +528,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
() => {
if (!DocumentButtonBar.hasPulledHack) {
DocumentButtonBar.hasPulledHack = true;
- let unchanged = this.dataDoc.unchanged;
+ const unchanged = this.dataDoc.unchanged;
this.pullFromGoogleDoc(unchanged ? this.checkState : this.updateState);
}
}
@@ -514,24 +549,15 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
() => this.tryUpdateHeight()
);
-
this.setupEditor(this.config, this.dataDoc, this.props.fieldKey);
- this._searchReactionDisposer = reaction(() => {
- return StrCast(this.layoutDoc.search_string);
- }, searchString => {
- if (searchString) {
- this.highlightSearchTerms([searchString]);
- }
- else {
- this.unhighlightSearchTerms();
- }
- }, { fireImmediately: true });
-
+ this._searchReactionDisposer = reaction(() => this.layoutDoc.searchMatch,
+ search => search ? this.highlightSearchTerms([Doc.SearchQuery()]) : this.unhighlightSearchTerms(),
+ { fireImmediately: true });
this._rulesReactionDisposer = reaction(() => {
- let ruleProvider = this.props.ruleProvider;
- let heading = NumCast(this.layoutDoc.heading);
+ const ruleProvider = this.props.ruleProvider;
+ const heading = NumCast(this.layoutDoc.heading);
if (ruleProvider instanceof Doc) {
return {
align: StrCast(ruleProvider["ruleAlign_" + heading], ""),
@@ -546,8 +572,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
this._ruleFontSize = rules ? rules.size : 0;
rules && setTimeout(() => {
const view = this._editorView!;
- if (this._proseRef) {
- let n = new NodeSelection(view.state.doc.resolve(0));
+ if (this.ProseRef) {
+ const n = new NodeSelection(view.state.doc.resolve(0));
if (this._editorView!.state.doc.textContent === "") {
view.dispatch(view.state.tr.setSelection(new TextSelection(view.state.doc.resolve(0), view.state.doc.resolve(2))).
replaceSelectionWith(this._editorView!.state.schema.nodes.paragraph.create({ align: rules.align }), true));
@@ -562,10 +588,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
this._scrollToRegionReactionDisposer = reaction(
() => StrCast(this.layoutDoc.scrollToLinkID),
async (scrollToLinkID) => {
- let findLinkFrag = (frag: Fragment, editor: EditorView) => {
+ const findLinkFrag = (frag: Fragment, editor: EditorView) => {
const nodes: Node[] = [];
frag.forEach((node, index) => {
- let examinedNode = findLinkNode(node, editor);
+ const examinedNode = findLinkNode(node, editor);
if (examinedNode && examinedNode.textContent) {
nodes.push(examinedNode);
start += index;
@@ -573,7 +599,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
});
return { frag: Fragment.fromArray(nodes), start: start };
};
- let findLinkNode = (node: Node, editor: EditorView) => {
+ const findLinkNode = (node: Node, editor: EditorView) => {
if (!node.isText) {
const content = findLinkFrag(node.content, editor);
return node.copy(content.frag);
@@ -585,8 +611,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
let start = -1;
if (this._editorView && scrollToLinkID) {
- let editor = this._editorView;
- let ret = findLinkFrag(editor.state.doc.content, editor);
+ const editor = this._editorView;
+ const ret = findLinkFrag(editor.state.doc.content, editor);
if (ret.frag.size > 2 && ret.start >= 0) {
let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start
@@ -605,33 +631,33 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
{ fireImmediately: true }
);
- setTimeout(() => this.tryUpdateHeight(), 0);
+ setTimeout(() => this.tryUpdateHeight(NumCast(this.layoutDoc.limitHeight, 0)));
}
pushToGoogleDoc = async () => {
this.pullFromGoogleDoc(async (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => {
- let modes = GoogleApiClientUtils.Docs.WriteMode;
+ const modes = GoogleApiClientUtils.Docs.WriteMode;
let mode = modes.Replace;
let reference: Opt<GoogleApiClientUtils.Docs.Reference> = Cast(this.dataDoc[GoogleRef], "string");
if (!reference) {
mode = modes.Insert;
reference = { title: StrCast(this.dataDoc.title) };
}
- let redo = async () => {
+ const redo = async () => {
if (this._editorView && reference) {
- let content = await RichTextUtils.GoogleDocs.Export(this._editorView.state);
- let response = await GoogleApiClientUtils.Docs.write({ reference, content, mode });
+ const content = await RichTextUtils.GoogleDocs.Export(this._editorView.state);
+ const response = await GoogleApiClientUtils.Docs.write({ reference, content, mode });
response && (this.dataDoc[GoogleRef] = response.documentId);
- let pushSuccess = response !== undefined && !("errors" in response);
+ const pushSuccess = response !== undefined && !("errors" in response);
dataDoc.unchanged = pushSuccess;
DocumentButtonBar.Instance.startPushOutcome(pushSuccess);
}
};
- let undo = () => {
+ const undo = () => {
if (!exportState) {
return;
}
- let content: GoogleApiClientUtils.Docs.Content = {
+ const content: GoogleApiClientUtils.Docs.Content = {
text: exportState.text,
requests: []
};
@@ -645,8 +671,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
pullFromGoogleDoc = async (handler: PullHandler) => {
- let dataDoc = this.dataDoc;
- let documentId = StrCast(dataDoc[GoogleRef]);
+ const dataDoc = this.dataDoc;
+ const documentId = StrCast(dataDoc[GoogleRef]);
let exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>;
if (documentId) {
exportState = await RichTextUtils.GoogleDocs.Import(documentId, dataDoc);
@@ -661,8 +687,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
dataDoc.data = new RichTextField(JSON.stringify(exportState.state.toJSON()));
setTimeout(() => {
if (this._editorView) {
- let state = this._editorView.state;
- let end = state.doc.content.size - 1;
+ const state = this._editorView.state;
+ const end = state.doc.content.size - 1;
this._editorView.dispatch(state.tr.setSelection(TextSelection.create(state.doc, end, end)));
}
}, 0);
@@ -677,9 +703,9 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
checkState = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => {
if (exportState && this._editorView) {
- let equalContent = _.isEqual(this._editorView.state.doc, exportState.state.doc);
- let equalTitles = dataDoc.title === exportState.title;
- let unchanged = equalContent && equalTitles;
+ const equalContent = isEqual(this._editorView.state.doc, exportState.state.doc);
+ const equalTitles = dataDoc.title === exportState.title;
+ const unchanged = equalContent && equalTitles;
dataDoc.unchanged = unchanged;
DocumentButtonBar.Instance.setPullState(unchanged);
}
@@ -707,7 +733,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
handlePaste = (view: EditorView, event: Event, slice: Slice): boolean => {
- let cbe = event as ClipboardEvent;
+ const cbe = event as ClipboardEvent;
const pdfDocId = cbe.clipboardData && cbe.clipboardData.getData("dash/pdfOrigin");
const pdfRegionId = cbe.clipboardData && cbe.clipboardData.getData("dash/pdfRegion");
if (pdfDocId && pdfRegionId) {
@@ -717,18 +743,18 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
setTimeout(async () => {
const extension = Doc.fieldExtensionDoc(pdfDoc, "data");
if (extension) {
- let targetAnnotations = await DocListCastAsync(extension.annotations);// bcz: NO... this assumes the pdf is using its 'data' field. need to have the PDF's view handle updating its own annotations
+ const targetAnnotations = await DocListCastAsync(extension.annotations);// bcz: NO... this assumes the pdf is using its 'data' field. need to have the PDF's view handle updating its own annotations
targetAnnotations && targetAnnotations.push(pdfRegion);
}
});
- let link = DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: pdfRegion, ctx: pdfDoc }, "note on " + pdfDoc.title, "pasted PDF link");
+ const link = DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: pdfRegion, ctx: pdfDoc }, "note on " + pdfDoc.title, "pasted PDF link");
if (link) {
cbe.clipboardData!.setData("dash/linkDoc", link[Id]);
- let linkId = link[Id];
- let frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(pdfDoc.title), linkId));
+ const linkId = link[Id];
+ const frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(pdfDoc.title), linkId));
slice = new Slice(frag, slice.openStart, slice.openEnd);
- var tr = view.state.tr.replaceSelection(slice);
+ const tr = view.state.tr.replaceSelection(slice);
view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste"));
}
}
@@ -758,56 +784,59 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
private setupEditor(config: any, doc: Doc, fieldKey: string) {
- let field = doc ? Cast(doc[fieldKey], RichTextField) : undefined;
+ const field = doc ? Cast(doc[fieldKey], RichTextField) : undefined;
let startup = StrCast(doc.documentText);
startup = startup.startsWith("@@@") ? startup.replace("@@@", "") : "";
if (!field && doc) {
- let text = StrCast(doc[fieldKey]);
+ const text = StrCast(doc[fieldKey]);
if (text) {
startup = text;
} else if (Cast(doc[fieldKey], "number")) {
startup = NumCast(doc[fieldKey], 99).toString();
}
}
- if (this._proseRef) {
- let self = this;
+ if (this.ProseRef) {
+ const self = this;
this._editorView && this._editorView.destroy();
- this._editorView = new EditorView(this._proseRef, {
+ this._editorView = new EditorView(this.ProseRef, {
state: field && field.Data ? EditorState.fromJSON(config, JSON.parse(field.Data)) : EditorState.create(config),
handleScrollToSelection: (editorView) => {
- let ref = editorView.domAtPos(editorView.state.selection.from);
+ const ref = editorView.domAtPos(editorView.state.selection.from);
let refNode = ref.node as any;
while (refNode && !("getBoundingClientRect" in refNode)) refNode = refNode.parentElement;
- let r1 = refNode && refNode.getBoundingClientRect();
- let r3 = self._ref.current!.getBoundingClientRect();
+ const r1 = refNode && refNode.getBoundingClientRect();
+ const r3 = self._ref.current!.getBoundingClientRect();
if (r1.top < r3.top || r1.top > r3.bottom) {
- r1 && (self._ref.current!.scrollTop += (r1.top - r3.top) * self.props.ScreenToLocalTransform().Scale);
+ r1 && (self._scrollRef.current!.scrollTop += (r1.top - r3.top) * self.props.ScreenToLocalTransform().Scale);
}
return true;
},
dispatchTransaction: this.dispatchTransaction,
nodeViews: {
+ dashComment(node, view, getPos) { return new DashDocCommentView(node, view, getPos); },
dashDoc(node, view, getPos) { return new DashDocView(node, view, getPos, self); },
image(node, view, getPos) { return new ImageResizeView(node, view, getPos, self.props.addDocTab); },
- star(node, view, getPos) { return new SummarizedView(node, view, getPos); },
+ 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,
});
- if (startup) {
+ this._editorView.state.schema.Document = this.props.Document;
+ if (startup && this._editorView) {
Doc.GetProto(doc).documentText = undefined;
this._editorView.dispatch(this._editorView.state.tr.insertText(startup));
}
}
- let selectOnLoad = this.props.Document[Id] === FormattedTextBox.SelectOnLoad;
+ const selectOnLoad = this.props.Document[Id] === FormattedTextBox.SelectOnLoad;
if (selectOnLoad) {
FormattedTextBox.SelectOnLoad = "";
this.props.select(false);
}
- this._editorView!.focus();
+ const rtf = doc ? Cast(doc[fieldKey], RichTextField) : undefined;
+ (selectOnLoad || (rtf && !rtf.Text)) && this._editorView!.focus();
// add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.round(Date.now() / 1000 / 5) })];
}
@@ -833,17 +862,22 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
this._pullReactionDisposer && this._pullReactionDisposer();
this._heightReactionDisposer && this._heightReactionDisposer();
this._searchReactionDisposer && this._searchReactionDisposer();
+ this._buttonBarReactionDisposer && this._buttonBarReactionDisposer();
this._editorView && this._editorView.destroy();
}
+
+ static _downEvent: any;
onPointerDown = (e: React.PointerEvent): void => {
+ this.doLinkOnDeselect();
+ FormattedTextBox._downEvent = true;
FormattedTextBoxComment.textBox = this;
- let pos = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
- pos && (this._nodeClicked = this._editorView!.state.doc.nodeAt(pos.pos));
if (this.props.onClick && e.button === 0) {
e.preventDefault();
}
- if (e.button === 0 && this.props.isSelected(true) && !e.altKey && !e.ctrlKey && !e.metaKey) {
- e.stopPropagation();
+ if (e.button === 0 && this.active(true) && !e.altKey && !e.ctrlKey && !e.metaKey) {
+ if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { // don't stop propagation if clicking in the sidebar
+ e.stopPropagation();
+ }
}
if (e.button === 2 || (e.button === 0 && e.ctrlKey)) {
e.preventDefault();
@@ -851,6 +885,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
onPointerUp = (e: React.PointerEvent): void => {
+ if (!FormattedTextBox._downEvent) return;
+ FormattedTextBox._downEvent = false;
if (!(e.nativeEvent as any).formattedHandled) {
FormattedTextBoxComment.textBox = this;
FormattedTextBoxComment.update(this._editorView!);
@@ -862,11 +898,17 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
}
- static InputBoxOverlay: FormattedTextBox | undefined;
@action
onFocused = (e: React.FocusEvent): void => {
- FormattedTextBox.InputBoxOverlay = this;
+ FormattedTextBox.FocusedBox = this;
this.tryUpdateHeight();
+
+ // see if we need to preserve the insertion point
+ const prosediv = this.ProseRef?.children?.[0] as any;
+ const keeplocation = prosediv?.keeplocation;
+ prosediv && (prosediv.keeplocation = undefined);
+ const pos = this._editorView?.state.selection.$from.pos || 1;
+ keeplocation && setTimeout(() => this._editorView?.dispatch(this._editorView?.state.tr.setSelection(TextSelection.create(this._editorView.state.doc, pos))));
}
onPointerWheel = (e: React.WheelEvent): void => {
// if a text note is not selected and scrollable, this prevents us from being able to scroll and zoom out at the same time
@@ -879,6 +921,20 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
static _userStyleSheet: any = addStyleSheet();
onClick = (e: React.MouseEvent): void => {
+ if ((this._editorView!.root as any).getSelection().isCollapsed) { // this is a hack to allow the cursor to be placed at the end of a document when the document ends in an inline dash comment. Apparently Chrome on Windows has a bug/feature which breaks this when clicking after the end of the text.
+ const pcords = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
+ const node = pcords && this._editorView!.state.doc.nodeAt(pcords.pos); // get what prosemirror thinks the clicked node is (if it's null, then we didn't click on any text)
+ if (pcords && node?.type === this._editorView!.state.schema.nodes.dashComment) {
+ this._editorView!.dispatch(this._editorView!.state.tr.setSelection(TextSelection.create(this._editorView!.state.doc, pcords.pos + 2)));
+ e.preventDefault();
+ }
+ if (!node && this.ProseRef) {
+ const lastNode = this.ProseRef.children[this.ProseRef.children.length - 1].children[this.ProseRef.children[this.ProseRef.children.length - 1].children.length - 1]; // get the last prosemirror div
+ if (e.clientY > lastNode.getBoundingClientRect().bottom) { // if we clicked below the last prosemirror div, then set the selection to be the end of the document
+ this._editorView!.dispatch(this._editorView!.state.tr.setSelection(TextSelection.create(this._editorView!.state.doc, this._editorView!.state.doc.content.size)));
+ }
+ }
+ }
if ((e.nativeEvent as any).formattedHandled) { e.stopPropagation(); return; }
(e.nativeEvent as any).formattedHandled = true;
// if (e.button === 0 && ((!this.props.isSelected(true) && !e.ctrlKey) || (this.props.isSelected(true) && e.ctrlKey)) && !e.metaKey && e.target) {
@@ -914,31 +970,42 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
// }
// }
- this.hitBulletTargets(e.clientX, e.clientY, e.nativeEvent.offsetX, e.shiftKey);
+ this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, false);
if (this._recording) setTimeout(() => { this.stopDictation(true); setTimeout(() => this.recordDictation(), 500); }, 500);
}
// this hackiness handles clicking on the list item bullets to do expand/collapse. the bullets are ::before pseudo elements so there's no real way to hit test against them.
- hitBulletTargets(x: number, y: number, offsetX: number, select: boolean = false) {
+ hitBulletTargets(x: number, y: number, select: boolean, highlightOnly: boolean) {
clearStyleSheetRules(FormattedTextBox._bulletStyleSheet);
- if (this.props.isSelected(true) && offsetX < 40) {
- let pos = this._editorView!.posAtCoords({ left: x, top: y });
- if (pos && pos.pos > 0) {
- let node = this._editorView!.state.doc.nodeAt(pos.pos);
- let node2 = node && node.type === schema.nodes.paragraph ? this._editorView!.state.doc.nodeAt(pos.pos - 1) : undefined;
- if (node === this._nodeClicked && node2 && (node2.type === schema.nodes.ordered_list || node2.type === schema.nodes.list_item)) {
- let hit = this._editorView!.domAtPos(pos.pos).node as any; // let beforeEle = document.querySelector("." + hit.className) as Element;
- let before = hit ? window.getComputedStyle(hit, ':before') : undefined;
- let beforeWidth = before ? Number(before.getPropertyValue('width').replace("px", "")) : undefined;
- if (beforeWidth && offsetX < beforeWidth) {
- let ol = this._editorView!.state.doc.nodeAt(pos.pos - 2) ? this._editorView!.state.doc.nodeAt(pos.pos - 2) : undefined;
- if (ol && ol.type === schema.nodes.ordered_list && select) {
- this._editorView!.dispatch(this._editorView!.state.tr.setSelection(new NodeSelection(this._editorView!.state.doc.resolve(pos.pos - 2))));
- addStyleSheetRule(FormattedTextBox._bulletStyleSheet, hit.className + ":before", { background: "gray" });
- } else {
- this._editorView!.dispatch(this._editorView!.state.tr.setNodeMarkup(pos.pos - 1, node2.type, { ...node2.attrs, visibility: !node2.attrs.visibility }));
- }
+ const pos = this._editorView!.posAtCoords({ left: x, top: y });
+ if (pos && this.props.isSelected(true)) {
+ // let beforeEle = document.querySelector("." + hit.className) as Element; // const before = hit ? window.getComputedStyle(hit, ':before') : undefined;
+ //const node = this._editorView!.state.doc.nodeAt(pos.pos);
+ const $pos = this._editorView!.state.doc.resolve(pos.pos);
+ let list_node = $pos.node().type === schema.nodes.list_item ? $pos.node() : undefined;
+ if ($pos.node().type === schema.nodes.ordered_list) {
+ for (let off = 1; off < 100; off++) {
+ const pos = this._editorView!.posAtCoords({ left: x + off, top: y });
+ const node = pos && this._editorView!.state.doc.nodeAt(pos.pos);
+ if (node?.type === schema.nodes.list_item) {
+ list_node = node;
+ break;
+ }
+ }
+ }
+ if (list_node && pos.inside >= 0 && this._editorView!.state.doc.nodeAt(pos.inside)!.attrs.bulletStyle === list_node.attrs.bulletStyle) {
+ if (select) {
+ const $olist_pos = this._editorView!.state.doc.resolve($pos.pos - $pos.parentOffset - 1);
+ if (!highlightOnly) {
+ this._editorView!.dispatch(this._editorView!.state.tr.setSelection(new NodeSelection($olist_pos)));
+ }
+ addStyleSheetRule(FormattedTextBox._bulletStyleSheet, list_node.attrs.mapStyle + list_node.attrs.bulletStyle + ":hover:before", { background: "lightgray" });
+ } else if (Math.abs(pos.pos - pos.inside) < 2) {
+ if (!highlightOnly) {
+ this._editorView!.dispatch(this._editorView!.state.tr.setNodeMarkup(pos.inside, list_node.type, { ...list_node.attrs, visibility: !list_node.attrs.visibility }));
+ this._editorView!.dispatch(this._editorView!.state.tr.setSelection(TextSelection.create(this._editorView!.state.doc, pos.inside)));
}
+ addStyleSheetRule(FormattedTextBox._bulletStyleSheet, list_node.attrs.mapStyle + list_node.attrs.bulletStyle + ":hover:before", { background: "lightgray" });
}
}
}
@@ -946,11 +1013,11 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
onMouseUp = (e: React.MouseEvent): void => {
e.stopPropagation();
- let view = this._editorView as any;
+ const view = this._editorView as any;
// this interposes on prosemirror's upHandler to prevent prosemirror's up from invoked multiple times when there
// are nested prosemirrors. We only want the lowest level prosemirror to be invoked.
if (view.mouseDown) {
- let originalUpHandler = view.mouseDown.up;
+ const originalUpHandler = view.mouseDown.up;
view.root.removeEventListener("mouseup", originalUpHandler);
view.mouseDown.up = (e: MouseEvent) => {
!(e as any).formattedHandled && originalUpHandler(e);
@@ -962,7 +1029,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
tooltipTextMenuPlugin() {
- let self = FormattedTextBox;
+ const self = FormattedTextBox;
return new Plugin({
view(newView) {
return self.ToolTipTextMenu = FormattedTextBox.getToolTip(newView);
@@ -971,7 +1038,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
tooltipLinkingMenuPlugin() {
- let myprops = this.props;
+ const myprops = this.props;
return new Plugin({
view(_editorView) {
return new TooltipLinkingMenu(_editorView, myprops);
@@ -986,15 +1053,35 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
this.doLinkOnDeselect();
}
+
+ _lastTimedMark: Mark | undefined = undefined;
onKeyPress = (e: React.KeyboardEvent) => {
+ if (e.altKey) {
+ e.preventDefault();
+ return;
+ }
+ const state = this._editorView!.state;
+ if (!state.selection.empty && e.key === "%") {
+ state.schema.EnteringStyle = true;
+ e.preventDefault();
+ e.stopPropagation();
+ return;
+ }
+
+ if (state.selection.empty || !state.schema.EnteringStyle) {
+ state.schema.EnteringStyle = false;
+ }
if (e.key === "Escape") {
+ this._editorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
+ (document.activeElement as any).blur?.();
SelectionManager.DeselectAll();
}
e.stopPropagation();
if (e.key === "Tab" || e.key === "Enter") {
e.preventDefault();
}
- let mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.round(Date.now() / 1000 / 5) });
+ const mark = e.key !== " " && this._lastTimedMark ? this._lastTimedMark : schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.round(Date.now() / 1000 / 5) });
+ this._lastTimedMark = mark;
this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(mark));
if (!this._undoTyping) {
@@ -1007,14 +1094,22 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
@action
- tryUpdateHeight() {
- const scrollHeight = this._ref.current?.scrollHeight;
+ tryUpdateHeight(limitHeight?: number) {
+ let scrollHeight = this._ref.current?.scrollHeight;
if (!this.layoutDoc.animateToPos && this.layoutDoc.autoHeight && scrollHeight &&
getComputedStyle(this._ref.current!.parentElement!).top === "0px") { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
- let nh = this.Document.isTemplateField ? 0 : NumCast(this.dataDoc.nativeHeight, 0);
- let dh = NumCast(this.layoutDoc.height, 0);
- this.layoutDoc.height = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0));
- this.dataDoc.nativeHeight = nh ? scrollHeight : undefined;
+ if (limitHeight && scrollHeight > limitHeight) {
+ scrollHeight = limitHeight;
+ this.layoutDoc.limitHeight = undefined;
+ this.layoutDoc.autoHeight = false;
+ }
+ const nh = this.Document.isTemplateField ? 0 : NumCast(this.dataDoc.nativeHeight, 0);
+ const dh = NumCast(this.layoutDoc.height, 0);
+ const newHeight = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0));
+ if (Math.abs(newHeight - dh) > 1) { // bcz: Argh! without this, we get into a React crash if the same document is opened in a freeform view and in the treeview. no idea why, but after dragging the freeform document, selecting it, and selecting text, it will compute to 1 pixel higher than the treeview which causes a cycle
+ this.layoutDoc.height = newHeight;
+ this.dataDoc.nativeHeight = nh ? scrollHeight : undefined;
+ }
}
}
@@ -1023,8 +1118,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
@computed get annotationsKey() { return "annotations"; }
render() {
TraceMobx();
- let rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
- let interactive = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground;
+ const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
+ const interactive = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground;
if (this.props.isSelected()) {
FormattedTextBox.ToolTipTextMenu!.updateFromDash(this._editorView!, undefined, this.props);
} else if (FormattedTextBoxComment.textBox === this) {
@@ -1045,27 +1140,27 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
onKeyDown={this.onKeyPress}
onFocus={this.onFocused}
onClick={this.onClick}
+ onPointerMove={e => this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, true)}
onBlur={this.onBlur}
onPointerUp={this.onPointerUp}
onPointerDown={this.onPointerDown}
onMouseUp={this.onMouseUp}
- onTouchStart={this.onTouchStart}
onWheel={this.onPointerWheel}
onPointerEnter={action(() => this._entered = true)}
onPointerLeave={action(() => this._entered = false)}
>
- <div className={`formattedTextBox-outer`} style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, }}>
+ <div className={`formattedTextBox-outer`} style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, }} ref={this._scrollRef}>
<div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: ((this.Document.isButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined }} ref={this.createDropTarget} />
</div>
- {this.sidebarWidthPercent === "0%" ?
- <div className="formattedTextBox-sidebar-handle" onPointerDown={e => e.stopPropagation()} onClick={e => this.toggleSidebar()} /> :
+ {this.props.Document.hideSidebar ? (null) : this.sidebarWidthPercent === "0%" ?
+ <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} onClick={e => this.toggleSidebar()} /> :
<div className={"formattedTextBox-sidebar" + (InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : "")}
style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${StrCast(this.extensionDoc?.backgroundColor, "transparent")}` }}>
<CollectionFreeFormView {...this.props}
- PanelHeight={() => this.props.PanelHeight()}
+ PanelHeight={this.props.PanelHeight}
PanelWidth={() => this.sidebarWidth}
annotationsKey={this.annotationsKey}
- isAnnotationOverlay={true}
+ isAnnotationOverlay={false}
focus={this.props.focus}
isSelected={this.props.isSelected}
select={emptyFunction}
@@ -1074,7 +1169,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
whenActiveChanged={this.whenActiveChanged}
removeDocument={this.removeDocument}
moveDocument={this.moveDocument}
- addDocument={this.addDocument}
+ addDocument={(doc: Doc) => { doc.hideSidebar = true; return this.addDocument(doc); }}
CollectionView={undefined}
ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth), 0)}
ruleProvider={undefined}
@@ -1082,7 +1177,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
ContainingCollectionDoc={this.props.ContainingCollectionDoc}
chromeCollapsed={true}>
</CollectionFreeFormView>
- <div className="formattedTextBox-sidebar-handle" onPointerDown={e => e.stopPropagation()} onClick={e => this.toggleSidebar()} />
+ <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} onClick={e => this.toggleSidebar()} />
</div>}
<div className="formattedTextBox-dictation"
onClick={e => {
@@ -1091,7 +1186,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
e.stopPropagation();
}} >
<FontAwesomeIcon className="formattedTExtBox-audioFont"
- style={{ color: this._recording ? "red" : "blue", opacity: this._recording ? 1 : 0.2 }} icon={"microphone"} size="sm" />
+ style={{ color: this._recording ? "red" : "blue", opacity: this._recording ? 1 : 0.5, display: this.props.isSelected() ? "" : "none" }} icon={"microphone"} size="sm" />
</div>
</div>
);
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index c076fd34a..5fd5d4ce1 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -4,7 +4,7 @@ import { EditorView } from "prosemirror-view";
import * as ReactDOM from 'react-dom';
import { Doc } from "../../../new_fields/Doc";
import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils } from "../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { DocumentManager } from "../../util/DocumentManager";
import { schema } from "../../util/RichTextSchema";
@@ -57,7 +57,6 @@ export class FormattedTextBoxComment {
static start: number;
static end: number;
static mark: Mark;
- static opened: boolean;
static textBox: FormattedTextBox | undefined;
static linkDoc: Doc | undefined;
constructor(view: any) {
@@ -81,7 +80,7 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.tooltip.style.display = "none";
FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipInput);
FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => {
- let keep = e.target && (e.target as any).type === "checkbox" ? true : false;
+ const keep = e.target && (e.target as any).type === "checkbox" ? true : false;
const textBox = FormattedTextBoxComment.textBox;
if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
@@ -89,11 +88,10 @@ export class FormattedTextBoxComment {
} else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, width: 200, height: 400 }), undefined, "onRight");
}
- FormattedTextBoxComment.opened = keep || !FormattedTextBoxComment.opened;
- textBox && FormattedTextBoxComment.start !== undefined && textBox.setAnnotation(
- FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark,
- FormattedTextBoxComment.opened, keep);
+ keep && textBox && FormattedTextBoxComment.start !== undefined && textBox.adoptAnnotation(
+ FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark);
e.stopPropagation();
+ e.preventDefault();
};
root && root.appendChild(FormattedTextBoxComment.tooltip);
}
@@ -103,17 +101,16 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.textBox = undefined;
FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
}
- public static SetState(textBox: any, opened: boolean, start: number, end: number, mark: Mark) {
+ public static SetState(textBox: any, start: number, end: number, mark: Mark) {
FormattedTextBoxComment.textBox = textBox;
FormattedTextBoxComment.start = start;
FormattedTextBoxComment.end = end;
FormattedTextBoxComment.mark = mark;
- FormattedTextBoxComment.opened = opened;
FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "");
}
static update(view: EditorView, lastState?: EditorState) {
- let state = view.state;
+ const state = view.state;
// Don't do anything if the document/selection didn't change
if (lastState && lastState.doc.eq(state.doc) &&
lastState.selection.eq(state.selection)) {
@@ -136,13 +133,13 @@ export class FormattedTextBoxComment {
// this section checks to see if the insertion point is over text entered by a different user. If so, it sets ths comment text to indicate the user and the modification date
if (state.selection.$from) {
nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
- let naft = findEndOfMark(state.selection.$from, view, findOtherUserMark);
- let noselection = view.state.selection.$from === view.state.selection.$to;
+ const naft = findEndOfMark(state.selection.$from, view, findOtherUserMark);
+ const noselection = view.state.selection.$from === view.state.selection.$to;
let child: any = null;
state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
- let mark = child && findOtherUserMark(child.marks);
+ const mark = child && findOtherUserMark(child.marks);
if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) {
- FormattedTextBoxComment.SetState(FormattedTextBoxComment.textBox, mark.attrs.opened, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark);
+ FormattedTextBoxComment.SetState(FormattedTextBoxComment.textBox, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark);
}
if (mark && child && ((nbef && naft) || !noselection)) {
FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " date=" + (new Date(mark.attrs.modified * 5000)).toDateString();
@@ -153,32 +150,36 @@ export class FormattedTextBoxComment {
// this checks if the selection is a hyperlink. If so, it displays the target doc's text for internal links, and the url of the target for external links.
if (set === "none" && state.selection.$from) {
nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
- let naft = findEndOfMark(state.selection.$from, view, findLinkMark);
+ const naft = findEndOfMark(state.selection.$from, view, findLinkMark);
let child: any = null;
state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
- let mark = child && findLinkMark(child.marks);
- if (mark && child && nbef && naft) {
+ const mark = child && findLinkMark(child.marks);
+ if (mark && child && nbef && naft && mark.attrs.showPreview) {
FormattedTextBoxComment.tooltipText.textContent = "external => " + mark.attrs.href;
+ (FormattedTextBoxComment.tooltipText as any).href = mark.attrs.href;
if (mark.attrs.href.startsWith("https://en.wikipedia.org/wiki/")) {
wiki().page(mark.attrs.href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(summary => FormattedTextBoxComment.tooltipText.textContent = summary.substring(0, 500)));
} else {
FormattedTextBoxComment.tooltipText.style.whiteSpace = "pre";
FormattedTextBoxComment.tooltipText.style.overflow = "hidden";
}
- (FormattedTextBoxComment.tooltipText as any).href = mark.attrs.href;
if (mark.attrs.href.indexOf(Utils.prepend("/doc/")) === 0) {
- let docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+ FormattedTextBoxComment.tooltipText.textContent = "target not found...";
+ (FormattedTextBoxComment.tooltipText as any).href = "";
+ const docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
docTarget && DocServer.GetRefField(docTarget).then(linkDoc => {
if (linkDoc instanceof Doc) {
+ (FormattedTextBoxComment.tooltipText as any).href = mark.attrs.href;
FormattedTextBoxComment.linkDoc = linkDoc;
- const target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : Cast(linkDoc.anchor1, Doc));
+ const target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc);
try {
ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
} catch (e) { }
if (target) {
ReactDOM.render(<ContentFittingDocumentView
- fitToBox={true}
Document={target}
+ LibraryPath={emptyPath}
+ fitToBox={true}
moveDocument={returnFalse}
getTransform={Transform.Identity}
active={returnFalse}
@@ -210,12 +211,12 @@ export class FormattedTextBoxComment {
if (set !== "none") {
// These are in screen coordinates
// let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
- let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
+ const start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
// The box in which the tooltip is positioned, to use as base
- let box = (document.getElementById("mainView-container") as any).getBoundingClientRect();
+ const box = (document.getElementById("mainView-container") as any).getBoundingClientRect();
// Find a center-ish x position from the selection endpoints (when
// crossing lines, end may be more to the left)
- let left = Math.max((start.left + end.left) / 2, start.left + 3);
+ const left = Math.max((start.left + end.left) / 2, start.left + 3);
FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
}
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index 60f547b1e..9462ff024 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -51,7 +51,7 @@ export class IconBox extends React.Component<FieldViewProps> {
}
public static DocumentIcon(layout: string) {
- let button = layout.indexOf("PDFBox") !== -1 ? faFilePdf :
+ const button = layout.indexOf("PDFBox") !== -1 ? faFilePdf :
layout.indexOf("ImageBox") !== -1 ? faImage :
layout.indexOf("Formatted") !== -1 ? faStickyNote :
layout.indexOf("Video") !== -1 ? faFilm :
@@ -65,14 +65,14 @@ export class IconBox extends React.Component<FieldViewProps> {
}
specificContextMenu = (): void => {
- let cm = ContextMenu.Instance;
+ const cm = ContextMenu.Instance;
cm.addItem({ description: this.props.Document.hideLabel ? "Show label with icon" : "Remove label from icon", event: this.setLabelField, icon: "tag" });
if (!this.props.Document.hideLabel) {
cm.addItem({ description: "Use Target Title", event: () => IconBox.AutomaticTitle(this.props.Document), icon: "text-height" });
}
}
render() {
- let label = this.props.Document.hideLabel ? "" : this.props.Document.title;
+ const label = this.props.Document.hideLabel ? "" : this.props.Document.title;
return (
<div className="iconBox-container" onContextMenu={this.specificContextMenu}>
{this.minimizedIcon}
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index ba4ef8879..cf5d999a7 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -1,4 +1,22 @@
-.imageBox-cont, .imageBox-cont-interactive {
+.imageBox, .imageBox-dragging{
+ pointer-events: all;
+ border-radius: inherit;
+ width:100%;
+ height:100%;
+ position: absolute;
+ transform-origin: top left;
+ .imageBox-fader {
+ pointer-events: all;
+ }
+}
+
+.imageBox-dragging {
+ .imageBox-fader {
+ pointer-events: none;
+ }
+}
+
+.imageBox-cont {
padding: 0vw;
position: absolute;
text-align: center;
@@ -8,19 +26,11 @@
max-height: 100%;
pointer-events: none;
background:transparent;
-}
-
-.imageBox-container {
- pointer-events: all;
- border-radius: inherit;
- width:100%;
- height:100%;
- position: absolute;
- transform-origin: top left;
-}
-
-.imageBox-cont-interactive {
- pointer-events: all;
+ img {
+ height: auto;
+ width: 100%;
+ pointer-events: all;
+ }
}
.imageBox-dot {
@@ -33,16 +43,6 @@
background: gray;
}
-.imageBox-cont img {
- height: auto;
- width: 100%;
-}
-
-.imageBox-cont-interactive img {
- height: auto;
- width: 100%;
-}
-
#google-photos {
transition: all 0.5s ease 0s;
width: 30px;
@@ -100,18 +100,18 @@
}
}
-#cf {
+.imageBox-fader {
position: relative;
width: 100%;
margin: 0 auto;
display: flex;
- align-items: center;
height: 100%;
overflow: hidden;
.imageBox-fadeBlocker {
width: 100%;
height: 100%;
+ position: absolute;
background: black;
display: flex;
flex-direction: row;
@@ -126,7 +126,7 @@
}
}
-#cf img {
+.imageBox-fader img {
position: absolute;
left: 0;
}
@@ -138,10 +138,12 @@
transition: opacity 1s ease-in-out;
}
-.imageBox-fadeBlocker:hover {
- -webkit-transition: opacity 1s ease-in-out;
- -moz-transition: opacity 1s ease-in-out;
- -o-transition: opacity 1s ease-in-out;
- transition: opacity 1s ease-in-out;
- opacity: 0;
+.imageBox:hover {
+ .imageBox-fadeBlocker {
+ -webkit-transition: opacity 1s ease-in-out;
+ -moz-transition: opacity 1s ease-in-out;
+ -o-transition: opacity 1s ease-in-out;
+ transition: opacity 1s ease-in-out;
+ opacity: 0;
+ }
} \ No newline at end of file
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index f21ce3bf2..09e627078 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -8,10 +8,9 @@ import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc';
import { List } from '../../../new_fields/List';
import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
import { ComputedField } from '../../../new_fields/ScriptField';
-import { BoolCast, Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types';
+import { Cast, NumCast } from '../../../new_fields/Types';
import { AudioField, ImageField } from '../../../new_fields/URLField';
-import { RouteStore } from '../../../server/RouteStore';
-import { Utils, returnOne, emptyFunction, OmitKeys } from '../../../Utils';
+import { Utils, returnOne, emptyFunction } from '../../../Utils';
import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
@@ -19,7 +18,6 @@ import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from "../../views/ContextMenu";
import { ContextMenuProps } from '../ContextMenuItem';
import { DocAnnotatableComponent } from '../DocComponent';
-import { InkingControl } from '../InkingControl';
import FaceRectangles from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
@@ -28,8 +26,9 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec
import { documentSchema } from '../../../new_fields/documentSchemas';
import { Id } from '../../../new_fields/FieldSymbols';
import { TraceMobx } from '../../../new_fields/util';
-var requestImageSize = require('../../util/request-image-size');
-var path = require('path');
+import { SelectionManager } from '../../util/SelectionManager';
+const requestImageSize = require('../../util/request-image-size');
+const path = require('path');
const { Howl } = require('howler');
@@ -67,18 +66,18 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
protected createDropTarget = (ele: HTMLDivElement) => {
this._dropDisposer && this._dropDisposer();
- ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }));
+ ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
}
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
- if (de.data instanceof DragManager.DocumentDragData) {
- if (de.mods === "AltKey" && de.data.draggedDocuments.length && de.data.draggedDocuments[0].data instanceof ImageField) {
- Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(de.data.draggedDocuments[0].data.url);
+ if (de.complete.docDragData) {
+ if (de.altKey && de.complete.docDragData.draggedDocuments.length && de.complete.docDragData.draggedDocuments[0].data instanceof ImageField) {
+ Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(de.complete.docDragData.draggedDocuments[0].data.url);
e.stopPropagation();
}
- de.mods === "MetaKey" && de.data.droppedDocuments.forEach(action((drop: Doc) => {
+ de.metaKey && de.complete.docDragData.droppedDocuments.forEach(action((drop: Doc) => {
this.extensionDoc && Doc.AddDocToList(Doc.GetProto(this.extensionDoc), "Alternates", drop);
e.stopPropagation();
}));
@@ -88,7 +87,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
recordAudioAnnotation = () => {
let gumStream: any;
let recorder: any;
- let self = this;
+ const self = this;
const extensionDoc = this.extensionDoc;
extensionDoc && navigator.mediaDevices.getUserMedia({
audio: true
@@ -98,16 +97,16 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
recorder.ondataavailable = async function (e: any) {
const formData = new FormData();
formData.append("file", e.data);
- const res = await fetch(Utils.prepend(RouteStore.upload), {
+ const res = await fetch(Utils.prepend("/upload"), {
method: 'POST',
body: formData
});
const files = await res.json();
const url = Utils.prepend(files[0].path);
// upload to server with known URL
- let audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", width: 200, height: 32 });
+ const audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", width: 200, height: 32 });
audioDoc.treeViewExpandedView = "layout";
- let audioAnnos = Cast(extensionDoc.audioAnnotations, listSpec(Doc));
+ const audioAnnos = Cast(extensionDoc.audioAnnotations, listSpec(Doc));
if (audioAnnos === undefined) {
extensionDoc.audioAnnotations = new List([audioDoc]);
} else {
@@ -126,10 +125,10 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
@undoBatch
rotate = action(() => {
- let nw = this.Document.nativeWidth;
- let nh = this.Document.nativeHeight;
- let w = this.Document.width;
- let h = this.Document.height;
+ const nw = this.Document.nativeWidth;
+ const nh = this.Document.nativeHeight;
+ const w = this.Document.width;
+ const h = this.Document.height;
this.Document.rotation = ((this.Document.rotation || 0) + 90) % 360;
this.Document.nativeWidth = nh;
this.Document.nativeHeight = nw;
@@ -140,12 +139,12 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
specificContextMenu = (e: React.MouseEvent): void => {
const field = Cast(this.Document[this.props.fieldKey], ImageField);
if (field) {
- let funcs: ContextMenuProps[] = [];
+ const funcs: ContextMenuProps[] = [];
funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
funcs.push({ description: "Rotate", event: this.rotate, icon: "expand-arrows-alt" });
- let existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers...");
- let modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
+ const existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers...");
+ const modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
modes.push({ description: "Generate Tags", event: this.generateMetadata, icon: "tag" });
modes.push({ description: "Find Faces", event: this.extractFaces, icon: "camera" });
!existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" });
@@ -155,8 +154,8 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
}
extractFaces = () => {
- let converter = (results: any) => {
- let faceDocs = new List<Doc>();
+ const converter = (results: any) => {
+ const faceDocs = new List<Doc>();
results.reduce((face: CognitiveServices.Image.Face, faceDocs: List<Doc>) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`)!), new List<Doc>());
return faceDocs;
};
@@ -164,12 +163,12 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
}
generateMetadata = (threshold: Confidence = Confidence.Excellent) => {
- let converter = (results: any) => {
- let tagDoc = new Doc;
- let tagsList = new List();
+ const converter = (results: any) => {
+ const tagDoc = new Doc;
+ const tagsList = new List();
results.tags.map((tag: Tag) => {
tagsList.push(tag.name);
- let sanitized = tag.name.replace(" ", "_");
+ const sanitized = tag.name.replace(" ", "_");
tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`);
});
this.extensionDoc && (this.extensionDoc.generatedTags = tagsList);
@@ -181,7 +180,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
}
@computed private get url() {
- let data = Cast(this.dataDoc[this.props.fieldKey], ImageField);
+ const data = Cast(this.dataDoc[this.props.fieldKey], ImageField);
return data ? data.url.href : undefined;
}
@@ -194,7 +193,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
} else if (!(lower.endsWith(".png") || lower.endsWith(".jpg") || lower.endsWith(".jpeg"))) {
return url.href;//Why is this here
}
- let ext = path.extname(url.href);
+ const ext = path.extname(url.href);
const suffix = this.props.renderDepth < 1 ? "_o" : this._curSuffix;
return url.href.replace(ext, suffix + ext);
}
@@ -208,19 +207,37 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
if (this._curSuffix === "_l") this._largeRetryCount++;
}
@action onError = () => {
- let timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount;
+ const timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount;
if (timeout < 10) {
setTimeout(this.retryPath, Math.min(10000, timeout * 5));
}
}
_curSuffix = "_m";
+ _resized = false;
resize = (srcpath: string) => {
requestImageSize(srcpath)
.then((size: any) => {
- let rotation = NumCast(this.dataDoc.rotation) % 180;
- let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size;
- let aspect = realsize.height / realsize.width;
+ const rotation = NumCast(this.dataDoc.rotation) % 180;
+ const realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size;
+ const aspect = realsize.height / realsize.width;
+ if (this.Document.width && (Math.abs(1 - NumCast(this.Document.height) / NumCast(this.Document.width) / (realsize.height / realsize.width)) > 0.1)) {
+ setTimeout(action(() => {
+ this._resized = true;
+ this.Document.height = this.Document[WidthSym]() * aspect;
+ this.Document.nativeHeight = realsize.height;
+ this.Document.nativeWidth = realsize.width;
+ }), 0);
+ } else this._resized = true;
+ })
+ .catch((err: any) => console.log(err));
+ }
+ fadesize = (srcpath: string) => {
+ requestImageSize(srcpath)
+ .then((size: any) => {
+ const rotation = NumCast(this.dataDoc.rotation) % 180;
+ const realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size;
+ const aspect = realsize.height / realsize.width;
if (this.Document.width && (Math.abs(1 - NumCast(this.Document.height) / NumCast(this.Document.width) / (realsize.height / realsize.width)) > 0.1)) {
setTimeout(action(() => {
this.Document.height = this.Document[WidthSym]() * aspect;
@@ -234,10 +251,10 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
@action
onPointerEnter = () => {
- let self = this;
- let audioAnnos = this.extensionDoc && DocListCast(this.extensionDoc.audioAnnotations);
+ const self = this;
+ const audioAnnos = this.extensionDoc && DocListCast(this.extensionDoc.audioAnnotations);
if (audioAnnos && audioAnnos.length && this._audioState === 0) {
- let anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)];
+ const anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)];
anno.data instanceof AudioField && new Howl({
src: [anno.data.url.href],
format: ["mp3"],
@@ -273,69 +290,74 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
const extensionDoc = this.extensionDoc;
if (!extensionDoc) return (null);
// let transform = this.props.ScreenToLocalTransform().inverse();
- let pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50;
+ const pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50;
// var [sptX, sptY] = transform.transformPoint(0, 0);
// let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight());
// let w = bptX - sptX;
- let nativeWidth = (this.Document.nativeWidth || pw);
- let nativeHeight = (this.Document.nativeHeight || 0);
- let paths = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
+ const nativeWidth = (this.Document.nativeWidth || pw);
+ const nativeHeight = (this.Document.nativeHeight || 1);
+ let paths = [[Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png"), nativeWidth / nativeHeight]];
// this._curSuffix = "";
// if (w > 20) {
- let alts = DocListCast(extensionDoc.Alternates);
- let altpaths = alts.filter(doc => doc.data instanceof ImageField).map(doc => this.choosePath((doc.data as ImageField).url));
- let field = this.dataDoc[this.props.fieldKey];
+ const alts = DocListCast(extensionDoc.Alternates);
+ const altpaths = alts.filter(doc => doc.data instanceof ImageField).map(doc => [this.choosePath((doc.data as ImageField).url), doc[WidthSym]() / doc[HeightSym]()]);
+ const field = this.dataDoc[this.props.fieldKey];
// if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s";
// else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m";
// else if (this._largeRetryCount < 10) this._curSuffix = "_l";
- if (field instanceof ImageField) paths = [this.choosePath(field.url)];
+ if (field instanceof ImageField) paths = [[this.choosePath(field.url), nativeWidth / nativeHeight]];
paths.push(...altpaths);
// }
- let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
- let rotation = NumCast(this.Document.rotation, 0);
- let aspect = (rotation % 180) ? this.Document[HeightSym]() / this.Document[WidthSym]() : 1;
- let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
- let srcpath = paths[Math.min(paths.length - 1, (this.Document.curPage || 0))];
- let fadepath = paths[Math.min(paths.length - 1, 1)];
+ const rotation = NumCast(this.Document.rotation, 0);
+ const aspect = (rotation % 180) ? this.Document[HeightSym]() / this.Document[WidthSym]() : 1;
+ const shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
+ const srcpath = paths[Math.min(paths.length - 1, (this.Document.curPage || 0))][0] as string;
+ const srcaspect = paths[Math.min(paths.length - 1, (this.Document.curPage || 0))][1] as number;
+ const fadepath = paths[Math.min(paths.length - 1, 1)][0] as string;
- !this.Document.ignoreAspect && this.resize(srcpath);
+ !this.Document.ignoreAspect && !this._resized && this.resize(srcpath);
- return (
- <div className={`imageBox-cont${interactive}`} key={this.props.Document[Id]} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
- <div id="cf">
- <img
- key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
- src={srcpath}
- style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
- width={nativeWidth}
- ref={this._imgRef}
- onError={this.onError} />
- {fadepath === srcpath ? (null) : <div className="imageBox-fadeBlocker"> <img className="imageBox-fadeaway"
+ return <div className="imageBox-cont" key={this.props.Document[Id]} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
+ <div className="imageBox-fader" >
+ <img key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
+ src={srcpath}
+ style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
+ width={nativeWidth}
+ ref={this._imgRef}
+ onError={this.onError} />
+ {fadepath === srcpath ? (null) : <div className="imageBox-fadeBlocker" style={{ width: nativeWidth, height: nativeWidth / srcaspect }}>
+ <img className="imageBox-fadeaway"
key={"fadeaway" + this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
src={fadepath}
- style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
+ style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})`, }}
width={nativeWidth}
ref={this._imgRef}
onError={this.onError} /></div>}
- </div>
- <div className="imageBox-audioBackground"
- onPointerDown={this.audioDown}
- onPointerEnter={this.onPointerEnter}
- style={{ height: `calc(${.1 * nativeHeight / nativeWidth * 100}%)` }}
- >
- <FontAwesomeIcon className="imageBox-audioFont"
- style={{ color: [DocListCast(extensionDoc.audioAnnotations).length ? "blue" : "gray", "green", "red"][this._audioState] }} icon={!DocListCast(extensionDoc.audioAnnotations).length ? "microphone" : faFileAudio} size="sm" />
- </div>
- {this.considerGooglePhotosLink()}
- <FaceRectangles document={extensionDoc} color={"#0000FF"} backgroundColor={"#0000FF"} />
- </div>);
+ </div>
+ <div className="imageBox-audioBackground"
+ onPointerDown={this.audioDown}
+ onPointerEnter={this.onPointerEnter}
+ style={{ height: `calc(${.1 * nativeHeight / nativeWidth * 100}%)` }}
+ >
+ <FontAwesomeIcon className="imageBox-audioFont"
+ style={{ color: [DocListCast(extensionDoc.audioAnnotations).length ? "blue" : "gray", "green", "red"][this._audioState] }} icon={!DocListCast(extensionDoc.audioAnnotations).length ? "microphone" : faFileAudio} size="sm" />
+ </div>
+ {this.considerGooglePhotosLink()}
+ <FaceRectangles document={extensionDoc} color={"#0000FF"} backgroundColor={"#0000FF"} />
+ </div>;
}
contentFunc = () => [this.content];
render() {
- return (<div className={"imageBox-container"} onContextMenu={this.specificContextMenu}
- style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
+ TraceMobx();
+ const dragging = !SelectionManager.GetIsDragging() ? "" : "-dragging";
+ return (<div className={`imageBox${dragging}`} onContextMenu={this.specificContextMenu}
+ style={{
+ transform: `scale(${this.props.ContentScaling()})`,
+ width: `${100 / this.props.ContentScaling()}%`,
+ height: `${100 / this.props.ContentScaling()}%`
+ }} >
<CollectionFreeFormView {...this.props}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index aa6e135fe..234a6a9d3 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -53,30 +53,30 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
}
}
public static CompileKVPScript(value: string): KVPScript | undefined {
- let eq = value.startsWith("=");
+ const eq = value.startsWith("=");
value = eq ? value.substr(1) : value;
const dubEq = value.startsWith(":=") ? "computed" : value.startsWith(";=") ? "script" : false;
value = dubEq ? value.substr(2) : value;
- let options: ScriptOptions = { addReturn: true, params: { this: "Doc" } };
+ const options: ScriptOptions = { addReturn: true, params: { this: "Doc", _last_: "any" }, editable: false };
if (dubEq) options.typecheck = false;
- let script = CompileScript(value, options);
+ const script = CompileScript(value, options);
if (!script.compiled) {
return undefined;
}
return { script, type: dubEq, onDelegate: eq };
}
- public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript): boolean {
+ public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean): boolean {
const { script, type, onDelegate } = kvpScript;
//const target = onDelegate ? Doc.Layout(doc.layout) : Doc.GetProto(doc); // bcz: TODO need to be able to set fields on layout templates
- const target = onDelegate ? doc : Doc.GetProto(doc);
+ const target = forceOnDelegate || onDelegate ? doc : Doc.GetProto(doc);
let field: Field;
if (type === "computed") {
field = new ComputedField(script);
} else if (type === "script") {
field = new ScriptField(script);
} else {
- let res = script.run({ this: target }, console.log);
+ const res = script.run({ this: target }, console.log);
if (!res.success) return false;
field = res.result;
}
@@ -88,10 +88,10 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
}
@undoBatch
- public static SetField(doc: Doc, key: string, value: string) {
+ public static SetField(doc: Doc, key: string, value: string, forceOnDelegate?: boolean) {
const script = this.CompileKVPScript(value);
if (!script) return false;
- return this.ApplyKVPScript(doc, key, script);
+ return this.ApplyKVPScript(doc, key, script, forceOnDelegate);
}
onPointerDown = (e: React.PointerEvent): void => {
@@ -106,14 +106,14 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
rowHeight = () => 30;
createTable = () => {
- let doc = this.fieldDocToLayout;
+ const doc = this.fieldDocToLayout;
if (!doc) {
return <tr><td>Loading...</td></tr>;
}
- let realDoc = doc;
+ const realDoc = doc;
- let ids: { [key: string]: string } = {};
- let protos = Doc.GetAllPrototypes(doc);
+ const ids: { [key: string]: string } = {};
+ const protos = Doc.GetAllPrototypes(doc);
for (const proto of protos) {
Object.keys(proto).forEach(key => {
if (!(key in ids) && realDoc[key] !== ComputedField.undefined) {
@@ -122,10 +122,10 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
});
}
- let rows: JSX.Element[] = [];
+ const rows: JSX.Element[] = [];
let i = 0;
const self = this;
- for (let key of Object.keys(ids).slice().sort()) {
+ for (const key of Object.keys(ids).slice().sort()) {
rows.push(<KeyValuePair doc={realDoc} addDocTab={this.props.addDocTab} PanelWidth={this.props.PanelWidth} PanelHeight={this.rowHeight}
ref={(function () {
let oldEl: KeyValuePair | undefined;
@@ -163,7 +163,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
@action
onDividerMove = (e: PointerEvent): void => {
- let nativeWidth = this._mainCont.current!.getBoundingClientRect();
+ const nativeWidth = this._mainCont.current!.getBoundingClientRect();
this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100));
}
@action
@@ -179,10 +179,10 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
}
getTemplate = async () => {
- let parent = Docs.Create.StackingDocument([], { width: 800, height: 800, title: "Template" });
+ const parent = Docs.Create.StackingDocument([], { width: 800, height: 800, title: "Template" });
parent.singleColumn = false;
parent.columnWidth = 100;
- for (let row of this.rows.filter(row => row.isChecked)) {
+ for (const row of this.rows.filter(row => row.isChecked)) {
await this.createTemplateField(parent, row);
row.uncheck();
}
@@ -190,17 +190,17 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
}
createTemplateField = async (parentStackingDoc: Doc, row: KeyValuePair) => {
- let metaKey = row.props.keyName;
- let sourceDoc = await Cast(this.props.Document.data, Doc);
+ const metaKey = row.props.keyName;
+ const sourceDoc = await Cast(this.props.Document.data, Doc);
if (!sourceDoc) {
return;
}
- let fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey);
+ const fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey);
if (!fieldTemplate) {
return;
}
- let previousViewType = fieldTemplate.viewType;
+ const previousViewType = fieldTemplate.viewType;
Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(parentStackingDoc));
previousViewType && (fieldTemplate.viewType = previousViewType);
@@ -208,14 +208,14 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
}
inferType = async (data: FieldResult, metaKey: string) => {
- let options = { width: 300, height: 300, title: metaKey };
+ const options = { width: 300, height: 300, title: metaKey };
if (data instanceof RichTextField || typeof data === "string" || typeof data === "number") {
return Docs.Create.TextDocument(options);
} else if (data instanceof List) {
if (data.length === 0) {
return Docs.Create.StackingDocument([], options);
}
- let first = await Cast(data[0], Doc);
+ const first = await Cast(data[0], Doc);
if (!first || !first.data) {
return Docs.Create.StackingDocument([], options);
}
@@ -235,7 +235,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
}
render() {
- let dividerDragger = this.splitPercentage === 0 ? (null) :
+ const dividerDragger = this.splitPercentage === 0 ? (null) :
<div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}>
<div className="keyValueBox-dividerDraggerThumb" onPointerDown={this.onDividerDown} />
</div>;
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 225565964..91f8bb3b0 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -5,7 +5,6 @@ import { emptyFunction, returnFalse, returnOne, returnZero } from '../../../Util
import { Docs } from '../../documents/Documents';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
-import { CollectionDockingView } from '../collections/CollectionDockingView';
import { ContextMenu } from '../ContextMenu';
import { EditableView } from "../EditableView";
import { FieldView, FieldViewProps } from './FieldView';
@@ -53,9 +52,10 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
}
render() {
- let props: FieldViewProps = {
+ const props: FieldViewProps = {
Document: this.props.doc,
DataDoc: this.props.doc,
+ LibraryPath: [],
ContainingCollectionView: undefined,
ContainingCollectionDoc: undefined,
ruleProvider: undefined,
@@ -73,7 +73,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
pinToPres: returnZero,
ContentScaling: returnOne
};
- let contents = <FieldView {...props} />;
+ const contents = <FieldView {...props} />;
// let fieldKey = Object.keys(props.Document).indexOf(props.fieldKey) !== -1 ? props.fieldKey : "(" + props.fieldKey + ")";
let protoCount = 0;
let doc: Doc | undefined = props.Document;
@@ -85,9 +85,9 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
doc = doc.proto;
}
const parenCount = Math.max(0, protoCount - 1);
- let keyStyle = protoCount === 0 ? "black" : "blue";
+ const keyStyle = protoCount === 0 ? "black" : "blue";
- let hover = { transition: "0.3s ease opacity", opacity: this.isPointerOver || this.isChecked ? 1 : 0 };
+ const hover = { transition: "0.3s ease opacity", opacity: this.isPointerOver || this.isChecked ? 1 : 0 };
return (
<tr className={this.props.rowStyle} onPointerEnter={action(() => this.isPointerOver = true)} onPointerLeave={action(() => this.isPointerOver = false)}>
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 46af63a7d..c7d6f988c 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -1,35 +1,163 @@
-.pdfBox-ui {
- position: absolute;
- width: 100%;
- height:100%;
- z-index: 1;
- pointer-events: none;
-}
-
-.pdfBox-cont,
-.pdfBox-cont-interactive {
+.pdfBox,
+.pdfBox-interactive {
display: inline-block;
- flex-direction: row;
+ position: absolute;
height: 100%;
- width:100%;
+ width: 100%;
overflow: hidden;
- position:absolute;
cursor:auto;
transform-origin: top left;
z-index: 0;
-}
-
-.pdfBox-title-outer {
- z-index: 0;
- position: absolute;
- width: 100%;
- height: 100%;
- background: lightslategray;
- .pdfBox-cont, .pdfBox-cont-interactive{
+ .pdfBox-ui {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ z-index: 1;
+ pointer-events: none;
+
+ .pdfBox-overlayButton {
+ border-bottom-left-radius: 50%;
+ display: flex;
+ justify-content: space-evenly;
+ align-items: center;
+ height: 20px;
+ background: none;
+ padding: 0;
+ position: absolute;
+ pointer-events: all;
+
+ .pdfBox-overlayButton-arrow {
+ width: 0;
+ height: 0;
+ border-top: 10px solid transparent;
+ border-bottom: 10px solid transparent;
+ border-right: 15px solid #121721;
+ transition: all 0.5s;
+ }
+
+ .pdfBox-overlayButton-iconCont {
+ background: #121721;
+ height: 20px;
+ width: 25px;
+ display: flex;
+ position: relative;
+ align-items: center;
+ justify-content: center;
+ border-radius: 3px;
+ pointer-events: all;
+ }
+ }
+
+ .pdfBox-nextIcon,
+ .pdfBox-prevIcon {
+ background: #121721;
+ height: 20px;
+ width: 25px;
+ display: flex;
+ position: relative;
+ align-items: center;
+ justify-content: center;
+ border-radius: 3px;
+ pointer-events: all;
+ padding: 0px;
+ }
+
+ .pdfBox-overlayButton:hover {
+ background: none;
+ }
+
+
+ .pdfBox-settingsCont {
+ position: absolute;
+ right: 0;
+ top: 3;
+ pointer-events: all;
+
+ .pdfBox-settingsButton {
+ border-bottom-left-radius: 50%;
+ display: flex;
+ justify-content: space-evenly;
+ align-items: center;
+ height: 20px;
+ background: none;
+ padding: 0;
+
+ .pdfBox-settingsButton-arrow {
+ width: 0;
+ height: 0;
+ border-top: 10px solid transparent;
+ border-bottom: 10px solid transparent;
+ border-right: 15px solid #121721;
+ transition: all 0.5s;
+ }
+
+ .pdfBox-settingsButton-iconCont {
+ background: #121721;
+ height: 20px;
+ width: 25px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-left: -2px;
+ border-radius: 3px;
+ }
+ }
+
+ .pdfBox-settingsButton:hover {
+ background: none;
+ }
+
+ .pdfBox-settingsFlyout {
+ position: absolute;
+ background: #323232;
+ box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
+ right: 20px;
+ border-radius: 7px;
+ padding: 20px;
+ display: flex;
+ flex-direction: column;
+ font-size: 14px;
+ transition: all 0.5s;
+
+ .pdfBox-settingsFlyout-title {
+ color: white;
+ }
+
+ .pdfBox-settingsFlyout-kvpInput {
+ margin-top: 20px;
+ display: grid;
+ grid-template-columns: 47.5% 5% 47.5%;
+ }
+ }
+ }
+
+ .pdfBox-overlayCont {
+ position: absolute;
+ width: calc(100% - 40px);
+ height: 20px;
+ background: #121721;
+ bottom: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
+ transition: left .5s;
+ pointer-events: all;
+
+ .pdfBox-searchBar {
+ width: 70%;
+ font-size: 14px;
+ }
+ }
+ }
+ .pdfBox-title-outer {
width: 150%;
height: 100%;
position: relative;
display: grid;
+ z-index: 0;
+ background: lightslategray;
+ transform-origin: top left;
.pdfBox-title {
color:lightgray;
@@ -38,7 +166,7 @@
transform-origin: 42% 15%;
width: 100%;
transform: rotate(55deg);
- font-size: 72;
+ font-size: 200;
padding: 5%;
overflow: hidden;
display: inline-block;
@@ -49,8 +177,7 @@
}
}
-
-.pdfBox-cont {
+.pdfBox {
pointer-events: none;
.collectionFreeFormView-none {
pointer-events: none;
@@ -64,7 +191,7 @@
}
}
-.pdfBox-cont-interactive {
+.pdfBox-interactive {
pointer-events: all;
.pdfViewer-text {
.textLayer {
@@ -73,129 +200,4 @@
}
}
}
-}
-
-
-.pdfBox-settingsCont {
- position: absolute;
- right: 0;
- top: 3;
- pointer-events: all;
-
- .pdfBox-settingsButton {
- border-bottom-left-radius: 50%;
- display: flex;
- justify-content: space-evenly;
- align-items: center;
- height: 30px;
- background: none;
- padding: 0;
-
- .pdfBox-settingsButton-arrow {
- width: 0;
- height: 0;
- border-top: 15px solid transparent;
- border-bottom: 15px solid transparent;
- border-right: 15px solid #121721;
- transition: all 0.5s;
- }
-
- .pdfBox-settingsButton-iconCont {
- background: #121721;
- height: 30px;
- width: 70px;
- display: flex;
- justify-content: center;
- align-items: center;
- margin-left: -2px;
- border-radius: 3px;
- }
- }
-
- .pdfBox-settingsButton:hover {
- background: none;
- }
-
- .pdfBox-settingsFlyout {
- position: absolute;
- background: #323232;
- box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
- right: 20px;
- border-radius: 7px;
- padding: 20px;
- display: flex;
- flex-direction: column;
- font-size: 14px;
- transition: all 0.5s;
-
- .pdfBox-settingsFlyout-title {
- color: white;
- }
-
- .pdfBox-settingsFlyout-kvpInput {
- margin-top: 20px;
- display: grid;
- grid-template-columns: 47.5% 5% 47.5%;
- }
- }
-}
-
-.pdfBox-overlayCont {
- position: absolute;
- width: calc(100% - 40px);
- height: 30px;
- background: #121721;
- bottom: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- overflow: hidden;
- transition: left .5s;
- pointer-events: all;
-
- .pdfBox-searchBar {
- width: 70%;
- font-size: 14px;
- }
-}
-
-.pdfBox-overlayButton {
- border-bottom-left-radius: 50%;
- display: flex;
- justify-content: space-evenly;
- align-items: center;
- height: 30px;
- background: none;
- padding: 0;
- position: absolute;
- pointer-events: all;
-
- .pdfBox-overlayButton-arrow {
- width: 0;
- height: 0;
- border-top: 15px solid transparent;
- border-bottom: 15px solid transparent;
- border-right: 15px solid #121721;
- transition: all 0.5s;
- }
-}
-
-.pdfBox-overlayButton-iconCont,
-.pdfBox-nextIcon,
-.pdfBox-prevIcon {
- padding: 0;
- background: #121721;
- height: 30px;
- width: 25px;
- display: inline-block;
- position: relative;
- justify-content: center;
- align-items: center;
- margin-left: -2px;
- border-radius: 3px;
- pointer-events: all;
-}
-
-.pdfBox-overlayButton:hover {
- background: none;
-}
+} \ No newline at end of file
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 23512543a..8370df6ba 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -3,11 +3,11 @@ import { action, observable, runInAction, reaction, IReactionDisposer, trace, un
import { observer } from "mobx-react";
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
-import { Opt, WidthSym, Doc } from "../../../new_fields/Doc";
+import { Opt, WidthSym, Doc, HeightSym } from "../../../new_fields/Doc";
import { makeInterface } from "../../../new_fields/Schema";
import { ScriptField } from '../../../new_fields/ScriptField';
-import { Cast } from "../../../new_fields/Types";
-import { PdfField } from "../../../new_fields/URLField";
+import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { PdfField, URLField } from "../../../new_fields/URLField";
import { Utils } from '../../../Utils';
import { KeyCodes } from '../../northstar/utils/KeyCodes';
import { undoBatch } from '../../util/UndoManager';
@@ -21,6 +21,7 @@ import { pageSchema } from "./ImageBox";
import "./PDFBox.scss";
import React = require("react");
import { documentSchema } from '../../../new_fields/documentSchemas';
+import { url } from 'inspector';
type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@@ -49,16 +50,38 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
constructor(props: any) {
super(props);
this._initialScale = this.props.ScreenToLocalTransform().Scale;
+ const nw = this.Document.nativeWidth = NumCast(this.extensionDocSync.nativeWidth, NumCast(this.Document.nativeWidth, 927));
+ const nh = this.Document.nativeHeight = NumCast(this.extensionDocSync.nativeHeight, NumCast(this.Document.nativeHeight, 1200));
+ !this.Document.fitWidth && !this.Document.ignoreAspect && (this.Document.height = this.Document[WidthSym]() * (nh / nw));
+
+ const backup = "oldPath";
+ const { Document } = this.props;
+ const { url: { href } } = Cast(Document[this.props.fieldKey], PdfField)!;
+ const pathCorrectionTest = /upload\_[a-z0-9]{32}.(.*)/g;
+ const matches = pathCorrectionTest.exec(href);
+ console.log("\nHere's the { url } being fed into the outer regex:");
+ console.log(href);
+ console.log("And here's the 'properPath' build from the captured filename:\n");
+ if (matches !== null && href.startsWith(window.location.origin)) {
+ const properPath = Utils.prepend(`/files/pdfs/${matches[0]}`);
+ console.log(properPath);
+ if (!properPath.includes(href)) {
+ console.log(`The two (url and proper path) were not equal`);
+ const proto = Doc.GetProto(Document);
+ proto[this.props.fieldKey] = new PdfField(properPath);
+ proto[backup] = href;
+ } else {
+ console.log(`The two (url and proper path) were equal`);
+ }
+ } else {
+ console.log("Outer matches was null!");
+ }
}
componentWillUnmount() {
this._selectReactionDisposer && this._selectReactionDisposer();
}
componentDidMount() {
- const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
- if (pdfUrl instanceof PdfField) {
- Pdfjs.getDocument(pdfUrl.url.pathname).promise.then(pdf => runInAction(() => this._pdf = pdf));
- }
this._selectReactionDisposer = reaction(() => this.props.isSelected(),
() => {
document.removeEventListener("keydown", this.onKeyDown);
@@ -67,9 +90,9 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
}
loaded = (nw: number, nh: number, np: number) => {
- this.dataDoc.numPages = np;
- this.Document.nativeWidth = nw * 96 / 72;
- this.Document.nativeHeight = nh * 96 / 72;
+ this.extensionDocSync.numPages = np;
+ this.extensionDocSync.nativeWidth = this.Document.nativeWidth = nw * 96 / 72;
+ this.extensionDocSync.nativeHeight = this.Document.nativeHeight = nh * 96 / 72;
!this.Document.fitWidth && !this.Document.ignoreAspect && (this.Document.height = this.Document[WidthSym]() * (nh / nw));
}
@@ -95,7 +118,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
@undoBatch
@action
private applyFilter = () => {
- let scriptText = this._scriptValue ? this._scriptValue :
+ const scriptText = this._scriptValue ? this._scriptValue :
this._keyValue && this._valueValue ? `this.${this._keyValue} === ${this._valueValue}` : "true";
this.props.Document.filterScript = ScriptField.MakeFunction(scriptText);
}
@@ -116,7 +139,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value;
settingsPanel() {
- let pageBtns = <>
+ const pageBtns = <>
<button className="pdfBox-overlayButton-iconCont" key="back" title="Page Back"
onPointerDown={e => e.stopPropagation()} onClick={e => this.backPage()} style={{ left: 45, top: 5 }}>
<FontAwesomeIcon style={{ color: "white" }} icon={"arrow-left"} size="sm" />
@@ -135,27 +158,27 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
<button title="Search" onClick={e => this.search(this._searchString, !e.shiftKey)}>
<FontAwesomeIcon icon="search" size="sm" color="white" /></button>
<button className="pdfBox-prevIcon " title="Previous Annotation" onClick={this.prevAnnotation} >
- <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-up"} size="sm" />
+ <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-up"} size="lg" />
</button>
<button className="pdfBox-nextIcon" title="Next Annotation" onClick={this.nextAnnotation} >
- <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-down"} size="sm" />
+ <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-down"} size="lg" />
</button>
</div>
<button className="pdfBox-overlayButton" key="search" onClick={action(() => this._searching = !this._searching)} title="Open Search Bar" style={{ bottom: 0, right: 0 }}>
<div className="pdfBox-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()}></div>
<div className="pdfBox-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
- <FontAwesomeIcon style={{ color: "white", padding: 5 }} icon={this._searching ? "times" : "search"} size="3x" /></div>
+ <FontAwesomeIcon style={{ color: "white" }} icon={this._searching ? "times" : "search"} size="lg" /></div>
</button>
<input value={`${(this.Document.curPage || 1)}`}
onChange={e => this.gotoPage(Number(e.currentTarget.value))}
- style={{ left: 5, top: 5, height: "30px", width: "30px", position: "absolute", pointerEvents: "all" }}
+ style={{ left: 5, top: 5, height: "20px", width: "20px", position: "absolute", pointerEvents: "all" }}
onClick={action(() => this._pageControls = !this._pageControls)} />
{this._pageControls ? pageBtns : (null)}
<div className="pdfBox-settingsCont" key="settings" onPointerDown={(e) => e.stopPropagation()}>
<button className="pdfBox-settingsButton" onClick={action(() => this._flyout = !this._flyout)} title="Open Annotation Settings" >
<div className="pdfBox-settingsButton-arrow" style={{ transform: `scaleX(${this._flyout ? -1 : 1})` }} />
<div className="pdfBox-settingsButton-iconCont">
- <FontAwesomeIcon style={{ color: "white", padding: 5 }} icon="cog" size="3x" />
+ <FontAwesomeIcon style={{ color: "white" }} icon="cog" size="lg" />
</div>
</button>
<div className="pdfBox-settingsFlyout" style={{ right: `${this._flyout ? 20 : -600}px` }} >
@@ -186,17 +209,22 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
specificContextMenu = (e: React.MouseEvent): void => {
const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
- let funcs: ContextMenuProps[] = [];
+ const funcs: ContextMenuProps[] = [];
pdfUrl && funcs.push({ description: "Copy path", event: () => Utils.CopyText(pdfUrl.url.pathname), icon: "expand-arrows-alt" });
funcs.push({ description: "Toggle Fit Width " + (this.Document.fitWidth ? "Off" : "On"), event: () => this.Document.fitWidth = !this.Document.fitWidth, icon: "expand-arrows-alt" });
ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" });
}
+ @computed get contentScaling() { return this.props.ContentScaling(); }
@computed get renderTitleBox() {
- let classname = "pdfBox-cont" + (this.active() ? "-interactive" : "");
- return <div className="pdfBox-title-outer" >
- <div className={classname} >
+ const classname = "pdfBox" + (this.active() ? "-interactive" : "");
+ return <div className={classname} style={{
+ width: !this.props.Document.fitWidth ? this.Document.nativeWidth || 0 : `${100 / this.contentScaling}%`,
+ height: !this.props.Document.fitWidth ? this.Document.nativeHeight || 0 : `${100 / this.contentScaling}%`,
+ transform: `scale(${this.contentScaling})`
+ }} >
+ <div className="pdfBox-title-outer">
<strong className="pdfBox-title" >{this.props.Document.title}</strong>
</div>
</div>;
@@ -205,7 +233,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
isChildActive = (outsideReaction?: boolean) => this._isChildActive;
@computed get renderPdfView() {
const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
- return <div className={"pdfBox-cont"} onContextMenu={this.specificContextMenu}>
+ return <div className={"pdfBox"} onContextMenu={this.specificContextMenu}>
<PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={this.loaded}
setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
@@ -220,10 +248,19 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
</div>;
}
+ _pdfjsRequested = false;
render() {
const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField, null);
if (this.props.isSelected() || this.props.Document.scrollY !== undefined) this._everActive = true;
- return !pdfUrl || !this._pdf || !this.extensionDoc || (!this._everActive && this.props.ScreenToLocalTransform().Scale > 2.5) ?
- this.renderTitleBox : this.renderPdfView;
+ if (pdfUrl && this.extensionDoc && (this._everActive || (this.extensionDoc.nativeWidth && this.props.ScreenToLocalTransform().Scale < 2.5))) {
+ if (pdfUrl instanceof PdfField && this._pdf) {
+ return this.renderPdfView;
+ }
+ if (!this._pdfjsRequested) {
+ this._pdfjsRequested = true;
+ Pdfjs.getDocument(pdfUrl.url.href).promise.then(pdf => runInAction(() => this._pdf = pdf));
+ }
+ }
+ return this.renderTitleBox;
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index cbb83b511..1e6894f37 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -42,7 +42,7 @@ export class PresBox extends React.Component<FieldViewProps> {
if (value) {
value.forEach((item, i) => {
if (item instanceof Doc && item.type !== DocumentType.PRESELEMENT) {
- let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" });
+ const pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" });
Doc.GetProto(pinDoc).presentationTargetDoc = item;
Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()');
value.splice(i, 1, pinDoc);
@@ -61,9 +61,9 @@ export class PresBox extends React.Component<FieldViewProps> {
next = async () => {
const current = NumCast(this.props.Document.selectedDoc);
//asking to get document at current index
- let docAtCurrentNext = await this.getDocAtIndex(current + 1);
+ const docAtCurrentNext = await this.getDocAtIndex(current + 1);
if (docAtCurrentNext !== undefined) {
- let presDocs = DocListCast(this.props.Document[this.props.fieldKey]);
+ const presDocs = DocListCast(this.props.Document[this.props.fieldKey]);
let nextSelected = current + 1;
for (; nextSelected < presDocs.length - 1; nextSelected++) {
@@ -78,15 +78,15 @@ export class PresBox extends React.Component<FieldViewProps> {
back = async () => {
const current = NumCast(this.props.Document.selectedDoc);
//requesting for the doc at current index
- let docAtCurrent = await this.getDocAtIndex(current);
+ const docAtCurrent = await this.getDocAtIndex(current);
if (docAtCurrent !== undefined) {
//asking for its presentation id.
let prevSelected = current;
let zoomOut: boolean = false;
- let presDocs = await DocListCastAsync(this.props.Document[this.props.fieldKey]);
- let currentsArray: Doc[] = [];
+ const presDocs = await DocListCastAsync(this.props.Document[this.props.fieldKey]);
+ const currentsArray: Doc[] = [];
for (; presDocs && prevSelected > 0 && presDocs[prevSelected].groupButton; prevSelected--) {
currentsArray.push(presDocs[prevSelected]);
}
@@ -104,8 +104,8 @@ export class PresBox extends React.Component<FieldViewProps> {
//If so making sure to zoom out, which goes back to state before zooming action
if (current > 0) {
if (zoomOut || docAtCurrent.showButton) {
- let prevScale = NumCast(this.childDocs[prevSelected].viewScale, null);
- let curScale = DocumentManager.Instance.getScaleOfDocView(this.childDocs[current]);
+ const prevScale = NumCast(this.childDocs[prevSelected].viewScale, null);
+ const curScale = DocumentManager.Instance.getScaleOfDocView(this.childDocs[current]);
if (prevScale !== undefined && prevScale !== curScale) {
DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale);
}
@@ -162,13 +162,13 @@ export class PresBox extends React.Component<FieldViewProps> {
* te option open, navigates to that element.
*/
navigateToElement = async (curDoc: Doc, fromDocIndex: number) => {
- let fromDoc = this.childDocs[fromDocIndex].presentationTargetDoc as Doc;
+ const fromDoc = this.childDocs[fromDocIndex].presentationTargetDoc as Doc;
let docToJump = curDoc;
let willZoom = false;
- let presDocs = DocListCast(this.props.Document[this.props.fieldKey]);
+ const presDocs = DocListCast(this.props.Document[this.props.fieldKey]);
let nextSelected = presDocs.indexOf(curDoc);
- let currentDocGroups: Doc[] = [];
+ const currentDocGroups: Doc[] = [];
for (; nextSelected < presDocs.length - 1; nextSelected++) {
if (!presDocs[nextSelected + 1].groupButton) {
break;
@@ -190,11 +190,11 @@ export class PresBox extends React.Component<FieldViewProps> {
//docToJump stayed same meaning, it was not in the group or was the last element in the group
if (docToJump === curDoc) {
//checking if curDoc has navigation open
- let target = await curDoc.presentationTargetDoc as Doc;
+ const target = await curDoc.presentationTargetDoc as Doc;
if (curDoc.navButton) {
DocumentManager.Instance.jumpToDocument(target, false);
} else if (curDoc.showButton) {
- let curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc);
+ const curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc);
//awaiting jump so that new scale can be found, since jumping is async
await DocumentManager.Instance.jumpToDocument(target, true);
curDoc.viewScale = DocumentManager.Instance.getScaleOfDocView(target);
@@ -207,11 +207,11 @@ export class PresBox extends React.Component<FieldViewProps> {
}
return;
}
- let curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc);
+ const curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc);
//awaiting jump so that new scale can be found, since jumping is async
await DocumentManager.Instance.jumpToDocument(await docToJump.presentationTargetDoc as Doc, willZoom);
- let newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.presentationTargetDoc as Doc);
+ const newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.presentationTargetDoc as Doc);
curDoc.viewScale = newScale;
//saving the scale that user was on
if (curScale !== 1) {
@@ -238,7 +238,7 @@ export class PresBox extends React.Component<FieldViewProps> {
public removeDocument = (doc: Doc) => {
const value = FieldValue(Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)));
if (value) {
- let indexOfDoc = value.indexOf(doc);
+ const indexOfDoc = value.indexOf(doc);
if (indexOfDoc !== - 1) {
value.splice(indexOfDoc, 1)[0];
return true;
@@ -337,13 +337,13 @@ export class PresBox extends React.Component<FieldViewProps> {
@action
initializeScaleViews = (docList: Doc[], viewtype: number) => {
this.props.Document.chromeStatus = "disabled";
- let hgt = (viewtype === CollectionViewType.Tree) ? 50 : 72;
+ const hgt = (viewtype === CollectionViewType.Tree) ? 50 : 72;
docList.forEach((doc: Doc) => {
doc.presBox = this.props.Document;
doc.presBoxKey = this.props.fieldKey;
doc.collapsedHeight = hgt;
doc.height = ComputedField.MakeFunction("this.collapsedHeight + Number(this.embedOpen ? 100:0)");
- let curScale = NumCast(doc.viewScale, null);
+ const curScale = NumCast(doc.viewScale, null);
if (curScale === undefined) {
doc.viewScale = 1;
}
@@ -352,7 +352,7 @@ export class PresBox extends React.Component<FieldViewProps> {
selectElement = (doc: Doc) => {
- let index = DocListCast(this.props.Document[this.props.fieldKey]).indexOf(doc);
+ const index = DocListCast(this.props.Document[this.props.fieldKey]).indexOf(doc);
index !== -1 && this.gotoDocument(index, NumCast(this.props.Document.selectedDoc));
}
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index 0a4c650a8..fabbf5196 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -1,6 +1,9 @@
-.videoBox-container {
+.videoBox {
pointer-events: all;
transform-origin: top left;
+ .videoBox-viewer {
+ opacity: 0.99; // hack! overcomes some kind of Chrome weirdness where buttons (e.g., snapshot) disappear at some point as the video is resized larger
+ }
.inkingCanvas-paths-markers {
opacity : 0.4; // we shouldn't have to do this, but since chrome crawls to a halt with z-index unset in videoBox-content, this is a workaround
}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index bd5bd918f..376d27380 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -9,10 +9,9 @@ import { Doc } from "../../../new_fields/Doc";
import { InkTool } from "../../../new_fields/InkField";
import { createSchema, makeInterface } from "../../../new_fields/Schema";
import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, StrCast } from "../../../new_fields/Types";
+import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
import { VideoField } from "../../../new_fields/URLField";
-import { RouteStore } from "../../../server/RouteStore";
-import { emptyFunction, returnOne, Utils } from "../../../Utils";
+import { Utils, emptyFunction, returnOne } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
import { ContextMenu } from "../ContextMenu";
@@ -23,7 +22,7 @@ import { InkingControl } from "../InkingControl";
import { FieldView, FieldViewProps } from './FieldView';
import "./VideoBox.scss";
import { documentSchema, positionSchema } from "../../../new_fields/documentSchemas";
-var path = require('path');
+const path = require('path');
export const timeSchema = createSchema({
currentTimecode: "number", // the current time of a video or other linear, time-based document. Note, should really get set on an extension field, but that's more complicated when it needs to be set since the extension doc needs to be found first
@@ -55,9 +54,9 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
}
videoLoad = () => {
- let aspect = this.player!.videoWidth / this.player!.videoHeight;
- var nativeWidth = (this.Document.nativeWidth || 0);
- var nativeHeight = (this.Document.nativeHeight || 0);
+ const aspect = this.player!.videoWidth / this.player!.videoHeight;
+ const nativeWidth = (this.Document.nativeWidth || 0);
+ const nativeHeight = (this.Document.nativeHeight || 0);
if (!nativeWidth || !nativeHeight) {
if (!this.Document.nativeWidth) this.Document.nativeWidth = this.player!.videoWidth;
this.Document.nativeHeight = (this.Document.nativeWidth || 0) / aspect;
@@ -102,12 +101,12 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
}
@action public Snapshot() {
- let width = this.Document.width || 0;
- let height = this.Document.height || 0;
- var canvas = document.createElement('canvas');
+ const width = this.Document.width || 0;
+ const height = this.Document.height || 0;
+ const canvas = document.createElement('canvas');
canvas.width = 640;
canvas.height = 640 * (this.Document.nativeHeight || 0) / (this.Document.nativeWidth || 1);
- var ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
+ const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
if (ctx) {
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "blue";
@@ -116,20 +115,20 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
}
if (!this._videoRef) { // can't find a way to take snapshots of videos
- let b = Docs.Create.ButtonDocument({
+ const b = Docs.Create.ButtonDocument({
x: (this.Document.x || 0) + width, y: (this.Document.y || 0),
width: 150, height: 50, title: (this.Document.currentTimecode || 0).toString()
});
b.onClick = ScriptField.MakeScript(`this.currentTimecode = ${(this.Document.currentTimecode || 0)}`);
} else {
//convert to desired file format
- var dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
+ const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
// if you want to preview the captured image,
- let filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.Document.title).replace(/\..*$/, "") + "_" + (this.Document.currentTimecode || 0).toString().replace(/\./, "_")));
+ const filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.Document.title).replace(/\..*$/, "") + "_" + (this.Document.currentTimecode || 0).toString().replace(/\./, "_")));
VideoBox.convertDataUri(dataUrl, filename).then(returnedFilename => {
if (returnedFilename) {
- let url = this.choosePath(Utils.prepend(returnedFilename));
- let imageSummary = Docs.Create.ImageDocument(url, {
+ const url = this.choosePath(Utils.prepend(returnedFilename));
+ const imageSummary = Docs.Create.ImageDocument(url, {
x: (this.Document.x || 0) + width, y: (this.Document.y || 0),
width: 150, height: height / width * 150, title: "--snapshot" + (this.Document.currentTimecode || 0) + " image-"
});
@@ -151,9 +150,9 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
if (this.props.setVideoBox) this.props.setVideoBox(this);
if (this.youtubeVideoId) {
- let youtubeaspect = 400 / 315;
- var nativeWidth = (this.Document.nativeWidth || 0);
- var nativeHeight = (this.Document.nativeHeight || 0);
+ const youtubeaspect = 400 / 315;
+ const nativeWidth = (this.Document.nativeWidth || 0);
+ const nativeHeight = (this.Document.nativeHeight || 0);
if (!nativeWidth || !nativeHeight) {
if (!this.Document.nativeWidth) this.Document.nativeWidth = 600;
this.Document.nativeHeight = (this.Document.nativeWidth || 0) / youtubeaspect;
@@ -182,7 +181,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
public static async convertDataUri(imageUri: string, returnedFilename: string) {
try {
- let posting = Utils.prepend(RouteStore.dataUriToImage);
+ const posting = Utils.prepend("/uploadURI");
const returnedUri = await rp.post(posting, {
body: {
uri: imageUri,
@@ -197,10 +196,10 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
}
}
specificContextMenu = (e: React.MouseEvent): void => {
- let field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
+ const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
if (field) {
- let url = field.url.href;
- let subitems: ContextMenuProps[] = [];
+ const url = field.url.href;
+ const subitems: ContextMenuProps[] = [];
subitems.push({ description: "Copy path", event: () => { Utils.CopyText(url); }, icon: "expand-arrows-alt" });
subitems.push({ description: "Toggle Show Controls", event: action(() => VideoBox._showControls = !VideoBox._showControls), icon: "expand-arrows-alt" });
subitems.push({ description: "Take Snapshot", event: () => this.Snapshot(), icon: "expand-arrows-alt" });
@@ -209,9 +208,9 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
}
@computed get content() {
- let field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
- let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
- let style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
+ const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
+ const interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
+ const style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
return !field ? <div>Loading</div> :
<video className={`${style}`} key="video" ref={this.setVideoRef} onCanPlay={this.videoLoad} controls={VideoBox._showControls}
onPlay={() => this.Play()} onSeeked={this.updateTimecode} onPause={() => this.Pause()} onClick={e => e.preventDefault()}>
@@ -221,7 +220,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
}
@computed get youtubeVideoId() {
- let field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
+ const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
return field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
}
@@ -232,9 +231,9 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
}
else this._youtubeContentCreated = false;
- let iframe = e.target;
+ const iframe = e.target;
let started = true;
- let onYoutubePlayerStateChange = (event: any) => runInAction(() => {
+ const onYoutubePlayerStateChange = (event: any) => runInAction(() => {
if (started && event.data === YT.PlayerState.PLAYING) {
started = false;
this._youtubePlayer && this._youtubePlayer.unMute();
@@ -244,12 +243,12 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
if (event.data === YT.PlayerState.PLAYING && !this._playing) this.Play(false);
if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false);
});
- let onYoutubePlayerReady = (event: any) => {
+ const onYoutubePlayerReady = (event: any) => {
this._reactionDisposer && this._reactionDisposer();
this._youtubeReactionDisposer && this._youtubeReactionDisposer();
this._reactionDisposer = reaction(() => this.Document.currentTimecode, () => !this._playing && this.Seek(this.Document.currentTimecode || 0));
this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => {
- let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected(true) && !DocumentDecorations.Instance.Interacting;
+ const interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected(true) && !DocumentDecorations.Instance.Interacting;
iframe.style.pointerEvents = interactive ? "all" : "none";
}, { fireImmediately: true });
};
@@ -262,20 +261,20 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
}
private get uIButtons() {
- let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
- let curTime = (this.Document.currentTimecode || 0);
- return ([<div className="videoBox-time" key="time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling})` }}>
+ const scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
+ const curTime = (this.Document.currentTimecode || 0);
+ return ([<div className="videoBox-time" key="time" onPointerDown={this.onResetDown} >
<span>{"" + Math.round(curTime)}</span>
<span style={{ fontSize: 8 }}>{" " + Math.round((curTime - Math.trunc(curTime)) * 100)}</span>
</div>,
- <div className="videoBox-snapshot" key="snap" onPointerDown={this.onSnapshot} style={{ transform: `scale(${scaling})` }}>
+ <div className="videoBox-snapshot" key="snap" onPointerDown={this.onSnapshot} >
<FontAwesomeIcon icon="camera" size="lg" />
</div>,
VideoBox._showControls ? (null) : [
- <div className="videoBox-play" key="play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling})` }}>
+ <div className="videoBox-play" key="play" onPointerDown={this.onPlayDown} >
<FontAwesomeIcon icon={this._playing ? "pause" : "play"} size="lg" />
</div>,
- <div className="videoBox-full" key="full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling})` }}>
+ <div className="videoBox-full" key="full" onPointerDown={this.onFullDown} >
F
</div>
]]);
@@ -319,8 +318,8 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
@computed get youtubeContent() {
this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
- let style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
- let start = untracked(() => Math.round(this.Document.currentTimecode || 0));
+ const style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
+ const start = untracked(() => Math.round(this.Document.currentTimecode || 0));
return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
onLoad={this.youtubeIframeLoaded} className={`${style}`} width={(this.Document.nativeWidth || 640)} height={(this.Document.nativeHeight || 390)}
src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=1&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />;
@@ -328,37 +327,39 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
@action.bound
addDocumentWithTimestamp(doc: Doc): boolean {
- var curTime = (this.Document.currentTimecode || -1);
+ const curTime = (this.Document.currentTimecode || -1);
curTime !== -1 && (doc.displayTimecode = curTime);
return this.addDocument(doc);
}
contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
render() {
- return (<div className={"videoBox-container"} onContextMenu={this.specificContextMenu}
+ return (<div className="videoBox" onContextMenu={this.specificContextMenu}
style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
- <CollectionFreeFormView {...this.props}
- PanelHeight={this.props.PanelHeight}
- PanelWidth={this.props.PanelWidth}
- annotationsKey={this.annotationsKey}
- focus={this.props.focus}
- isSelected={this.props.isSelected}
- isAnnotationOverlay={true}
- select={emptyFunction}
- active={this.annotationsActive}
- ContentScaling={returnOne}
- whenActiveChanged={this.whenActiveChanged}
- removeDocument={this.removeDocument}
- moveDocument={this.moveDocument}
- addDocument={this.addDocumentWithTimestamp}
- CollectionView={undefined}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
- ruleProvider={undefined}
- renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- chromeCollapsed={true}>
- {this.contentFunc}
- </CollectionFreeFormView>
+ <div className="videoBox-viewer" >
+ <CollectionFreeFormView {...this.props}
+ PanelHeight={this.props.PanelHeight}
+ PanelWidth={this.props.PanelWidth}
+ annotationsKey={this.annotationsKey}
+ focus={this.props.focus}
+ isSelected={this.props.isSelected}
+ isAnnotationOverlay={true}
+ select={emptyFunction}
+ active={this.annotationsActive}
+ ContentScaling={returnOne}
+ whenActiveChanged={this.whenActiveChanged}
+ removeDocument={this.removeDocument}
+ moveDocument={this.moveDocument}
+ addDocument={this.addDocumentWithTimestamp}
+ CollectionView={undefined}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ ruleProvider={undefined}
+ renderDepth={this.props.renderDepth + 1}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+ chromeCollapsed={true}>
+ {this.contentFunc}
+ </CollectionFreeFormView>
+ </div>
{this.uIButtons}
</div >);
}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 5af743859..b35ea0bb0 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -36,11 +36,11 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
componentWillMount() {
- let field = Cast(this.props.Document[this.props.fieldKey], WebField);
+ const field = Cast(this.props.Document[this.props.fieldKey], WebField);
if (field && field.url.href.indexOf("youtube") !== -1) {
- let youtubeaspect = 400 / 315;
- var nativeWidth = NumCast(this.layoutDoc.nativeWidth);
- var nativeHeight = NumCast(this.layoutDoc.nativeHeight);
+ const youtubeaspect = 400 / 315;
+ const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
+ const nativeHeight = NumCast(this.layoutDoc.nativeHeight);
if (!nativeWidth || !nativeHeight || Math.abs(nativeWidth / nativeHeight - youtubeaspect) > 0.05) {
if (!nativeWidth) this.layoutDoc.nativeWidth = 600;
this.layoutDoc.nativeHeight = NumCast(this.layoutDoc.nativeWidth) / youtubeaspect;
@@ -65,7 +65,7 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
@action
setURL() {
- let urlField: FieldResult<WebField> = Cast(this.props.Document.data, WebField);
+ const urlField: FieldResult<WebField> = Cast(this.props.Document.data, WebField);
if (urlField) this.url = urlField.url.toString();
else this.url = "";
}
@@ -80,10 +80,10 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
switchToText = () => {
let url: string = "";
- let field = Cast(this.props.Document[this.props.fieldKey], WebField);
+ const field = Cast(this.props.Document[this.props.fieldKey], WebField);
if (field) url = field.url.href;
- let newBox = Docs.Create.TextDocument({
+ const newBox = Docs.Create.TextDocument({
x: NumCast(this.props.Document.x),
y: NumCast(this.props.Document.y),
title: url,
@@ -167,7 +167,7 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
@computed
get content() {
- let field = this.dataDoc[this.props.fieldKey];
+ const field = this.dataDoc[this.props.fieldKey];
let view;
if (field instanceof HtmlField) {
view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
@@ -176,15 +176,15 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
} else {
view = <iframe src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
}
- let content =
+ const content =
<div style={{ width: "100%", height: "100%", position: "absolute" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
{this.urlEditor()}
{view}
</div>;
- let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
+ const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
- let classname = "webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+ const classname = "webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
return (
<>
<div className={classname} >
@@ -194,7 +194,7 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
</>);
}
render() {
- return (<div className={"imageBox-container"} >
+ return (<div className={"webBox-container"} >
<CollectionFreeFormView {...this.props}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 936af9ab8..6599c0e3c 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -62,11 +62,11 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
}
deleteAnnotation = () => {
- let annotation = DocListCast(this.props.extensionDoc.annotations);
- let group = FieldValue(Cast(this.props.document.group, Doc));
+ const annotation = DocListCast(this.props.extensionDoc.annotations);
+ const group = FieldValue(Cast(this.props.document.group, Doc));
if (group) {
if (annotation.indexOf(group) !== -1) {
- let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
+ const newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
this.props.extensionDoc.annotations = new List<Doc>(newAnnotations);
}
@@ -77,7 +77,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
}
pinToPres = () => {
- let group = FieldValue(Cast(this.props.document.group, Doc));
+ const group = FieldValue(Cast(this.props.document.group, Doc));
group && this.props.pinToPres(group);
}
@@ -93,7 +93,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
e.stopPropagation();
}
else if (e.button === 0) {
- let annoGroup = await Cast(this.props.document.group, Doc);
+ const annoGroup = await Cast(this.props.document.group, Doc);
if (annoGroup) {
DocumentManager.Instance.FollowLink(undefined, annoGroup,
(doc: Doc, maxLocation: string) => this.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"),
@@ -105,9 +105,9 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
addTag = (key: string, value: string): boolean => {
- let group = FieldValue(Cast(this.props.document.group, Doc));
+ const group = FieldValue(Cast(this.props.document.group, Doc));
if (group) {
- let valNum = parseInt(value);
+ const valNum = parseInt(value);
group[key] = isNaN(valNum) ? value : valNum;
return true;
}
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index c64741769..503696ae9 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -12,19 +12,17 @@ export default class PDFMenu extends AntimodeMenu {
static Instance: PDFMenu;
private _commentCont = React.createRef<HTMLButtonElement>();
- private _snippetButton: React.RefObject<HTMLButtonElement> = React.createRef();
@observable private _keyValue: string = "";
@observable private _valueValue: string = "";
@observable private _added: boolean = false;
@observable public Highlighting: boolean = false;
- @observable public Status: "pdf" | "annotation" | "snippet" | "" = "";
+ @observable public Status: "pdf" | "annotation" | "" = "";
public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
public Highlight: (color: string) => Opt<Doc> = (color: string) => undefined;
public Delete: () => void = unimplementedFunction;
- public Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = unimplementedFunction;
public AddTag: (key: string, value: string) => boolean = returnFalse;
public PinToPres: () => void = unimplementedFunction;
public Marquee: { left: number; top: number; width: number; height: number; } | undefined;
@@ -80,34 +78,6 @@ export default class PDFMenu extends AntimodeMenu {
this.Delete();
}
- snippetStart = (e: React.PointerEvent) => {
- document.removeEventListener("pointermove", this.snippetDrag);
- document.addEventListener("pointermove", this.snippetDrag);
- document.removeEventListener("pointerup", this.snippetEnd);
- document.addEventListener("pointerup", this.snippetEnd);
-
- e.stopPropagation();
- e.preventDefault();
- }
-
- snippetDrag = (e: PointerEvent) => {
- e.stopPropagation();
- e.preventDefault();
- if (!this._dragging) {
- this._dragging = true;
-
- this.Marquee && this.Snippet(this.Marquee);
- }
- }
-
- snippetEnd = (e: PointerEvent) => {
- this._dragging = false;
- document.removeEventListener("pointermove", this.snippetDrag);
- document.removeEventListener("pointerup", this.snippetEnd);
- e.stopPropagation();
- e.preventDefault();
- }
-
@action
keyChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
this._keyValue = e.currentTarget.value;
@@ -128,14 +98,12 @@ export default class PDFMenu extends AntimodeMenu {
}
render() {
- let buttons = this.Status === "pdf" || this.Status === "snippet" ?
+ const buttons = this.Status === "pdf" ?
[
<button key="1" className="antimodeMenu-button" title="Click to Highlight" onClick={this.highlightClicked} style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
<FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} /></button>,
<button key="2" className="antimodeMenu-button" title="Drag to Annotate" ref={this._commentCont} onPointerDown={this.pointerDown}>
<FontAwesomeIcon icon="comment-alt" size="lg" /></button>,
- <button key="3" className="antimodeMenu-button" title="Drag to Snippetize Selection" style={{ display: this.Status === "snippet" ? "" : "none" }} onPointerDown={this.snippetStart} ref={this._snippetButton}>
- <FontAwesomeIcon icon="cut" size="lg" /></button>,
<button key="4" className="antimodeMenu-button" title="Pin Menu" onClick={this.togglePin} style={this.Pinned ? { backgroundColor: "#121212" } : {}}>
<FontAwesomeIcon icon="thumbtack" size="lg" style={{ transition: "transform 0.1s", transform: this.Pinned ? "rotate(45deg)" : "" }} /> </button>
] : [
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index ac018aa0e..4f81c6f70 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -1,5 +1,5 @@
-.pdfViewer-viewer, .pdfViewer-viewer-zoomed {
+.pdfViewer, .pdfViewer-zoomed {
pointer-events: all;
width: 100%;
height: 100%;
@@ -91,7 +91,7 @@
z-index: 10;
}
}
-.pdfViewer-viewer-zoomed {
+.pdfViewer-zoomed {
overflow-x: scroll;
}
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index f1c500391..62467ce4d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -30,6 +30,7 @@ import { DocumentDecorations } from "../DocumentDecorations";
import { InkingControl } from "../InkingControl";
import { InkTool } from "../../../new_fields/InkField";
import { TraceMobx } from "../../../new_fields/util";
+import { PdfField } from "../../../new_fields/URLField";
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
const pdfjsLib = require("pdfjs-dist");
@@ -39,7 +40,7 @@ export const pageSchema = createSchema({
rotation: "number",
scrollY: "number",
scrollHeight: "number",
- search_string: "string"
+ serachMatch: "boolean"
});
pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
@@ -125,13 +126,16 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
!this.props.Document.lockedTransform && (this.props.Document.lockedTransform = true);
// change the address to be the file address of the PNG version of each page
// file address of the pdf
- this._coverPath = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${(this.Document.curPage || 1)}.PNG`)));
+ const { url: { href } } = Cast(this.props.Document[this.props.fieldKey], PdfField)!;
+ this._coverPath = href.startsWith(window.location.origin) ?
+ JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/pdfs/".length, this.props.url.length - ".pdf".length)}-${(this.Document.curPage || 1)}.png`))) :
+ { width: 100, height: 100, path: "" };
runInAction(() => this._showWaiting = this._showCover = true);
this.props.startupLive && this.setupPdfJsViewer();
- this._searchReactionDisposer = reaction(() => this.Document.search_string, searchString => {
- if (searchString) {
- this.search(searchString, true);
- this._lastSearch = searchString;
+ this._searchReactionDisposer = reaction(() => this.Document.searchMatch, search => {
+ if (search) {
+ this.search(Doc.SearchQuery(), true);
+ this._lastSearch = Doc.SearchQuery();
}
else {
setTimeout(() => this._lastSearch === "mxytzlaf" && this.search("mxytzlaf", true), 200); // bcz: how do we clear search highlights?
@@ -168,7 +172,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
copy = (e: ClipboardEvent) => {
if (this.props.active(true) && e.clipboardData) {
- let annoDoc = this.makeAnnotationDocument("rgba(3,144,152,0.3)"); // copied text markup color (blueish)
+ const annoDoc = this.makeAnnotationDocument("rgba(3,144,152,0.3)"); // copied text markup color (blueish)
if (annoDoc) {
e.clipboardData.setData("text/plain", this._selectionText);
e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]);
@@ -211,7 +215,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
this._filterReactionDisposer = reaction(
() => ({ scriptField: Cast(this.Document.filterScript, ScriptField), annos: this._annotations.slice() }),
action(({ scriptField, annos }: { scriptField: FieldResult<ScriptField>, annos: Doc[] }) => {
- let oldScript = this._script.originalScript;
+ const oldScript = this._script.originalScript;
this._script = scriptField && scriptField.script.compiled ? scriptField.script : CompileScript("return true") as CompiledScript;
if (this._script.originalScript !== oldScript) {
this.Index = -1;
@@ -239,8 +243,8 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
this.gotoPage(this.Document.curPage || 1);
}));
document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false));
- var pdfLinkService = new PDFJSViewer.PDFLinkService();
- let pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService });
+ const pdfLinkService = new PDFJSViewer.PDFLinkService();
+ const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService });
this._pdfViewer = new PDFJSViewer.PDFViewer({
container: this._mainCont.current,
viewer: this._viewer.current,
@@ -259,12 +263,12 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
if (this._savedAnnotations.size() === 0) return undefined;
let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {});
let mainAnnoDocProto = Doc.GetProto(mainAnnoDoc);
- let annoDocs: Doc[] = [];
+ const annoDocs: Doc[] = [];
let maxX = -Number.MAX_VALUE;
let minY = Number.MAX_VALUE;
if ((this._savedAnnotations.values()[0][0] as any).marqueeing) {
- let anno = this._savedAnnotations.values()[0][0];
- let annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, title: "Annotation on " + this.Document.title });
+ const anno = this._savedAnnotations.values()[0][0];
+ const annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, title: "Annotation on " + this.Document.title });
if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
if (anno.style.height) annoDoc.height = parseInt(anno.style.height);
@@ -279,7 +283,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
mainAnnoDocProto.y = annoDoc.y;
} else {
this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => value.map(anno => {
- let annoDoc = new Doc();
+ const annoDoc = new Doc();
if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
if (anno.style.height) annoDoc.height = parseInt(anno.style.height);
@@ -323,7 +327,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
@action
scrollToAnnotation = (scrollToAnnotation: Doc) => {
if (scrollToAnnotation) {
- let offset = this.visibleHeight() / 2 * 96 / 72;
+ const offset = this.visibleHeight() / 2 * 96 / 72;
this._mainCont.current && smoothScroll(500, this._mainCont.current, NumCast(scrollToAnnotation.y) - offset);
Doc.linkFollowHighlight(scrollToAnnotation);
}
@@ -355,7 +359,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
this._annotationLayer.current.append(div);
div.style.backgroundColor = "yellow";
div.style.opacity = "0.5";
- let savedPage = this._savedAnnotations.getValue(page);
+ const savedPage = this._savedAnnotations.getValue(page);
if (savedPage) {
savedPage.push(div);
this._savedAnnotations.setValue(page, savedPage);
@@ -381,7 +385,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
});
}
else if (this._mainCont.current) {
- let executeFind = () => {
+ const executeFind = () => {
this._pdfViewer.findController.executeCommand('find', {
caseSensitive: false,
findPrevious: !fwd,
@@ -397,7 +401,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
@action
onPointerDown = (e: React.PointerEvent): void => {
- let hit = document.elementFromPoint(e.clientX, e.clientY);
+ const hit = document.elementFromPoint(e.clientX, e.clientY);
if (hit && hit.localName === "span" && this.props.isSelected(true)) { // drag selecting text stops propagation
e.button === 0 && e.stopPropagation();
}
@@ -408,13 +412,13 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
if ((this.Document.scale || 1) !== 1) return;
if ((e.button !== 0 || e.altKey) && this.active(true)) {
this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true);
+ //e.stopPropagation();
}
this._marqueeing = false;
if (!e.altKey && e.button === 0 && this.active(true)) {
// clear out old marquees and initialize menu for new selection
PDFMenu.Instance.StartDrag = this.startDrag;
PDFMenu.Instance.Highlight = this.highlight;
- PDFMenu.Instance.Snippet = this.createSnippet;
PDFMenu.Instance.Status = "pdf";
PDFMenu.Instance.fadeOut(true);
this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
@@ -424,7 +428,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
}
else if (this._mainCont.current) {
// set marquee x and y positions to the spatially transformed position
- let boundingRect = this._mainCont.current.getBoundingClientRect();
+ const boundingRect = this._mainCont.current.getBoundingClientRect();
this._startX = this._marqueeX = (e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width);
this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height) + this._mainCont.current.scrollTop;
this._marqueeHeight = this._marqueeWidth = 0;
@@ -441,7 +445,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
onSelectMove = (e: PointerEvent): void => {
if (this._marqueeing && this._mainCont.current) {
// transform positions and find the width and height to set the marquee to
- let boundingRect = this._mainCont.current.getBoundingClientRect();
+ const boundingRect = this._mainCont.current.getBoundingClientRect();
this._marqueeWidth = ((e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width)) - this._startX;
this._marqueeHeight = ((e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height)) - this._startY + this._mainCont.current.scrollTop;
this._marqueeX = Math.min(this._startX, this._startX + this._marqueeWidth);
@@ -459,16 +463,16 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
@action
createTextAnnotation = (sel: Selection, selRange: Range) => {
if (this._mainCont.current) {
- let boundingRect = this._mainCont.current.getBoundingClientRect();
- let clientRects = selRange.getClientRects();
+ const boundingRect = this._mainCont.current.getBoundingClientRect();
+ const clientRects = selRange.getClientRects();
for (let i = 0; i < clientRects.length; i++) {
- let rect = clientRects.item(i);
+ const rect = clientRects.item(i);
if (rect) {
- let scaleY = this._mainCont.current.offsetHeight / boundingRect.height;
- let scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
+ const scaleY = this._mainCont.current.offsetHeight / boundingRect.height;
+ const scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
if (rect.width !== this._mainCont.current.clientWidth &&
(i === 0 || !intersectRect(clientRects[i], clientRects[i - 1]))) {
- let annoBox = document.createElement("div");
+ const annoBox = document.createElement("div");
annoBox.className = "pdfViewer-annotationBox";
// transforms the positions from screen onto the pdf div
annoBox.style.top = ((rect.top - boundingRect.top) * scaleY / this._zoomed + this._mainCont.current.scrollTop).toString();
@@ -496,10 +500,10 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
this._savedAnnotations.clear();
if (this._marqueeing) {
if (this._marqueeWidth > 10 || this._marqueeHeight > 10) {
- let marquees = this._mainCont.current!.getElementsByClassName("pdfViewer-dragAnnotationBox");
+ const marquees = this._mainCont.current!.getElementsByClassName("pdfViewer-dragAnnotationBox");
if (marquees && marquees.length) { // copy the marquee and convert it to a permanent annotation.
- let style = (marquees[0] as HTMLDivElement).style;
- let copy = document.createElement("div");
+ const style = (marquees[0] as HTMLDivElement).style;
+ const copy = document.createElement("div");
copy.style.left = style.left;
copy.style.top = style.top;
copy.style.width = style.width;
@@ -512,7 +516,6 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
}
if (!e.ctrlKey) {
- PDFMenu.Instance.Status = "snippet";
PDFMenu.Instance.Marquee = { left: this._marqueeX, top: this._marqueeY, width: this._marqueeWidth, height: this._marqueeHeight };
}
PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
@@ -520,9 +523,9 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
this._marqueeing = false;
}
else {
- let sel = window.getSelection();
+ const sel = window.getSelection();
if (sel && sel.type === "Range") {
- let selRange = sel.getRangeAt(0);
+ const selRange = sel.getRangeAt(0);
this.createTextAnnotation(sel, selRange);
PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
}
@@ -542,7 +545,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
@action
highlight = (color: string) => {
// creates annotation documents for current highlights
- let annotationDoc = this.makeAnnotationDocument(color);
+ const annotationDoc = this.makeAnnotationDocument(color);
annotationDoc && this.props.addDocument && this.props.addDocument(annotationDoc);
return annotationDoc;
}
@@ -555,24 +558,19 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
startDrag = (e: PointerEvent, ele: HTMLElement): void => {
e.preventDefault();
e.stopPropagation();
- let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "Note linked to " + this.props.Document.title });
+ const targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "Note linked to " + this.props.Document.title });
const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)"); // yellowish highlight color when dragging out a text selection
if (annotationDoc) {
- let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc);
- DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, {
- handlers: {
- dragComplete: () => !(dragData as any).linkedToDoc &&
- DocUtils.MakeLink({ doc: annotationDoc }, { doc: dragData.dropDocument, ctx: dragData.targetContext }, `Annotation from ${this.Document.title}`, "link from PDF")
-
- },
- hideSource: false
+ DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, {
+ dragComplete: e => !e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc &&
+ DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument, ctx: e.annoDragData.targetContext }, `Annotation from ${this.Document.title}`, "link from PDF")
});
}
}
createSnippet = (marquee: { left: number, top: number, width: number, height: number }): void => {
- let view = Doc.MakeAlias(this.props.Document);
- let data = Doc.MakeDelegate(Doc.GetProto(this.props.Document));
+ const view = Doc.MakeAlias(this.props.Document);
+ const data = Doc.MakeDelegate(Doc.GetProto(this.props.Document));
data.title = StrCast(data.title) + "_snippet";
view.proto = data;
view.nativeHeight = marquee.height;
@@ -601,12 +599,13 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
if (!this.props.Document[HeightSym]() || !this.props.Document.nativeHeight) {
setTimeout((() => {
this.Document.height = this.Document[WidthSym]() * this._coverPath.height / this._coverPath.width;
- this.Document.nativeHeight = nativeWidth * this._coverPath.height / this._coverPath.width;
+ this.Document.nativeHeight = (this.Document.nativeWidth || 0) * this._coverPath.height / this._coverPath.width;
}).bind(this), 0);
}
- let nativeWidth = (this.Document.nativeWidth || 0);
- let nativeHeight = (this.Document.nativeHeight || 0);
- return <img key={this._coverPath.path} src={this._coverPath.path} onError={action(() => this._coverPath.path = "http://www.cs.brown.edu/~bcz/face.gif")} onLoad={action(() => this._showWaiting = false)}
+ const nativeWidth = (this.Document.nativeWidth || 0);
+ const nativeHeight = (this.Document.nativeHeight || 0);
+ const resolved = Utils.prepend(this._coverPath.path);
+ return <img key={resolved} src={resolved} onError={action(() => this._coverPath.path = "http://www.cs.brown.edu/~bcz/face.gif")} onLoad={action(() => this._showWaiting = false)}
style={{ position: "absolute", display: "inline-block", top: 0, left: 0, width: `${nativeWidth}px`, height: `${nativeHeight}px` }} />;
}
@@ -614,7 +613,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
onZoomWheel = (e: React.WheelEvent) => {
e.stopPropagation();
if (e.ctrlKey) {
- let curScale = Number(this._pdfViewer.currentScaleValue);
+ const curScale = Number(this._pdfViewer.currentScaleValue);
this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - curScale * e.deltaY / 1000));
this._zoomed = Number(this._pdfViewer.currentScaleValue);
}
@@ -633,6 +632,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
@computed get overlayLayer() {
return <div className={`pdfViewer-overlay${InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : ""}`} id="overlay" style={{ transform: `scale(${this._zoomed})` }}>
<CollectionFreeFormView {...this.props}
+ LibraryPath={this.props.ContainingCollectionView?.props.LibraryPath ?? []}
annotationsKey={this.annotationsKey}
setPreviewCursor={this.setPreviewCursor}
PanelHeight={this.panelWidth}
@@ -660,6 +660,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
@computed get pdfViewerDiv() {
return <div className={"pdfViewer-text" + ((!DocumentDecorations.Instance.Interacting && (this.props.isSelected() || this.props.isChildActive())) ? "-selected" : "")} ref={this._viewer} />;
}
+ @computed get contentScaling() { return this.props.ContentScaling(); }
@computed get standinViews() {
return <>
{this._showCover ? this.getCoverImage() : (null)}
@@ -673,16 +674,16 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
marqueeing = () => this._marqueeing;
visibleHeight = () => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96;
contentZoom = () => this._zoomed;
- @computed get contentScaling() { return this.props.ContentScaling(); }
render() {
TraceMobx();
return !this.extensionDoc ? (null) :
- <div className={"pdfViewer-viewer" + (this._zoomed !== 1 ? "-zoomed" : "")} ref={this._mainCont}
+ <div className={"pdfViewer" + (this._zoomed !== 1 ? "-zoomed" : "")} ref={this._mainCont}
+ onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick}
style={{
width: !this.props.Document.fitWidth ? NumCast(this.props.Document.nativeWidth) : `${100 / this.contentScaling}%`,
height: !this.props.Document.fitWidth ? NumCast(this.props.Document.nativeHeight) : `${100 / this.contentScaling}%`,
- transform: `scale(${this.contentScaling})`
- }} onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick}>
+ transform: `scale(${this.props.ContentScaling()})`
+ }} >
{this.pdfViewerDiv}
{this.overlayLayer}
{this.annotationLayer}
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index f50a3a0ef..37c837414 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -9,7 +9,7 @@ import { documentSchema } from '../../../new_fields/documentSchemas';
import { Id } from "../../../new_fields/FieldSymbols";
import { createSchema, makeInterface } from '../../../new_fields/Schema';
import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction, returnFalse } from "../../../Utils";
+import { emptyFunction, returnFalse, emptyPath } from "../../../Utils";
import { DocumentType } from "../../documents/DocumentTypes";
import { Transform } from "../../util/Transform";
import { CollectionViewType } from '../collections/CollectionView';
@@ -161,17 +161,18 @@ export class PresElementBox extends DocComponent<FieldViewProps, PresDocument>(P
return (null);
}
- let propDocWidth = NumCast(this.layoutDoc.nativeWidth);
- let propDocHeight = NumCast(this.layoutDoc.nativeHeight);
- let scale = () => 175 / NumCast(this.layoutDoc.nativeWidth, 175);
+ const propDocWidth = NumCast(this.layoutDoc.nativeWidth);
+ const propDocHeight = NumCast(this.layoutDoc.nativeHeight);
+ const scale = () => 175 / NumCast(this.layoutDoc.nativeWidth, 175);
return (
<div className="presElementBox-embedded" style={{
height: propDocHeight === 0 ? NumCast(this.layoutDoc.height) - NumCast(this.layoutDoc.collapsedHeight) : propDocHeight * scale(),
width: propDocWidth === 0 ? "auto" : propDocWidth * scale(),
}}>
<ContentFittingDocumentView
- fitToBox={StrCast(this.targetDoc.type).indexOf(DocumentType.COL) !== -1}
Document={this.targetDoc}
+ LibraryPath={emptyPath}
+ fitToBox={StrCast(this.targetDoc.type).indexOf(DocumentType.COL) !== -1}
addDocument={returnFalse}
removeDocument={returnFalse}
ruleProvider={undefined}
@@ -193,9 +194,9 @@ export class PresElementBox extends DocComponent<FieldViewProps, PresDocument>(P
}
render() {
- let treecontainer = this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.viewType === CollectionViewType.Tree;
- let className = "presElementBox-item" + (this.currentIndex === this.indexInPres ? " presElementBox-selected" : "");
- let pbi = "presElementBox-interaction";
+ const treecontainer = this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.viewType === CollectionViewType.Tree;
+ const className = "presElementBox-item" + (this.currentIndex === this.indexInPres ? " presElementBox-selected" : "");
+ const pbi = "presElementBox-interaction";
return (
<div className={className} key={this.props.Document[Id] + this.indexInPres}
style={{ outlineWidth: Doc.IsBrushed(this.targetDoc) ? `1px` : "0px", }}
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx
index 62f3aba4c..684f50766 100644
--- a/src/client/views/search/FilterBox.tsx
+++ b/src/client/views/search/FilterBox.tsx
@@ -62,15 +62,6 @@ export class FilterBox extends React.Component {
super(props);
FilterBox.Instance = this;
}
-
- componentDidMount = () => {
- document.addEventListener("pointerdown", (e) => {
- if (!e.defaultPrevented && e.timeStamp !== this._pointerTime) {
- SearchBox.Instance.closeSearch();
- }
- });
- }
-
setupAccordion() {
$('document').ready(function () {
const acc = document.getElementsByClassName('filter-header');
@@ -79,7 +70,7 @@ export class FilterBox extends React.Component {
acc[i].addEventListener("click", function (this: HTMLElement) {
this.classList.toggle("active");
- var panel = this.nextElementSibling as HTMLElement;
+ const panel = this.nextElementSibling as HTMLElement;
if (panel.style.maxHeight) {
panel.style.overflow = "hidden";
panel.style.maxHeight = "";
@@ -96,7 +87,7 @@ export class FilterBox extends React.Component {
}
});
- let el = acc[i] as HTMLElement;
+ const el = acc[i] as HTMLElement;
el.click();
}
});
@@ -105,14 +96,14 @@ export class FilterBox extends React.Component {
@action.bound
minimizeAll() {
$('document').ready(function () {
- var acc = document.getElementsByClassName('filter-header');
+ const acc = document.getElementsByClassName('filter-header');
// tslint:disable-next-line: prefer-for-of
for (var i = 0; i < acc.length; i++) {
- let classList = acc[i].classList;
+ const classList = acc[i].classList;
if (classList.contains("active")) {
acc[i].classList.toggle("active");
- var panel = acc[i].nextElementSibling as HTMLElement;
+ const panel = acc[i].nextElementSibling as HTMLElement;
panel.style.overflow = "hidden";
panel.style.maxHeight = "";
}
@@ -128,10 +119,10 @@ export class FilterBox extends React.Component {
}
basicRequireWords(query: string): string {
- let oldWords = query.split(" ");
- let newWords: string[] = [];
+ const oldWords = query.split(" ");
+ const newWords: string[] = [];
oldWords.forEach(word => {
- let newWrd = "+" + word;
+ const newWrd = "+" + word;
newWords.push(newWrd);
});
query = newWords.join(" ");
@@ -140,7 +131,7 @@ export class FilterBox extends React.Component {
}
basicFieldFilters(query: string, type: string): string {
- let oldWords = query.split(" ");
+ const oldWords = query.split(" ");
let mod = "";
if (type === Keys.AUTHOR) {
@@ -151,9 +142,9 @@ export class FilterBox extends React.Component {
mod = " title_t:";
}
- let newWords: string[] = [];
+ const newWords: string[] = [];
oldWords.forEach(word => {
- let newWrd = mod + word;
+ const newWrd = mod + word;
newWords.push(newWrd);
});
@@ -183,11 +174,11 @@ export class FilterBox extends React.Component {
//gets all of the collections of all the docviews that are selected
//if a collection is the only thing selected, search only in that collection (not its container)
getCurCollections(): Doc[] {
- let selectedDocs: DocumentView[] = SelectionManager.SelectedDocuments();
- let collections: Doc[] = [];
+ const selectedDocs: DocumentView[] = SelectionManager.SelectedDocuments();
+ const collections: Doc[] = [];
selectedDocs.forEach(async element => {
- let layout: string = StrCast(element.props.Document.baseLayout);
+ const layout: string = StrCast(element.props.Document.layout);
//checks if selected view (element) is a collection. if it is, adds to list to search through
if (layout.indexOf("Collection") > -1) {
//makes sure collections aren't added more than once
@@ -229,14 +220,14 @@ export class FilterBox extends React.Component {
}
addCollectionFilter(query: string): string {
- let collections: Doc[] = this.getCurCollections();
- let oldWords = query.split(" ");
+ const collections: Doc[] = this.getCurCollections();
+ const oldWords = query.split(" ");
- let collectionString: string[] = [];
+ const collectionString: string[] = [];
collections.forEach(doc => {
- let proto = doc.proto;
- let protoId = (proto || doc)[Id];
- let colString: string = "{!join from=data_l to=id}id:" + protoId + " ";
+ const proto = doc.proto;
+ const protoId = (proto || doc)[Id];
+ const colString: string = "{!join from=data_l to=id}id:" + protoId + " ";
collectionString.push(colString);
});
@@ -254,9 +245,9 @@ export class FilterBox extends React.Component {
if (this._icons.length === 9) {
return docs;
}
- let finalDocs: Doc[] = [];
+ const finalDocs: Doc[] = [];
docs.forEach(doc => {
- let layoutresult = Cast(doc.type, "string");
+ const layoutresult = Cast(doc.type, "string");
if (layoutresult && this._icons.includes(layoutresult)) {
finalDocs.push(doc);
}
diff --git a/src/client/views/search/IconButton.tsx b/src/client/views/search/IconButton.tsx
index d2cfe7fad..f01508141 100644
--- a/src/client/views/search/IconButton.tsx
+++ b/src/client/views/search/IconButton.tsx
@@ -108,7 +108,7 @@ export class IconButton extends React.Component<IconButtonProps>{
@action.bound
onClick = () => {
- let newList: string[] = FilterBox.Instance.getIcons();
+ const newList: string[] = FilterBox.Instance.getIcons();
if (!this._isSelected) {
this._isSelected = true;
diff --git a/src/client/views/search/NaviconButton.tsx b/src/client/views/search/NaviconButton.tsx
index 3fa36b163..0fa4a0fca 100644
--- a/src/client/views/search/NaviconButton.tsx
+++ b/src/client/views/search/NaviconButton.tsx
@@ -4,7 +4,7 @@ import "./NaviconButton.scss";
import * as $ from 'jquery';
import { observable } from 'mobx';
-export interface NaviconProps{
+export interface NaviconProps {
onClick(): void;
}
@@ -13,19 +13,21 @@ export class NaviconButton extends React.Component<NaviconProps> {
@observable private _ref: React.RefObject<HTMLAnchorElement> = React.createRef();
componentDidMount = () => {
- let that = this;
- if(this._ref.current){this._ref.current.addEventListener("click", function(e) {
- e.preventDefault();
- if(that._ref.current){
- that._ref.current.classList.toggle('active');
- return false;
- }
- });}
+ const that = this;
+ if (this._ref.current) {
+ this._ref.current.addEventListener("click", function (e) {
+ e.preventDefault();
+ if (that._ref.current) {
+ that._ref.current.classList.toggle('active');
+ return false;
+ }
+ });
+ }
}
render() {
return (
- <a id="hamburger-icon" href="#" ref = {this._ref} title="Menu">
+ <a id="hamburger-icon" href="#" ref={this._ref} title="Menu">
<span className="line line-1"></span>
<span className="line line-2"></span>
<span className="line line-3"></span>
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index bc11604a5..0825580b7 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -69,13 +69,8 @@
top: 300px;
display: flex;
flex-direction: column;
- // height: 560px;
height: 100%;
- // overflow: hidden;
- // overflow-y: auto;
- max-height: 560px;
- overflow: hidden;
- overflow-y: auto;
+ overflow: visible;
.no-result {
width: 500px;
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 899a35f48..dd1ac7421 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -8,18 +8,15 @@ import * as rp from 'request-promise';
import { Doc } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
import { Cast, NumCast } from '../../../new_fields/Types';
-import { RouteStore } from '../../../server/RouteStore';
import { Utils } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { SetupDrag } from '../../util/DragManager';
import { SearchUtil } from '../../util/SearchUtil';
-import { MainView } from '../MainView';
import { FilterBox } from './FilterBox';
import "./FilterBox.scss";
import "./SearchBox.scss";
import { SearchItem } from './SearchItem';
import { IconBar } from './IconBar';
-import { string } from 'prop-types';
library.add(faTimes);
@@ -86,11 +83,15 @@ export class SearchBox extends React.Component {
this._maxSearchIndex = 0;
}
- enter = (e: React.KeyboardEvent) => { if (e.key === "Enter") { this.submitSearch(); } };
+ enter = (e: React.KeyboardEvent) => {
+ if (e.key === "Enter") {
+ this.submitSearch();
+ }
+ }
public static async convertDataUri(imageUri: string, returnedFilename: string) {
try {
- let posting = Utils.prepend(RouteStore.dataUriToImage);
+ const posting = Utils.prepend("/uploadURI");
const returnedUri = await rp.post(posting, {
body: {
uri: imageUri,
@@ -145,6 +146,7 @@ export class SearchBox extends React.Component {
}
+ private NumResults = 25;
private lockPromise?: Promise<void>;
getResults = async (query: string) => {
if (this.lockPromise) {
@@ -152,7 +154,7 @@ export class SearchBox extends React.Component {
}
this.lockPromise = new Promise(async res => {
while (this._results.length <= this._endIndex && (this._numTotalResults === -1 || this._maxSearchIndex < this._numTotalResults)) {
- this._curRequest = SearchUtil.Search(query, true, { fq: this.filterQuery, start: this._maxSearchIndex, rows: 10, hl: true, "hl.fl": "*" }).then(action(async (res: SearchUtil.DocSearchResult) => {
+ this._curRequest = SearchUtil.Search(query, true, { fq: this.filterQuery, start: this._maxSearchIndex, rows: this.NumResults, hl: true, "hl.fl": "*" }).then(action(async (res: SearchUtil.DocSearchResult) => {
// happens at the beginning
if (res.numFound !== this._numTotalResults && this._numTotalResults === -1) {
@@ -166,7 +168,7 @@ export class SearchBox extends React.Component {
const docs = await Promise.all(res.docs.map(async doc => (await Cast(doc.extendsDoc, Doc)) || doc));
const highlights: typeof res.highlighting = {};
docs.forEach((doc, index) => highlights[doc[Id]] = highlightList[index]);
- let filteredDocs = FilterBox.Instance.filterDocsByType(docs);
+ const filteredDocs = FilterBox.Instance.filterDocsByType(docs);
runInAction(() => {
// this._results.push(...filteredDocs);
filteredDocs.forEach(doc => {
@@ -186,7 +188,7 @@ export class SearchBox extends React.Component {
this._curRequest = undefined;
}));
- this._maxSearchIndex += 10;
+ this._maxSearchIndex += this.NumResults;
await this._curRequest;
}
@@ -198,8 +200,8 @@ export class SearchBox extends React.Component {
collectionRef = React.createRef<HTMLSpanElement>();
startDragCollection = async () => {
- let res = await this.getAllResults(FilterBox.Instance.getFinalQuery(this._searchString));
- let filtered = FilterBox.Instance.filterDocsByType(res.docs);
+ const res = await this.getAllResults(FilterBox.Instance.getFinalQuery(this._searchString));
+ const filtered = FilterBox.Instance.filterDocsByType(res.docs);
// console.log(this._results)
const docs = filtered.map(doc => {
const isProto = Doc.GetT(doc, "isPrototype", "boolean", true);
@@ -232,8 +234,7 @@ export class SearchBox extends React.Component {
y += 300;
}
}
- return Docs.Create.FreeformDocument(docs, { width: 400, height: 400, panX: 175, panY: 175, backgroundColor: "grey", title: `Search Docs: "${this._searchString}"` });
-
+ return Docs.Create.TreeDocument(docs, { width: 200, height: 400, backgroundColor: "grey", title: `Search Docs: "${this._searchString}"` });
}
@action.bound
@@ -266,10 +267,10 @@ export class SearchBox extends React.Component {
@action
resultsScrolled = (e?: React.UIEvent<HTMLDivElement>) => {
- let scrollY = e ? e.currentTarget.scrollTop : this.resultsRef.current ? this.resultsRef.current.scrollTop : 0;
- let buffer = 4;
- let startIndex = Math.floor(Math.max(0, scrollY / 70 - buffer));
- let endIndex = Math.ceil(Math.min(this._numTotalResults - 1, startIndex + (560 / 70) + buffer));
+ const scrollY = e ? e.currentTarget.scrollTop : this.resultsRef.current ? this.resultsRef.current.scrollTop : 0;
+ const itemHght = 53;
+ const startIndex = Math.floor(Math.max(0, scrollY / itemHght));
+ const endIndex = Math.ceil(Math.min(this._numTotalResults - 1, startIndex + (this.resultsRef.current!.getBoundingClientRect().height / itemHght)));
this._endIndex = endIndex === -1 ? 12 : endIndex;
@@ -307,7 +308,7 @@ export class SearchBox extends React.Component {
this.getResults(this._searchString);
if (i < this._results.length) result = this._results[i];
if (result) {
- let highlights = Array.from([...Array.from(new Set(result[1]).values())]).filter(v => v !== "search_string");
+ const highlights = Array.from([...Array.from(new Set(result[1]).values())]);
this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} lines={result[2]} highlighting={highlights} />;
this._isSearch[i] = "search";
}
@@ -315,7 +316,7 @@ export class SearchBox extends React.Component {
else {
result = this._results[i];
if (result) {
- let highlights = Array.from([...Array.from(new Set(result[1]).values())]).filter(v => v !== "search_string");
+ const highlights = Array.from([...Array.from(new Set(result[1]).values())]);
this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} lines={result[2]} highlighting={highlights} />;
this._isSearch[i] = "search";
}
@@ -337,9 +338,9 @@ export class SearchBox extends React.Component {
render() {
return (
- <div className="searchBox-container">
+ <div className="searchBox-container" onPointerDown={e => { e.stopPropagation(); e.preventDefault(); }}>
<div className="searchBox-bar">
- <span className="searchBox-barChild searchBox-collection" onPointerDown={SetupDrag(this.collectionRef, this.startDragCollection)} ref={this.collectionRef} title="Drag Results as Collection">
+ <span className="searchBox-barChild searchBox-collection" onPointerDown={SetupDrag(this.collectionRef, () => this._searchString ? this.startDragCollection() : undefined)} ref={this.collectionRef} title="Drag Results as Collection">
<FontAwesomeIcon icon="object-group" size="lg" />
</span>
<input value={this._searchString} onChange={this.onChange} type="text" placeholder="Search..." id="search-input" ref={this.inputRef}
@@ -347,13 +348,13 @@ export class SearchBox extends React.Component {
style={{ width: this._searchbarOpen ? "500px" : "100px" }} />
<button className="searchBox-barChild searchBox-filter" title="Advanced Filtering Options" onClick={FilterBox.Instance.openFilter} onPointerDown={FilterBox.Instance.stopProp}><FontAwesomeIcon icon="ellipsis-v" color="white" /></button>
</div>
- {(this._numTotalResults > 0 || !this._searchbarOpen) ? (null) :
- (<div className="searchBox-quickFilter" onPointerDown={this.openSearch}>
- <div className="filter-panel"><IconBar /></div>
- </div>)}
+ <div className="searchBox-quickFilter" onPointerDown={this.openSearch}>
+ <div className="filter-panel"><IconBar /></div>
+ </div>
<div className="searchBox-results" onScroll={this.resultsScrolled} style={{
display: this._resultsOpen ? "flex" : "none",
- height: this.resFull ? "560px" : this.resultHeight, overflow: this.resFull ? "auto" : "visible"
+ height: this.resFull ? "auto" : this.resultHeight,
+ overflow: "visibile" // this.resFull ? "auto" : "visible"
}} ref={this.resultsRef}>
{this._visibleElements}
</div>
diff --git a/src/client/views/search/SearchItem.scss b/src/client/views/search/SearchItem.scss
index 9f12994c3..469f062b2 100644
--- a/src/client/views/search/SearchItem.scss
+++ b/src/client/views/search/SearchItem.scss
@@ -1,22 +1,14 @@
@import "../globalCssVariables";
-.search-overview {
+.searchItem-overview {
display: flex;
flex-direction: reverse;
justify-content: flex-end;
z-index: 0;
}
-.link-count {
- background: black;
- border-radius: 20px;
- color: white;
- width: 15px;
- text-align: center;
- margin-top: 5px;
-}
.searchBox-placeholder,
-.search-overview .search-item {
+.searchItem-overview .searchItem {
width: 100%;
background: $light-color-secondary;
border-color: $intermediate-color;
@@ -26,19 +18,19 @@
max-height: 150px;
height: auto;
z-index: 0;
- display: inline-block;
- overflow: auto;
+ display: flex;
+ overflow: visible;
- .main-search-info {
+ .searchItem-body {
display: flex;
flex-direction: row;
width: 100%;
- .search-title-container {
+ .searchItem-title-container {
width: 100%;
overflow: hidden;
- .search-title {
+ .searchItem-title {
text-transform: uppercase;
text-align: left;
width: 100%;
@@ -46,75 +38,28 @@
}
}
- .search-info {
+ .searchItem-info {
display: flex;
justify-content: flex-end;
- .link-container.item {
- margin-left: auto;
- margin-right: auto;
- height: 26px;
- width: 26px;
- border-radius: 13px;
- background: $dark-color;
- color: $light-color-secondary;
- display: flex;
- justify-content: center;
- align-items: center;
- -webkit-transition: all 0.2s ease-in-out;
- -moz-transition: all 0.2s ease-in-out;
- -o-transition: all 0.2s ease-in-out;
- transition: all 0.2s ease-in-out;
- transform-origin: top right;
- overflow: hidden;
- position: relative;
-
-
- .link-extended {
- // display: none;
- visibility: hidden;
- opacity: 0;
- position: relative;
- z-index: 500;
- overflow: hidden;
- -webkit-transition: opacity 0.2s ease-in-out .2s, visibility 0s linear 0s;
- -moz-transition: opacity 0.2s ease-in-out .2s, visibility 0s linear 0s;
- -o-transition: opacity 0.2s ease-in-out .2s, visibility 0s linear 0s;
- transition: opacity 0.2s ease-in-out .2s, visibility 0s linear 0s;
- // transition-delay: 1s;
- }
-
- }
-
- .link-container.item:hover {
- width: 70px;
- }
-
- .link-container.item:hover .link-count {
- opacity: 0;
- }
-
- .link-container.item:hover .link-extended {
- opacity: 1;
- visibility: visible;
- // display: inline;
- }
-
.icon-icons {
width: 50px
}
.icon-live {
width: 175px;
+ height: 0px;
}
+ .icon-icons {
+ height:auto;
+ }
.icon-icons,
.icon-live {
- height: auto;
margin: auto;
- overflow: hidden;
+ overflow: visible;
- .search-type {
+ .searchItem-type {
display: inline-block;
width: 100%;
position: absolute;
@@ -133,11 +78,11 @@
}
}
- .search-type:hover+.search-label {
+ .searchItem-type:hover+.searchItem-label {
opacity: 1;
}
- .search-label {
+ .searchItem-label {
font-size: 10;
position: relative;
right: 0px;
@@ -151,8 +96,6 @@
}
.icon-live:hover {
- height: 175px;
-
.pdfBox-cont {
img {
width: 100% !important;
@@ -161,48 +104,51 @@
}
}
- .search-info:hover {
+ .searchItem-info:hover {
width: 60%;
}
}
}
-.search-item:hover~.searchBox-instances,
+.searchItem:hover~.searchBox-instances,
.searchBox-instances:hover,
.searchBox-instances:active {
opacity: 1;
background: $lighter-alt-accent;
- width:150px
}
-.search-item:hover {
+.searchItem:hover {
transition: all 0.2s;
background: $lighter-alt-accent;
}
-.search-highlighting {
+.searchItem-highlighting {
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
}
.searchBox-instances {
- float: left;
opacity: 1;
- width: 0px;
+ width:40px;
+ height:40px;
+ background: gray;
transition: all 0.2s ease;
color: black;
overflow: hidden;
+ right:-100;
+ display:inline-block;
}
-.search-overview:hover {
+.searchItem-overview:hover {
z-index: 1;
}
.searchBox-placeholder {
min-height: 50px;
margin-left: 150px;
+ width: calc(100% - 150px);
text-transform: uppercase;
text-align: left;
font-weight: bold;
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index f1d825aa0..32ba5d19d 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -4,24 +4,24 @@ import { faCaretUp, faChartBar, faFile, faFilePdf, faFilm, faFingerprint, faGlob
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, DocListCast } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils } from "../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils, emptyPath } from "../../../Utils";
import { DocumentType } from "../../documents/DocumentTypes";
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager, SetupDrag } from "../../util/DragManager";
-import { LinkManager } from "../../util/LinkManager";
import { SearchUtil } from "../../util/SearchUtil";
import { Transform } from "../../util/Transform";
import { SEARCH_THUMBNAIL_SIZE } from "../../views/globalCssVariables.scss";
import { CollectionViewType } from "../collections/CollectionView";
import { CollectionDockingView } from "../collections/CollectionDockingView";
import { ContextMenu } from "../ContextMenu";
-import { DocumentView } from "../nodes/DocumentView";
import { SearchBox } from "./SearchBox";
import "./SearchItem.scss";
import "./SelectorContextMenu.scss";
+import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
+import { ButtonSelector, ParentDocSelector } from "../collections/ParentDocumentSelector";
export interface SearchItemProps {
doc: Doc;
@@ -52,7 +52,7 @@ export class SelectorContextMenu extends React.Component<SearchItemProps> {
}
async fetchDocuments() {
- let aliases = (await SearchUtil.GetViewsOfDocument(this.props.doc)).filter(doc => doc !== this.props.doc);
+ const aliases = (await SearchUtil.GetViewsOfDocument(this.props.doc)).filter(doc => doc !== this.props.doc);
const { docs } = await SearchUtil.Search("", true, { fq: `data_l:"${this.props.doc[Id]}"` });
const map: Map<Doc, Doc> = new Map;
const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", true, { fq: `data_l:"${doc[Id]}"` }).then(result => result.docs)));
@@ -82,7 +82,7 @@ export class SelectorContextMenu extends React.Component<SearchItemProps> {
<div className="parents">
<p className="contexts">Contexts:</p>
{[...this._docs, ...this._otherDocs].map(doc => {
- let item = React.createRef<HTMLDivElement>();
+ const item = React.createRef<HTMLDivElement>();
return <div className="collection" key={doc.col[Id] + doc.target[Id]} ref={item}>
<div className="collection-item" onPointerDown={
SetupDrag(item, () => doc.col, undefined, undefined, undefined, undefined, () => SearchBox.Instance.closeSearch())}>
@@ -135,56 +135,50 @@ export class SearchItem extends React.Component<SearchItemProps> {
@observable _displayDim = 50;
componentDidMount() {
- this.props.doc.search_string = this.props.query;
- this.props.doc.search_fields = this.props.highlighting.join(", ");
+ Doc.SetSearchQuery(this.props.query);
+ this.props.doc.searchMatch = true;
}
componentWillUnmount() {
- this.props.doc.search_string = undefined;
- this.props.doc.search_fields = undefined;
+ this.props.doc.searchMatch = undefined;
}
//@computed
@action
public DocumentIcon() {
- let layoutresult = StrCast(this.props.doc.type);
+ const layoutresult = StrCast(this.props.doc.type);
if (!this._useIcons) {
- let returnXDimension = () => this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE);
- let returnYDimension = () => this._displayDim;
- let scale = () => returnXDimension() / NumCast(Doc.Layout(this.props.doc).nativeWidth, returnXDimension());
+ const returnXDimension = () => this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE);
+ const returnYDimension = () => this._displayDim;
const docview = <div
onPointerDown={action(() => {
this._useIcons = !this._useIcons;
this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE);
})}
- onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))}
- onPointerLeave={action(() => this._displayDim = 50)} >
- <DocumentView
- fitToBox={StrCast(this.props.doc.type).indexOf(DocumentType.COL) !== -1}
+ onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))} >
+ <ContentFittingDocumentView
Document={this.props.doc}
+ LibraryPath={emptyPath}
+ fitToBox={StrCast(this.props.doc.type).indexOf(DocumentType.COL) !== -1}
addDocument={returnFalse}
removeDocument={returnFalse}
ruleProvider={undefined}
- ScreenToLocalTransform={Transform.Identity}
addDocTab={returnFalse}
pinToPres={returnFalse}
+ getTransform={Transform.Identity}
renderDepth={1}
PanelWidth={returnXDimension}
PanelHeight={returnYDimension}
focus={emptyFunction}
- backgroundColor={returnEmptyString}
- parentActive={returnFalse}
+ moveDocument={returnFalse}
+ active={returnFalse}
whenActiveChanged={returnFalse}
- bringToFront={emptyFunction}
- zoomToScale={emptyFunction}
- getScale={returnOne}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
- ContentScaling={scale}
+ setPreviewScript={emptyFunction}
+ previewScript={undefined}
/>
</div>;
return docview;
}
- let button = layoutresult.indexOf(DocumentType.PDF) !== -1 ? faFilePdf :
+ const button = layoutresult.indexOf(DocumentType.PDF) !== -1 ? faFilePdf :
layoutresult.indexOf(DocumentType.IMG) !== -1 ? faImage :
layoutresult.indexOf(DocumentType.TEXT) !== -1 ? faStickyNote :
layoutresult.indexOf(DocumentType.VID) !== -1 ? faFilm :
@@ -194,40 +188,28 @@ export class SearchItem extends React.Component<SearchItemProps> {
layoutresult.indexOf(DocumentType.HIST) !== -1 ? faChartBar :
layoutresult.indexOf(DocumentType.WEB) !== -1 ? faGlobeAsia :
faCaretUp;
- return <div onPointerDown={action(() => { this._useIcons = false; this._displayDim = Number(SEARCH_THUMBNAIL_SIZE); })} >
+ return <div onClick={action(() => { this._useIcons = false; this._displayDim = Number(SEARCH_THUMBNAIL_SIZE); })} >
<FontAwesomeIcon icon={button} size="2x" />
</div>;
}
collectionRef = React.createRef<HTMLDivElement>();
- startDocDrag = () => {
- let doc = this.props.doc;
- const isProto = Doc.GetT(doc, "isPrototype", "boolean", true);
- if (isProto) {
- return Doc.MakeDelegate(doc);
- } else {
- return Doc.MakeAlias(doc);
- }
- }
-
- @computed
- get linkCount() { return LinkManager.Instance.getAllRelatedLinks(this.props.doc).length; }
@action
pointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e); }
nextHighlight = (e: React.PointerEvent) => {
- e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e);
- let sstring = StrCast(this.props.doc.search_string);
- this.props.doc.search_string = "";
- setTimeout(() => this.props.doc.search_string = sstring, 0);
+ e.preventDefault();
+ e.button === 0 && SearchBox.Instance.openSearch(e);
+ this.props.doc.searchMatch = false;
+ setTimeout(() => this.props.doc.searchMatch = true, 0);
}
highlightDoc = (e: React.PointerEvent) => {
if (this.props.doc.type === DocumentType.LINK) {
if (this.props.doc.anchor1 && this.props.doc.anchor2) {
- let doc1 = Cast(this.props.doc.anchor1, Doc, null);
- let doc2 = Cast(this.props.doc.anchor2, Doc, null);
+ const doc1 = Cast(this.props.doc.anchor1, Doc, null);
+ const doc2 = Cast(this.props.doc.anchor2, Doc, null);
Doc.BrushDoc(doc1);
Doc.BrushDoc(doc2);
}
@@ -241,8 +223,8 @@ export class SearchItem extends React.Component<SearchItemProps> {
if (this.props.doc.type === DocumentType.LINK) {
if (this.props.doc.anchor1 && this.props.doc.anchor2) {
- let doc1 = Cast(this.props.doc.anchor1, Doc, null);
- let doc2 = Cast(this.props.doc.anchor2, Doc, null);
+ const doc1 = Cast(this.props.doc.anchor1, Doc, null);
+ const doc2 = Cast(this.props.doc.anchor2, Doc, null);
Doc.UnBrushDoc(doc1);
Doc.UnBrushDoc(doc2);
}
@@ -264,46 +246,62 @@ export class SearchItem extends React.Component<SearchItemProps> {
ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
}
+ _downX = 0;
+ _downY = 0;
+ _target: any;
onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
+ this._downX = e.clientX;
+ this._downY = e.clientY;
e.stopPropagation();
- const doc = Doc.IsPrototype(this.props.doc) ? Doc.MakeDelegate(this.props.doc) : this.props.doc;
- DragManager.StartDocumentDrag([e.currentTarget], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, {
- handlers: { dragComplete: emptyFunction },
- hideSource: false,
- });
+ this._target = e.currentTarget;
+ document.removeEventListener("pointermove", this.onPointerMoved);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ document.addEventListener("pointermove", this.onPointerMoved);
+ document.addEventListener("pointerup", this.onPointerUp);
+ }
+ onPointerMoved = (e: PointerEvent) => {
+ if (Math.abs(e.clientX - this._downX) > Utils.DRAG_THRESHOLD ||
+ Math.abs(e.clientY - this._downY) > Utils.DRAG_THRESHOLD) {
+ console.log("DRAGGIGNG");
+ document.removeEventListener("pointermove", this.onPointerMoved);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ const doc = Doc.IsPrototype(this.props.doc) ? Doc.MakeDelegate(this.props.doc) : this.props.doc;
+ DragManager.StartDocumentDrag([this._target], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
+ }
+ }
+ onPointerUp = (e: PointerEvent) => {
+ document.removeEventListener("pointermove", this.onPointerMoved);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ }
+
+ @computed
+ get contextButton() {
+ return <ParentDocSelector Views={DocumentManager.Instance.DocumentViews} Document={this.props.doc} addDocTab={(doc, data, where) => CollectionDockingView.AddRightSplit(doc, data)} />;
}
render() {
const doc1 = Cast(this.props.doc.anchor1, Doc);
const doc2 = Cast(this.props.doc.anchor2, Doc);
- return (
- <div className="search-overview" onPointerDown={this.pointerDown} onContextMenu={this.onContextMenu}>
- <div className="search-item" onPointerDown={this.nextHighlight} onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc} id="result"
- onClick={this.onClick}>
- <div className="main-search-info">
- <div title="Drag as document" onPointerDown={this.onPointerDown} style={{ marginRight: "7px" }}> <FontAwesomeIcon icon="file" size="lg" />
- <div className="link-container item">
- <div className="link-count" title={`${this.linkCount + " links"}`}>{this.linkCount}</div>
- </div>
- </div>
- <div className="search-title-container">
- <div className="search-title">{StrCast(this.props.doc.title)}</div>
- <div className="search-highlighting">{this.props.highlighting.length ? "Matched fields:" + this.props.highlighting.join(", ") : this.props.lines.length ? this.props.lines[0] : ""}</div>
- {this.props.lines.filter((m, i) => i).map((l, i) => <div id={i.toString()} className="search-highlighting">`${l}`</div>)}
- </div>
- <div className="search-info" style={{ width: this._useIcons ? "15%" : "400px" }}>
- <div className={`icon-${this._useIcons ? "icons" : "live"}`}>
- <div className="search-type" title="Click to Preview">{this.DocumentIcon()}</div>
- <div className="search-label">{this.props.doc.type ? this.props.doc.type : "Other"}</div>
- </div>
- </div>
+ return <div className="searchItem-overview" onPointerDown={this.pointerDown} onContextMenu={this.onContextMenu}>
+ <div className="searchItem" onPointerDown={this.nextHighlight} onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc}>
+ <div className="searchItem-body" onClick={this.onClick}>
+ <div className="searchItem-title-container">
+ <div className="searchItem-title">{StrCast(this.props.doc.title)}</div>
+ <div className="searchItem-highlighting">{this.props.highlighting.length ? "Matched fields:" + this.props.highlighting.join(", ") : this.props.lines.length ? this.props.lines[0] : ""}</div>
+ {this.props.lines.filter((m, i) => i).map((l, i) => <div id={i.toString()} className="searchItem-highlighting">`${l}`</div>)}
+ </div>
+ </div>
+ <div className="searchItem-info" style={{ width: this._useIcons ? "30px" : "100%" }}>
+ <div className={`icon-${this._useIcons ? "icons" : "live"}`}>
+ <div className="searchItem-type" title="Click to Preview" onPointerDown={this.onPointerDown}>{this.DocumentIcon()}</div>
+ <div className="searchItem-label">{this.props.doc.type ? this.props.doc.type : "Other"}</div>
</div>
</div>
- <div className="searchBox-instances">
+ <div className="searchItem-context" title="Drag as document">
{(doc1 instanceof Doc && doc2 instanceof Doc) && this.props.doc.type === DocumentType.LINK ? <LinkContextMenu doc1={doc1} doc2={doc2} /> :
- <SelectorContextMenu {...this.props} />}
+ this.contextButton}
</div>
</div>
- );
+ </div>;
}
} \ No newline at end of file
diff --git a/src/client/views/search/ToggleBar.tsx b/src/client/views/search/ToggleBar.tsx
index ed5ecd3ba..e4d7f2fd5 100644
--- a/src/client/views/search/ToggleBar.tsx
+++ b/src/client/views/search/ToggleBar.tsx
@@ -33,8 +33,7 @@ export class ToggleBar extends React.Component<ToggleBarProps>{
}
componentDidMount = () => {
-
- let totalWidth = 265;
+ const totalWidth = 265;
if (this._originalStatus) {
this._forwardTimeline.add({