aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgeireann <60007097+geireann@users.noreply.github.com>2021-08-02 15:44:44 -0400
committergeireann <60007097+geireann@users.noreply.github.com>2021-08-02 15:44:44 -0400
commit94705e6cf119b9cdfcc0d857f71051eac10235c9 (patch)
tree2d399160a319b7931b22f7b7fd38e9ad3483ea33 /src
parent1291e8a45ec9e3aeccd2ca74c0f549a18a16f0d7 (diff)
parent44ed361a9f59a16bbd1b5f2483ba7eb10df2fa82 (diff)
Merge branch 'master' into sharing_scenario
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts12
-rw-r--r--src/client/ClientRecommender.scss2
-rw-r--r--src/client/apis/google_docs/GooglePhotosClientUtils.ts2
-rw-r--r--src/client/documents/DocumentTypes.ts4
-rw-r--r--src/client/documents/Documents.ts112
-rw-r--r--src/client/goldenLayout.js3090
-rw-r--r--src/client/util/CaptureManager.scss2
-rw-r--r--src/client/util/CurrentUserUtils.ts57
-rw-r--r--src/client/util/DocumentManager.ts4
-rw-r--r--src/client/util/DragManager.ts35
-rw-r--r--src/client/util/HypothesisUtils.ts2
-rw-r--r--src/client/util/InteractionUtils.tsx2
-rw-r--r--src/client/util/LinkManager.ts38
-rw-r--r--src/client/util/Scripting.ts2
-rw-r--r--src/client/util/SelectionManager.ts3
-rw-r--r--src/client/util/SettingsManager.scss4
-rw-r--r--src/client/util/SettingsManager.tsx79
-rw-r--r--src/client/views/AntimodeMenu.scss8
-rw-r--r--src/client/views/ContextMenu.scss20
-rw-r--r--src/client/views/ContextMenu.tsx2
-rw-r--r--src/client/views/DocComponent.tsx2
-rw-r--r--src/client/views/DocumentButtonBar.scss51
-rw-r--r--src/client/views/DocumentButtonBar.tsx35
-rw-r--r--src/client/views/DocumentDecorations.scss34
-rw-r--r--src/client/views/DocumentDecorations.tsx2
-rw-r--r--src/client/views/EditableView.scss8
-rw-r--r--src/client/views/GlobalKeyHandler.ts2
-rw-r--r--src/client/views/InkControls.tsx142
-rw-r--r--src/client/views/InkHandles.tsx124
-rw-r--r--src/client/views/InkStroke.scss11
-rw-r--r--src/client/views/InkStrokeProperties.ts375
-rw-r--r--src/client/views/InkingStroke.scss11
-rw-r--r--src/client/views/InkingStroke.tsx219
-rw-r--r--src/client/views/LightboxView.scss30
-rw-r--r--src/client/views/LightboxView.tsx22
-rw-r--r--src/client/views/Main.scss6
-rw-r--r--src/client/views/MainView.scss115
-rw-r--r--src/client/views/MainView.tsx78
-rw-r--r--src/client/views/MarqueeAnnotator.tsx10
-rw-r--r--src/client/views/PropertiesButtons.scss16
-rw-r--r--src/client/views/PropertiesView.scss37
-rw-r--r--src/client/views/PropertiesView.tsx6
-rw-r--r--src/client/views/SidebarAnnos.tsx2
-rw-r--r--src/client/views/StyleProvider.tsx43
-rw-r--r--src/client/views/TemplateMenu.scss6
-rw-r--r--src/client/views/_nodeModuleOverrides.scss53
-rw-r--r--src/client/views/animationtimeline/Keyframe.scss10
-rw-r--r--src/client/views/animationtimeline/Keyframe.tsx2
-rw-r--r--src/client/views/animationtimeline/Timeline.scss6
-rw-r--r--src/client/views/animationtimeline/TimelineMenu.scss10
-rw-r--r--src/client/views/animationtimeline/TimelineOverview.scss2
-rw-r--r--src/client/views/animationtimeline/Track.scss6
-rw-r--r--src/client/views/collections/CollectionDockingView.scss114
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx2
-rw-r--r--src/client/views/collections/CollectionLinearView.scss38
-rw-r--r--src/client/views/collections/CollectionLinearView.tsx10
-rw-r--r--src/client/views/collections/CollectionMenu.scss147
-rw-r--r--src/client/views/collections/CollectionMenu.tsx2
-rw-r--r--src/client/views/collections/CollectionPileView.tsx15
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx8
-rw-r--r--src/client/views/collections/CollectionStackingView.scss14
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx6
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx2
-rw-r--r--src/client/views/collections/CollectionTreeView.scss8
-rw-r--r--src/client/views/collections/CollectionView.scss4
-rw-r--r--src/client/views/collections/TabDocView.scss59
-rw-r--r--src/client/views/collections/TabDocView.tsx114
-rw-r--r--src/client/views/collections/TreeView.scss6
-rw-r--r--src/client/views/collections/TreeView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx5
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx7
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx23
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss22
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx10
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTable.tsx2
-rw-r--r--src/client/views/global/globalCssVariables.scss (renamed from src/client/views/globalCssVariables.scss)58
-rw-r--r--src/client/views/global/globalCssVariables.scss.d.ts (renamed from src/client/views/globalCssVariables.scss.d.ts)0
-rw-r--r--src/client/views/global/globalEnums.tsx38
-rw-r--r--src/client/views/linking/LinkEditor.scss22
-rw-r--r--src/client/views/linking/LinkMenu.scss28
-rw-r--r--src/client/views/linking/LinkMenu.tsx8
-rw-r--r--src/client/views/linking/LinkMenuGroup.tsx2
-rw-r--r--src/client/views/linking/LinkMenuItem.scss20
-rw-r--r--src/client/views/linking/LinkPopup.scss45
-rw-r--r--src/client/views/linking/LinkPopup.tsx114
-rw-r--r--src/client/views/nodes/AudioBox.tsx2
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx15
-rw-r--r--src/client/views/nodes/DocumentLinksButton.scss57
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx160
-rw-r--r--src/client/views/nodes/DocumentView.scss8
-rw-r--r--src/client/views/nodes/DocumentView.tsx12
-rw-r--r--src/client/views/nodes/FieldView.tsx6
-rw-r--r--src/client/views/nodes/FontIconBox.scss13
-rw-r--r--src/client/views/nodes/FontIconBox.tsx3
-rw-r--r--src/client/views/nodes/ImageBox.tsx4
-rw-r--r--src/client/views/nodes/KeyValueBox.scss20
-rw-r--r--src/client/views/nodes/KeyValuePair.scss2
-rw-r--r--src/client/views/nodes/LinkDescriptionPopup.scss65
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx4
-rw-r--r--src/client/views/nodes/PDFBox.scss5
-rw-r--r--src/client/views/nodes/PDFBox.tsx24
-rw-r--r--src/client/views/nodes/RadialMenu.scss4
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx4
-rw-r--r--src/client/views/nodes/VideoBox.tsx8
-rw-r--r--src/client/views/nodes/WebBox.scss3
-rw-r--r--src/client/views/nodes/WebBox.tsx3
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss8
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx20
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.scss4
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx19
-rw-r--r--src/client/views/nodes/formattedText/TooltipTextMenu.scss10
-rw-r--r--src/client/views/nodes/trails/PresBox.scss (renamed from src/client/views/nodes/PresBox.scss)144
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx (renamed from src/client/views/nodes/PresBox.tsx)184
-rw-r--r--src/client/views/nodes/trails/PresElementBox.scss (renamed from src/client/views/presentationview/PresElementBox.scss)0
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx (renamed from src/client/views/presentationview/PresElementBox.tsx)50
-rw-r--r--src/client/views/nodes/trails/PresEnums.ts28
-rw-r--r--src/client/views/nodes/trails/index.ts3
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx17
-rw-r--r--src/client/views/pdf/PDFViewer.tsx3
-rw-r--r--src/client/views/search/CheckBox.scss6
-rw-r--r--src/client/views/search/CollectionFilters.scss2
-rw-r--r--src/client/views/search/IconBar.scss2
-rw-r--r--src/client/views/search/IconButton.scss6
-rw-r--r--src/client/views/search/IconButton.tsx4
-rw-r--r--src/client/views/search/NaviconButton.scss4
-rw-r--r--src/client/views/search/SearchBox.scss10
-rw-r--r--src/client/views/search/SelectorContextMenu.scss6
-rw-r--r--src/client/views/search/ToggleBar.scss8
-rw-r--r--src/client/views/topbar/TopBar.scss217
-rw-r--r--src/client/views/topbar/TopBar.tsx66
-rw-r--r--src/client/views/webcam/DashWebRTCVideo.scss2
-rw-r--r--src/fields/Doc.ts127
-rw-r--r--src/fields/InkField.ts31
-rw-r--r--src/fields/URLField.ts15
-rw-r--r--src/mobile/AudioUpload.scss2
-rw-r--r--src/mobile/ImageUpload.scss2
-rw-r--r--src/mobile/ImageUpload.tsx4
-rw-r--r--src/server/DashSession/Session/agents/server_worker.ts9
-rw-r--r--src/server/DashUploadUtils.ts2
-rw-r--r--src/server/server_Initialization.ts3
144 files changed, 4229 insertions, 3138 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index ef9c51b8b..194c38a6f 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -67,7 +67,6 @@ export namespace Utils {
export function prepend(extension: string): string {
return window.location.origin + extension;
}
-
export function fileUrl(filename: string): string {
return prepend(`/files/${filename}`);
}
@@ -191,11 +190,12 @@ export namespace Utils {
return { h: h, s: s, l: l };
}
- export function scrollIntoView(targetY: number, targetHgt: number, scrollTop: number, contextHgt: number) {
- if (scrollTop + contextHgt < targetY + targetHgt * 1.1) {
- return Math.ceil(targetY + targetHgt * 1.1 - contextHgt);
- } else if (scrollTop > targetY - targetHgt * .1) {
- return Math.max(0, Math.floor(targetY - targetHgt * .1));
+ export function scrollIntoView(targetY: number, targetHgt: number, scrollTop: number, contextHgt: number, minSpacing: number) {
+ if (scrollTop + contextHgt < targetY + minSpacing + targetHgt) {
+ return Math.ceil(targetY + minSpacing + targetHgt - contextHgt);
+ }
+ if (scrollTop > targetY - minSpacing - targetHgt) {
+ return Math.max(0, Math.floor(targetY - minSpacing - targetHgt));
}
}
diff --git a/src/client/ClientRecommender.scss b/src/client/ClientRecommender.scss
index 3f9102f15..178c7fdab 100644
--- a/src/client/ClientRecommender.scss
+++ b/src/client/ClientRecommender.scss
@@ -1,4 +1,4 @@
-// @import "/views/globalCssVariables.scss";
+// @import "/views/global/globalCssVariables.scss";
.space{
border: 1px dashed blue;
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index 899e65a16..ff9460b62 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -285,7 +285,7 @@ export namespace GooglePhotos {
const photos = await endpoint();
const albumId = StrCast(collection.albumId);
if (albumId && albumId.length) {
- const enrichment = new photos.TextEnrichment(content || Utils.prepend("/doc/" + collection[Id]));
+ const enrichment = new photos.TextEnrichment(content || Doc.globalServerPath(collection));
const position = new photos.AlbumPosition(photos.AlbumPosition.POSITIONS.FIRST_IN_ALBUM);
const enrichmentItem = await photos.albums.addEnrichment(albumId, enrichment, position);
if (enrichmentItem) {
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 8565784b4..dba7ff907 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -18,7 +18,7 @@ export enum DocumentType {
LABEL = "label", // simple text label
BUTTON = "button", // onClick button
WEBCAM = "webcam", // webcam
- HTMLANCHOR = "htmlanchor", // text selection anchor in PDF/Web
+ MARKER = "marker", // generic marker document not intended to be viewed independently of its context (e.g., for text selections in PDF/Web/RTF)
DATE = "date", // calendar view of a date
SCRIPTING = "script", // script editor
EQUATION = "equation", // equation editor
@@ -40,6 +40,4 @@ export enum DocumentType {
LINKDB = "linkdb", // database of links ??? why do we have this
SCRIPTDB = "scriptdb", // database of scripts
GROUPDB = "groupdb", // database of groups
-
- TEXTANCHOR = "textanchor" // selection of text in a text box
} \ No newline at end of file
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index a27fa9cf2..142c37ea4 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -30,7 +30,7 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie
import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
import { ContextMenu } from "../views/ContextMenu";
import { ContextMenuProps } from "../views/ContextMenuItem";
-import { DFLT_IMAGE_NATIVE_DIM } from "../views/globalCssVariables.scss";
+import { DFLT_IMAGE_NATIVE_DIM } from "../views/global/globalCssVariables.scss";
import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from "../views/InkingStroke";
import { AudioBox } from "../views/nodes/AudioBox";
import { ColorBox } from "../views/nodes/ColorBox";
@@ -45,14 +45,14 @@ import { LabelBox } from "../views/nodes/LabelBox";
import { LinkBox } from "../views/nodes/LinkBox";
import { LinkDescriptionPopup } from "../views/nodes/LinkDescriptionPopup";
import { PDFBox } from "../views/nodes/PDFBox";
-import { PresBox } from "../views/nodes/PresBox";
+import { PresBox } from "../views/nodes/trails/PresBox";
import { ScreenshotBox } from "../views/nodes/ScreenshotBox";
import { ScriptingBox } from "../views/nodes/ScriptingBox";
import { SliderBox } from "../views/nodes/SliderBox";
import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
import { VideoBox } from "../views/nodes/VideoBox";
import { WebBox } from "../views/nodes/WebBox";
-import { PresElementBox } from "../views/presentationview/PresElementBox";
+import { PresElementBox } from "../views/nodes/trails/PresElementBox";
import { SearchBox } from "../views/search/SearchBox";
import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
import { DocumentType } from "./DocumentTypes";
@@ -427,7 +427,7 @@ export namespace Docs {
[DocumentType.PRESELEMENT, {
layout: { view: PresElementBox, dataField: defaultDataKey }
}],
- [DocumentType.HTMLANCHOR, {
+ [DocumentType.MARKER, {
layout: { view: CollectionView, dataField: defaultDataKey },
options: { links: ComputedField.MakeFunction("links(self)") as any, hideLinkButton: true }
}],
@@ -452,10 +452,6 @@ export namespace Docs {
layout: { view: EmptyBox, dataField: defaultDataKey },
options: { links: ComputedField.MakeFunction("links(self)") as any }
}],
- [DocumentType.TEXTANCHOR, {
- layout: { view: EmptyBox, dataField: defaultDataKey },
- options: { targetDropAction: "move", links: ComputedField.MakeFunction("links(self)") as any, hideLinkButton: true }
- }]
]);
const suffix = "Proto";
@@ -554,84 +550,6 @@ export namespace Docs {
export namespace Create {
/**
- * Synchronously returns a collection into which
- * the device documents will be put. This is initially empty,
- * but gets populated by updates from the web socket. When everything is over,
- * this function cleans up after itself.
- * s
- * Look at Websocket.ts for the server-side counterpart to this
- * function.
- */
- export function Buxton() {
- let responded = false;
- const loading = new Doc;
- loading.title = "Please wait for the import script...";
- const parent = TreeDocument([loading], {
- title: "The Buxton Collection",
- _width: 400,
- _height: 400
- });
- const parentProto = Doc.GetProto(parent);
- const { _socket } = DocServer;
-
- // just in case, clean up
- _socket.off(MessageStore.BuxtonDocumentResult.Message);
- _socket.off(MessageStore.BuxtonImportComplete.Message);
-
- // this is where the client handles the receipt of a new valid parsed document
- Utils.AddServerHandler(_socket, MessageStore.BuxtonDocumentResult, ({ device, invalid: errors }) => {
- if (!responded) {
- responded = true;
- parentProto.data = new List<Doc>();
- }
- if (device) {
- const { title, __images, additionalMedia } = device;
- delete device.__images;
- delete device.additionalMedia;
- const { ImageDocument, StackingDocument } = Docs.Create;
- const constructed = __images.map(({ url, nativeWidth, nativeHeight }) => ({ url: Utils.prepend(url), nativeWidth, nativeHeight }));
- const deviceImages = constructed.map(({ url, nativeWidth, nativeHeight }, i) => {
- const imageDoc = ImageDocument(url, {
- title: `image${i}.${extname(url)}`,
- _nativeWidth: nativeWidth,
- _nativeHeight: nativeHeight
- });
- const media = additionalMedia[i];
- if (media) {
- for (const key of Object.keys(media)) {
- imageDoc[`additionalMedia_${key}`] = Utils.prepend(`/files/${key}/buxton/${media[key]}`);
- }
- }
- return imageDoc;
- });
- // the main document we create
- const doc = StackingDocument(deviceImages, { title, hero: new ImageField(constructed[0].url) });
- doc.nameAliases = new List<string>([title.toLowerCase()]);
- // add the parsed attributes to this main document
- Doc.Get.FromJson({ data: device, appendToExisting: { targetDoc: Doc.GetProto(doc) } });
- Doc.AddDocToList(parentProto, "data", doc);
- } else if (errors) {
- console.log("Documents:" + errors);
- } else {
- alert("A Buxton document import was completely empty (??)");
- }
- });
-
- // when the import is complete, we stop listening for these creation
- // and termination events and alert the user
- Utils.AddServerHandler(_socket, MessageStore.BuxtonImportComplete, ({ deviceCount, errorCount }) => {
- _socket.off(MessageStore.BuxtonDocumentResult.Message);
- _socket.off(MessageStore.BuxtonImportComplete.Message);
- alert(`Successfully imported ${deviceCount} device${deviceCount === 1 ? "" : "s"}, with ${errorCount} error${errorCount === 1 ? "" : "s"}, in ${(Date.now() - startTime) / 1000} seconds.`);
- });
- const startTime = Date.now();
- Utils.Emit(_socket, MessageStore.BeginBuxtonImport, ""); // signal the server to start importing
- return parent; // synchronously return the collection, to be populateds
- }
-
- Scripting.addGlobal(Buxton);
-
- /**
* This function receives the relevant document prototype and uses
* it to create a new of that base-level prototype, or the
* underlying data document, which it then delegates again
@@ -672,16 +590,16 @@ export namespace Docs {
viewProps["acl-Override"] = "None";
viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add;
const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewProps, true, true);
- ![DocumentType.LINK, DocumentType.TEXTANCHOR, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
+ ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
- !Doc.IsSystem(dataDoc) && ![DocumentType.HTMLANCHOR, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR, DocumentType.TEXTANCHOR].includes(proto.type as any) &&
+ !Doc.IsSystem(dataDoc) && ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) &&
!dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc);
return viewDoc;
}
export function ImageDocument(url: string, options: DocumentOptions = {}) {
- const imgField = new ImageField(new URL(url));
+ const imgField = new ImageField(url);
return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: path.basename(url), ...options });
}
@@ -695,11 +613,11 @@ export namespace Docs {
}
export function VideoDocument(url: string, options: DocumentOptions = {}) {
- return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(new URL(url)), options);
+ return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(url), options);
}
export function YoutubeDocument(url: string, options: DocumentOptions = {}) {
- return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(new URL(url)), options);
+ return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(url), options);
}
export function WebCamDocument(url: string, options: DocumentOptions = {}) {
@@ -715,7 +633,7 @@ export namespace Docs {
}
export function AudioDocument(url: string, options: DocumentOptions = {}) {
- return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(new URL(url)),
+ return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url),
{ ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any });
}
@@ -788,11 +706,11 @@ export namespace Docs {
}
export function PdfDocument(url: string, options: DocumentOptions = {}) {
- return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(new URL(url)), options);
+ return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(url), options);
}
export function WebDocument(url: string, options: DocumentOptions = {}) {
- return InstanceFromProto(Prototypes.get(DocumentType.WEB), url ? new WebField(new URL(url)) : undefined, options);
+ return InstanceFromProto(Prototypes.get(DocumentType.WEB), url ? new WebField(url) : undefined, options);
}
export function HtmlDocument(html: string, options: DocumentOptions = {}) {
@@ -804,7 +722,7 @@ export namespace Docs {
}
export function TextanchorDocument(options: DocumentOptions = {}, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.TEXTANCHOR), undefined, options, id);
+ return InstanceFromProto(Prototypes.get(DocumentType.MARKER), undefined, options, id);
}
export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -813,7 +731,7 @@ export namespace Docs {
return inst;
}
export function HTMLAnchorDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.HTMLANCHOR), new List(documents), options, id);
+ return InstanceFromProto(Prototypes.get(DocumentType.MARKER), new List(documents), options, id);
}
export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -1335,9 +1253,7 @@ export namespace DocUtils {
newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55;
newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55;
newCollection._width = newCollection._height = 110;
- //newCollection.borderRounding = "40px";
newCollection._jitterRotation = 10;
- newCollection._backgroundColor = "gray";
return newCollection;
}
diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index 9cfea7f3f..238f1ac0a 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -82,16 +82,16 @@
return target;
};
- /**
- * This is based on Paul Irish's shim, but looks quite odd in comparison. Why?
- * Because
- * a) it shouldn't affect the global requestAnimationFrame function
- * b) it shouldn't pass on the time that has passed
- *
- * @param {Function} fn
- *
- * @returns {void}
- */
+ /**
+ * This is based on Paul Irish's shim, but looks quite odd in comparison. Why?
+ * Because
+ * a) it shouldn't affect the global requestAnimationFrame function
+ * b) it shouldn't pass on the time that has passed
+ *
+ * @param {Function} fn
+ *
+ * @returns {void}
+ */
lm.utils.animFrame = function (fn) {
return (window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
@@ -178,16 +178,16 @@
.replace('.', '');
};
- /**
- * A basic XSS filter. It is ultimately up to the
- * implementing developer to make sure their particular
- * applications and usecases are save from cross site scripting attacks
- *
- * @param {String} input
- * @param {Boolean} keepTags
- *
- * @returns {String} filtered input
- */
+ /**
+ * A basic XSS filter. It is ultimately up to the
+ * implementing developer to make sure their particular
+ * applications and usecases are save from cross site scripting attacks
+ *
+ * @param {String} input
+ * @param {Boolean} keepTags
+ *
+ * @returns {String} filtered input
+ */
lm.utils.filterXss = function (input, keepTags) {
var output = input
@@ -206,41 +206,41 @@
}
};
- /**
- * Removes html tags from a string
- *
- * @param {String} input
- *
- * @returns {String} input without tags
- */
+ /**
+ * Removes html tags from a string
+ *
+ * @param {String} input
+ *
+ * @returns {String} input without tags
+ */
lm.utils.stripTags = function (input) {
return $.trim(input.replace(/(<([^>]+)>)/ig, ''));
};
- /**
- * A generic and very fast EventEmitter
- * implementation. On top of emitting the
- * actual event it emits an
- *
- * lm.utils.EventEmitter.ALL_EVENT
- *
- * event for every event triggered. This allows
- * to hook into it and proxy events forwards
- *
- * @constructor
- */
+ /**
+ * A generic and very fast EventEmitter
+ * implementation. On top of emitting the
+ * actual event it emits an
+ *
+ * lm.utils.EventEmitter.ALL_EVENT
+ *
+ * event for every event triggered. This allows
+ * to hook into it and proxy events forwards
+ *
+ * @constructor
+ */
lm.utils.EventEmitter = function () {
this._mSubscriptions = {};
this._mSubscriptions[lm.utils.EventEmitter.ALL_EVENT] = [];
- /**
- * Listen for events
- *
- * @param {String} sEvent The name of the event to listen to
- * @param {Function} fCallback The callback to execute when the event occurs
- * @param {[Object]} oContext The value of the this pointer within the callback function
- *
- * @returns {void}
- */
+ /**
+ * Listen for events
+ *
+ * @param {String} sEvent The name of the event to listen to
+ * @param {Function} fCallback The callback to execute when the event occurs
+ * @param {[Object]} oContext The value of the this pointer within the callback function
+ *
+ * @returns {void}
+ */
this.on = function (sEvent, fCallback, oContext) {
if (!lm.utils.isFunction(fCallback)) {
throw new Error('Tried to listen to event ' + sEvent + ' with non-function callback ' + fCallback);
@@ -253,14 +253,14 @@
this._mSubscriptions[sEvent].push({ fn: fCallback, ctx: oContext });
};
- /**
- * Emit an event and notify listeners
- *
- * @param {String} sEvent The name of the event
- * @param {Mixed} various additional arguments that will be passed to the listener
- *
- * @returns {void}
- */
+ /**
+ * Emit an event and notify listeners
+ *
+ * @param {String} sEvent The name of the event
+ * @param {Mixed} various additional arguments that will be passed to the listener
+ *
+ * @returns {void}
+ */
this.emit = function (sEvent) {
var i, ctx, args;
@@ -286,15 +286,15 @@
}
};
- /**
- * Removes a listener for an event, or all listeners if no callback and context is provided.
- *
- * @param {String} sEvent The name of the event
- * @param {Function} fCallback The previously registered callback method (optional)
- * @param {Object} oContext The previously registered context (optional)
- *
- * @returns {void}
- */
+ /**
+ * Removes a listener for an event, or all listeners if no callback and context is provided.
+ *
+ * @param {String} sEvent The name of the event
+ * @param {Function} fCallback The previously registered callback method (optional)
+ * @param {Object} oContext The previously registered context (optional)
+ *
+ * @returns {void}
+ */
this.unbind = function (sEvent, fCallback, oContext) {
if (!this._mSubscriptions[sEvent]) {
throw new Error('No subscribtions to unsubscribe for event ' + sEvent);
@@ -318,28 +318,28 @@
}
};
- /**
- * Alias for unbind
- */
+ /**
+ * Alias for unbind
+ */
this.off = this.unbind;
- /**
- * Alias for emit
- */
+ /**
+ * Alias for emit
+ */
this.trigger = this.emit;
};
- /**
- * The name of the event that's triggered for every other event
- *
- * usage
- *
- * myEmitter.on( lm.utils.EventEmitter.ALL_EVENT, function( eventName, argsArray ){
- * //do stuff
- * });
- *
- * @type {String}
- */
+ /**
+ * The name of the event that's triggered for every other event
+ *
+ * usage
+ *
+ * myEmitter.on( lm.utils.EventEmitter.ALL_EVENT, function( eventName, argsArray ){
+ * //do stuff
+ * });
+ *
+ * @type {String}
+ */
lm.utils.EventEmitter.ALL_EVENT = '__all';
lm.utils.DragListener = function (eElement, nButtonCode) {
lm.utils.EventEmitter.call(this);
@@ -349,14 +349,14 @@
this._eBody = $(document.body);
this._nButtonCode = nButtonCode || 0;
- /**
- * The delay after which to start the drag in milliseconds
- */
+ /**
+ * The delay after which to start the drag in milliseconds
+ */
this._nDelay = 200;
- /**
- * The distance the mouse needs to be moved to qualify as a drag
- */
+ /**
+ * The distance the mouse needs to be moved to qualify as a drag
+ */
this._nDistance = 10;//TODO - works better with delay only
this._nX = 0;
@@ -459,16 +459,16 @@
};
}
});
- /**
- * The main class that will be exposed as GoldenLayout.
- *
- * @public
- * @constructor
- * @param {GoldenLayout config} config
- * @param {[DOM element container]} container Can be a jQuery selector string or a Dom element. Defaults to body
- *
- * @returns {VOID}
- */
+ /**
+ * The main class that will be exposed as GoldenLayout.
+ *
+ * @public
+ * @constructor
+ * @param {GoldenLayout config} config
+ * @param {[DOM element container]} container Can be a jQuery selector string or a Dom element. Defaults to body
+ *
+ * @returns {VOID}
+ */
lm.LayoutManager = function (config, container) {
if (!$ || typeof $.noConflict !== 'function') {
@@ -521,59 +521,59 @@
};
};
- /**
- * Hook that allows to access private classes
- */
+ /**
+ * Hook that allows to access private classes
+ */
lm.LayoutManager.__lm = lm;
- /**
- * Takes a GoldenLayout configuration object and
- * replaces its keys and values recursively with
- * one letter codes
- *
- * @static
- * @public
- * @param {Object} config A GoldenLayout config object
- *
- * @returns {Object} minified config
- */
+ /**
+ * Takes a GoldenLayout configuration object and
+ * replaces its keys and values recursively with
+ * one letter codes
+ *
+ * @static
+ * @public
+ * @param {Object} config A GoldenLayout config object
+ *
+ * @returns {Object} minified config
+ */
lm.LayoutManager.minifyConfig = function (config) {
return (new lm.utils.ConfigMinifier()).minifyConfig(config);
};
- /**
- * Takes a configuration Object that was previously minified
- * using minifyConfig and returns its original version
- *
- * @static
- * @public
- * @param {Object} minifiedConfig
- *
- * @returns {Object} the original configuration
- */
+ /**
+ * Takes a configuration Object that was previously minified
+ * using minifyConfig and returns its original version
+ *
+ * @static
+ * @public
+ * @param {Object} minifiedConfig
+ *
+ * @returns {Object} the original configuration
+ */
lm.LayoutManager.unminifyConfig = function (config) {
return (new lm.utils.ConfigMinifier()).unminifyConfig(config);
};
lm.utils.copy(lm.LayoutManager.prototype, {
- /**
- * Register a component with the layout manager. If a configuration node
- * of type component is reached it will look up componentName and create the
- * associated component
- *
- * {
- * type: "component",
- * componentName: "EquityNewsFeed",
- * componentState: { "feedTopic": "us-bluechips" }
- * }
- *
- * @public
- * @param {String} name
- * @param {Function} constructor
- *
- * @returns {void}
- */
+ /**
+ * Register a component with the layout manager. If a configuration node
+ * of type component is reached it will look up componentName and create the
+ * associated component
+ *
+ * {
+ * type: "component",
+ * componentName: "EquityNewsFeed",
+ * componentState: { "feedTopic": "us-bluechips" }
+ * }
+ *
+ * @public
+ * @param {String} name
+ * @param {Function} constructor
+ *
+ * @returns {void}
+ */
registerComponent: function (name, constructor) {
if (typeof constructor !== 'function') {
throw new Error('Please register a constructor function');
@@ -586,12 +586,12 @@
this._components[name] = constructor;
},
- /**
- * Creates a layout configuration object based on the the current state
- *
- * @public
- * @returns {Object} GoldenLayout configuration
- */
+ /**
+ * Creates a layout configuration object based on the the current state
+ *
+ * @public
+ * @returns {Object} GoldenLayout configuration
+ */
toConfig: function (root) {
var config, next, i;
@@ -603,18 +603,18 @@
throw new Error('Root must be a ContentItem');
}
- /*
- * settings & labels
- */
+ /*
+ * settings & labels
+ */
config = {
settings: lm.utils.copy({}, this.config.settings),
dimensions: lm.utils.copy({}, this.config.dimensions),
labels: lm.utils.copy({}, this.config.labels)
};
- /*
- * Content
- */
+ /*
+ * Content
+ */
config.content = [];
next = function (configNode, item) {
var key, i;
@@ -641,30 +641,30 @@
next(config, this.root);
}
- /*
- * Retrieve config for subwindows
- */
+ /*
+ * Retrieve config for subwindows
+ */
this._$reconcilePopoutWindows();
config.openPopouts = [];
for (i = 0; i < this.openPopouts.length; i++) {
config.openPopouts.push(this.openPopouts[i].toConfig());
}
- /*
- * Add maximised item
- */
+ /*
+ * Add maximised item
+ */
config.maximisedItemId = this._maximisedItem ? '__glMaximised' : null;
return config;
},
- /**
- * Returns a previously registered component
- *
- * @public
- * @param {String} name The name used
- *
- * @returns {Function}
- */
+ /**
+ * Returns a previously registered component
+ *
+ * @public
+ * @param {String} name The name used
+ *
+ * @returns {Function}
+ */
getComponent: function (name) {
if (this._components[name] === undefined) {
throw new lm.errors.ConfigurationError('Unknown component "' + name + '"');
@@ -673,44 +673,44 @@
return this._components[name];
},
- /**
- * Creates the actual layout. Must be called after all initial components
- * are registered. Recurses through the configuration and sets up
- * the item tree.
- *
- * If called before the document is ready it adds itself as a listener
- * to the document.ready event
- *
- * @public
- *
- * @returns {void}
- */
+ /**
+ * Creates the actual layout. Must be called after all initial components
+ * are registered. Recurses through the configuration and sets up
+ * the item tree.
+ *
+ * If called before the document is ready it adds itself as a listener
+ * to the document.ready event
+ *
+ * @public
+ *
+ * @returns {void}
+ */
init: function () {
- /**
- * Create the popout windows straight away. If popouts are blocked
- * an error is thrown on the same 'thread' rather than a timeout and can
- * be caught. This also prevents any further initilisation from taking place.
- */
+ /**
+ * Create the popout windows straight away. If popouts are blocked
+ * an error is thrown on the same 'thread' rather than a timeout and can
+ * be caught. This also prevents any further initilisation from taking place.
+ */
if (this._subWindowsCreated === false) {
this._createSubWindows();
this._subWindowsCreated = true;
}
- /**
- * If the document isn't ready yet, wait for it.
- */
+ /**
+ * If the document isn't ready yet, wait for it.
+ */
if (document.readyState === 'loading' || document.body === null) {
$(document).ready(lm.utils.fnBind(this.init, this));
return;
}
- /**
- * If this is a subwindow, wait a few milliseconds for the original
- * page's js calls to be executed, then replace the bodies content
- * with GoldenLayout
- */
+ /**
+ * If this is a subwindow, wait a few milliseconds for the original
+ * page's js calls to be executed, then replace the bodies content
+ * with GoldenLayout
+ */
if (this.isSubWindow === true && this._creationTimeoutPassed === false) {
setTimeout(lm.utils.fnBind(this.init, this), 7);
this._creationTimeoutPassed = true;
@@ -732,15 +732,15 @@
this.emit('initialised');
},
- /**
- * Updates the layout managers size
- *
- * @public
- * @param {[int]} width height in pixels
- * @param {[int]} height width in pixels
- *
- * @returns {void}
- */
+ /**
+ * Updates the layout managers size
+ *
+ * @public
+ * @param {[int]} width height in pixels
+ * @param {[int]} height width in pixels
+ *
+ * @returns {void}
+ */
updateSize: function (width, height) {
if (arguments.length === 2) {
this.width = width;
@@ -763,13 +763,13 @@
}
},
- /**
- * Destroys the LayoutManager instance itself as well as every ContentItem
- * within it. After this is called nothing should be left of the LayoutManager.
- *
- * @public
- * @returns {void}
- */
+ /**
+ * Destroys the LayoutManager instance itself as well as every ContentItem
+ * within it. After this is called nothing should be left of the LayoutManager.
+ *
+ * @public
+ * @returns {void}
+ */
destroy: function () {
if (this.isInitialised === false) {
return;
@@ -793,16 +793,16 @@
this._dragSources = [];
},
- /**
- * Recursively creates new item tree structures based on a provided
- * ItemConfiguration object
- *
- * @public
- * @param {Object} config ItemConfig
- * @param {[ContentItem]} parent The item the newly created item should be a child of
- *
- * @returns {lm.items.ContentItem}
- */
+ /**
+ * Recursively creates new item tree structures based on a provided
+ * ItemConfiguration object
+ *
+ * @public
+ * @param {Object} config ItemConfig
+ * @param {[ContentItem]} parent The item the newly created item should be a child of
+ *
+ * @returns {lm.items.ContentItem}
+ */
createContentItem: function (config, parent) {
var typeErrorMsg, contentItem;
@@ -823,9 +823,9 @@
}
- /**
- * We add an additional stack around every component that's not within a stack anyways.
- */
+ /**
+ * We add an additional stack around every component that's not within a stack anyways.
+ */
if (
// If this is a component
config.type === 'component' &&
@@ -851,17 +851,17 @@
return contentItem;
},
- /**
- * Creates a popout window with the specified content and dimensions
- *
- * @param {Object|lm.itemsAbstractContentItem} configOrContentItem
- * @param {[Object]} dimensions A map with width, height, left and top
- * @param {[String]} parentId the id of the element this item will be appended to
- * when popIn is called
- * @param {[Number]} indexInParent The position of this item within its parent element
+ /**
+ * Creates a popout window with the specified content and dimensions
+ *
+ * @param {Object|lm.itemsAbstractContentItem} configOrContentItem
+ * @param {[Object]} dimensions A map with width, height, left and top
+ * @param {[String]} parentId the id of the element this item will be appended to
+ * when popIn is called
+ * @param {[Number]} indexInParent The position of this item within its parent element
- * @returns {lm.controls.BrowserPopout}
- */
+ * @returns {lm.controls.BrowserPopout}
+ */
createPopout: function (configOrContentItem, dimensions, parentId, indexInParent) {
var config = configOrContentItem,
isItem = configOrContentItem instanceof lm.items.AbstractContentItem,
@@ -879,14 +879,14 @@
config = this.toConfig(configOrContentItem).content;
parentId = lm.utils.getUniqueId();
- /**
- * If the item is the only component within a stack or for some
- * other reason the only child of its parent the parent will be destroyed
- * when the child is removed.
- *
- * In order to support this we move up the tree until we find something
- * that will remain after the item is being popped out
- */
+ /**
+ * If the item is the only component within a stack or for some
+ * other reason the only child of its parent the parent will be destroyed
+ * when the child is removed.
+ *
+ * In order to support this we move up the tree until we find something
+ * that will remain after the item is being popped out
+ */
parent = configOrContentItem.parent;
child = configOrContentItem;
while (parent.contentItems.length === 1 && !parent.isRoot) {
@@ -946,16 +946,16 @@
return browserPopout;
},
- /**
- * Attaches DragListener to any given DOM element
- * and turns it into a way of creating new ContentItems
- * by 'dragging' the DOM element into the layout
- *
- * @param {jQuery DOM element} element
- * @param {Object|Function} itemConfig for the new item to be created, or a function which will provide it
- *
- * @returns {void}
- */
+ /**
+ * Attaches DragListener to any given DOM element
+ * and turns it into a way of creating new ContentItems
+ * by 'dragging' the DOM element into the layout
+ *
+ * @param {jQuery DOM element} element
+ * @param {Object|Function} itemConfig for the new item to be created, or a function which will provide it
+ *
+ * @returns {void}
+ */
createDragSource: function (element, itemConfig) {
this.config.settings.constrainDragToContainer = false;
var dragSource = new lm.controls.DragSource($(element), itemConfig, this);
@@ -964,17 +964,17 @@
return dragSource;
},
- /**
- * Programmatically selects an item. This deselects
- * the currently selected item, selects the specified item
- * and emits a selectionChanged event
- *
- * @param {lm.item.AbstractContentItem} item#
- * @param {[Boolean]} _$silent Wheather to notify the item of its selection
- * @event selectionChanged
- *
- * @returns {VOID}
- */
+ /**
+ * Programmatically selects an item. This deselects
+ * the currently selected item, selects the specified item
+ * and emits a selectionChanged event
+ *
+ * @param {lm.item.AbstractContentItem} item#
+ * @param {[Boolean]} _$silent Wheather to notify the item of its selection
+ * @event selectionChanged
+ *
+ * @returns {VOID}
+ */
selectItem: function (item, _$silent) {
if (this.config.settings.selectionEnabled !== true) {
@@ -998,9 +998,9 @@
this.emit('selectionChanged', item);
},
- /*************************
- * PACKAGE PRIVATE
- *************************/
+ /*************************
+ * PACKAGE PRIVATE
+ *************************/
_$maximiseItem: function (contentItem) {
if (this._maximisedItem !== null) {
this._$minimiseItem(this._maximisedItem);
@@ -1028,20 +1028,20 @@
this.emit('stateChanged');
},
- /**
- * This method is used to get around sandboxed iframe restrictions.
- * If 'allow-top-navigation' is not specified in the iframe's 'sandbox' attribute
- * (as is the case with codepens) the parent window is forbidden from calling certain
- * methods on the child, such as window.close() or setting document.location.href.
- *
- * This prevented GoldenLayout popouts from popping in in codepens. The fix is to call
- * _$closeWindow on the child window's gl instance which (after a timeout to disconnect
- * the invoking method from the close call) closes itself.
- *
- * @packagePrivate
- *
- * @returns {void}
- */
+ /**
+ * This method is used to get around sandboxed iframe restrictions.
+ * If 'allow-top-navigation' is not specified in the iframe's 'sandbox' attribute
+ * (as is the case with codepens) the parent window is forbidden from calling certain
+ * methods on the child, such as window.close() or setting document.location.href.
+ *
+ * This prevented GoldenLayout popouts from popping in in codepens. The fix is to call
+ * _$closeWindow on the child window's gl instance which (after a timeout to disconnect
+ * the invoking method from the close call) closes itself.
+ *
+ * @packagePrivate
+ *
+ * @returns {void}
+ */
_$closeWindow: function () {
window.setTimeout(function () {
window.close();
@@ -1088,13 +1088,13 @@
var i, area, allContentItems = this._getAllContentItems();
this._itemAreas = [];
- /**
- * If the last item is dragged out, highlight the entire container size to
- * allow to re-drop it. allContentItems[ 0 ] === this.root at this point
- *
- * Don't include root into the possible drop areas though otherwise since it
- * will used for every gap in the layout, e.g. splitters
- */
+ /**
+ * If the last item is dragged out, highlight the entire container size to
+ * allow to re-drop it. allContentItems[ 0 ] === this.root at this point
+ *
+ * Don't include root into the possible drop areas though otherwise since it
+ * will used for every gap in the layout, e.g. splitters
+ */
if (allContentItems.length === 1) {
this._itemAreas.push(this.root._$getArea());
return;
@@ -1124,18 +1124,18 @@
}
},
- /**
- * Takes a contentItem or a configuration and optionally a parent
- * item and returns an initialised instance of the contentItem.
- * If the contentItem is a function, it is first called
- *
- * @packagePrivate
- *
- * @param {lm.items.AbtractContentItem|Object|Function} contentItemOrConfig
- * @param {lm.items.AbtractContentItem} parent Only necessary when passing in config
- *
- * @returns {lm.items.AbtractContentItem}
- */
+ /**
+ * Takes a contentItem or a configuration and optionally a parent
+ * item and returns an initialised instance of the contentItem.
+ * If the contentItem is a function, it is first called
+ *
+ * @packagePrivate
+ *
+ * @param {lm.items.AbtractContentItem|Object|Function} contentItemOrConfig
+ * @param {lm.items.AbtractContentItem} parent Only necessary when passing in config
+ *
+ * @returns {lm.items.AbtractContentItem}
+ */
_$normalizeContentItem: function (contentItemOrConfig, parent) {
if (!contentItemOrConfig) {
throw new Error('No content item defined');
@@ -1158,15 +1158,15 @@
}
},
- /**
- * Iterates through the array of open popout windows and removes the ones
- * that are effectively closed. This is necessary due to the lack of reliably
- * listening for window.close / unload events in a cross browser compatible fashion.
- *
- * @packagePrivate
- *
- * @returns {void}
- */
+ /**
+ * Iterates through the array of open popout windows and removes the ones
+ * that are effectively closed. This is necessary due to the lack of reliably
+ * listening for window.close / unload events in a cross browser compatible fashion.
+ *
+ * @packagePrivate
+ *
+ * @returns {void}
+ */
_$reconcilePopoutWindows: function () {
var openPopouts = [], i;
@@ -1185,17 +1185,17 @@
},
- /***************************
- * PRIVATE
- ***************************/
- /**
- * Returns a flattened array of all content items,
- * regardles of level or type
- *
- * @private
- *
- * @returns {void}
- */
+ /***************************
+ * PRIVATE
+ ***************************/
+ /**
+ * Returns a flattened array of all content items,
+ * regardles of level or type
+ *
+ * @private
+ *
+ * @returns {void}
+ */
_getAllContentItems: function () {
var allContentItems = [];
@@ -1214,13 +1214,13 @@
return allContentItems;
},
- /**
- * Binds to DOM/BOM events on init
- *
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Binds to DOM/BOM events on init
+ *
+ * @private
+ *
+ * @returns {void}
+ */
_bindEvents: function () {
if (this._isFullPage) {
$(window).resize(this._resizeFunction);
@@ -1228,27 +1228,27 @@
$(window).on('unload beforeunload', this._unloadFunction);
},
- /**
- * Debounces resize events
- *
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Debounces resize events
+ *
+ * @private
+ *
+ * @returns {void}
+ */
_onResize: function () {
clearTimeout(this._resizeTimeoutId);
this._resizeTimeoutId = setTimeout(lm.utils.fnBind(this.updateSize, this), 100);
},
- /**
- * Extends the default config with the user specific settings and applies
- * derivations. Please note that there's a seperate method (AbstractContentItem._extendItemNode)
- * that deals with the extension of item configs
- *
- * @param {Object} config
- * @static
- * @returns {Object} config
- */
+ /**
+ * Extends the default config with the user specific settings and applies
+ * derivations. Please note that there's a seperate method (AbstractContentItem._extendItemNode)
+ * that deals with the extension of item configs
+ *
+ * @param {Object} config
+ * @static
+ * @returns {Object} config
+ */
_createConfig: function (config) {
var windowConfigKey = lm.utils.getQueryStringParam('gl-window');
@@ -1283,14 +1283,14 @@
return config;
},
- /**
- * This is executed when GoldenLayout detects that it is run
- * within a previously opened popout window.
- *
- * @private
- *
- * @returns {void}
- */
+ /**
+ * This is executed when GoldenLayout detects that it is run
+ * within a previously opened popout window.
+ *
+ * @private
+ *
+ * @returns {void}
+ */
_adjustToWindowMode: function () {
var popInButton = $('<div class="lm_popin" title="' + this.config.labels.popin + '">' +
'<div class="lm_icon"></div>' +
@@ -1310,26 +1310,26 @@
.css('visibility', 'visible')
.append(popInButton);
- /*
- * This seems a bit pointless, but actually causes a reflow/re-evaluation getting around
- * slickgrid's "Cannot find stylesheet." bug in chrome
- */
+ /*
+ * This seems a bit pointless, but actually causes a reflow/re-evaluation getting around
+ * slickgrid's "Cannot find stylesheet." bug in chrome
+ */
var x = document.body.offsetHeight; // jshint ignore:line
- /*
- * Expose this instance on the window object
- * to allow the opening window to interact with
- * it
- */
+ /*
+ * Expose this instance on the window object
+ * to allow the opening window to interact with
+ * it
+ */
window.__glInstance = this;
},
- /**
- * Creates Subwindows (if there are any). Throws an error
- * if popouts are blocked.
- *
- * @returns {void}
- */
+ /**
+ * Creates Subwindows (if there are any). Throws an error
+ * if popouts are blocked.
+ *
+ * @returns {void}
+ */
_createSubWindows: function () {
var i, popout;
@@ -1345,13 +1345,13 @@
}
},
- /**
- * Determines what element the layout will be created in
- *
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Determines what element the layout will be created in
+ *
+ * @private
+ *
+ * @returns {void}
+ */
_setContainer: function () {
var container = $(this.container || 'body');
@@ -1377,13 +1377,13 @@
this.container = container;
},
- /**
- * Kicks of the initial, recursive creation chain
- *
- * @param {Object} config GoldenLayout Config
- *
- * @returns {void}
- */
+ /**
+ * Kicks of the initial, recursive creation chain
+ *
+ * @param {Object} config GoldenLayout Config
+ *
+ * @returns {void}
+ */
_create: function (config) {
var errorMsg;
@@ -1410,12 +1410,12 @@
}
},
- /**
- * Called when the window is closed or the user navigates away
- * from the page
- *
- * @returns {void}
- */
+ /**
+ * Called when the window is closed or the user navigates away
+ * from the page
+ *
+ * @returns {void}
+ */
_onUnload: function () {
if (this.config.settings.closePopoutsOnUnload === true) {
for (var i = 0; i < this.openPopouts.length; i++) {
@@ -1424,11 +1424,11 @@
}
},
- /**
- * Adjusts the number of columns to be lower to fit the screen and still maintain minItemWidth.
- *
- * @returns {void}
- */
+ /**
+ * Adjusts the number of columns to be lower to fit the screen and still maintain minItemWidth.
+ *
+ * @returns {void}
+ */
_adjustColumnsResponsive: function () {
// If there is no min width set, or not content items, do nothing.
@@ -1470,21 +1470,21 @@
this._updatingColumnsResponsive = false;
},
- /**
- * Determines if responsive layout should be used.
- *
- * @returns {bool} - True if responsive layout should be used; otherwise false.
- */
+ /**
+ * Determines if responsive layout should be used.
+ *
+ * @returns {bool} - True if responsive layout should be used; otherwise false.
+ */
_useResponsiveLayout: function () {
return this.config.settings && (this.config.settings.responsiveMode == 'always' || (this.config.settings.responsiveMode == 'onload' && this._firstLoad));
},
- /**
- * Adds all children of a node to another container recursively.
- * @param {object} container - Container to add child content items to.
- * @param {object} node - Node to search for content items.
- * @returns {void}
- */
+ /**
+ * Adds all children of a node to another container recursively.
+ * @param {object} container - Container to add child content items to.
+ * @param {object} node - Node to search for content items.
+ * @returns {void}
+ */
_addChildContentItemsToContainer: function (container, node) {
if (node.type === 'stack') {
node.contentItems.forEach(function (item) {
@@ -1499,10 +1499,10 @@
}
},
- /**
- * Finds all the stack containers.
- * @returns {array} - The found stack containers.
- */
+ /**
+ * Finds all the stack containers.
+ * @returns {array} - The found stack containers.
+ */
_findAllStackContainers: function () {
var stackContainers = [];
this._findAllStackContainersRecursive(stackContainers, this.root);
@@ -1510,14 +1510,14 @@
return stackContainers;
},
- /**
- * Finds all the stack containers.
- *
- * @param {array} - Set of containers to populate.
- * @param {object} - Current node to process.
- *
- * @returns {void}
- */
+ /**
+ * Finds all the stack containers.
+ *
+ * @param {array} - Set of containers to populate.
+ * @param {object} - Current node to process.
+ *
+ * @returns {void}
+ */
_findAllStackContainersRecursive: function (stackContainers, node) {
node.contentItems.forEach(lm.utils.fnBind(function (item) {
if (item.type == 'stack') {
@@ -1530,9 +1530,9 @@
}
});
- /**
- * Expose the Layoutmanager as the single entrypoint using UMD
- */
+ /**
+ * Expose the Layoutmanager as the single entrypoint using UMD
+ */
(function () {
/* global define */
if (typeof define === 'function' && define.amd) {
@@ -1567,15 +1567,15 @@
showCloseIcon: true,
responsiveMode: 'onload', // Can be onload, always, or none.
tabOverlapAllowance: 0, // maximum pixel overlap per tab
- reorderOnTabMenuClick: true,
+ reorderOnTabMenuClick: false, //do not reorder! - horizontal scroll
tabControlOffset: 10
},
dimensions: {
- borderWidth: 5,
+ borderWidth: 3,
borderGrabWidth: 5,
minItemHeight: 10,
- minItemWidth: 10,
- headerHeight: 20,
+ minItemWidth: 20,
+ headerHeight: 27,
dragProxyWidth: 300,
dragProxyHeight: 200
},
@@ -1611,36 +1611,36 @@
lm.utils.copy(lm.container.ItemContainer.prototype, {
- /**
- * Get the inner DOM element the container's content
- * is intended to live in
- *
- * @returns {DOM element}
- */
+ /**
+ * Get the inner DOM element the container's content
+ * is intended to live in
+ *
+ * @returns {DOM element}
+ */
getElement: function () {
return this._contentElement;
},
- /**
- * Hide the container. Notifies the containers content first
- * and then hides the DOM node. If the container is already hidden
- * this should have no effect
- *
- * @returns {void}
- */
+ /**
+ * Hide the container. Notifies the containers content first
+ * and then hides the DOM node. If the container is already hidden
+ * this should have no effect
+ *
+ * @returns {void}
+ */
hide: function () {
this.emit('hide');
this.isHidden = true;
this._element.hide();
},
- /**
- * Shows a previously hidden container. Notifies the
- * containers content first and then shows the DOM element.
- * If the container is already visible this has no effect.
- *
- * @returns {void}
- */
+ /**
+ * Shows a previously hidden container. Notifies the
+ * containers content first and then shows the DOM element.
+ * If the container is already visible this has no effect.
+ *
+ * @returns {void}
+ */
show: function () {
this.emit('show');
this.isHidden = false;
@@ -1651,19 +1651,19 @@
}
},
- /**
- * Set the size from within the container. Traverses up
- * the item tree until it finds a row or column element
- * and resizes its items accordingly.
- *
- * If this container isn't a descendant of a row or column
- * it returns false
- * @todo Rework!!!
- * @param {Number} width The new width in pixel
- * @param {Number} height The new height in pixel
- *
- * @returns {Boolean} resizeSuccesful
- */
+ /**
+ * Set the size from within the container. Traverses up
+ * the item tree until it finds a row or column element
+ * and resizes its items accordingly.
+ *
+ * If this container isn't a descendant of a row or column
+ * it returns false
+ * @todo Rework!!!
+ * @param {Number} width The new width in pixel
+ * @param {Number} height The new height in pixel
+ *
+ * @returns {Boolean} resizeSuccesful
+ */
setSize: function (width, height) {
var rowOrColumn = this.parent,
rowOrColumnChild = this,
@@ -1679,9 +1679,9 @@
rowOrColumn = rowOrColumn.parent;
- /**
- * No row or column has been found
- */
+ /**
+ * No row or column has been found
+ */
if (rowOrColumn.isRoot) {
return false;
}
@@ -1707,13 +1707,13 @@
return true;
},
- /**
- * Closes the container if it is closable. Can be called by
- * both the component within at as well as the contentItem containing
- * it. Emits a close event before the container itself is closed.
- *
- * @returns {void}
- */
+ /**
+ * Closes the container if it is closable. Can be called by
+ * both the component within at as well as the contentItem containing
+ * it. Emits a close event before the container itself is closed.
+ *
+ * @returns {void}
+ */
close: function () {
if (this._config.isClosable) {
this.emit('close');
@@ -1721,55 +1721,55 @@
}
},
- /**
- * Returns the current state object
- *
- * @returns {Object} state
- */
+ /**
+ * Returns the current state object
+ *
+ * @returns {Object} state
+ */
getState: function () {
return this._config.componentState;
},
- /**
- * Merges the provided state into the current one
- *
- * @param {Object} state
- *
- * @returns {void}
- */
+ /**
+ * Merges the provided state into the current one
+ *
+ * @param {Object} state
+ *
+ * @returns {void}
+ */
extendState: function (state) {
this.setState($.extend(true, this.getState(), state));
},
- /**
- * Notifies the layout manager of a stateupdate
- *
- * @param {serialisable} state
- */
+ /**
+ * Notifies the layout manager of a stateupdate
+ *
+ * @param {serialisable} state
+ */
setState: function (state) {
this._config.componentState = state;
this.parent.emitBubblingEvent('stateChanged');
},
- /**
- * Set's the components title
- *
- * @param {String} title
- */
+ /**
+ * Set's the components title
+ *
+ * @param {String} title
+ */
setTitle: function (title) {
this.parent.setTitle(title);
},
- /**
- * Set's the containers size. Called by the container's component.
- * To set the size programmatically from within the container please
- * use the public setSize method
- *
- * @param {[Int]} width in px
- * @param {[Int]} height in px
- *
- * @returns {void}
- */
+ /**
+ * Set's the containers size. Called by the container's component.
+ * To set the size programmatically from within the container please
+ * use the public setSize method
+ *
+ * @param {[Int]} width in px
+ * @param {[Int]} height in px
+ *
+ * @returns {void}
+ */
_$setSize: function (width, height) {
if (width !== this.width || height !== this.height) {
this.width = width;
@@ -1784,22 +1784,22 @@
}
});
- /**
- * Pops a content item out into a new browser window.
- * This is achieved by
- *
- * - Creating a new configuration with the content item as root element
- * - Serializing and minifying the configuration
- * - Opening the current window's URL with the configuration as a GET parameter
- * - GoldenLayout when opened in the new window will look for the GET parameter
- * and use it instead of the provided configuration
- *
- * @param {Object} config GoldenLayout item config
- * @param {Object} dimensions A map with width, height, top and left
- * @param {String} parentId The id of the element the item will be appended to on popIn
- * @param {Number} indexInParent The position of this element within its parent
- * @param {lm.LayoutManager} layoutManager
- */
+ /**
+ * Pops a content item out into a new browser window.
+ * This is achieved by
+ *
+ * - Creating a new configuration with the content item as root element
+ * - Serializing and minifying the configuration
+ * - Opening the current window's URL with the configuration as a GET parameter
+ * - GoldenLayout when opened in the new window will look for the GET parameter
+ * and use it instead of the provided configuration
+ *
+ * @param {Object} config GoldenLayout item config
+ * @param {Object} dimensions A map with width, height, top and left
+ * @param {String} parentId The id of the element the item will be appended to on popIn
+ * @param {Number} indexInParent The position of this element within its parent
+ * @param {lm.LayoutManager} layoutManager
+ */
lm.controls.BrowserPopout = function (config, dimensions, parentId, indexInParent, layoutManager) {
lm.utils.EventEmitter.call(this);
this.isInitialised = false;
@@ -1853,10 +1853,10 @@
}
},
- /**
- * Returns the popped out item to its original position. If the original
- * parent isn't available anymore it falls back to the layout's topmost element
- */
+ /**
+ * Returns the popped out item to its original position. If the original
+ * parent isn't available anymore it falls back to the layout's topmost element
+ */
popIn: function () {
var childConfig,
parentItem,
@@ -1864,22 +1864,22 @@
if (this._parentId) {
- /*
- * The $.extend call seems a bit pointless, but it's crucial to
- * copy the config returned by this.getGlInstance().toConfig()
- * onto a new object. Internet Explorer keeps the references
- * to objects on the child window, resulting in the following error
- * once the child window is closed:
- *
- * The callee (server [not server application]) is not available and disappeared
- */
+ /*
+ * The $.extend call seems a bit pointless, but it's crucial to
+ * copy the config returned by this.getGlInstance().toConfig()
+ * onto a new object. Internet Explorer keeps the references
+ * to objects on the child window, resulting in the following error
+ * once the child window is closed:
+ *
+ * The callee (server [not server application]) is not available and disappeared
+ */
childConfig = $.extend(true, {}, this.getGlInstance().toConfig()).content[0];
parentItem = this._layoutManager.root.getItemsById(this._parentId)[0];
- /*
- * Fallback if parentItem is not available. Either add it to the topmost
- * item or make it the topmost item if the layout is empty
- */
+ /*
+ * Fallback if parentItem is not available. Either add it to the topmost
+ * item or make it the topmost item if the layout is empty
+ */
if (!parentItem) {
if (this._layoutManager.root.contentItems.length > 0) {
parentItem = this._layoutManager.root.contentItems[0];
@@ -1894,28 +1894,28 @@
this.close();
},
- /**
- * Creates the URL and window parameter
- * and opens a new window
- *
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Creates the URL and window parameter
+ * and opens a new window
+ *
+ * @private
+ *
+ * @returns {void}
+ */
_createWindow: function () {
var checkReadyInterval,
url = this._createUrl(),
- /**
- * Bogus title to prevent re-usage of existing window with the
- * same title. The actual title will be set by the new window's
- * GoldenLayout instance if it detects that it is in subWindowMode
- */
+ /**
+ * Bogus title to prevent re-usage of existing window with the
+ * same title. The actual title will be set by the new window's
+ * GoldenLayout instance if it detects that it is in subWindowMode
+ */
title = Math.floor(Math.random() * 1000000).toString(36),
- /**
- * The options as used in the window.open string
- */
+ /**
+ * The options as used in the window.open string
+ */
options = this._serializeWindowOptions({
width: this._dimensions.width,
height: this._dimensions.height,
@@ -1946,12 +1946,12 @@
.on('load', lm.utils.fnBind(this._positionWindow, this))
.on('unload beforeunload', lm.utils.fnBind(this._onClose, this));
- /**
- * Polling the childwindow to find out if GoldenLayout has been initialised
- * doesn't seem optimal, but the alternatives - adding a callback to the parent
- * window or raising an event on the window object - both would introduce knowledge
- * about the parent to the child window which we'd rather avoid
- */
+ /**
+ * Polling the childwindow to find out if GoldenLayout has been initialised
+ * doesn't seem optimal, but the alternatives - adding a callback to the parent
+ * window or raising an event on the window object - both would introduce knowledge
+ * about the parent to the child window which we'd rather avoid
+ */
checkReadyInterval = setInterval(lm.utils.fnBind(function () {
if (this._popoutWindow.__glInstance && this._popoutWindow.__glInstance.isInitialised) {
this._onInitialised();
@@ -1960,13 +1960,13 @@
}, this), 10);
},
- /**
- * Serialises a map of key:values to a window options string
- *
- * @param {Object} windowOptions
- *
- * @returns {String} serialised window options
- */
+ /**
+ * Serialises a map of key:values to a window options string
+ *
+ * @param {Object} windowOptions
+ *
+ * @returns {String} serialised window options
+ */
_serializeWindowOptions: function (windowOptions) {
var windowOptionsString = [], key;
@@ -1977,12 +1977,12 @@
return windowOptionsString.join(',');
},
- /**
- * Creates the URL for the new window, including the
- * config GET parameter
- *
- * @returns {String} URL
- */
+ /**
+ * Creates the URL for the new window, including the
+ * config GET parameter
+ *
+ * @returns {String} URL
+ */
_createUrl: function () {
var config = { content: this._config },
storageKey = 'gl-window-config-' + lm.utils.getUniqueId(),
@@ -2008,57 +2008,57 @@
}
},
- /**
- * Move the newly created window roughly to
- * where the component used to be.
- *
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Move the newly created window roughly to
+ * where the component used to be.
+ *
+ * @private
+ *
+ * @returns {void}
+ */
_positionWindow: function () {
this._popoutWindow.moveTo(this._dimensions.left, this._dimensions.top);
this._popoutWindow.focus();
},
- /**
- * Callback when the new window is opened and the GoldenLayout instance
- * within it is initialised
- *
- * @returns {void}
- */
+ /**
+ * Callback when the new window is opened and the GoldenLayout instance
+ * within it is initialised
+ *
+ * @returns {void}
+ */
_onInitialised: function () {
this.isInitialised = true;
this.getGlInstance().on('popIn', this.popIn, this);
this.emit('initialised');
},
- /**
- * Invoked 50ms after the window unload event
- *
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Invoked 50ms after the window unload event
+ *
+ * @private
+ *
+ * @returns {void}
+ */
_onClose: function () {
setTimeout(lm.utils.fnBind(this.emit, this, ['closed']), 50);
}
});
- /**
- * This class creates a temporary container
- * for the component whilst it is being dragged
- * and handles drag events
- *
- * @constructor
- * @private
- *
- * @param {Number} x The initial x position
- * @param {Number} y The initial y position
- * @param {lm.utils.DragListener} dragListener
- * @param {lm.LayoutManager} layoutManager
- * @param {lm.item.AbstractContentItem} contentItem
- * @param {lm.item.AbstractContentItem} originalParent
- */
+ /**
+ * This class creates a temporary container
+ * for the component whilst it is being dragged
+ * and handles drag events
+ *
+ * @constructor
+ * @private
+ *
+ * @param {Number} x The initial x position
+ * @param {Number} y The initial y position
+ * @param {lm.utils.DragListener} dragListener
+ * @param {lm.LayoutManager} layoutManager
+ * @param {lm.item.AbstractContentItem} contentItem
+ * @param {lm.item.AbstractContentItem} originalParent
+ */
lm.controls.DragProxy = function (x, y, dragListener, layoutManager, contentItem, originalParent) {
lm.utils.EventEmitter.call(this);
@@ -2120,19 +2120,19 @@
lm.utils.copy(lm.controls.DragProxy.prototype, {
- /**
- * Callback on every mouseMove event during a drag. Determines if the drag is
- * still within the valid drag area and calls the layoutManager to highlight the
- * current drop area
- *
- * @param {Number} offsetX The difference from the original x position in px
- * @param {Number} offsetY The difference from the original y position in px
- * @param {jQuery DOM event} event
- *
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Callback on every mouseMove event during a drag. Determines if the drag is
+ * still within the valid drag area and calls the layoutManager to highlight the
+ * current drop area
+ *
+ * @param {Number} offsetX The difference from the original x position in px
+ * @param {Number} offsetY The difference from the original y position in px
+ * @param {jQuery DOM event} event
+ *
+ * @private
+ *
+ * @returns {void}
+ */
_onDrag: function (offsetX, offsetY, event) {
event = event.originalEvent && event.originalEvent.touches ? event.originalEvent.touches[0] : event;
@@ -2148,16 +2148,16 @@
this._setDropPosition(x, y);
},
- /**
- * Sets the target position, highlighting the appropriate area
- *
- * @param {Number} x The x position in px
- * @param {Number} y The y position in px
- *
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Sets the target position, highlighting the appropriate area
+ *
+ * @param {Number} x The x position in px
+ * @param {Number} y The y position in px
+ *
+ * @private
+ *
+ * @returns {void}
+ */
_setDropPosition: function (x, y) {
this.element.css({ left: x, top: y });
this._area = this._layoutManager._$getArea(x, y);
@@ -2168,43 +2168,43 @@
}
},
- /**
- * Callback when the drag has finished. Determines the drop area
- * and adds the child to it
- *
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Callback when the drag has finished. Determines the drop area
+ * and adds the child to it
+ *
+ * @private
+ *
+ * @returns {void}
+ */
_onDrop: function () {
this._layoutManager.dropTargetIndicator.hide();
- /*
- * Valid drop area found
- */
+ /*
+ * Valid drop area found
+ */
if (this._area !== null) {
this._area.contentItem._$onDrop(this._contentItem, this._area);
- /**
- * No valid drop area available at present, but one has been found before.
- * Use it
- */
+ /**
+ * No valid drop area available at present, but one has been found before.
+ * Use it
+ */
} else if (this._lastValidArea !== null) {
this._lastValidArea.contentItem._$onDrop(this._contentItem, this._lastValidArea);
- /**
- * No valid drop area found during the duration of the drag. Return
- * content item to its original position if a original parent is provided.
- * (Which is not the case if the drag had been initiated by createDragSource)
- */
+ /**
+ * No valid drop area found during the duration of the drag. Return
+ * content item to its original position if a original parent is provided.
+ * (Which is not the case if the drag had been initiated by createDragSource)
+ */
} else if (this._originalParent) {
this._originalParent.addChild(this._contentItem);
- /**
- * The drag didn't ultimately end up with adding the content item to
- * any container. In order to ensure clean up happens, destroy the
- * content item.
- */
+ /**
+ * The drag didn't ultimately end up with adding the content item to
+ * any container. In order to ensure clean up happens, destroy the
+ * content item.
+ */
} else {
this._contentItem._$destroy();
}
@@ -2215,18 +2215,18 @@
this._layoutManager.emit('itemDropped', this._contentItem);
},
- /**
- * Removes the item from its original position within the tree
- *
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Removes the item from its original position within the tree
+ *
+ * @private
+ *
+ * @returns {void}
+ */
_updateTree: function () {
- /**
- * parent is null if the drag had been initiated by a external drag source
- */
+ /**
+ * parent is null if the drag had been initiated by a external drag source
+ */
if (this._contentItem.parent) {
this._contentItem.parent.removeChild(this._contentItem, true);
}
@@ -2234,13 +2234,13 @@
this._contentItem._$setParent(this);
},
- /**
- * Updates the Drag Proxie's dimensions
- *
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Updates the Drag Proxie's dimensions
+ *
+ * @private
+ *
+ * @returns {void}
+ */
_setDimensions: function () {
var dimensions = this._layoutManager.config.dimensions,
width = dimensions.dragProxyWidth,
@@ -2259,16 +2259,16 @@
}
});
- /**
- * Allows for any DOM item to create a component on drag
- * start tobe dragged into the Layout
- *
- * @param {jQuery element} element
- * @param {Object} itemConfig the configuration for the contentItem that will be created
- * @param {LayoutManager} layoutManager
- *
- * @constructor
- */
+ /**
+ * Allows for any DOM item to create a component on drag
+ * start tobe dragged into the Layout
+ *
+ * @param {jQuery element} element
+ * @param {Object} itemConfig the configuration for the contentItem that will be created
+ * @param {LayoutManager} layoutManager
+ *
+ * @constructor
+ */
lm.controls.DragSource = function (element, itemConfig, layoutManager) {
this._element = element;
this._itemConfig = itemConfig;
@@ -2280,11 +2280,11 @@
lm.utils.copy(lm.controls.DragSource.prototype, {
- /**
- * Called initially and after every drag
- *
- * @returns {void}
- */
+ /**
+ * Called initially and after every drag
+ *
+ * @returns {void}
+ */
_createDragListener: function () {
if (this._dragListener !== null) {
this._dragListener.destroy();
@@ -2306,14 +2306,14 @@
}
},
- /**
- * Callback for the DragListener's dragStart event
- *
- * @param {int} x the x position of the mouse on dragStart
- * @param {int} y the x position of the mouse on dragStart
- *
- * @returns {void}
- */
+ /**
+ * Callback for the DragListener's dragStart event
+ *
+ * @param {int} x the x position of the mouse on dragStart
+ * @param {int} y the x position of the mouse on dragStart
+ *
+ * @returns {void}
+ */
_onDragStart: function (x, y) {
var itemConfig = this._itemConfig;
if (lm.utils.isFunction(itemConfig)) {
@@ -2355,12 +2355,12 @@
this.element.hide();
}
});
- /**
- * This class represents a header above a Stack ContentItem.
- *
- * @param {lm.LayoutManager} layoutManager
- * @param {lm.item.AbstractContentItem} parent
- */
+ /**
+ * This class represents a header above a Stack ContentItem.
+ *
+ * @param {lm.LayoutManager} layoutManager
+ * @param {lm.item.AbstractContentItem} parent
+ */
lm.controls.Header = function (layoutManager, parent) {
lm.utils.EventEmitter.call(this);
@@ -2390,24 +2390,24 @@
this._createControls();
};
+ // '<ul class="lm_tabdropdown_list"></ul>',
lm.controls.Header._template = [
'<div class="lm_header">',
'<ul class="lm_tabs"></ul>',
'<ul class="lm_controls"></ul>',
- '<ul class="lm_tabdropdown_list"></ul>',
'</div>'
].join('');
lm.utils.copy(lm.controls.Header.prototype, {
- /**
- * Creates a new tab and associates it with a contentItem
- *
- * @param {lm.item.AbstractContentItem} contentItem
- * @param {Integer} index The position of the tab
- *
- * @returns {void}
- */
+ /**
+ * Creates a new tab and associates it with a contentItem
+ *
+ * @param {lm.item.AbstractContentItem} contentItem
+ * @param {Integer} index The position of the tab
+ *
+ * @returns {void}
+ */
createTab: function (contentItem, index) {
var tab, i;
@@ -2441,13 +2441,13 @@
this._updateTabSizes();
},
- /**
- * Finds a tab based on the contentItem its associated with and removes it.
- *
- * @param {lm.item.AbstractContentItem} contentItem
- *
- * @returns {void}
- */
+ /**
+ * Finds a tab based on the contentItem its associated with and removes it.
+ *
+ * @param {lm.item.AbstractContentItem} contentItem
+ *
+ * @returns {void}
+ */
removeTab: function (contentItem) {
for (var i = 0; i < this.tabs.length; i++) {
if (this.tabs[i].contentItem === contentItem) {
@@ -2460,11 +2460,11 @@
throw new Error('contentItem is not controlled by this header');
},
- /**
- * The programmatical equivalent of clicking a Tab.
- *
- * @param {lm.item.AbstractContentItem} contentItem
- */
+ /**
+ * The programmatical equivalent of clicking a Tab.
+ *
+ * @param {lm.item.AbstractContentItem} contentItem
+ */
setActiveContentItem: function (contentItem) {
var i, j, isActive, activeTab;
@@ -2477,32 +2477,33 @@
}
}
- if (this.layoutManager.config.settings.reorderOnTabMenuClick) {
- /**
- * If the tab selected was in the dropdown, move everything down one to make way for this one to be the first.
- * This will make sure the most used tabs stay visible.
- */
- if (this._lastVisibleTabIndex !== -1 && this.parent.config.activeItemIndex > this._lastVisibleTabIndex) {
- activeTab = this.tabs[this.parent.config.activeItemIndex];
- for (j = this.parent.config.activeItemIndex; j > 0; j--) {
- this.tabs[j] = this.tabs[j - 1];
- }
- this.tabs[0] = activeTab;
- this.parent.config.activeItemIndex = 0;
- }
- }
+ // glr: removed for new tab manager
+ // if (this.layoutManager.config.settings.reorderOnTabMenuClick) {
+ // /**
+ // * If the tab selected was in the dropdown, move everything down one to make way for this one to be the first.
+ // * This will make sure the most used tabs stay visible.
+ // */
+ // if (this._lastVisibleTabIndex !== -1 && this.parent.config.activeItemIndex > this._lastVisibleTabIndex) {
+ // activeTab = this.tabs[this.parent.config.activeItemIndex];
+ // for (j = this.parent.config.activeItemIndex; j > 0; j--) {
+ // this.tabs[j] = this.tabs[j - 1];
+ // }
+ // this.tabs[0] = activeTab;
+ // this.parent.config.activeItemIndex = 0;
+ // }
+ // }
this._updateTabSizes();
this.parent.emitBubblingEvent('stateChanged');
},
- /**
- * Programmatically operate with header position.
- *
- * @param {string} position one of ('top','left','right','bottom') to set or empty to get it.
- *
- * @returns {string} previous header position
- */
+ /**
+ * Programmatically operate with header position.
+ *
+ * @param {string} position one of ('top','left','right','bottom') to set or empty to get it.
+ *
+ * @returns {string} previous header position
+ */
position: function (position) {
var previous = this.parent._header.show;
if (previous && !this.parent._side)
@@ -2514,14 +2515,14 @@
return previous;
},
- /**
- * Programmatically set closability.
- *
- * @package private
- * @param {Boolean} isClosable Whether to enable/disable closability.
- *
- * @returns {Boolean} Whether the action was successful
- */
+ /**
+ * Programmatically set closability.
+ *
+ * @package private
+ * @param {Boolean} isClosable Whether to enable/disable closability.
+ *
+ * @returns {Boolean} Whether the action was successful
+ */
_$setClosable: function (isClosable) {
if (this.closeButton && this._isClosable()) {
this.closeButton.element[isClosable ? "show" : "hide"]();
@@ -2531,13 +2532,13 @@
return false;
},
- /**
- * Destroys the entire header
- *
- * @package private
- *
- * @returns {void}
- */
+ /**
+ * Destroys the entire header
+ *
+ * @package private
+ *
+ * @returns {void}
+ */
_$destroy: function () {
this.emit('destroy', this);
@@ -2548,20 +2549,20 @@
this.element.remove();
},
- /**
- * get settings from header
- *
- * @returns {string} when exists
- */
+ /**
+ * get settings from header
+ *
+ * @returns {string} when exists
+ */
_getHeaderSetting: function (name) {
if (name in this.parent._header)
return this.parent._header[name];
},
- /**
- * Creates the popout, maximise and close buttons in the header's top right corner
- *
- * @returns {void}
- */
+ /**
+ * Creates the popout, maximise and close buttons in the header's top right corner
+ *
+ * @returns {void}
+ */
_createControls: function () {
var closeStack,
popout,
@@ -2573,26 +2574,26 @@
tabDropdownLabel,
showTabDropdown;
- /**
- * Dropdown to show additional tabs.
- */
+ /**
+ * Dropdown to show additional tabs.
+ */
showTabDropdown = lm.utils.fnBind(this._showAdditionalTabsDropdown, this);
tabDropdownLabel = this.layoutManager.config.labels.tabDropdown;
- this.tabDropdownButton = new lm.controls.HeaderButton(this, tabDropdownLabel, 'lm_tabdropdown', showTabDropdown);
- this.tabDropdownButton.element.hide();
+ // this.tabDropdownButton = new lm.controls.HeaderButton(this, tabDropdownLabel, 'lm_tabdropdown', showTabDropdown);
+ // this.tabDropdownButton.element.hide();
- /**
- * Popout control to launch component in new window.
- */
+ /**
+ * Popout control to launch component in new window.
+ */
if (this._getHeaderSetting('popout')) {
popout = lm.utils.fnBind(this._onPopoutClick, this);
label = this._getHeaderSetting('popout');
new lm.controls.HeaderButton(this, label, 'lm_popout', popout);
}
- /**
- * Maximise control - set the component to the full size of the layout
- */
+ /**
+ * Maximise control - set the component to the full size of the layout
+ */
if (this._getHeaderSetting('maximise')) {
maximise = lm.utils.fnBind(this.parent.toggleMaximise, this.parent);
maximiseLabel = this._getHeaderSetting('maximise');
@@ -2608,9 +2609,9 @@
});
}
- /**
- * Close button
- */
+ /**
+ * Close button
+ */
if (this._isClosable()) {
closeStack = lm.utils.fnBind(this.parent.remove, this.parent);
label = this._getHeaderSetting('close');
@@ -2618,30 +2619,30 @@
}
},
- /**
- * Shows drop down for additional tabs when there are too many to display.
- *
- * @returns {void}
- */
+ /**
+ * Shows drop down for additional tabs when there are too many to display.
+ *
+ * @returns {void}
+ */
_showAdditionalTabsDropdown: function () {
this.tabDropdownContainer.show();
},
- /**
- * Hides drop down for additional tabs when there are too many to display.
- *
- * @returns {void}
- */
+ /**
+ * Hides drop down for additional tabs when there are too many to display.
+ *
+ * @returns {void}
+ */
_hideAdditionalTabsDropdown: function (e) {
this.tabDropdownContainer.hide();
},
- /**
- * Checks whether the header is closable based on the parent config and
- * the global config.
- *
- * @returns {Boolean} Whether the header is closable.
- */
+ /**
+ * Checks whether the header is closable based on the parent config and
+ * the global config.
+ *
+ * @returns {Boolean} Whether the header is closable.
+ */
_isClosable: function () {
return this.parent.config.isClosable && this.layoutManager.config.settings.showCloseIcon;
},
@@ -2655,32 +2656,29 @@
},
- /**
- * Invoked when the header's background is clicked (not it's tabs or controls)
- *
- * @param {jQuery DOM event} event
- *
- * @returns {void}
- */
+ /**
+ * Invoked when the header's background is clicked (not it's tabs or controls)
+ *
+ * @param {jQuery DOM event} event
+ *
+ * @returns {void}
+ */
_onHeaderClick: function (event) {
if (event.target === this.element[0]) {
this.parent.select();
}
},
- /**
- * Pushes the tabs to the tab dropdown if the available space is not sufficient
- *
- * @returns {void}
- */
+ /**
+ * Pushes the tabs to the tab dropdown if the available space is not sufficient
+ *
+ * @returns {void}
+ */
_updateTabSizes: function (showTabMenu) {
if (this.tabs.length === 0) {
return;
}
- //Show the menu based on function argument
- this.tabDropdownButton.element.toggle(showTabMenu === true);
-
var size = function (val) {
return val ? 'width' : 'height';
};
@@ -2835,14 +2833,14 @@
}
});
- /**
- * Represents an individual tab within a Stack's header
- *
- * @param {lm.controls.Header} header
- * @param {lm.items.AbstractContentItem} contentItem
- *
- * @constructor
- */
+ /**
+ * Represents an individual tab within a Stack's header
+ *
+ * @param {lm.controls.Header} header
+ * @param {lm.items.AbstractContentItem} contentItem
+ *
+ * @constructor
+ */
lm.controls.Tab = function (header, contentItem) {
this.header = header;
this.contentItem = contentItem;
@@ -2888,37 +2886,37 @@
}
};
- /**
- * The tab's html template
- *
- * @type {String}
- */
+ /**
+ * The tab's html template
+ *
+ * @type {String}
+ */
lm.controls.Tab._template = '<li class="lm_tab"><i class="lm_left"></i>' +
'<div class="lm_title_wrap"><input class="lm_title"/></div><div class="lm_close_tab"></div>' +
'<i class="lm_right"></i></li>';
lm.utils.copy(lm.controls.Tab.prototype, {
- /**
- * Sets the tab's title to the provided string and sets
- * its title attribute to a pure text representation (without
- * html tags) of the same string.
- *
- * @public
- * @param {String} title can contain html
- */
+ /**
+ * Sets the tab's title to the provided string and sets
+ * its title attribute to a pure text representation (without
+ * html tags) of the same string.
+ *
+ * @public
+ * @param {String} title can contain html
+ */
setTitle: function (title) {
this.element.attr('title', lm.utils.stripTags(title));
this.titleElement.html(title);
},
- /**
- * Sets this tab's active state. To programmatically
- * switch tabs, use header.setActiveContentItem( item ) instead.
- *
- * @public
- * @param {Boolean} isActive
- */
+ /**
+ * Sets this tab's active state. To programmatically
+ * switch tabs, use header.setActiveContentItem( item ) instead.
+ *
+ * @public
+ * @param {Boolean} isActive
+ */
setActive: function (isActive) {
if (isActive === this.isActive) {
return;
@@ -2932,12 +2930,12 @@
}
},
- /**
- * Destroys the tab
- *
- * @private
- * @returns {void}
- */
+ /**
+ * Destroys the tab
+ *
+ * @private
+ * @returns {void}
+ */
_$destroy: function () {
this._layoutManager.emit('tabDestroyed', this);
this.element.off('mousedown touchstart', this._onTabClickFn);
@@ -2950,15 +2948,15 @@
this.element.remove();
},
- /**
- * Callback for the DragListener
- *
- * @param {Number} x The tabs absolute x position
- * @param {Number} y The tabs absolute y position
- *
- * @private
- * @returns {void}
- */
+ /**
+ * Callback for the DragListener
+ *
+ * @param {Number} x The tabs absolute x position
+ * @param {Number} y The tabs absolute y position
+ *
+ * @private
+ * @returns {void}
+ */
_onDragStart: function (x, y) {
if (this.contentItem.parent.isMaximised === true) {
this.contentItem.parent.toggleMaximise();
@@ -2973,14 +2971,14 @@
);
},
- /**
- * Callback when the tab is clicked
- *
- * @param {jQuery DOM event} event
- *
- * @private
- * @returns {void}
- */
+ /**
+ * Callback when the tab is clicked
+ *
+ * @param {jQuery DOM event} event
+ *
+ * @private
+ * @returns {void}
+ */
_onTabClick: function (event) {
// left mouse button or tap
if (event.button === 0 || event.type === 'touchstart') {
@@ -2995,30 +2993,30 @@
}
},
- /**
- * Callback when the tab's close button is
- * clicked
- *
- * @param {jQuery DOM event} event
- *
- * @private
- * @returns {void}
- */
+ /**
+ * Callback when the tab's close button is
+ * clicked
+ *
+ * @param {jQuery DOM event} event
+ *
+ * @private
+ * @returns {void}
+ */
_onCloseClick: function (event) {
event.stopPropagation();
this.header.parent.removeChild(this.contentItem);
},
- /**
- * Callback to capture tab close button mousedown
- * to prevent tab from activating.
- *
- * @param (jQuery DOM event) event
- *
- * @private
- * @returns {void}
- */
+ /**
+ * Callback to capture tab close button mousedown
+ * to prevent tab from activating.
+ *
+ * @param (jQuery DOM event) event
+ *
+ * @private
+ * @returns {void}
+ */
_onCloseMousedown: function (event) {
event.stopPropagation();
}
@@ -3040,9 +3038,9 @@
},
transitionElements: function (fromElement, toElement) {
- /**
- * TODO - This is not quite as cool as expected. Review.
- */
+ /**
+ * TODO - This is not quite as cool as expected. Review.
+ */
return;
this._toElement = toElement;
this._animationStartTime = lm.utils.now();
@@ -3096,27 +3094,27 @@
lm.errors.ConfigurationError.prototype = new Error();
- /**
- * This is the baseclass that all content items inherit from.
- * Most methods provide a subset of what the sub-classes do.
- *
- * It also provides a number of functions for tree traversal
- *
- * @param {lm.LayoutManager} layoutManager
- * @param {item node configuration} config
- * @param {lm.item} parent
- *
- * @event stateChanged
- * @event beforeItemDestroyed
- * @event itemDestroyed
- * @event itemCreated
- * @event componentCreated
- * @event rowCreated
- * @event columnCreated
- * @event stackCreated
- *
- * @constructor
- */
+ /**
+ * This is the baseclass that all content items inherit from.
+ * Most methods provide a subset of what the sub-classes do.
+ *
+ * It also provides a number of functions for tree traversal
+ *
+ * @param {lm.LayoutManager} layoutManager
+ * @param {item node configuration} config
+ * @param {lm.item} parent
+ *
+ * @event stateChanged
+ * @event beforeItemDestroyed
+ * @event itemDestroyed
+ * @event itemCreated
+ * @event componentCreated
+ * @event rowCreated
+ * @event columnCreated
+ * @event stackCreated
+ *
+ * @constructor
+ */
lm.items.AbstractContentItem = function (layoutManager, config, parent) {
lm.utils.EventEmitter.call(this);
@@ -3146,26 +3144,26 @@
lm.utils.copy(lm.items.AbstractContentItem.prototype, {
- /**
- * Set the size of the component and its children, called recursively
- *
- * @abstract
- * @returns void
- */
+ /**
+ * Set the size of the component and its children, called recursively
+ *
+ * @abstract
+ * @returns void
+ */
setSize: function () {
throw new Error('Abstract Method');
},
- /**
- * Calls a method recursively downwards on the tree
- *
- * @param {String} functionName the name of the function to be called
- * @param {[Array]}functionArguments optional arguments that are passed to every function
- * @param {[bool]} bottomUp Call methods from bottom to top, defaults to false
- * @param {[bool]} skipSelf Don't invoke the method on the class that calls it, defaults to false
- *
- * @returns {void}
- */
+ /**
+ * Calls a method recursively downwards on the tree
+ *
+ * @param {String} functionName the name of the function to be called
+ * @param {[Array]}functionArguments optional arguments that are passed to every function
+ * @param {[bool]} bottomUp Call methods from bottom to top, defaults to false
+ * @param {[bool]} skipSelf Don't invoke the method on the class that calls it, defaults to false
+ *
+ * @returns {void}
+ */
callDownwards: function (functionName, functionArguments, bottomUp, skipSelf) {
var i;
@@ -3180,53 +3178,53 @@
}
},
- /**
- * Removes a child node (and its children) from the tree
- *
- * @param {lm.items.ContentItem} contentItem
- *
- * @returns {void}
- */
+ /**
+ * Removes a child node (and its children) from the tree
+ *
+ * @param {lm.items.ContentItem} contentItem
+ *
+ * @returns {void}
+ */
removeChild: function (contentItem, keepChild) {
- /*
- * Get the position of the item that's to be removed within all content items this node contains
- */
+ /*
+ * Get the position of the item that's to be removed within all content items this node contains
+ */
var index = lm.utils.indexOf(contentItem, this.contentItems);
- /*
- * Make sure the content item to be removed is actually a child of this item
- */
+ /*
+ * Make sure the content item to be removed is actually a child of this item
+ */
if (index === -1) {
throw new Error('Can\'t remove child item. Unknown content item');
}
- /**
- * Call ._$destroy on the content item. This also calls ._$destroy on all its children
- */
+ /**
+ * Call ._$destroy on the content item. This also calls ._$destroy on all its children
+ */
if (keepChild !== true) {
this.contentItems[index]._$destroy();
}
- /**
- * Remove the content item from this nodes array of children
- */
+ /**
+ * Remove the content item from this nodes array of children
+ */
this.contentItems.splice(index, 1);
- /**
- * Remove the item from the configuration
- */
+ /**
+ * Remove the item from the configuration
+ */
this.config.content.splice(index, 1);
- /**
- * If this node still contains other content items, adjust their size
- */
+ /**
+ * If this node still contains other content items, adjust their size
+ */
if (this.contentItems.length > 0) {
this.callDownwards('setSize');
- /**
- * If this was the last content item, remove this node as well
- */
+ /**
+ * If this was the last content item, remove this node as well
+ */
} else if (!(this instanceof lm.items.Root) && this.config.isClosable === true) {
const stack = this;
const rowOrCol = stack.parent;
@@ -3244,14 +3242,14 @@
}
},
- /**
- * Sets up the tree structure for the newly added child
- * The responsibility for the actual DOM manipulations lies
- * with the concrete item
- *
- * @param {lm.items.AbstractContentItem} contentItem
- * @param {[Int]} index If omitted item will be appended
- */
+ /**
+ * Sets up the tree structure for the newly added child
+ * The responsibility for the actual DOM manipulations lies
+ * with the concrete item
+ *
+ * @param {lm.items.AbstractContentItem} contentItem
+ * @param {[Int]} index If omitted item will be appended
+ */
addChild: function (contentItem, index) {
if (index === undefined) {
index = this.contentItems.length;
@@ -3271,15 +3269,15 @@
}
},
- /**
- * Replaces oldChild with newChild. This used to use jQuery.replaceWith... which for
- * some reason removes all event listeners, so isn't really an option.
- *
- * @param {lm.item.AbstractContentItem} oldChild
- * @param {lm.item.AbstractContentItem} newChild
- *
- * @returns {void}
- */
+ /**
+ * Replaces oldChild with newChild. This used to use jQuery.replaceWith... which for
+ * some reason removes all event listeners, so isn't really an option.
+ *
+ * @param {lm.item.AbstractContentItem} oldChild
+ * @param {lm.item.AbstractContentItem} newChild
+ *
+ * @returns {void}
+ */
replaceChild: function (oldChild, newChild, _$destroyOldChild) {
newChild = this.layoutManager._$normalizeContentItem(newChild);
@@ -3293,23 +3291,23 @@
parentNode.replaceChild(newChild.element[0], oldChild.element[0]);
- /*
- * Optionally destroy the old content item
- */
+ /*
+ * Optionally destroy the old content item
+ */
if (_$destroyOldChild === true) {
oldChild.parent = null;
oldChild._$destroy();
}
- /*
- * Wire the new contentItem into the tree
- */
+ /*
+ * Wire the new contentItem into the tree
+ */
this.contentItems[index] = newChild;
newChild.parent = this;
- /*
- * Update tab reference
- */
+ /*
+ * Update tab reference
+ */
if (this.isStack) {
this.header.tabs[index].contentItem = newChild;
}
@@ -3322,33 +3320,33 @@
this.callDownwards('setSize');
},
- /**
- * Convenience method.
- * Shorthand for this.parent.removeChild( this )
- *
- * @returns {void}
- */
+ /**
+ * Convenience method.
+ * Shorthand for this.parent.removeChild( this )
+ *
+ * @returns {void}
+ */
remove: function () {
this.parent.removeChild(this);
},
- /**
- * Removes the component from the layout and creates a new
- * browser window with the component and its children inside
- *
- * @returns {lm.controls.BrowserPopout}
- */
+ /**
+ * Removes the component from the layout and creates a new
+ * browser window with the component and its children inside
+ *
+ * @returns {lm.controls.BrowserPopout}
+ */
popout: function () {
var browserPopout = this.layoutManager.createPopout(this);
this.emitBubblingEvent('stateChanged');
return browserPopout;
},
- /**
- * Maximises the Item or minimises it if it is already maximised
- *
- * @returns {void}
- */
+ /**
+ * Maximises the Item or minimises it if it is already maximised
+ *
+ * @returns {void}
+ */
toggleMaximise: function (e) {
e && e.preventDefault();
if (this.isMaximised === true) {
@@ -3361,11 +3359,11 @@
this.emitBubblingEvent('stateChanged');
},
- /**
- * Selects the item if it is not already selected
- *
- * @returns {void}
- */
+ /**
+ * Selects the item if it is not already selected
+ *
+ * @returns {void}
+ */
select: function () {
if (this.layoutManager.selectedItem !== this) {
this.layoutManager.selectItem(this, true);
@@ -3373,11 +3371,11 @@
}
},
- /**
- * De-selects the item if it is selected
- *
- * @returns {void}
- */
+ /**
+ * De-selects the item if it is selected
+ *
+ * @returns {void}
+ */
deselect: function () {
if (this.layoutManager.selectedItem === this) {
this.layoutManager.selectedItem = null;
@@ -3385,28 +3383,28 @@
}
},
- /**
- * Set this component's title
- *
- * @public
- * @param {String} title
- *
- * @returns {void}
- */
+ /**
+ * Set this component's title
+ *
+ * @public
+ * @param {String} title
+ *
+ * @returns {void}
+ */
setTitle: function (title) {
this.config.title = title;
this.emit('titleChanged', title);
this.emit('stateChanged');
},
- /**
- * Checks whether a provided id is present
- *
- * @public
- * @param {String} id
- *
- * @returns {Boolean} isPresent
- */
+ /**
+ * Checks whether a provided id is present
+ *
+ * @public
+ * @param {String} id
+ *
+ * @returns {Boolean} isPresent
+ */
hasId: function (id) {
if (!this.config.id) {
return false;
@@ -3417,15 +3415,15 @@
}
},
- /**
- * Adds an id. Adds it as a string if the component doesn't
- * have an id yet or creates/uses an array
- *
- * @public
- * @param {String} id
- *
- * @returns {void}
- */
+ /**
+ * Adds an id. Adds it as a string if the component doesn't
+ * have an id yet or creates/uses an array
+ *
+ * @public
+ * @param {String} id
+ *
+ * @returns {void}
+ */
addId: function (id) {
if (this.hasId(id)) {
return;
@@ -3440,15 +3438,15 @@
}
},
- /**
- * Removes an existing id. Throws an error
- * if the id is not present
- *
- * @public
- * @param {String} id
- *
- * @returns {void}
- */
+ /**
+ * Removes an existing id. Throws an error
+ * if the id is not present
+ *
+ * @public
+ * @param {String} id
+ *
+ * @returns {void}
+ */
removeId: function (id) {
if (!this.hasId(id)) {
throw new Error('Id not found');
@@ -3462,9 +3460,9 @@
}
},
- /****************************************
- * SELECTOR
- ****************************************/
+ /****************************************
+ * SELECTOR
+ ****************************************/
getItemsByFilter: function (filter) {
var result = [],
next = function (contentItem) {
@@ -3508,9 +3506,9 @@
return instances;
},
- /****************************************
- * PACKAGE PRIVATE
- ****************************************/
+ /****************************************
+ * PACKAGE PRIVATE
+ ****************************************/
_$getItemsByProperty: function (key, value) {
return this.getItemsByFilter(function (item) {
return item[key] === value;
@@ -3555,11 +3553,11 @@
}
},
- /**
- * Destroys this item ands its children
- *
- * @returns {void}
- */
+ /**
+ * Destroys this item ands its children
+ *
+ * @returns {void}
+ */
_$destroy: function () {
this.emitBubblingEvent('beforeItemDestroyed');
this.callDownwards('_$destroy', [], true, true);
@@ -3567,17 +3565,17 @@
this.emitBubblingEvent('itemDestroyed');
},
- /**
- * Returns the area the component currently occupies in the format
- *
- * {
- * x1: int
- * xy: int
- * y1: int
- * y2: int
- * contentItem: contentItem
- * }
- */
+ /**
+ * Returns the area the component currently occupies in the format
+ *
+ * {
+ * x1: int
+ * xy: int
+ * y1: int
+ * y2: int
+ * contentItem: contentItem
+ * }
+ */
_$getArea: function (element) {
element = element || this.element;
@@ -3595,17 +3593,17 @@
};
},
- /**
- * The tree of content items is created in two steps: First all content items are instantiated,
- * then init is called recursively from top to bottem. This is the basic init function,
- * it can be used, extended or overwritten by the content items
- *
- * Its behaviour depends on the content item
- *
- * @package private
- *
- * @returns {void}
- */
+ /**
+ * The tree of content items is created in two steps: First all content items are instantiated,
+ * then init is called recursively from top to bottem. This is the basic init function,
+ * it can be used, extended or overwritten by the content items
+ *
+ * Its behaviour depends on the content item
+ *
+ * @package private
+ *
+ * @returns {void}
+ */
_$init: function () {
var i;
this.setSize();
@@ -3619,26 +3617,26 @@
this.emitBubblingEvent(this.type + 'Created');
},
- /**
- * Emit an event that bubbles up the item tree.
- *
- * @param {String} name The name of the event
- *
- * @returns {void}
- */
+ /**
+ * Emit an event that bubbles up the item tree.
+ *
+ * @param {String} name The name of the event
+ *
+ * @returns {void}
+ */
emitBubblingEvent: function (name) {
var event = new lm.utils.BubblingEvent(name, this);
this.emit(name, event);
},
- /**
- * Private method, creates all content items for this node at initialisation time
- * PLEASE NOTE, please see addChild for adding contentItems add runtime
- * @private
- * @param {configuration item node} config
- *
- * @returns {void}
- */
+ /**
+ * Private method, creates all content items for this node at initialisation time
+ * PLEASE NOTE, please see addChild for adding contentItems add runtime
+ * @private
+ * @param {configuration item node} config
+ *
+ * @returns {void}
+ */
_createContentItems: function (config) {
var oContentItem, i;
@@ -3652,13 +3650,13 @@
}
},
- /**
- * Extends an item configuration node with default settings
- * @private
- * @param {configuration item node} config
- *
- * @returns {configuration item node} extended config
- */
+ /**
+ * Extends an item configuration node with default settings
+ * @private
+ * @param {configuration item node} config
+ *
+ * @returns {configuration item node} extended config
+ */
_extendItemNode: function (config) {
for (var key in lm.config.itemDefaultConfig) {
@@ -3670,26 +3668,26 @@
return config;
},
- /**
- * Called for every event on the item tree. Decides whether the event is a bubbling
- * event and propagates it to its parent
- *
- * @param {String} name the name of the event
- * @param {lm.utils.BubblingEvent} event
- *
- * @returns {void}
- */
+ /**
+ * Called for every event on the item tree. Decides whether the event is a bubbling
+ * event and propagates it to its parent
+ *
+ * @param {String} name the name of the event
+ * @param {lm.utils.BubblingEvent} event
+ *
+ * @returns {void}
+ */
_propagateEvent: function (name, event) {
if (event instanceof lm.utils.BubblingEvent &&
event.isPropagationStopped === false &&
this.isInitialised === true) {
- /**
- * In some cases (e.g. if an element is created from a DragSource) it
- * doesn't have a parent and is not below root. If that's the case
- * propagate the bubbling event from the top level of the substree directly
- * to the layoutManager
- */
+ /**
+ * In some cases (e.g. if an element is created from a DragSource) it
+ * doesn't have a parent and is not below root. If that's the case
+ * propagate the bubbling event from the top level of the substree directly
+ * to the layoutManager
+ */
if (this.isRoot === false && this.parent) {
this.parent.emit.apply(this.parent, Array.prototype.slice.call(arguments, 0));
} else {
@@ -3698,16 +3696,16 @@
}
},
- /**
- * All raw events bubble up to the root element. Some events that
- * are propagated to - and emitted by - the layoutManager however are
- * only string-based, batched and sanitized to make them more usable
- *
- * @param {String} name the name of the event
- *
- * @private
- * @returns {void}
- */
+ /**
+ * All raw events bubble up to the root element. Some events that
+ * are propagated to - and emitted by - the layoutManager however are
+ * only string-based, batched and sanitized to make them more usable
+ *
+ * @param {String} name the name of the event
+ *
+ * @private
+ * @returns {void}
+ */
_scheduleEventPropagationToLayoutManager: function (name, event) {
if (lm.utils.indexOf(name, this._throttledEvents) === -1) {
this.layoutManager.emit(name, event.origin);
@@ -3720,25 +3718,25 @@
},
- /**
- * Callback for events scheduled by _scheduleEventPropagationToLayoutManager
- *
- * @param {String} name the name of the event
- *
- * @private
- * @returns {void}
- */
+ /**
+ * Callback for events scheduled by _scheduleEventPropagationToLayoutManager
+ *
+ * @param {String} name the name of the event
+ *
+ * @private
+ * @returns {void}
+ */
_propagateEventToLayoutManager: function (name, event) {
this._pendingEventPropagations[name] = false;
this.layoutManager.emit(name, event);
}
});
- /**
- * @param {[type]} layoutManager [description]
- * @param {[type]} config [description]
- * @param {[type]} parent [description]
- */
+ /**
+ * @param {[type]} layoutManager [description]
+ * @param {[type]} config [description]
+ * @param {[type]} parent [description]
+ */
lm.items.Component = function (layoutManager, config, parent) {
lm.items.AbstractContentItem.call(this, layoutManager, config, parent);
@@ -3798,11 +3796,11 @@
lm.items.AbstractContentItem.prototype._$destroy.call(this);
},
- /**
- * Dragging onto a component directly is not an option
- *
- * @returns null
- */
+ /**
+ * Dragging onto a component directly is not an option
+ *
+ * @returns null
+ */
_$getArea: function () {
return null;
}
@@ -3841,9 +3839,9 @@
this.element.width(width);
this.element.height(height);
- /*
- * Root can be empty
- */
+ /*
+ * Root can be empty
+ */
if (this.contentItems[0]) {
this.contentItems[0].element.width(width);
this.contentItems[0].element.height(height);
@@ -3917,18 +3915,18 @@
lm.utils.copy(lm.items.RowOrColumn.prototype, {
- /**
- * Add a new contentItem to the Row or Column
- *
- * @param {lm.item.AbstractContentItem} contentItem
- * @param {[int]} index The position of the new item within the Row or Column.
- * If no index is provided the item will be added to the end
- * @param {[bool]} _$suspendResize If true the items won't be resized. This will leave the item in
- * an inconsistent state and is only intended to be used if multiple
- * children need to be added in one go and resize is called afterwards
- *
- * @returns {void}
- */
+ /**
+ * Add a new contentItem to the Row or Column
+ *
+ * @param {lm.item.AbstractContentItem} contentItem
+ * @param {[int]} index The position of the new item within the Row or Column.
+ * If no index is provided the item will be added to the end
+ * @param {[bool]} _$suspendResize If true the items won't be resized. This will leave the item in
+ * an inconsistent state and is only intended to be used if multiple
+ * children need to be added in one go and resize is called afterwards
+ *
+ * @returns {void}
+ */
addChild: function (contentItem, index, _$suspendResize) {
var newItemSize, itemSize, i, splitterElement;
@@ -3986,14 +3984,14 @@
},
- /**
- * Removes a child of this element
- *
- * @param {lm.items.AbstractContentItem} contentItem
- * @param {boolean} keepChild If true the child will be removed, but not destroyed
- *
- * @returns {void}
- */
+ /**
+ * Removes a child of this element
+ *
+ * @param {lm.items.AbstractContentItem} contentItem
+ * @param {boolean} keepChild If true the child will be removed, but not destroyed
+ *
+ * @returns {void}
+ */
removeChild: function (contentItem, keepChild) {
var removedItemSize = contentItem.config[this._dimension],
index = lm.utils.indexOf(contentItem, this.contentItems),
@@ -4005,10 +4003,10 @@
throw new Error('Can\'t remove child. ContentItem is not child of this Row or Column');
}
- /**
- * Remove the splitter before the item or after if the item happens
- * to be the first in the row/column
- */
+ /**
+ * Remove the splitter before the item or after if the item happens
+ * to be the first in the row/column
+ */
if (this._splitter[splitterIndex]) {
this._splitter[splitterIndex]._$destroy();
this._splitter.splice(splitterIndex, 1);
@@ -4019,9 +4017,9 @@
if (this.contentItems[i].config.fixed)
fixedItemSize += this.contentItems[i].config[this._dimension];
}
- /**
- * Allocate the space that the removed item occupied to the remaining items
- */
+ /**
+ * Allocate the space that the removed item occupied to the remaining items
+ */
for (i = 0; i < this.contentItems.length; i++) {
if (this.contentItems[i].config.fixed)
;
@@ -4044,14 +4042,14 @@
}
},
- /**
- * Replaces a child of this Row or Column with another contentItem
- *
- * @param {lm.items.AbstractContentItem} oldChild
- * @param {lm.items.AbstractContentItem} newChild
- *
- * @returns {void}
- */
+ /**
+ * Replaces a child of this Row or Column with another contentItem
+ *
+ * @param {lm.items.AbstractContentItem} oldChild
+ * @param {lm.items.AbstractContentItem} newChild
+ *
+ * @returns {void}
+ */
replaceChild: function (oldChild, newChild) {
var size = oldChild.config[this._dimension];
lm.items.AbstractContentItem.prototype.replaceChild.call(this, oldChild, newChild);
@@ -4060,11 +4058,11 @@
this.emitBubblingEvent('stateChanged');
},
- /**
- * Called whenever the dimensions of this item or one of its parents change
- *
- * @returns {void}
- */
+ /**
+ * Called whenever the dimensions of this item or one of its parents change
+ *
+ * @returns {void}
+ */
setSize: function () {
if (this.contentItems.length > 0) {
this._calculateRelativeSizes();
@@ -4074,15 +4072,15 @@
this.emit('resize');
},
- /**
- * Invoked recursively by the layout manager. AbstractContentItem.init appends
- * the contentItem's DOM elements to the container, RowOrColumn init adds splitters
- * in between them
- *
- * @package private
- * @override AbstractContentItem._$init
- * @returns {void}
- */
+ /**
+ * Invoked recursively by the layout manager. AbstractContentItem.init appends
+ * the contentItem's DOM elements to the container, RowOrColumn init adds splitters
+ * in between them
+ *
+ * @package private
+ * @override AbstractContentItem._$init
+ * @returns {void}
+ */
_$init: function () {
if (this.isInitialised === true) return;
@@ -4095,15 +4093,15 @@
}
},
- /**
- * Turns the relative sizes calculated by _calculateRelativeSizes into
- * absolute pixel values and applies them to the children's DOM elements
- *
- * Assigns additional pixels to counteract Math.floor
- *
- * @private
- * @returns {void}
- */
+ /**
+ * Turns the relative sizes calculated by _calculateRelativeSizes into
+ * absolute pixel values and applies them to the children's DOM elements
+ *
+ * Assigns additional pixels to counteract Math.floor
+ *
+ * @private
+ * @returns {void}
+ */
_setAbsoluteSizes: function () {
var i,
sizeData = this._calculateAbsoluteSizes();
@@ -4123,10 +4121,10 @@
}
},
- /**
- * Calculates the absolute sizes of all of the children of this Item.
- * @returns {object} - Set with absolute sizes and additional pixels.
- */
+ /**
+ * Calculates the absolute sizes of all of the children of this Item.
+ * @returns {object} - Set with absolute sizes and additional pixels.
+ */
_calculateAbsoluteSizes: function () {
var i,
totalSplitterSize = (this.contentItems.length - 1) * this._splitterSize,
@@ -4164,27 +4162,27 @@
};
},
- /**
- * Calculates the relative sizes of all children of this Item. The logic
- * is as follows:
- *
- * - Add up the total size of all items that have a configured size
- *
- * - If the total == 100 (check for floating point errors)
- * Excellent, job done
- *
- * - If the total is > 100,
- * set the size of items without set dimensions to 1/3 and add this to the total
- * set the size off all items so that the total is hundred relative to their original size
- *
- * - If the total is < 100
- * If there are items without set dimensions, distribute the remainder to 100 evenly between them
- * If there are no items without set dimensions, increase all items sizes relative to
- * their original size so that they add up to 100
- *
- * @private
- * @returns {void}
- */
+ /**
+ * Calculates the relative sizes of all children of this Item. The logic
+ * is as follows:
+ *
+ * - Add up the total size of all items that have a configured size
+ *
+ * - If the total == 100 (check for floating point errors)
+ * Excellent, job done
+ *
+ * - If the total is > 100,
+ * set the size of items without set dimensions to 1/3 and add this to the total
+ * set the size off all items so that the total is hundred relative to their original size
+ *
+ * - If the total is < 100
+ * If there are items without set dimensions, distribute the remainder to 100 evenly between them
+ * If there are no items without set dimensions, increase all items sizes relative to
+ * their original size so that they add up to 100
+ *
+ * @private
+ * @returns {void}
+ */
_calculateRelativeSizes: function () {
var i,
@@ -4200,17 +4198,17 @@
}
}
- /**
- * Everything adds up to hundred, all good :-)
- */
+ /**
+ * Everything adds up to hundred, all good :-)
+ */
if (Math.round(total) === 100) {
this._respectMinItemWidth();
return;
}
- /**
- * Allocate the remaining size to the items without a set dimension
- */
+ /**
+ * Allocate the remaining size to the items without a set dimension
+ */
if (Math.round(total) < 100 && itemsWithoutSetDimension.length > 0) {
for (i = 0; i < itemsWithoutSetDimension.length; i++) {
itemsWithoutSetDimension[i].config[dimension] = (100 - total) / itemsWithoutSetDimension.length;
@@ -4219,12 +4217,12 @@
return;
}
- /**
- * If the total is > 100, but there are also items without a set dimension left, assing 50
- * as their dimension and add it to the total
- *
- * This will be reset in the next step
- */
+ /**
+ * If the total is > 100, but there are also items without a set dimension left, assing 50
+ * as their dimension and add it to the total
+ *
+ * This will be reset in the next step
+ */
if (Math.round(total) > 100) {
for (i = 0; i < itemsWithoutSetDimension.length; i++) {
itemsWithoutSetDimension[i].config[dimension] = 50;
@@ -4232,9 +4230,9 @@
}
}
- /**
- * Set every items size relative to 100 relative to its size to total
- */
+ /**
+ * Set every items size relative to 100 relative to its size to total
+ */
for (i = 0; i < this.contentItems.length; i++) {
this.contentItems[i].config[dimension] = (this.contentItems[i].config[dimension] / total) * 100;
}
@@ -4242,10 +4240,10 @@
this._respectMinItemWidth();
},
- /**
- * Adjusts the column widths to respect the dimensions minItemWidth if set.
- * @returns {}
- */
+ /**
+ * Adjusts the column widths to respect the dimensions minItemWidth if set.
+ * @returns {}
+ */
_respectMinItemWidth: function () {
var minItemWidth = this.layoutManager.config.dimensions ? (this.layoutManager.config.dimensions.minItemWidth || 0) : 0,
sizeData = null,
@@ -4266,9 +4264,9 @@
sizeData = this._calculateAbsoluteSizes();
- /**
- * Figure out how much we are under the min item size total and how much room we have to use.
- */
+ /**
+ * Figure out how much we are under the min item size total and how much room we have to use.
+ */
for (var i = 0; i < this.contentItems.length; i++) {
contentItem = this.contentItems[i];
@@ -4288,16 +4286,16 @@
allEntries.push(entry);
}
- /**
- * If there is nothing under min, or there is not enough over to make up the difference, do nothing.
- */
+ /**
+ * If there is nothing under min, or there is not enough over to make up the difference, do nothing.
+ */
if (totalUnderMin === 0 || totalUnderMin > totalOverMin) {
return;
}
- /**
- * Evenly reduce all columns that are over the min item width to make up the difference.
- */
+ /**
+ * Evenly reduce all columns that are over the min item width to make up the difference.
+ */
reducePercent = totalUnderMin / totalOverMin;
remainingWidth = totalUnderMin;
for (i = 0; i < entriesOverMin.length; i++) {
@@ -4307,31 +4305,31 @@
entry.width -= reducedWidth;
}
- /**
- * Take anything remaining from the last item.
- */
+ /**
+ * Take anything remaining from the last item.
+ */
if (remainingWidth !== 0) {
allEntries[allEntries.length - 1].width -= remainingWidth;
}
- /**
- * Set every items size relative to 100 relative to its size to total
- */
+ /**
+ * Set every items size relative to 100 relative to its size to total
+ */
for (i = 0; i < this.contentItems.length; i++) {
this.contentItems[i].config.width = (allEntries[i].width / sizeData.totalWidth) * 100;
}
},
- /**
- * Instantiates a new lm.controls.Splitter, binds events to it and adds
- * it to the array of splitters at the position specified as the index argument
- *
- * What it doesn't do though is append the splitter to the DOM
- *
- * @param {Int} index The position of the splitter
- *
- * @returns {lm.controls.Splitter}
- */
+ /**
+ * Instantiates a new lm.controls.Splitter, binds events to it and adds
+ * it to the array of splitters at the position specified as the index argument
+ *
+ * What it doesn't do though is append the splitter to the DOM
+ *
+ * @param {Int} index The position of the splitter
+ *
+ * @returns {lm.controls.Splitter}
+ */
_createSplitter: function (index) {
var splitter;
splitter = new lm.controls.Splitter(this._isColumn, this._splitterSize, this._splitterGrabSize);
@@ -4342,16 +4340,16 @@
return splitter;
},
- /**
- * Locates the instance of lm.controls.Splitter in the array of
- * registered splitters and returns a map containing the contentItem
- * before and after the splitters, both of which are affected if the
- * splitter is moved
- *
- * @param {lm.controls.Splitter} splitter
- *
- * @returns {Object} A map of contentItems that the splitter affects
- */
+ /**
+ * Locates the instance of lm.controls.Splitter in the array of
+ * registered splitters and returns a map containing the contentItem
+ * before and after the splitters, both of which are affected if the
+ * splitter is moved
+ *
+ * @param {lm.controls.Splitter} splitter
+ *
+ * @returns {Object} A map of contentItems that the splitter affects
+ */
_getItemsForSplitter: function (splitter) {
var index = lm.utils.indexOf(splitter, this._splitter);
@@ -4361,11 +4359,11 @@
};
},
- /**
- * Gets the minimum dimensions for the given item configuration array
- * @param item
- * @private
- */
+ /**
+ * Gets the minimum dimensions for the given item configuration array
+ * @param item
+ * @private
+ */
_getMinimumDimensions: function (arr) {
var minWidth = 0, minHeight = 0;
@@ -4377,14 +4375,14 @@
return { horizontal: minWidth, vertical: minHeight };
},
- /**
- * Invoked when a splitter's dragListener fires dragStart. Calculates the splitters
- * movement area once (so that it doesn't need calculating on every mousemove event)
- *
- * @param {lm.controls.Splitter} splitter
- *
- * @returns {void}
- */
+ /**
+ * Invoked when a splitter's dragListener fires dragStart. Calculates the splitters
+ * movement area once (so that it doesn't need calculating on every mousemove event)
+ *
+ * @param {lm.controls.Splitter} splitter
+ *
+ * @returns {void}
+ */
_onSplitterDragStart: function (splitter) {
var items = this._getItemsForSplitter(splitter),
minSize = this.layoutManager.config.dimensions[this._isColumn ? 'minItemHeight' : 'minItemWidth'];
@@ -4400,16 +4398,16 @@
this._splitterMaxPosition = items.after.element[this._dimension]() - (afterMinSize || minSize);
},
- /**
- * Invoked when a splitter's DragListener fires drag. Updates the splitters DOM position,
- * but not the sizes of the elements the splitter controls in order to minimize resize events
- *
- * @param {lm.controls.Splitter} splitter
- * @param {Int} offsetX Relative pixel values to the splitters original position. Can be negative
- * @param {Int} offsetY Relative pixel values to the splitters original position. Can be negative
- *
- * @returns {void}
- */
+ /**
+ * Invoked when a splitter's DragListener fires drag. Updates the splitters DOM position,
+ * but not the sizes of the elements the splitter controls in order to minimize resize events
+ *
+ * @param {lm.controls.Splitter} splitter
+ * @param {Int} offsetX Relative pixel values to the splitters original position. Can be negative
+ * @param {Int} offsetY Relative pixel values to the splitters original position. Can be negative
+ *
+ * @returns {void}
+ */
_onSplitterDrag: function (splitter, offsetX, offsetY) {
var offset = this._isColumn ? offsetY : offsetX;
@@ -4419,15 +4417,15 @@
}
},
- /**
- * Invoked when a splitter's DragListener fires dragStop. Resets the splitters DOM position,
- * and applies the new sizes to the elements before and after the splitter and their children
- * on the next animation frame
- *
- * @param {lm.controls.Splitter} splitter
- *
- * @returns {void}
- */
+ /**
+ * Invoked when a splitter's DragListener fires dragStop. Resets the splitters DOM position,
+ * and applies the new sizes to the elements before and after the splitter and their children
+ * on the next animation frame
+ *
+ * @param {lm.controls.Splitter} splitter
+ *
+ * @returns {void}
+ */
_onSplitterDragStop: function (splitter) {
var items = this._getItemsForSplitter(splitter),
@@ -4575,13 +4573,13 @@
this.emitBubblingEvent('stateChanged');
},
- /**
- * Validates that the stack is still closable or not. If a stack is able
- * to close, but has a non closable component added to it, the stack is no
- * longer closable until all components are closable.
- *
- * @returns {void}
- */
+ /**
+ * Validates that the stack is still closable or not. If a stack is able
+ * to close, but has a non closable component added to it, the stack is no
+ * longer closable until all components are closable.
+ *
+ * @returns {void}
+ */
_$validateClosability: function () {
var contentItem,
isClosable,
@@ -4607,51 +4605,51 @@
},
- /**
- * Ok, this one is going to be the tricky one: The user has dropped {contentItem} onto this stack.
- *
- * It was dropped on either the stacks header or the top, right, bottom or left bit of the content area
- * (which one of those is stored in this._dropSegment). Now, if the user has dropped on the header the case
- * is relatively clear: We add the item to the existing stack... job done (might be good to have
- * tab reordering at some point, but lets not sweat it right now)
- *
- * If the item was dropped on the content part things are a bit more complicated. If it was dropped on either the
- * top or bottom region we need to create a new column and place the items accordingly.
- * Unless, of course if the stack is already within a column... in which case we want
- * to add the newly created item to the existing column...
- * either prepend or append it, depending on wether its top or bottom.
- *
- * Same thing for rows and left / right drop segments... so in total there are 9 things that can potentially happen
- * (left, top, right, bottom) * is child of the right parent (row, column) + header drop
- *
- * @param {lm.item} contentItem
- *
- * @returns {void}
- */
+ /**
+ * Ok, this one is going to be the tricky one: The user has dropped {contentItem} onto this stack.
+ *
+ * It was dropped on either the stacks header or the top, right, bottom or left bit of the content area
+ * (which one of those is stored in this._dropSegment). Now, if the user has dropped on the header the case
+ * is relatively clear: We add the item to the existing stack... job done (might be good to have
+ * tab reordering at some point, but lets not sweat it right now)
+ *
+ * If the item was dropped on the content part things are a bit more complicated. If it was dropped on either the
+ * top or bottom region we need to create a new column and place the items accordingly.
+ * Unless, of course if the stack is already within a column... in which case we want
+ * to add the newly created item to the existing column...
+ * either prepend or append it, depending on wether its top or bottom.
+ *
+ * Same thing for rows and left / right drop segments... so in total there are 9 things that can potentially happen
+ * (left, top, right, bottom) * is child of the right parent (row, column) + header drop
+ *
+ * @param {lm.item} contentItem
+ *
+ * @returns {void}
+ */
_$onDrop: function (contentItem) {
- /*
- * The item was dropped on the header area. Just add it as a child of this stack and
- * get the hell out of this logic
- */
+ /*
+ * The item was dropped on the header area. Just add it as a child of this stack and
+ * get the hell out of this logic
+ */
if (this._dropSegment === 'header') {
this._resetHeaderDropZone();
this.addChild(contentItem, this._dropIndex);
return;
}
- /*
- * The stack is empty. Let's just add the element.
- */
+ /*
+ * The stack is empty. Let's just add the element.
+ */
if (this._dropSegment === 'body') {
this.addChild(contentItem);
return;
}
- /*
- * The item was dropped on the top-, left-, bottom- or right- part of the content. Let's
- * aggregate some conditions to make the if statements later on more readable
- */
+ /*
+ * The item was dropped on the top-, left-, bottom- or right- part of the content. Let's
+ * aggregate some conditions to make the if statements later on more readable
+ */
var isVertical = this._dropSegment === 'top' || this._dropSegment === 'bottom',
isHorizontal = this._dropSegment === 'left' || this._dropSegment === 'right',
insertBefore = this._dropSegment === 'top' || this._dropSegment === 'left',
@@ -4662,9 +4660,9 @@
stack,
rowOrColumn;
- /*
- * The content item can be either a component or a stack. If it is a component, wrap it into a stack
- */
+ /*
+ * The content item can be either a component or a stack. If it is a component, wrap it into a stack
+ */
if (contentItem.isComponent) {
stack = this.layoutManager.createContentItem({
type: 'stack',
@@ -4675,20 +4673,20 @@
contentItem = stack;
}
- /*
- * If the item is dropped on top or bottom of a column or left and right of a row, it's already
- * layd out in the correct way. Just add it as a child
- */
+ /*
+ * If the item is dropped on top or bottom of a column or left and right of a row, it's already
+ * layd out in the correct way. Just add it as a child
+ */
if (hasCorrectParent) {
index = lm.utils.indexOf(this, this.parent.contentItems);
this.parent.addChild(contentItem, insertBefore ? index : index + 1, true);
this.config[dimension] *= 0.5;
contentItem.config[dimension] = this.config[dimension];
this.parent.callDownwards('setSize');
- /*
- * This handles items that are dropped on top or bottom of a row or left / right of a column. We need
- * to create the appropriate contentItem for them to live in
- */
+ /*
+ * This handles items that are dropped on top or bottom of a row or left / right of a column. We need
+ * to create the appropriate contentItem for them to live in
+ */
} else {
type = isVertical ? 'column' : 'row';
rowOrColumn = this.layoutManager.createContentItem({ type: type }, this);
@@ -4703,15 +4701,15 @@
}
},
- /**
- * If the user hovers above the header part of the stack, indicate drop positions for tabs.
- * otherwise indicate which segment of the body the dragged item would be dropped on
- *
- * @param {Int} x Absolute Screen X
- * @param {Int} y Absolute Screen Y
- *
- * @returns {void}
- */
+ /**
+ * If the user hovers above the header part of the stack, indicate drop positions for tabs.
+ * otherwise indicate which segment of the body the dragged item would be dropped on
+ *
+ * @param {Int} x Absolute Screen X
+ * @param {Int} y Absolute Screen Y
+ *
+ * @returns {void}
+ */
_$highlightDropZone: function (x, y) {
var segment, area;
@@ -4761,17 +4759,17 @@
}
};
- /**
- * If this Stack is a parent to rows, columns or other stacks only its
- * header is a valid dropzone.
- */
+ /**
+ * If this Stack is a parent to rows, columns or other stacks only its
+ * header is a valid dropzone.
+ */
if (this._activeContentItem && this._activeContentItem.isComponent === false) {
return headerArea;
}
- /**
- * Highlight the entire body if the stack is empty
- */
+ /**
+ * Highlight the entire body if the stack is empty
+ */
if (this.contentItems.length === 0) {
this._contentAreaDimensions.body = {
@@ -4875,7 +4873,7 @@
this.layoutManager.dropTargetIndicator.highlightArea({
x1: headerOffset.left,
x2: headerOffset.left + 100,
- y1: headerOffset.top + this.header.element.height() - 20,
+ y1: headerOffset.top + this.header.element.height() - 25,
y2: headerOffset.top + this.header.element.height()
});
@@ -4971,13 +4969,13 @@
lm.utils.BubblingEvent.prototype.stopPropagation = function () {
this.isPropagationStopped = true;
};
- /**
- * Minifies and unminifies configs by replacing frequent keys
- * and values with one letter substitutes. Config options must
- * retain array position/index, add new options at the end.
- *
- * @constructor
- */
+ /**
+ * Minifies and unminifies configs by replacing frequent keys
+ * and values with one letter substitutes. Config options must
+ * retain array position/index, add new options at the end.
+ *
+ * @constructor
+ */
lm.utils.ConfigMinifier = function () {
this._keys = [
'settings',
@@ -5037,154 +5035,154 @@
lm.utils.copy(lm.utils.ConfigMinifier.prototype, {
- /**
- * Takes a GoldenLayout configuration object and
- * replaces its keys and values recursively with
- * one letter counterparts
- *
- * @param {Object} config A GoldenLayout config object
- *
- * @returns {Object} minified config
- */
+ /**
+ * Takes a GoldenLayout configuration object and
+ * replaces its keys and values recursively with
+ * one letter counterparts
+ *
+ * @param {Object} config A GoldenLayout config object
+ *
+ * @returns {Object} minified config
+ */
minifyConfig: function (config) {
var min = {};
this._nextLevel(config, min, '_min');
return min;
},
- /**
- * Takes a configuration Object that was previously minified
- * using minifyConfig and returns its original version
- *
- * @param {Object} minifiedConfig
- *
- * @returns {Object} the original configuration
- */
+ /**
+ * Takes a configuration Object that was previously minified
+ * using minifyConfig and returns its original version
+ *
+ * @param {Object} minifiedConfig
+ *
+ * @returns {Object} the original configuration
+ */
unminifyConfig: function (minifiedConfig) {
var orig = {};
this._nextLevel(minifiedConfig, orig, '_max');
return orig;
},
- /**
- * Recursive function, called for every level of the config structure
- *
- * @param {Array|Object} orig
- * @param {Array|Object} min
- * @param {String} translationFn
- *
- * @returns {void}
- */
+ /**
+ * Recursive function, called for every level of the config structure
+ *
+ * @param {Array|Object} orig
+ * @param {Array|Object} min
+ * @param {String} translationFn
+ *
+ * @returns {void}
+ */
_nextLevel: function (from, to, translationFn) {
var key, minKey;
for (key in from) {
- /**
- * For in returns array indices as keys, so let's cast them to numbers
- */
+ /**
+ * For in returns array indices as keys, so let's cast them to numbers
+ */
if (from instanceof Array) key = parseInt(key, 10);
- /**
- * In case something has extended Object prototypes
- */
+ /**
+ * In case something has extended Object prototypes
+ */
if (!from.hasOwnProperty(key)) continue;
- /**
- * Translate the key to a one letter substitute
- */
+ /**
+ * Translate the key to a one letter substitute
+ */
minKey = this[translationFn](key, this._keys);
- /**
- * For Arrays and Objects, create a new Array/Object
- * on the minified object and recurse into it
- */
+ /**
+ * For Arrays and Objects, create a new Array/Object
+ * on the minified object and recurse into it
+ */
if (typeof from[key] === 'object') {
to[minKey] = from[key] instanceof Array ? [] : {};
this._nextLevel(from[key], to[minKey], translationFn);
- /**
- * For primitive values (Strings, Numbers, Boolean etc.)
- * minify the value
- */
+ /**
+ * For primitive values (Strings, Numbers, Boolean etc.)
+ * minify the value
+ */
} else {
to[minKey] = this[translationFn](from[key], this._values);
}
}
},
- /**
- * Minifies value based on a dictionary
- *
- * @param {String|Boolean} value
- * @param {Array<String|Boolean>} dictionary
- *
- * @returns {String} The minified version
- */
+ /**
+ * Minifies value based on a dictionary
+ *
+ * @param {String|Boolean} value
+ * @param {Array<String|Boolean>} dictionary
+ *
+ * @returns {String} The minified version
+ */
_min: function (value, dictionary) {
- /**
- * If a value actually is a single character, prefix it
- * with ___ to avoid mistaking it for a minification code
- */
+ /**
+ * If a value actually is a single character, prefix it
+ * with ___ to avoid mistaking it for a minification code
+ */
if (typeof value === 'string' && value.length === 1) {
return '___' + value;
}
var index = lm.utils.indexOf(value, dictionary);
- /**
- * value not found in the dictionary, return it unmodified
- */
+ /**
+ * value not found in the dictionary, return it unmodified
+ */
if (index === -1) {
return value;
- /**
- * value found in dictionary, return its base36 counterpart
- */
+ /**
+ * value found in dictionary, return its base36 counterpart
+ */
} else {
return index.toString(36);
}
},
_max: function (value, dictionary) {
- /**
- * value is a single character. Assume that it's a translation
- * and return the original value from the dictionary
- */
+ /**
+ * value is a single character. Assume that it's a translation
+ * and return the original value from the dictionary
+ */
if (typeof value === 'string' && value.length === 1) {
return dictionary[parseInt(value, 36)];
}
- /**
- * value originally was a single character and was prefixed with ___
- * to avoid mistaking it for a translation. Remove the prefix
- * and return the original character
- */
+ /**
+ * value originally was a single character and was prefixed with ___
+ * to avoid mistaking it for a translation. Remove the prefix
+ * and return the original character
+ */
if (typeof value === 'string' && value.substr(0, 3) === '___') {
return value[3];
}
- /**
- * value was not minified
- */
+ /**
+ * value was not minified
+ */
return value;
}
});
- /**
- * An EventEmitter singleton that propagates events
- * across multiple windows. This is a little bit trickier since
- * windows are allowed to open childWindows in their own right
- *
- * This means that we deal with a tree of windows. Hence the rules for event propagation are:
- *
- * - Propagate events from this layout to both parents and children
- * - Propagate events from parent to this and children
- * - Propagate events from children to the other children (but not the emitting one) and the parent
- *
- * @constructor
- *
- * @param {lm.LayoutManager} layoutManager
- */
+ /**
+ * An EventEmitter singleton that propagates events
+ * across multiple windows. This is a little bit trickier since
+ * windows are allowed to open childWindows in their own right
+ *
+ * This means that we deal with a tree of windows. Hence the rules for event propagation are:
+ *
+ * - Propagate events from this layout to both parents and children
+ * - Propagate events from parent to this and children
+ * - Propagate events from children to the other children (but not the emitting one) and the parent
+ *
+ * @constructor
+ *
+ * @param {lm.LayoutManager} layoutManager
+ */
lm.utils.EventHub = function (layoutManager) {
lm.utils.EventEmitter.call(this);
this._layoutManager = layoutManager;
@@ -5195,15 +5193,15 @@
$(window).on('gl_child_event', this._boundOnEventFromChild);
};
- /**
- * Called on every event emitted on this eventHub, regardles of origin.
- *
- * @private
- *
- * @param {Mixed}
- *
- * @returns {void}
- */
+ /**
+ * Called on every event emitted on this eventHub, regardles of origin.
+ *
+ * @private
+ *
+ * @param {Mixed}
+ *
+ * @returns {void}
+ */
lm.utils.EventHub.prototype._onEventFromThis = function () {
var args = Array.prototype.slice.call(arguments);
@@ -5217,40 +5215,40 @@
this._childEventSource = null;
};
- /**
- * Called by the parent layout.
- *
- * @param {Array} args Event name + arguments
- *
- * @returns {void}
- */
+ /**
+ * Called by the parent layout.
+ *
+ * @param {Array} args Event name + arguments
+ *
+ * @returns {void}
+ */
lm.utils.EventHub.prototype._$onEventFromParent = function (args) {
this._dontPropagateToParent = args[0];
this.emit.apply(this, args);
};
- /**
- * Callback for child events raised on the window
- *
- * @param {DOMEvent} event
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Callback for child events raised on the window
+ *
+ * @param {DOMEvent} event
+ * @private
+ *
+ * @returns {void}
+ */
lm.utils.EventHub.prototype._onEventFromChild = function (event) {
this._childEventSource = event.originalEvent.__gl;
this.emit.apply(this, event.originalEvent.__glArgs);
};
- /**
- * Propagates the event to the parent by emitting
- * it on the parent's DOM window
- *
- * @param {Array} args Event name + arguments
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Propagates the event to the parent by emitting
+ * it on the parent's DOM window
+ *
+ * @param {Array} args Event name + arguments
+ * @private
+ *
+ * @returns {void}
+ */
lm.utils.EventHub.prototype._propagateToParent = function (args) {
var event,
eventName = 'gl_child_event';
@@ -5274,14 +5272,14 @@
}
};
- /**
- * Propagate events to children
- *
- * @param {Array} args Event name + arguments
- * @private
- *
- * @returns {void}
- */
+ /**
+ * Propagate events to children
+ *
+ * @param {Array} args Event name + arguments
+ * @private
+ *
+ * @returns {void}
+ */
lm.utils.EventHub.prototype._propagateToChildren = function (args) {
var childGl, i;
@@ -5295,25 +5293,25 @@
};
- /**
- * Destroys the EventHub
- *
- * @public
- * @returns {void}
- */
+ /**
+ * Destroys the EventHub
+ *
+ * @public
+ * @returns {void}
+ */
lm.utils.EventHub.prototype.destroy = function () {
$(window).off('gl_child_event', this._boundOnEventFromChild);
};
- /**
- * A specialised GoldenLayout component that binds GoldenLayout container
- * lifecycle events to react components
- *
- * @constructor
- *
- * @param {lm.container.ItemContainer} container
- * @param {Object} state state is not required for react components
- */
+ /**
+ * A specialised GoldenLayout component that binds GoldenLayout container
+ * lifecycle events to react components
+ *
+ * @constructor
+ *
+ * @param {lm.container.ItemContainer} container
+ * @param {Object} state state is not required for react components
+ */
lm.utils.ReactComponentHandler = function (container, state) {
this._reactComponent = null;
this._originalComponentWillUpdate = null;
@@ -5326,15 +5324,15 @@
lm.utils.copy(lm.utils.ReactComponentHandler.prototype, {
- /**
- * Creates the react class and component and hydrates it with
- * the initial state - if one is present
- *
- * By default, react's getInitialState will be used
- *
- * @private
- * @returns {void}
- */
+ /**
+ * Creates the react class and component and hydrates it with
+ * the initial state - if one is present
+ *
+ * By default, react's getInitialState will be used
+ *
+ * @private
+ * @returns {void}
+ */
_render: function () {
this._reactComponent = ReactDOM.render(this._getReactComponent(), this._container.getElement()[0]);
this._originalComponentWillUpdate = this._reactComponent.componentWillUpdate || function () {
@@ -5345,36 +5343,36 @@
}
},
- /**
- * Removes the component from the DOM and thus invokes React's unmount lifecycle
- *
- * @private
- * @returns {void}
- */
+ /**
+ * Removes the component from the DOM and thus invokes React's unmount lifecycle
+ *
+ * @private
+ * @returns {void}
+ */
_destroy: function () {
ReactDOM.unmountComponentAtNode(this._container.getElement()[0]);
this._container.off('open', this._render, this);
this._container.off('destroy', this._destroy, this);
},
- /**
- * Hooks into React's state management and applies the componentstate
- * to GoldenLayout
- *
- * @private
- * @returns {void}
- */
+ /**
+ * Hooks into React's state management and applies the componentstate
+ * to GoldenLayout
+ *
+ * @private
+ * @returns {void}
+ */
_onUpdate: function (nextProps, nextState) {
this._container.setState(nextState);
this._originalComponentWillUpdate.call(this._reactComponent, nextProps, nextState);
},
- /**
- * Retrieves the react class from GoldenLayout's registry
- *
- * @private
- * @returns {React.Class}
- */
+ /**
+ * Retrieves the react class from GoldenLayout's registry
+ *
+ * @private
+ * @returns {React.Class}
+ */
_getReactClass: function () {
var componentName = this._container._config.component;
var reactClass;
@@ -5393,12 +5391,12 @@
return reactClass;
},
- /**
- * Copies and extends the properties array and returns the React element
- *
- * @private
- * @returns {React.Element}
- */
+ /**
+ * Copies and extends the properties array and returns the React element
+ *
+ * @private
+ * @returns {React.Element}
+ */
_getReactComponent: function () {
var defaultProps = {
glEventHub: this._container.layoutManager.eventHub,
diff --git a/src/client/util/CaptureManager.scss b/src/client/util/CaptureManager.scss
index 71539ee1e..a5024247e 100644
--- a/src/client/util/CaptureManager.scss
+++ b/src/client/util/CaptureManager.scss
@@ -1,4 +1,4 @@
-@import "../views/globalCssVariables";
+@import "../views/global/globalCssVariables";
.capture-interface {
//background-color: whitesmoke !important;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index c96a6f656..66f9d060f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -35,6 +35,9 @@ import { UndoManager } from "./UndoManager";
import { SnappingManager } from "./SnappingManager";
import { InkTool } from "../../fields/InkField";
import { SharingManager } from "./SharingManager";
+import { computedFn } from "mobx-utils";
+import { ColorScheme } from "./SettingsManager";
+import { Colors } from "../views/global/globalEnums";
export let resolvedPorts: { server: number, socket: number };
@@ -405,14 +408,18 @@ export class CurrentUserUtils {
selection: { type: "text", anchor: 1, head: 1 },
storedMarks: []
};
- const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { title: "header", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, cloneFieldFilter: new List<string>(["system"]) }, "header"); // text needs to be a space to allow templateText to be created
+ const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), {
+ title: "text", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all",
+ _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, _fitWidth: true,
+ cloneFieldFilter: new List<string>(["system"])
+ }, "header");
const headerBtnHgt = 10;
headerTemplate[DataSym].layout =
- "<div style='height:100%'>" +
- ` <FormattedTextBox {...props} fieldKey={'text'} height='calc(100% - ${headerBtnHgt}px - {this._headerHeight}px)'/>` +
- " <FormattedTextBox {...props} fieldKey={'header'} dontSelectOnLoad='true' ignoreAutoHeight='true' fontSize='{this._headerFontSize}px' height='{this._headerHeight||1}px' background={this._headerColor ||this.target.mySharedDocs.userColor||'lightGray'} />" +
- ` <HTMLdiv fontSize='${headerBtnHgt - 1}' onClick={‘(this._headerHeight=Math.min(Math.max(1,this._height-30),this._headerHeight===1?50:1)) && (this._autoHeightMargins=this._headerHeight+${headerBtnHgt})’} height='${headerBtnHgt}px' background='yellow' >Metadata</HTMLdiv>` +
- "</div>";
+ "<HTMLdiv transformOrigin='top left' width='{100/scale}%' height='{100/scale}%' transform='scale({scale})'>" +
+ ` <FormattedTextBox {...props} dontScale='true' fieldKey={'text'} height='calc(100% - ${headerBtnHgt}px - {this._headerHeight}px)'/>` +
+ " <FormattedTextBox {...props} dontScale='true' fieldKey={'header'} dontSelectOnLoad='true' ignoreAutoHeight='true' fontSize='{this._headerFontSize}px' height='{(this._headerHeight||1)}px' background='{this._headerColor ||this.target.mySharedDocs.userColor||`lightGray`}' />" +
+ ` <HTMLdiv fontSize='${headerBtnHgt - 1}px' height='${headerBtnHgt}px' background='yellow' onClick={‘(this._headerHeight=scale*Math.min(Math.max(1,this._height-30),this._headerHeight===1?50:1)) && (this._autoHeightMargins=this._headerHeight+${headerBtnHgt})’} >Metadata</HTMLdiv>` +
+ "</HTMLdiv>";
// "<div style={'height:100%'}>" +
// " <FormattedTextBox {...props} fieldKey={'header'} dontSelectOnLoad={'true'} ignoreAutoHeight={'true'} pointerEvents='{this._headerPointerEvents||`none`}' fontSize='{this._headerFontSize}px' height='{this._headerHeight}px' background='{this._headerColor||this.target.mySharedDocs.userColor}' />" +
@@ -468,7 +475,7 @@ export class CurrentUserUtils {
{ toolTip: "Tap to create a videoWall", title: "Wall", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWall as Doc },
{ toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true },
{ toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc },
- { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true },
+ // { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true },
{ toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScript as Doc },
{ toolTip: "Tap to create a mobile view in a new pane, drag for a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc },
{ toolTip: "Tap to create a custom header note document, drag for a custom header note", title: "Custom", icon: "window-maximize", click: 'openOnRight(delegateDragFactory(this.dragFactory))', drag: 'delegateDragFactory(this.dragFactory)', dragFactory: doc.emptyHeader as Doc },
@@ -529,8 +536,8 @@ export class CurrentUserUtils {
{ title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
{ title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
{ title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
- { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' },
- { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' },
+ // { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' },
+ // { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' },
{ title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' },
];
}
@@ -556,7 +563,6 @@ export class CurrentUserUtils {
dontUndo: true,
title,
target,
- backgroundColor: "black",
_dropAction: "alias",
_removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
_width: 60,
@@ -571,8 +577,10 @@ export class CurrentUserUtils {
title: "menuItemPanel",
childDropAction: "alias",
_chromeHidden: true,
+ backgroundColor: Colors.DARK_GRAY,
+ boxShadow: "rgba(0,0,0,0)",
dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
- backgroundColor: "black", ignoreClick: true,
+ ignoreClick: true,
_gridGap: 0,
_yMargin: 0,
_yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true
@@ -582,8 +590,6 @@ export class CurrentUserUtils {
PromiseValue(Cast(doc.menuStack, Doc)).then(stack => {
stack && PromiseValue(stack.data).then(btns => {
DocListCastAsync(btns).then(bts => bts?.forEach(btn => {
- btn.color = "white";
- btn._backgroundColor = "";
btn.dontUndo = true;
btn.system = true;
if (btn.title === "Catalog" || btn.title === "My Files") { // migration from Catalog to My Files
@@ -754,7 +760,7 @@ export class CurrentUserUtils {
await doc.myDashboards;
if (doc.myDashboards === undefined) {
doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
- title: "My Dashboards", _height: 400, childHideLinkButton: true,
+ title: "My Dashboards", _showTitle: "title", _height: 400, childHideLinkButton: true,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
treeViewTruncateTitleWidth: 150, ignoreClick: true,
_lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true
@@ -770,7 +776,7 @@ export class CurrentUserUtils {
await doc.myPresentations;
if (doc.myPresentations === undefined) {
doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], {
- title: "My Presentations", _height: 100,
+ title: "My Trails", _showTitle: "title", _height: 100,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
treeViewTruncateTitleWidth: 150, ignoreClick: true,
_lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true
@@ -789,7 +795,7 @@ export class CurrentUserUtils {
doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true });
doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileRoot as Doc, doc.myFileOrphans as Doc], {
- title: "My Documents", _height: 100,
+ title: "My Documents", _showTitle: "title", _height: 100,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
treeViewTruncateTitleWidth: 150, ignoreClick: true,
isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true,
@@ -803,7 +809,7 @@ export class CurrentUserUtils {
// setup Recently Closed library item
if (doc.myRecentlyClosedDocs === undefined) {
doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
- title: "Recently Closed", treeViewShowClearButton: true, childHideLinkButton: true,
+ title: "Recently Closed", _showTitle: "title", treeViewShowClearButton: true, childHideLinkButton: true,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
treeViewTruncateTitleWidth: 150, ignoreClick: true,
_lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true
@@ -834,7 +840,7 @@ export class CurrentUserUtils {
doc.treeViewOpen = true;
doc.treeViewExpandedView = "fields";
doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], {
- treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, title: "My UserDoc",
+ treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, title: "My UserDoc", _showTitle: "title",
treeViewTruncateTitleWidth: 150, ignoreClick: true,
_lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true
})) as any as Doc;
@@ -854,6 +860,7 @@ export class CurrentUserUtils {
static async setupSidebarButtons(doc: Doc) {
CurrentUserUtils.setupSidebarContainer(doc);
await CurrentUserUtils.setupToolsBtnPanel(doc);
+ CurrentUserUtils.setupImportSidebar(doc);
CurrentUserUtils.setupDashboards(doc);
CurrentUserUtils.setupPresentations(doc);
CurrentUserUtils.setupFilesystem(doc);
@@ -885,6 +892,7 @@ export class CurrentUserUtils {
(doc["dockedBtn-undo"] as Doc).dontUndo = true;
(doc["dockedBtn-redo"] as Doc).dontUndo = true;
}
+
// sets up the default set of documents to be shown in the Overlay layer
static setupOverlays(doc: Doc) {
if (doc.myOverlayDocs === undefined) {
@@ -918,7 +926,8 @@ export class CurrentUserUtils {
if (!sharedDocs) {
sharedDocs = Docs.Create.TreeDocument([], {
title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
- _showTitle: "title", ignoreClick: false, _lockedPosition: true, "acl-Public": SharingPermissions.Add, "_acl-Public": SharingPermissions.Add, _chromeHidden: true,
+ _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Add, "_acl-Public": SharingPermissions.Add,
+ _chromeHidden: true, boxShadow: "0 0",
}, sharingDocumentId + "outer", sharingDocumentId);
(sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Add;
}
@@ -933,14 +942,14 @@ export class CurrentUserUtils {
static setupImportSidebar(doc: Doc) {
if (doc.myImportDocs === undefined) {
doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], {
- title: "My ImportDocuments", _forceActive: true, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
+ title: "My ImportDocuments", _forceActive: true, ignoreClick: true, _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
childDropAction: "alias", _autoHeight: true, _yMargin: 50, _gridGap: 15, _lockedPosition: true, system: true, _chromeHidden: true,
}));
}
if (doc.myImportPanel === undefined) {
const uploads = Cast(doc.myImportDocs, Doc, null);
const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", icon: "upload", system: true });
- doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true }));
+ doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, _showTitle: "title", ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true, boxShadow: "0 0" }));
}
}
@@ -1001,10 +1010,14 @@ export class CurrentUserUtils {
const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || [];
SnappingManager.SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]);
}, { fireImmediately: true });
+ // Document properties on load
doc.system = true;
+ doc.darkScheme = ColorScheme.Dark;
doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode;
doc.title = Doc.CurrentUserEmail;
doc._raiseWhenDragged = true;
+ doc._showLabel = false;
+ doc._showMenuLabel = true;
doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)");
doc.activeInkWidth = StrCast(doc.activeInkWidth, "1");
doc.activeInkBezier = StrCast(doc.activeInkBezier, "0");
@@ -1215,7 +1228,7 @@ export class CurrentUserUtils {
Doc.AddDocToList(myPresentations, "data", presentation);
userDoc.activePresentation = presentation;
- const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
+ const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`);
const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
const createDashboard = ScriptField.MakeScript(`createNewDashboard()`);
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 304215a8f..5b092258a 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -28,7 +28,7 @@ export class DocumentManager {
DocListCast(view.rootDoc.links).forEach(link => {
const whichOtherAnchor = view.props.LayoutTemplateString?.includes("anchor2") ? "anchor1" : "anchor2";
const otherDoc = link && (link[whichOtherAnchor] as Doc);
- const otherDocAnno = otherDoc?.type === DocumentType.TEXTANCHOR ? otherDoc.annotationOn as Doc : undefined;
+ const otherDocAnno = DocumentType.MARKER === otherDoc?.type ? otherDoc.annotationOn as Doc : undefined;
otherDoc && DocumentManager.Instance.DocumentViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, otherDoc) || Doc.AreProtosEqual(dv.rootDoc, otherDocAnno)).
forEach(otherView => {
if (otherView.rootDoc.type !== DocumentType.LINK || otherView.props.LayoutTemplateString !== view.props.LayoutTemplateString) {
@@ -162,7 +162,7 @@ export class DocumentManager {
const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined;
const targetDocContext = contextDoc || annotatedDoc;
const targetDocContextView = targetDocContext && getFirstDocView(targetDocContext);
- const focusView = !docView && targetDoc.type === DocumentType.TEXTANCHOR && annoContainerView ? annoContainerView : docView;
+ const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
if (!docView && annoContainerView && !focusView) {
annoContainerView.focus(targetDoc); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
}
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 88bf6f36d..5e16de617 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,4 +1,4 @@
-import { action, observable, runInAction } from "mobx";
+import { action } from "mobx";
import { DateField } from "../../fields/DateField";
import { Doc, Field, Opt } from "../../fields/Doc";
import { List } from "../../fields/List";
@@ -7,14 +7,23 @@ import { listSpec } from "../../fields/Schema";
import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
import { ScriptField } from "../../fields/ScriptField";
import { Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types";
-import { emptyFunction, returnTrue } from "../../Utils";
+import { emptyFunction } from "../../Utils";
import { Docs, DocUtils } from "../documents/Documents";
-import * as globalCssVariables from "../views/globalCssVariables.scss";
-import { UndoManager } from "./UndoManager";
-import { SnappingManager } from "./SnappingManager";
+import * as globalCssVariables from "../views/global/globalCssVariables.scss";
import { DocumentView } from "../views/nodes/DocumentView";
+import { SnappingManager } from "./SnappingManager";
+import { UndoManager } from "./UndoManager";
export type dropActionType = "alias" | "copy" | "move" | "same" | "proto" | "none" | undefined; // undefined = move, "same" = move but don't call removeDropProperties
+
+/**
+ * Initialize drag
+ * @param _reference: The HTMLElement that is being dragged
+ * @param docFunc: The Dash document being moved
+ * @param moveFunc: The function called when the document is moved
+ * @param dropAction: What to do with the document when it is dropped
+ * @param dragStarted: Method to call when the drag is started
+ */
export function SetupDrag(
_reference: React.RefObject<HTMLElement>,
docFunc: () => Doc | Promise<Doc> | undefined,
@@ -210,16 +219,16 @@ export namespace DragManager {
dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc);
return dropDoc;
};
- const finishDrag = (e: DragCompleteEvent) => {
+ const finishDrag = async (e: DragCompleteEvent) => {
const docDragData = e.docDragData;
dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails
if (docDragData && !docDragData.droppedDocuments.length) {
docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
docDragData.droppedDocuments =
- dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) :
+ await Promise.all(dragData.draggedDocuments.map(async d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) :
docDragData.dropAction === "alias" ? Doc.MakeAlias(d) :
docDragData.dropAction === "proto" ? Doc.GetProto(d) :
- docDragData.dropAction === "copy" ? Doc.MakeClone(d) : d);
+ docDragData.dropAction === "copy" ? (await Doc.MakeClone(d)).clone : d));
!["same", "proto"].includes(docDragData.dropAction as any) && docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []);
const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
@@ -427,7 +436,7 @@ export namespace DragManager {
SnappingManager.clearSnapLines();
batch.end();
});
- const moveHandler = (e: PointerEvent) => {
+ const moveHandler = async (e: PointerEvent) => {
e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop
if (dragData instanceof DocumentDragData) {
dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : dragData.defaultDropAction;
@@ -438,7 +447,7 @@ export namespace DragManager {
dragData.removeDocument?.(dragData.draggedDocuments[0]);
}
AbortDrag();
- finishDrag?.(new DragCompleteEvent(true, dragData));
+ await finishDrag?.(new DragCompleteEvent(true, dragData));
DragManager.StartWindowDrag?.({
pageX: e.pageX,
pageY: e.pageY,
@@ -509,7 +518,7 @@ export namespace DragManager {
`translate(${(xs[i] += moveVec.x) + (options?.offsetX || 0)}px, ${(ys[i] += moveVec.y) + (options?.offsetY || 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`)
);
};
- const upHandler = (e: PointerEvent) => {
+ const upHandler = async (e: PointerEvent) => {
dispatchDrag(document.elementFromPoint(e.x, e.y) || document.body, e, new DragCompleteEvent(false, dragData), snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom), finishDrag, options);
endDrag();
};
@@ -517,7 +526,7 @@ export namespace DragManager {
document.addEventListener("pointerup", upHandler);
}
- function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number, y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions) {
+ async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number, y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions) {
const dropArgs = {
bubbles: true,
detail: {
@@ -531,7 +540,7 @@ export namespace DragManager {
}
};
target.dispatchEvent(new CustomEvent<DropEvent>("dashPreDrop", dropArgs));
- finishDrag?.(complete);
+ await finishDrag?.(complete);
target.dispatchEvent(new CustomEvent<DropEvent>("dashOnDrop", dropArgs));
options?.dragComplete?.(complete);
}
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index 8ddfce772..635673025 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -126,7 +126,7 @@ export namespace Hypothesis {
});
const annotationId = StrCast(linkDoc.annotationId);
- const linkUrl = Utils.prepend("/doc/" + sourceDoc[Id]);
+ const linkUrl = Doc.globalServerPath(sourceDoc);
const interval = setInterval(() => {// keep trying to edit until annotations have loaded and editing is successful
!success && document.dispatchEvent(new CustomEvent<{ targetUrl: string, id: string }>("deleteLink", {
detail: { targetUrl: linkUrl, id: annotationId },
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 01d00db30..ba935e3bf 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -208,7 +208,7 @@ export namespace InteractionUtils {
<polyline
points={strpts}
style={{
- filter: drawHalo ? "url(#inkSelectionHalo)" : undefined,
+ // filter: drawHalo ? "url(#inkSelectionHalo)" : undefined,
fill: fill ? fill : "none",
opacity: strokeWidth !== width ? 0.5 : undefined,
pointerEvents: pevents as any,
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 3c3d5c3b8..08f4ac9b7 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,15 +1,14 @@
+import { observable, observe, action } from "mobx";
import { computedFn } from "mobx-utils";
-import { Doc, DocListCast, Opt, DirectLinksSym, Field } from "../../fields/Doc";
-import { BoolCast, Cast, StrCast, PromiseValue } from "../../fields/Types";
+import { DirectLinksSym, Doc, DocListCast, Field, Opt } from "../../fields/Doc";
+import { List } from "../../fields/List";
+import { ProxyField } from "../../fields/Proxy";
+import { BoolCast, Cast, PromiseValue, StrCast } from "../../fields/Types";
import { LightboxView } from "../views/LightboxView";
import { DocumentViewSharedProps, ViewAdjustment } from "../views/nodes/DocumentView";
import { DocumentManager } from "./DocumentManager";
import { SharingManager } from "./SharingManager";
import { UndoManager } from "./UndoManager";
-import { observe, observable, reaction } from "mobx";
-import { listSpec } from "../../fields/Schema";
-import { List } from "../../fields/List";
-import { ProxyField } from "../../fields/Proxy";
type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
/*
@@ -34,7 +33,7 @@ export class LinkManager {
LinkManager._instance = this;
setTimeout(() => {
LinkManager.userDocs = [Doc.LinkDBDoc().data as Doc, ...SharingManager.Instance.users.map(user => user.linkDatabase)];
- const addLinkToDoc = (link: Doc): any => {
+ const addLinkToDoc = action((link: Doc): any => {
const a1 = link?.anchor1;
const a2 = link?.anchor2;
if (a1 instanceof Promise || a2 instanceof Promise) return PromiseValue(a1).then(a1 => PromiseValue(a2).then(a2 => addLinkToDoc(link)));
@@ -43,8 +42,8 @@ export class LinkManager {
Doc.GetProto(a2)[DirectLinksSym].add(link);
Doc.GetProto(link)[DirectLinksSym].add(link);
}
- };
- const remLinkFromDoc = (link: Doc): any => {
+ });
+ const remLinkFromDoc = action((link: Doc): any => {
const a1 = link?.anchor1;
const a2 = link?.anchor2;
if (a1 instanceof Promise || a2 instanceof Promise) return PromiseValue(a1).then(a1 => PromiseValue(a2).then(a2 => remLinkFromDoc(link)));
@@ -53,7 +52,7 @@ export class LinkManager {
Doc.GetProto(a2)[DirectLinksSym].delete(link);
Doc.GetProto(link)[DirectLinksSym].delete(link);
}
- };
+ });
const watchUserLinks = (userLinks: List<Doc>) => {
const toRealField = (field: Field) => field instanceof ProxyField ? field.value() : field; // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields
observe(userLinks, change => {
@@ -75,8 +74,10 @@ export class LinkManager {
});
}
- public addLink(linkDoc: Doc) {
- return Doc.AddDocToList(Doc.LinkDBDoc(), "data", linkDoc);
+ public addLink(linkDoc: Doc, checkExists = false) {
+ if (!checkExists || !DocListCast(Doc.LinkDBDoc().data).includes(linkDoc)) {
+ Doc.AddDocToList(Doc.LinkDBDoc(), "data", linkDoc);
+ }
}
public deleteLink(linkDoc: Doc) { return Doc.RemoveDocFromList(Doc.LinkDBDoc(), "data", linkDoc); }
public deleteAllLinksOnAnchor(anchor: Doc) { LinkManager.Instance.relatedLinker(anchor).forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc)); }
@@ -85,19 +86,6 @@ export class LinkManager {
public getAllDirectLinks(anchor: Doc): Doc[] {
return Array.from(Doc.GetProto(anchor)[DirectLinksSym]);
} // finds all links that contain the given anchor
- public getAllLinks(): Doc[] { return []; }//this.allLinks(); }
-
- // allLinks = computedFn(function allLinks(this: any): Doc[] {
- // const linkData = Doc.LinkDBDoc().data;
- // const lset = new Set<Doc>(DocListCast(linkData));
- // SharingManager.Instance.users.forEach(user => DocListCast(user.linkDatabase?.data).forEach(doc => lset.add(doc)));
- // LinkManager.Instance.allLinks().filter(link => {
- // const a1 = Cast(link?.anchor1, Doc, null);
- // const a2 = Cast(link?.anchor2, Doc, null);
- // return link && ((a1?.author !== undefined && a2?.author !== undefined) || link.author === Doc.CurrentUserEmail) && (Doc.AreProtosEqual(anchor, a1) || Doc.AreProtosEqual(anchor, a2) || Doc.AreProtosEqual(link, anchor));
- // });
- // return Array.from(lset);
- // }, true);
relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
const lfield = Doc.LayoutFieldKey(anchor);
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index c3c3083be..f981f84cd 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -181,7 +181,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
if (batch) {
batch.end();
}
- onError?.(error);
+ onError?.(script + " " + error);
return { success: false, error, result: errorVal };
}
};
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 00f0894c7..dbcc49f3d 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -4,6 +4,7 @@ import { Doc, Opt } from "../../fields/Doc";
import { CollectionSchemaView } from "../views/collections/collectionSchema/CollectionSchemaView";
import { CollectionViewType } from "../views/collections/CollectionView";
import { DocumentView } from "../views/nodes/DocumentView";
+import { DocumentType } from "../documents/DocumentTypes";
export namespace SelectionManager {
@@ -22,7 +23,7 @@ export namespace SelectionManager {
@action
SelectView(docView: DocumentView, ctrlPressed: boolean): void {
// if doc is not in SelectedDocuments, add it
- if (!manager.SelectedViews.get(docView)) {
+ if (!manager.SelectedViews.get(docView) && docView.props.Document.type !== DocumentType.MARKER) {
if (!ctrlPressed) {
this.DeselectAll();
}
diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss
index d8342ea56..c9db94419 100644
--- a/src/client/util/SettingsManager.scss
+++ b/src/client/util/SettingsManager.scss
@@ -1,4 +1,4 @@
-@import "../views/globalCssVariables";
+@import "../views/global/globalCssVariables";
.settings-interface {
//background-color: whitesmoke !important;
@@ -264,7 +264,7 @@
//margin-top: 4px;
&:hover {
- background: $main-accent;
+ background: $medium-gray;
}
}
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 777394b05..3987497b8 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -18,6 +18,12 @@ const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
+export enum ColorScheme {
+ Dark = "Dark",
+ Light = "Light",
+ System = "Match System"
+}
+
@observer
export class SettingsManager extends React.Component<{}> {
public static Instance: SettingsManager;
@@ -32,7 +38,7 @@ export class SettingsManager extends React.Component<{}> {
@observable activeTab = "Accounts";
@computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; }
-
+ @computed get colorScheme() { return Doc.UserDoc().colorScheme; }
constructor(props: {}) {
super(props);
@@ -69,6 +75,28 @@ export class SettingsManager extends React.Component<{}> {
else DocServer.Control.makeEditable();
});
+ @undoBatch
+ @action
+ changeColorScheme = action((e: React.ChangeEvent) => {
+ const scheme: ColorScheme = (e.currentTarget as any).value;
+ switch (scheme) {
+ case ColorScheme.Light:
+ Doc.UserDoc().colorScheme = ColorScheme.Light;
+ addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "#d3d3d3 !important" });
+ break;
+ case ColorScheme.Dark:
+ Doc.UserDoc().colorScheme = ColorScheme.Dark;
+ addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "black !important" });
+ break;
+ case ColorScheme.System: default:
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
+ Doc.UserDoc().colorScheme = e.matches ? ColorScheme.Dark : ColorScheme.Light;
+ });
+ break;
+ }
+ });
+
+
@computed get colorsContent() {
const colorBox = (func: (color: ColorState) => void) => <SketchPicker onChange={func} color={StrCast(this.backgroundColor)}
presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
@@ -90,8 +118,7 @@ export class SettingsManager extends React.Component<{}> {
</Flyout>
</div>;
- const fontFamilies = ["Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
- const fontSizes = ["7px", "8px", "9px", "10px", "12px", "14px", "16px", "18px", "20px", "24px", "32px", "48px", "72px"];
+ const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.System];
return <div className="colors-content">
<div className="preferences-color">
@@ -102,14 +129,11 @@ export class SettingsManager extends React.Component<{}> {
<div className="preferences-color-text">Border/Header Color</div>
{userColorFlyout}
</div>
- <div className="preferences-font">
- <div className="preferences-font-text">Default Font</div>
- <div className="preferences-font-controls">
- <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7px")}>
- {fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)}
- </select>
- <select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, "Times New Roman")} >
- {fontFamilies.map(font => <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}> {font} </option>)}
+ <div className="preferences-colorScheme">
+ <div className="preferences-color-text">Color Scheme</div>
+ <div className="preferences-color-controls">
+ <select className="scheme-select" onChange={this.changeColorScheme} defaultValue={StrCast(Doc.UserDoc().colorScheme)}>
+ {colorSchemes.map(scheme => <option key={scheme} value={scheme}> {scheme} </option>)}
</select>
</div>
</div>
@@ -132,6 +156,16 @@ export class SettingsManager extends React.Component<{}> {
checked={BoolCast(Doc.UserDoc()._raiseWhenDragged)} />
<div className="preferences-check">Raise on drag</div>
</div>
+ <div>
+ <input type="checkbox" onChange={e => Doc.UserDoc()._showLabel = !Doc.UserDoc()._showLabel}
+ checked={BoolCast(Doc.UserDoc()._showLabel)} />
+ <div className="preferences-check">Show tool button labels</div>
+ </div>
+ <div>
+ <input type="checkbox" onChange={e => Doc.UserDoc()._showMenuLabel = !Doc.UserDoc()._showMenuLabel}
+ checked={BoolCast(Doc.UserDoc()._showMenuLabel)} />
+ <div className="preferences-check">Show menu button labels</div>
+ </div>
</div>;
}
@@ -149,6 +183,27 @@ export class SettingsManager extends React.Component<{}> {
</div>;
}
+ @computed get textContent() {
+
+ const fontFamilies = ["Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text", "Roboto"];
+ const fontSizes = ["7px", "8px", "9px", "10px", "12px", "14px", "16px", "18px", "20px", "24px", "32px", "48px", "72px"];
+
+ return (
+ <div className="tab-content appearances-content">
+ <div className="preferences-font">
+ <div className="preferences-font-text">Default Font</div>
+ <div className="preferences-font-controls">
+ <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7px")}>
+ {fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)}
+ </select>
+ <select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, "Times New Roman")} >
+ {fontFamilies.map(font => <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}> {font} </option>)}
+ </select>
+ </div>
+ </div>
+ </div>);
+ }
+
@action
changeVal = (e: React.ChangeEvent, pass: string) => {
const value = (e.target as any).value;
@@ -228,7 +283,7 @@ export class SettingsManager extends React.Component<{}> {
// { title: "Accounts", ele: this.accountsContent }, { title: "Preferences", ele: this.preferencesContent }];
const tabs = [{ title: "Accounts", ele: this.accountsContent }, { title: "Modes", ele: this.modesContent },
- { title: "Appearance", ele: this.appearanceContent }];
+ { title: "Appearance", ele: this.appearanceContent }, { title: "Text", ele: this.textContent }];
return <div className="settings-interface">
<div className="settings-panel">
diff --git a/src/client/views/AntimodeMenu.scss b/src/client/views/AntimodeMenu.scss
index a275901be..8a0e5480e 100644
--- a/src/client/views/AntimodeMenu.scss
+++ b/src/client/views/AntimodeMenu.scss
@@ -1,14 +1,14 @@
-@import "./globalCssVariables";
+@import "./global/globalCssVariables";
.antimodeMenu-cont {
position: absolute;
z-index: 10001;
height: $antimodemenu-height;
- background: #323232;
- box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
+ background: $dark-gray;
+ border-bottom: $standard-border;
+ // box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
// border-radius: 0px 6px 6px 6px;
- z-index: 1001;
display: flex;
&.with-rows {
diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss
index b514de5f2..795529780 100644
--- a/src/client/views/ContextMenu.scss
+++ b/src/client/views/ContextMenu.scss
@@ -1,10 +1,10 @@
-@import "globalCssVariables";
+@import "global/globalCssVariables";
.contextMenu-cont {
position: absolute;
display: flex;
z-index: $contextMenu-zindex;
- box-shadow: $intermediate-color 0.2vw 0.2vw 0.4vw;
+ box-shadow: $medium-gray 0.2vw 0.2vw 0.4vw;
flex-direction: column;
background: whitesmoke;
padding-top: 10px;
@@ -14,17 +14,17 @@
}
// .contextMenu-item:first-child {
-// background: $intermediate-color;
-// color: $light-color;
+// background: $medium-gray;
+// color: $white;
// }
// .contextMenu-item:first-child::placeholder {
-// color: $light-color;
+// color: $white;
// }
// .contextMenu-item:first-child:hover {
-// background: $intermediate-color;
-// color: $light-color;
+// background: $medium-gray;
+// color: $white;
// }
.contextMenu-subMenu-cont {
@@ -94,7 +94,7 @@
.contextMenu-item:hover {
border-width: .11px;
border-style: none;
- border-color: $intermediate-color; // rgb(187, 186, 186);
+ border-color: $medium-gray; // rgb(187, 186, 186);
border-bottom-style: solid;
border-top-style: solid;
@@ -122,7 +122,7 @@
transition: all .1s;
border-width: .11px;
border-style: none;
- border-color: $intermediate-color; // rgb(187, 186, 186);
+ border-color: $medium-gray; // rgb(187, 186, 186);
// padding: 10px 0px 10px 0px;
white-space: nowrap;
font-size: 13px;
@@ -137,7 +137,7 @@
.contextMenu-item:hover {
transition: all 0.1s ease;
- background: $lighter-alt-accent;
+ background: $light-blue;
}
.contextMenu-description {
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index d96de72e3..c4fabbf99 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -218,7 +218,7 @@ export class ContextMenu extends React.Component {
@computed get menuItems() {
if (!this._searchString) {
- return this._items.map(item => <ContextMenuItem {...item} noexpand={this.itemsNeedSearch ? true : (item as any).noexpand} key={item.description} closeMenu={this.closeMenu} />);
+ return this._items.map((item, ind) => <ContextMenuItem {...item} noexpand={this.itemsNeedSearch ? true : (item as any).noexpand} key={ind + item.description} closeMenu={this.closeMenu} />);
}
return this.filteredViews;
}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 6168e5447..fc36c7e43 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -112,7 +112,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
styleFromLayoutString = (scale: number) => {
const style: { [key: string]: any } = {};
- const divKeys = ["width", "height", "fontSize", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"];
+ const divKeys = ["width", "height", "fontSize", "transform", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"];
const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a property expression string: { script } into a value
return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result as string || "";
};
diff --git a/src/client/views/DocumentButtonBar.scss b/src/client/views/DocumentButtonBar.scss
index 09ae14016..a112f4745 100644
--- a/src/client/views/DocumentButtonBar.scss
+++ b/src/client/views/DocumentButtonBar.scss
@@ -1,4 +1,4 @@
-@import "globalCssVariables";
+@import "global/globalCssVariables";
$linkGap : 3px;
@@ -7,13 +7,13 @@ $linkGap : 3px;
}
.documentButtonBar-linkButton-empty:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.05);
cursor: pointer;
}
.documentButtonBar-linkButton-nonempty:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.05);
cursor: pointer;
}
@@ -25,8 +25,8 @@ $linkGap : 3px;
border-radius: 50%;
opacity: 0.9;
pointer-events: auto;
- background-color: $dark-color;
- color: $light-color;
+ background-color: $dark-gray;
+ color: $white;
text-transform: uppercase;
letter-spacing: 2px;
font-size: 75%;
@@ -37,39 +37,60 @@ $linkGap : 3px;
align-items: center;
&:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.05);
cursor: pointer;
}
}
.documentButtonBar {
- margin-top: $linkGap;
- grid-column: 1/4;
- width: max-content;
- height: auto;
display: flex;
flex-direction: row;
}
.documentButtonBar-button {
- pointer-events: auto;
- padding-right: 5px;
- width: 25px;
+ cursor: pointer;
+ display: flex;
+ width: 30px;
+ height: 30px;
+ align-content: center;
+ justify-content: center;
+ align-items: center;
}
+// depracated (now use .documentButtonBar-icon) for standard buttons
.documentButtonBar-linker {
height: 20px;
width: 20px;
text-align: center;
border-radius: 50%;
pointer-events: auto;
- background-color: $dark-color;
+ background-color: $dark-gray;
+ border: none;
+ transition: 0.2s ease all;
+
+ &:hover {
+ background-color: $medium-gray;
+ }
+}
+
+.documentButtonBar-icon {
+ height: 80%;
+ width: 80%;
+ font-size: 100%;
+ text-align: center;
+ border-radius: 50%;
+ pointer-events: auto;
+ background-color: $dark-gray;
border: none;
transition: 0.2s ease all;
+ display: flex;
+ align-content: center;
+ justify-content: center;
+ align-items: center;
&:hover {
- background-color: $main-accent;
+ background-color: $black;
}
}
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index a5d80cd22..df1e6899d 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tooltip } from '@material-ui/core';
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../fields/Doc";
+import { Doc, DocCastAsync } from "../../fields/Doc";
import { RichTextField } from '../../fields/RichTextField';
import { Cast, NumCast, StrCast } from "../../fields/Types";
import { emptyFunction, setupMoveUpEvents, simulateMouseClick } from "../../Utils";
@@ -24,7 +24,7 @@ import { DocumentView } from './nodes/DocumentView';
import { GoogleRef } from "./nodes/formattedText/FormattedTextBox";
import { TemplateMenu } from "./TemplateMenu";
import React = require("react");
-import { PresBox } from './nodes/PresBox';
+import { PresBox } from './nodes/trails/PresBox';
import { undoBatch } from '../util/UndoManager';
import { CollectionViewType } from './collections/CollectionView';
const higflyout = require("@hig/flyout");
@@ -110,7 +110,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
const animation = this.isAnimatingPulse ? "shadow-pulse 1s linear infinite" : "none";
return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{`${published ? "Push" : "Publish"} to Google Docs`}</div></>}>
<div
- className="documentButtonBar-linker"
+ className="documentButtonBar-button"
style={{ animation }}
onClick={async () => {
await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
@@ -139,7 +139,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
return !targetDoc || !dataDoc || !dataDoc[GoogleRef] ? (null) : <Tooltip
title={<><div className="dash-tooltip">{title}</div></>}>
- <div className="documentButtonBar-linker"
+ <div className="documentButtonBar-button"
style={{ backgroundColor: this.pullColor }}
onPointerEnter={action(e => {
if (e.altKey) {
@@ -188,8 +188,8 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
const targetDoc = this.view0?.props.Document;
return !targetDoc ? (null) : <Tooltip title={
<div className="dash-tooltip">{"follow primary link on click"}</div>}>
- <div className="documentButtonBar-linker"
- style={{ color: targetDoc.isLinkButton ? "black" : "white", backgroundColor: targetDoc.isLinkButton ? "white" : "black" }}
+ <div className="documentButtonBar-icon"
+ style={{ color: targetDoc.isLinkButton ? "black" : "white" }}
onClick={undoBatch(e => this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, false, false)))}>
<FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="hand-point-right" />
</div>
@@ -200,7 +200,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
const targetDoc = this.view0?.props.Document;
return !targetDoc ? (null) : <Tooltip title={
<div className="dash-tooltip">{SelectionManager.Views().length > 1 ? "Pin multiple documents to presentation" : "Pin to presentation"}</div>}>
- <div className="documentButtonBar-linker"
+ <div className="documentButtonBar-icon"
style={{ color: "white" }}
onClick={undoBatch(e => this.props.views().map(view => view && TabDocView.PinDoc(view.props.Document, { setPosition: e.shiftKey ? true : undefined })))}>
<FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
@@ -243,7 +243,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 17, transform: 'translate(0, 1px)' }} />;
const targetDoc = this.view0?.props.Document;
return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Pin with current view"}</div></>}>
- <div className="documentButtonBar-linker" onClick={() => this.pinWithView(targetDoc)}>
+ <div className="documentButtonBar-icon" onClick={() => this.pinWithView(targetDoc)}>
{presPinWithViewIcon}
</div>
</Tooltip>;
@@ -253,8 +253,8 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
get shareButton() {
const targetDoc = this.view0?.props.Document;
return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Open Sharing Manager"}</div></>}>
- <div className="documentButtonBar-linker" style={{ color: "white" }} onClick={e => SharingManager.Instance.open(this.view0, targetDoc)}>
- <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="users" />
+ <div className="documentButtonBar-icon" style={{ color: "white" }} onClick={e => SharingManager.Instance.open(this.view0, targetDoc)}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon="users" />
</div></Tooltip >;
}
@@ -262,8 +262,8 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
get menuButton() {
const targetDoc = this.view0?.props.Document;
return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{`Open Context Menu`}</div></>}>
- <div className="documentButtonBar-linker" style={{ color: "white", cursor: "pointer" }} onClick={e => this.openContextMenu(e)}>
- <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="bars" />
+ <div className="documentButtonBar-icon" style={{ color: "white", cursor: "pointer" }} onClick={e => this.openContextMenu(e)}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon="bars" />
</div></Tooltip >;
}
@@ -271,9 +271,9 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
get moreButton() {
const targetDoc = this.view0?.props.Document;
return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{`${CurrentUserUtils.propertiesWidth > 0 ? "Close" : "Open"} Properties Panel`}</div></>}>
- <div className="documentButtonBar-linker" style={{ color: "white", cursor: "e-resize" }} onClick={action(e =>
+ <div className="documentButtonBar-icon" style={{ color: "white", cursor: "e-resize" }} onClick={action(e =>
CurrentUserUtils.propertiesWidth = CurrentUserUtils.propertiesWidth > 0 ? 0 : 250)}>
- <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="ellipsis-h"
+ <FontAwesomeIcon className="documentdecorations-icon" icon="ellipsis-h"
/>
</div></Tooltip >;
}
@@ -286,7 +286,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
<Flyout anchorPoint={anchorPoints.LEFT_TOP}
content={<MetadataEntryMenu docs={this.props.views().filter(dv => dv).map(dv => dv!.props.Document)} suggestWithFunction /> /* tfs: @bcz This might need to be the data document? */}>
<div className={"documentButtonBar-linkButton-" + "empty"} onPointerDown={e => e.stopPropagation()} >
- {<FontAwesomeIcon className="documentdecorations-icon" icon="tag" size="sm" />}
+ {<FontAwesomeIcon className="documentdecorations-icon" icon="tag" />}
</div>
</Flyout>
</div></Tooltip>;
@@ -348,16 +348,17 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
if (!this.view0) return (null);
const isText = this.view0.props.Document[this.view0.LayoutFieldKey] instanceof RichTextField;
+ const doc = this.view0?.props.Document;
const considerPull = isText && this.considerGoogleDocsPull;
const considerPush = isText && this.considerGoogleDocsPush;
return <div className="documentButtonBar">
<div className="documentButtonBar-button">
<DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={true} />
</div>
- {DocumentLinksButton.StartLink || !Doc.UserDoc()["documentLinksButton-fullMenu"] ? <div className="documentButtonBar-button">
+ {(DocumentLinksButton.StartLink || Doc.UserDoc()["documentLinksButton-fullMenu"]) && DocumentLinksButton.StartLink != doc ? <div className="documentButtonBar-button">
<DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
</div> : (null)}
- {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
+ {/*!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
{this.templateButton}
</div>
/*<div className="documentButtonBar-button">
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index db2d56aa8..316f63240 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -1,4 +1,4 @@
-@import "globalCssVariables";
+@import "global/globalCssVariables";
$linkGap : 3px;
@@ -49,7 +49,7 @@ $linkGap : 3px;
.documentDecorations-bottomResizer,
.documentDecorations-rightResizer {
pointer-events: auto;
- background: $alt-accent;
+ background: $medium-gray;
opacity: 0.1;
&:hover {
opacity: 1;
@@ -251,19 +251,18 @@ $linkGap : 3px;
}
.linkButton-empty:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.05);
cursor: pointer;
}
.linkButton-nonempty:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.05);
cursor: pointer;
}
.link-button-container {
- padding: $linkGap;
border-radius: 10px;
width: max-content;
height: auto;
@@ -271,7 +270,10 @@ $linkGap : 3px;
flex-direction: row;
z-index: 998;
position: absolute;
- background: $alt-accent;
+ justify-content: center;
+ align-items: center;
+ gap: 5px;
+ background: $medium-gray;
}
.linkButtonWrapper {
@@ -286,8 +288,8 @@ $linkGap : 3px;
text-align: center;
border-radius: 50%;
pointer-events: auto;
- color: $dark-color;
- border: $dark-color 1px solid;
+ color: $dark-gray;
+ border: $dark-gray 1px solid;
}
.linkButton-linker:hover {
@@ -302,8 +304,8 @@ $linkGap : 3px;
border-radius: 50%;
opacity: 0.9;
pointer-events: auto;
- background-color: $dark-color;
- color: $light-color;
+ background-color: $dark-gray;
+ color: $white;
text-transform: uppercase;
letter-spacing: 2px;
font-size: 75%;
@@ -314,7 +316,7 @@ $linkGap : 3px;
align-items: center;
&:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.05);
cursor: pointer;
}
@@ -334,7 +336,7 @@ $linkGap : 3px;
}
.documentdecorations-icon {
- margin-top: 3px;
+ margin: 0px;
}
.templating-button,
.docDecs-tagButton {
@@ -343,13 +345,13 @@ $linkGap : 3px;
border-radius: 50%;
opacity: 0.9;
font-size: 14;
- background-color: $dark-color;
- color: $light-color;
+ background-color: $dark-gray;
+ color: $white;
text-align: center;
cursor: pointer;
&:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.05);
}
}
@@ -365,7 +367,7 @@ $linkGap : 3px;
width: max-content;
font-family: $sans-serif;
font-size: 12px;
- background-color: $light-color-secondary;
+ background-color: $light-gray;
padding: 2px 12px;
list-style: none;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 65a97a49d..d24ab974c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -201,7 +201,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
(e: PointerEvent, down: number[], delta: number[]) => {
const movement = { X: delta[0], Y: e.clientY - down[1] };
const angle = Math.max(1, Math.abs(movement.Y / 10));
- InkStrokeProperties.Instance?.rotate(2 * movement.X / angle * (Math.PI / 180));
+ InkStrokeProperties.Instance?.rotateInk(2 * movement.X / angle * (Math.PI / 180));
return false;
},
() => this._rotateUndo?.end(),
diff --git a/src/client/views/EditableView.scss b/src/client/views/EditableView.scss
index 5dc0c1962..1aebedf2e 100644
--- a/src/client/views/EditableView.scss
+++ b/src/client/views/EditableView.scss
@@ -26,4 +26,10 @@
width: 100%;
background: inherit;
pointer-events: all;
-} \ No newline at end of file
+}
+
+.editableView-input:focus {
+ border: none;
+ outline: none;
+}
+ \ No newline at end of file
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index c4162a6bb..76eb4c142 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -114,7 +114,7 @@ export class KeyManager {
case "escape":
DocumentLinksButton.StartLink = undefined;
DocumentLinksButton.StartLinkView = undefined;
- InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = false);
+ InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlButton = false);
CurrentUserUtils.SelectedTool = InkTool.None;
var doDeselect = true;
if (SnappingManager.GetIsDragging()) {
diff --git a/src/client/views/InkControls.tsx b/src/client/views/InkControls.tsx
new file mode 100644
index 000000000..6213a4075
--- /dev/null
+++ b/src/client/views/InkControls.tsx
@@ -0,0 +1,142 @@
+import React = require("react");
+import { observable, action } from "mobx";
+import { observer } from "mobx-react";
+import { InkStrokeProperties } from "./InkStrokeProperties";
+import { setupMoveUpEvents, emptyFunction } from "../../Utils";
+import { UndoManager } from "../util/UndoManager";
+import { ControlPoint, InkData, PointData } from "../../fields/InkField";
+import { Transform } from "../util/Transform";
+import { Colors } from "./global/globalEnums";
+import { Doc } from "../../fields/Doc";
+import { listSpec } from "../../fields/Schema";
+import { Cast } from "../../fields/Types";
+
+export interface InkControlProps {
+ inkDoc: Doc;
+ data: InkData;
+ addedPoints: PointData[];
+ format: number[];
+ ScreenToLocalTransform: () => Transform;
+}
+
+@observer
+export class InkControls extends React.Component<InkControlProps> {
+ @observable private _overControl = -1;
+ @observable private _overAddPoint = -1;
+
+ /**
+ * Handles the movement of a selected control point when the user clicks and drags.
+ * @param controlIndex The index of the currently selected control point.
+ */
+ @action
+ onControlDown = (e: React.PointerEvent, controlIndex: number): void => {
+ if (InkStrokeProperties.Instance) {
+ InkStrokeProperties.Instance.moveControl(0, 0, 1);
+ const controlUndo = UndoManager.StartBatch("DocDecs set radius");
+ const screenScale = this.props.ScreenToLocalTransform().Scale;
+ const order = controlIndex % 4;
+ const handleIndexA = order === 2 ? controlIndex - 1 : controlIndex - 2;
+ const handleIndexB = order === 2 ? controlIndex + 2 : controlIndex + 1;
+ const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number"));
+ setupMoveUpEvents(this, e,
+ (e: PointerEvent, down: number[], delta: number[]) => {
+ InkStrokeProperties.Instance?.moveControl(-delta[0] * screenScale, -delta[1] * screenScale, controlIndex);
+ return false;
+ },
+ () => controlUndo?.end(),
+ action((e: PointerEvent, doubleTap: boolean | undefined) => {
+ if (doubleTap && brokenIndices && brokenIndices.includes(controlIndex)) {
+ InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB);
+ }
+ }));
+ }
+ }
+
+ /**
+ * Deletes the currently selected point.
+ */
+ @action
+ onDelete = (e: KeyboardEvent) => {
+ if (["-", "Backspace", "Delete"].includes(e.key)) {
+ if (InkStrokeProperties.Instance?.deletePoints()) e.stopPropagation();
+ }
+ }
+
+ /**
+ * Changes the current selected control point.
+ */
+ @action
+ changeCurrPoint = (i: number) => {
+ if (InkStrokeProperties.Instance) {
+ InkStrokeProperties.Instance._currentPoint = i;
+ document.addEventListener("keydown", this.onDelete, true);
+ }
+ }
+
+ /**
+ * Updates whether a user has hovered over a particular control point or point that could be added
+ * on click.
+ */
+ @action onEnterControl = (i: number) => { this._overControl = i; };
+ @action onLeaveControl = () => { this._overControl = -1; };
+ @action onEnterAddPoint = (i: number) => { this._overAddPoint = i; };
+ @action onLeaveAddPoint = () => { this._overAddPoint = -1; };
+
+ render() {
+ const formatInstance = InkStrokeProperties.Instance;
+ if (!formatInstance) return (null);
+
+ // Accessing the current ink's data and extracting all control points.
+ const data = this.props.data;
+ const controlPoints: ControlPoint[] = [];
+ if (data.length >= 4) {
+ for (let i = 0; i <= data.length - 4; i += 4) {
+ controlPoints.push({ X: data[i].X, Y: data[i].Y, I: i });
+ controlPoints.push({ X: data[i + 3].X, Y: data[i + 3].Y, I: i + 3 });
+ }
+ }
+ const addedPoints = this.props.addedPoints;
+ const [left, top, scaleX, scaleY, strokeWidth] = this.props.format;
+
+ return (
+ <>
+ {addedPoints.map((pts, i) =>
+ <svg height="10" width="10" key={`add${i}`}>
+ <circle
+ cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2}
+ cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
+ r={strokeWidth / 1.5}
+ stroke={this._overAddPoint === i ? Colors.MEDIUM_BLUE : "transparent"}
+ strokeWidth={0} fill={this._overAddPoint === i ? Colors.MEDIUM_BLUE : "transparent"}
+ onPointerDown={() => { formatInstance?.addPoints(pts.X, pts.Y, addedPoints, i, controlPoints); }}
+ onMouseEnter={() => this.onEnterAddPoint(i)}
+ onMouseLeave={this.onLeaveAddPoint}
+ pointerEvents="all"
+ cursor="all-scroll"
+ />
+ </svg>
+ )}
+ {controlPoints.map((control, i) =>
+ <svg height="10" width="10" key={`ctrl${i}`}>
+ <rect
+ x={(control.X - left - strokeWidth / 2) * scaleX}
+ y={(control.Y - top - strokeWidth / 2) * scaleY}
+ height={this._overControl === i ? strokeWidth * 1.5 : strokeWidth}
+ width={this._overControl === i ? strokeWidth * 1.5 : strokeWidth}
+ strokeWidth={strokeWidth / 6} stroke={Colors.MEDIUM_BLUE}
+ fill={formatInstance?._currentPoint === control.I ? Colors.MEDIUM_BLUE : Colors.WHITE}
+ onPointerDown={(e) => {
+ this.changeCurrPoint(control.I);
+ this.onControlDown(e, control.I);
+ }}
+ onMouseEnter={() => this.onEnterControl(i)}
+ onMouseLeave={this.onLeaveControl}
+ pointerEvents="all"
+ cursor="default"
+ />
+ </svg>
+ )}
+ </>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/InkHandles.tsx b/src/client/views/InkHandles.tsx
new file mode 100644
index 000000000..f1eb4b9db
--- /dev/null
+++ b/src/client/views/InkHandles.tsx
@@ -0,0 +1,124 @@
+import React = require("react");
+import { observable, action } from "mobx";
+import { observer } from "mobx-react";
+import { InkStrokeProperties } from "./InkStrokeProperties";
+import { setupMoveUpEvents, emptyFunction } from "../../Utils";
+import { UndoManager } from "../util/UndoManager";
+import { InkData, HandlePoint, HandleLine } from "../../fields/InkField";
+import { Transform } from "../util/Transform";
+import { Doc } from "../../fields/Doc";
+import { listSpec } from "../../fields/Schema";
+import { List } from "../../fields/List";
+import { Cast } from "../../fields/Types";
+import { Colors } from "./global/globalEnums";
+
+export interface InkHandlesProps {
+ inkDoc: Doc;
+ data: InkData;
+ format: number[];
+ ScreenToLocalTransform: () => Transform;
+}
+
+@observer
+export class InkHandles extends React.Component<InkHandlesProps> {
+ /**
+ * Handles the movement of a selected handle point when the user clicks and drags.
+ * @param handleNum The index of the currently selected handle point.
+ */
+ onHandleDown = (e: React.PointerEvent, handleIndex: number): void => {
+ if (InkStrokeProperties.Instance) {
+ InkStrokeProperties.Instance.moveControl(0, 0, 1);
+ const controlUndo = UndoManager.StartBatch("DocDecs set radius");
+ const screenScale = this.props.ScreenToLocalTransform().Scale;
+ const order = handleIndex % 4;
+ const oppositeHandleIndex = order === 1 ? handleIndex - 3 : handleIndex + 3;
+ const controlIndex = order === 1 ? handleIndex - 1 : handleIndex + 2;
+ document.addEventListener("keydown", (e: KeyboardEvent) => this.onBreakTangent(e, controlIndex), true);
+ setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => {
+ InkStrokeProperties.Instance?.moveHandle(-delta[0] * screenScale, -delta[1] * screenScale, handleIndex, oppositeHandleIndex, controlIndex);
+ return false;
+ }, () => controlUndo?.end(), emptyFunction
+ );
+ }
+ }
+
+ /**
+ * Breaks tangent handle movement when ‘Alt’ key is held down. Adds the current handle index and
+ * its matching (opposite) handle to a list of broken handle indices.
+ * @param handleNum The index of the currently selected handle point.
+ */
+ @action
+ onBreakTangent = (e: KeyboardEvent, controlIndex: number) => {
+ const doc: Doc = this.props.inkDoc;
+ if (["Alt"].includes(e.key)) {
+ e.stopPropagation();
+ if (doc) {
+ const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number")) || new List;
+ if (brokenIndices && !brokenIndices.includes(controlIndex)) {
+ brokenIndices.push(controlIndex);
+ }
+ doc.brokenInkIndices = brokenIndices;
+ }
+ }
+ }
+
+ render() {
+ const formatInstance = InkStrokeProperties.Instance;
+ if (!formatInstance) return (null);
+
+ // Accessing the current ink's data and extracting all handle points and handle lines.
+ const data = this.props.data;
+ const handlePoints: HandlePoint[] = [];
+ const handleLines: HandleLine[] = [];
+ if (data.length >= 4) {
+ for (let i = 0; i <= data.length - 4; i += 4) {
+ handlePoints.push({ X: data[i + 1].X, Y: data[i + 1].Y, I: i + 1, dot1: i, dot2: i === 0 ? i : i - 1 });
+ handlePoints.push({ X: data[i + 2].X, Y: data[i + 2].Y, I: i + 2, dot1: i + 3, dot2: i === data.length ? i + 3 : i + 4 });
+ }
+ // Adding first and last (single) handle lines.
+ handleLines.push({ X1: data[0].X, Y1: data[0].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: data[1].Y, dot1: 0, dot2: 0 });
+ handleLines.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[data.length - 1].X, Y2: data[data.length - 1].Y, X3: data[data.length - 1].X, Y3: data[data.length - 1].Y, dot1: data.length - 1, dot2: data.length - 1 });
+ for (let i = 2; i < data.length - 4; i += 4) {
+ handleLines.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 1].X, Y2: data[i + 1].Y, X3: data[i + 3].X, Y3: data[i + 3].Y, dot1: i + 1, dot2: i + 2 });
+ }
+ }
+ const [left, top, scaleX, scaleY, strokeWidth] = this.props.format;
+
+ return (
+ <>
+ {handlePoints.map((pts, i) =>
+ <svg height="10" width="10" key={`hdl${i}`}>
+ <circle
+ cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2}
+ cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
+ r={strokeWidth / 2}
+ strokeWidth={0}
+ fill={Colors.MEDIUM_BLUE}
+ onPointerDown={(e) => this.onHandleDown(e, pts.I)}
+ pointerEvents="all"
+ cursor="default"
+ display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} />
+ </svg>)}
+ {handleLines.map((pts, i) =>
+ <svg height="100" width="100" key={`line${i}`}>
+ <line
+ x1={(pts.X1 - left - strokeWidth / 2) * scaleX + strokeWidth / 2}
+ y1={(pts.Y1 - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
+ x2={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2}
+ y2={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
+ stroke={Colors.MEDIUM_BLUE}
+ strokeWidth={strokeWidth / 4}
+ display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} />
+ <line
+ x1={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2}
+ y1={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
+ x2={(pts.X3 - left - strokeWidth / 2) * scaleX + strokeWidth / 2}
+ y2={(pts.Y3 - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
+ stroke={Colors.MEDIUM_BLUE}
+ strokeWidth={strokeWidth / 4}
+ display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} />
+ </svg>)}
+ </>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/InkStroke.scss b/src/client/views/InkStroke.scss
new file mode 100644
index 000000000..812a79bd5
--- /dev/null
+++ b/src/client/views/InkStroke.scss
@@ -0,0 +1,11 @@
+.inkStroke {
+ mix-blend-mode: multiply;
+ stroke-linejoin: round;
+ stroke-linecap: round;
+ overflow: visible !important;
+ transform-origin: top left;
+
+ svg:not(:root) {
+ overflow: visible !important;
+ }
+}
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index b13b04f68..76ca5b5ec 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -1,124 +1,44 @@
import { action, computed, observable } from "mobx";
-import { ColorState } from 'react-color';
-import { Doc, Field, Opt } from "../../fields/Doc";
+import { Doc, DocListCast, Field, Opt } from "../../fields/Doc";
import { Document } from "../../fields/documentSchemas";
-import { InkField, InkData } from "../../fields/InkField";
+import { InkField, InkData, PointData, ControlPoint } from "../../fields/InkField";
+import { List } from "../../fields/List";
+import { listSpec } from "../../fields/Schema";
import { Cast, NumCast } from "../../fields/Types";
import { DocumentType } from "../documents/DocumentTypes";
import { SelectionManager } from "../util/SelectionManager";
import { undoBatch } from "../util/UndoManager";
-import { bool } from "sharp";
export class InkStrokeProperties {
static Instance: InkStrokeProperties | undefined;
- private _lastFill = "#D0021B";
- private _lastLine = "#D0021B";
- private _lastDash = "2";
- private _inkDocs: { x: number, y: number, width: number, height: number }[] = [];
-
@observable _lock = false;
- @observable _controlBtn = false;
- @observable _currPoint = -1;
+ @observable _controlButton = false;
+ @observable _currentPoint = -1;
- getField(key: string) {
- return this.selectedInk?.reduce((p, i) =>
- (p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt<string>);
+ constructor() {
+ InkStrokeProperties.Instance = this;
}
@computed get selectedInk() {
const inks = SelectionManager.Views().filter(i => Document(i.rootDoc).type === DocumentType.INK);
return inks.length ? inks : undefined;
}
- @computed get unFilled() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; }
- @computed get unStrokd() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; }
- @computed get solidFil() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; }
- @computed get solidStk() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; }
- @computed get dashdStk() { return !this.unStrokd && this.getField("strokeDash") || ""; }
- @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; }
- @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; }
- @computed get widthStk() { return this.getField("strokeWidth") || "1"; }
- @computed get markHead() { return this.getField("strokeStartMarker") || ""; }
- @computed get markTail() { return this.getField("strokeEndMarker") || ""; }
- @computed get shapeHgt() { return this.getField("_height"); }
- @computed get shapeWid() { return this.getField("_width"); }
- @computed get shapeXps() { return this.getField("x"); }
- @computed get shapeYps() { return this.getField("y"); }
- @computed get shapeRot() { return this.getField("rotation"); }
- set unFilled(value) { this.colorFil = value ? "" : this._lastFill; }
- set solidFil(value) { this.unFilled = !value; }
- set colorFil(value) { value && (this._lastFill = value); this.selectedInk?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); }
- set colorStk(value) { value && (this._lastLine = value); this.selectedInk?.forEach(i => i.rootDoc.color = value ? value : undefined); }
- set markHead(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeStartMarker = value); }
- set markTail(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeEndMarker = value); }
- set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; }
- set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; }
- set dashdStk(value) {
- value && (this._lastDash = value) && (this.unStrokd = false);
- this.selectedInk?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined);
- }
- set shapeXps(value) { this.selectedInk?.forEach(i => i.rootDoc.x = Number(value)); }
- set shapeYps(value) { this.selectedInk?.forEach(i => i.rootDoc.y = Number(value)); }
- set shapeRot(value) { this.selectedInk?.forEach(i => i.rootDoc.rotation = Number(value)); }
- set widthStk(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = Number(value)); }
- set shapeWid(value) {
- this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
- const oldWidth = NumCast(i.rootDoc._width);
- i.rootDoc._width = Number(value);
- this._lock && (i.rootDoc._height = (i.rootDoc._width * NumCast(i.rootDoc._height)) / oldWidth);
- });
- }
- set shapeHgt(value) {
- this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
- const oldHeight = NumCast(i.rootDoc._height);
- i.rootDoc._height = Number(value);
- this._lock && (i.rootDoc._width = (i.rootDoc._height * NumCast(i.rootDoc._width)) / oldHeight);
- });
- }
-
- constructor() {
- InkStrokeProperties.Instance = this;
- }
- @undoBatch
- @action
- addPoints = (x: number, y: number, pts: { X: number, Y: number }[], index: number, control: { X: number, Y: number }[]) => {
- this.selectedInk?.forEach(action(inkView => {
- if (this.selectedInk?.length === 1) {
- const doc = Document(inkView.rootDoc);
- if (doc.type === DocumentType.INK) {
- const ink = Cast(doc.data, InkField)?.inkData;
- if (ink) {
- const newPoints: { X: number, Y: number }[] = [];
- var counter = 0;
- for (var k = 0; k < index; k++) {
- control.forEach(pt => (pts[k].X === pt.X && pts[k].Y === pt.Y) && counter++);
- }
- //decide where to put the new coordinate
- const spNum = Math.floor(counter / 2) * 4 + 2;
-
- for (var i = 0; i < spNum; i++) {
- ink[i] && newPoints.push({ X: ink[i].X, Y: ink[i].Y });
- }
- for (var j = 0; j < 4; j++) {
- newPoints.push({ X: x, Y: y });
-
- }
- for (var i = spNum; i < ink.length; i++) {
- newPoints.push({ X: ink[i].X, Y: ink[i].Y });
- }
- this._currPoint = -1;
- Doc.GetProto(doc).data = new InkField(newPoints);
- }
- }
- }
- }));
+ getField(key: string) {
+ return this.selectedInk?.reduce((p, i) =>
+ (p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt<string>);
}
+ /**
+ * Helper function that enables other functions to be applied to a particular ink instance.
+ * @param func The inputted function.
+ * @param requireCurrPoint Indicates whether the current selected point is needed.
+ */
applyFunction = (func: (doc: Doc, ink: InkData, ptsXscale: number, ptsYscale: number) => { X: number, Y: number }[] | undefined, requireCurrPoint: boolean = false) => {
var appliedFunc = false;
this.selectedInk?.forEach(action(inkView => {
- if (this.selectedInk?.length === 1 && (!requireCurrPoint || this._currPoint !== -1)) {
+ if (this.selectedInk?.length === 1 && (!requireCurrPoint || this._currentPoint !== -1)) {
const doc = Document(inkView.rootDoc);
if (doc.type === DocumentType.INK && doc.width && doc.height) {
const ink = Cast(doc.data, InkField)?.inkData;
@@ -145,17 +65,136 @@ export class InkStrokeProperties {
return appliedFunc;
}
+ /**
+ * Adds a new control point to the ink instance when editing its format.
+ * @param index The index of the new point.
+ * @param control The list of all control points of the ink.
+ */
+ @undoBatch
+ @action
+ addPoints = (x: number, y: number, points: InkData, index: number, controls: { X: number, Y: number }[]) => {
+ this.applyFunction((doc: Doc, ink: InkData) => {
+ const newControl = { X: x, Y: y };
+ const newPoints: InkData = [];
+ let [counter, start, end] = [0, 0, 0];
+ for (let k = 0; k < points.length; k++) {
+ if (end === 0) {
+ controls.forEach((control) => {
+ if (control.X === points[k].X && control.Y === points[k].Y) {
+ if (k < index) {
+ counter++;
+ start = k;
+ } else if (k > index) {
+ end = k;
+ }
+ }
+ });
+ }
+ }
+ if (end === 0) end = points.length - 1;
+ // Index of new control point with regards to the ink data.
+ const newIndex = Math.floor(counter / 2) * 4 + 2;
+ // Creating new ink data with the new control point and handle points inputted.
+ for (let i = 0; i < ink.length; i++) {
+ if (i === newIndex) {
+ const [handleA, handleB] = this.getNewHandlePoints(points.slice(start, index + 1), points.slice(index, end), newControl);
+ newPoints.push(handleA, newControl, newControl, handleB);
+ // Adjusting the magnitude of the left handle line of the right neighboring control point.
+ const [rightControl, rightHandle] = [points[end], ink[i]];
+ const scaledVector = this.getScaledHandlePoint(false, start, end, index, rightControl, rightHandle);
+ rightHandle && newPoints.push({ X: rightControl.X - scaledVector.X, Y: rightControl.Y - scaledVector.Y });
+ } else if (i === newIndex - 1) {
+ // Adjusting the magnitude of the right handle line of the left neighboring control point.
+ const [leftControl, leftHandle] = [points[start], ink[i]];
+ const scaledVector = this.getScaledHandlePoint(true, start, end, index, leftControl, leftHandle);
+ leftHandle && newPoints.push({ X: leftControl.X - scaledVector.X, Y: leftControl.Y - scaledVector.Y });
+ } else {
+ ink[i] && newPoints.push({ X: ink[i].X, Y: ink[i].Y });
+ }
+
+ }
+ let brokenIndices = Cast(doc.brokenInkIndices, listSpec("number"));
+ // Updating the indices of the control points whose handle tangency has been broken.
+ if (brokenIndices) {
+ brokenIndices = new List(brokenIndices.map((control) => {
+ if (control >= newIndex) {
+ return control + 4;
+ } else {
+ return control;
+ }
+ }));
+ }
+ doc.brokenInkIndices = brokenIndices;
+ this._currentPoint = -1;
+ return newPoints;
+ });
+ }
+
+ /**
+ * Scales a handle point of a control point that is adjacent to a newly added one.
+ * @param isLeft Determines if the current control point is on the left or right side of the newly added one.
+ * @param start Beginning index of curve from the left control point to the newly added one.
+ * @param end Final index of curve from the newly added control point to its right neighbor.
+ */
+ getScaledHandlePoint(isLeft: boolean, start: number, end: number, index: number, control: PointData, handle: PointData) {
+ const prevSize = end - start;
+ const newSize = isLeft ? index - start : end - index;
+ const handleVector = { X: control.X - handle.X, Y: control.Y - handle.Y };
+ const scaledVector = { X: handleVector.X * (newSize / prevSize), Y: handleVector.Y * (newSize / prevSize) };
+ return scaledVector;
+ }
+
+ /**
+ * Determines the position of the handle points of a newly added control point by finding the
+ * tangent vectors to the split curve at the new control. Given the properties of Bézier curves,
+ * the tangent vector to a control point is equivalent to the first/last (depending on the direction
+ * of the curve) leg of the Bézier curve's derivative.
+ * (Source: https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html)
+ *
+ * @param C The curve represented by all points from the previous control until the newly added point.
+ * @param D The curve represented by all points from the newly added point to the next control.
+ * @param newControl The newly added control point.
+ */
+ getNewHandlePoints = (C: PointData[], D: PointData[], newControl: PointData) => {
+ const [m, n] = [C.length, D.length];
+ let handleSizeA = Math.sqrt((Math.pow(newControl.X - C[0].X, 2)) + (Math.pow(newControl.Y - C[0].Y, 2)));
+ let handleSizeB = Math.sqrt((Math.pow(D[n - 1].X - newControl.X, 2)) + (Math.pow(D[n - 1].Y - newControl.Y, 2)));
+ // Scaling adjustments to improve the ratio between the magnitudes of the two handle lines.
+ // (Ensures that the new point added doesn't augment the inital shape of the curve much).
+ if (handleSizeA < 75 && handleSizeB < 75) {
+ handleSizeA *= 3;
+ handleSizeB *= 3;
+ }
+ if (Math.abs(handleSizeA - handleSizeB) < 50) {
+ handleSizeA *= 5;
+ handleSizeB *= 5;
+ } else if (Math.abs(handleSizeA - handleSizeB) < 150) {
+ handleSizeA *= 2;
+ handleSizeB *= 2;
+ }
+ // Finding the last leg of the derivative curve of C.
+ const dC = { X: (handleSizeA / n) * (C[m - 1].X - C[m - 2].X), Y: (handleSizeA / n) * (C[m - 1].Y - C[m - 2].Y) };
+ // Finding the first leg of the derivative curve of D.
+ const dD = { X: (handleSizeB / m) * (D[1].X - D[0].X), Y: (handleSizeB / m) * (D[1].Y - D[0].Y) };
+ const handleA = { X: newControl.X - dC.X, Y: newControl.Y - dC.Y };
+ const handleB = { X: newControl.X + dD.X, Y: newControl.Y + dD.Y };
+ return [handleA, handleB];
+ }
+
+ /**
+ * Deletes the current control point of the selected ink instance.
+ */
@undoBatch
@action
deletePoints = () => this.applyFunction((doc: Doc, ink: InkData) => {
- var newPoints: { X: number, Y: number }[] = [];
- const toRemove = Math.floor(((this._currPoint + 2) / 4));
- for (var i = 0; i < ink.length; i++) {
+ const newPoints: { X: number, Y: number }[] = [];
+ const toRemove = Math.floor(((this._currentPoint + 2) / 4));
+ for (let i = 0; i < ink.length; i++) {
if (Math.floor((i + 2) / 4) !== toRemove && (toRemove !== 0 || i > 3)) {
newPoints.push({ X: ink[i].X, Y: ink[i].Y });
}
}
- this._currPoint = -1;
+ this._currentPoint = -1;
if (newPoints.length < 4) return undefined;
if (newPoints.length === 4) {
const newerPoints: { X: number, Y: number }[] = [];
@@ -166,12 +205,16 @@ export class InkStrokeProperties {
return newerPoints;
}
return newPoints;
- }, true);
+ }, true)
+ /**
+ * Rotates the entire selected ink instance.
+ * @param angle The angle at which to rotate the ink in radians.
+ */
@undoBatch
@action
- rotate = (angle: number) => {
- this.applyFunction((doc: Doc, ink: InkData, ptsXscale: number, ptsYscale: number) => {
+ rotateInk = (angle: number) => {
+ this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => {
const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X));
const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y));
const centerPoint = { X: (oldXrange.min + oldXrange.max) / 2, Y: (oldYrange.min + oldYrange.max) / 2 };
@@ -186,42 +229,116 @@ export class InkStrokeProperties {
});
}
+ /**
+ * Handles the movement/scaling of a control point.
+ */
@undoBatch
@action
- control = (xDiff: number, yDiff: number, controlNum: number) =>
- this.applyFunction((doc: Doc, ink: InkData, ptsXscale: number, ptsYscale: number) => {
+ moveControl = (deltaX: number, deltaY: number, controlIndex: number) =>
+ this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => {
const newPoints: { X: number, Y: number }[] = [];
- const order = controlNum % 4;
+ const order = controlIndex % 4;
for (var i = 0; i < ink.length; i++) {
- newPoints.push(
- (controlNum === i ||
- (order === 0 && i === controlNum + 1) ||
- (order === 0 && controlNum !== 0 && i === controlNum - 2) ||
- (order === 0 && controlNum !== 0 && i === controlNum - 1) ||
- (order === 3 && i === controlNum - 1) ||
- (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 1) ||
- (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 2) ||
- ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlNum === 0 || controlNum === ink.length - 1))
- ) ?
- { X: ink[i].X - xDiff / ptsXscale, Y: ink[i].Y - yDiff / ptsYscale } :
- { X: ink[i].X, Y: ink[i].Y });
+ const leftHandlePoint = order === 0 && i === controlIndex + 1;
+ const rightHandlePoint = order === 0 && controlIndex !== 0 && i === controlIndex - 2;
+ if (controlIndex === i ||
+ leftHandlePoint ||
+ rightHandlePoint ||
+ (order === 0 && controlIndex !== 0 && i === controlIndex - 1) ||
+ (order === 3 && i === controlIndex - 1) ||
+ (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 1) ||
+ (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 2) ||
+ ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlIndex === 0 || controlIndex === ink.length - 1))) {
+ newPoints.push({ X: ink[i].X - deltaX / xScale, Y: ink[i].Y - deltaY / yScale });
+ } else {
+ newPoints.push({ X: ink[i].X, Y: ink[i].Y });
+ }
}
return newPoints;
+ })
+
+ /**
+ * Snaps a control point with broken tangency back to synced rotation.
+ * @param handleIndexA The handle point that retains its current position.
+ * @param handleIndexB The handle point that is rotated to be 180 degrees from its opposite.
+ */
+ snapHandleTangent = (controlIndex: number, handleIndexA: number, handleIndexB: number) => {
+ this.applyFunction((doc: Doc, ink: InkData) => {
+ const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number"));
+ if (brokenIndices) {
+ brokenIndices.splice(brokenIndices.indexOf(controlIndex), 1);
+ doc.brokenInkIndices = brokenIndices;
+ const [controlPoint, handleA, handleB] = [ink[controlIndex], ink[handleIndexA], ink[handleIndexB]];
+ const oppositeHandleA = this.rotatePoint(handleA, controlPoint, Math.PI);
+ const angleDifference = this.angleChange(handleB, oppositeHandleA, controlPoint);
+ const newHandleB = this.rotatePoint(handleB, controlPoint, angleDifference);
+ ink[handleIndexB] = newHandleB;
+ return ink;
+ }
});
+ }
- @undoBatch
+ /**
+ * Rotates the target point about the origin point for a given angle (radians).
+ */
@action
- switchStk = (color: ColorState) => {
- const val = String(color.hex);
- this.colorStk = val;
- return true;
+ rotatePoint = (target: PointData, origin: PointData, angle: number) => {
+ let rotatedTarget = { X: target.X - origin.X, Y: target.Y - origin.Y };
+ const newX = Math.cos(angle) * rotatedTarget.X - Math.sin(angle) * rotatedTarget.Y;
+ const newY = Math.sin(angle) * rotatedTarget.X + Math.cos(angle) * rotatedTarget.Y;
+ rotatedTarget.X = newX + origin.X;
+ rotatedTarget.Y = newY + origin.Y;
+ return rotatedTarget;
+ }
+
+ /**
+ * Finds the angle (in radians) between two inputted vectors.
+ *
+ * α = arccos(a·b / |a|·|b|), where a and b are both vectors.
+ */
+ angleBetweenTwoVectors = (vectorA: PointData, vectorB: PointData) => {
+ const magnitudeA = Math.sqrt(vectorA.X * vectorA.X + vectorA.Y * vectorA.Y);
+ const magnitudeB = Math.sqrt(vectorB.X * vectorB.X + vectorB.Y * vectorB.Y);
+ // Normalizing the vectors.
+ vectorA = { X: vectorA.X / magnitudeA, Y: vectorA.Y / magnitudeA };
+ vectorB = { X: vectorB.X / magnitudeB, Y: vectorB.Y / magnitudeB };
+ const dotProduct = vectorB.X * vectorA.X + vectorB.Y * vectorA.Y;
+ return Math.acos(dotProduct);
}
+ /**
+ * Finds the angle difference (in radians) between two vectors relative to an arbitrary origin.
+ */
+ angleChange = (a: PointData, b: PointData, origin: PointData) => {
+ // Finding vector representation of inputted points relative to new origin.
+ const vectorA = { X: a.X - origin.X, Y: a.Y - origin.Y };
+ const vectorB = { X: b.X - origin.X, Y: b.Y - origin.Y };
+ const crossProduct = vectorB.X * vectorA.Y - vectorB.Y * vectorA.X;
+ // Determining whether rotation is clockwise or counterclockwise.
+ const sign = crossProduct < 0 ? 1 : -1;
+ const theta = this.angleBetweenTwoVectors(vectorA, vectorB);
+ return sign * theta;
+ }
+
+ /**
+ * Handles the movement/scaling of a handle point.
+ */
@undoBatch
@action
- switchFil = (color: ColorState) => {
- const val = String(color.hex);
- this.colorFil = val;
- return true;
- }
+ moveHandle = (deltaX: number, deltaY: number, handleIndex: number, oppositeHandleIndex: number, controlIndex: number) =>
+ this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => {
+ const oldHandlePoint = ink[handleIndex];
+ let oppositeHandlePoint = ink[oppositeHandleIndex];
+ const controlPoint = ink[controlIndex];
+ const newHandlePoint = { X: ink[handleIndex].X - deltaX / xScale, Y: ink[handleIndex].Y - deltaY / yScale };
+ ink[handleIndex] = newHandlePoint;
+ const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number"));
+ // Rotate opposite handle if user hasn't held 'Alt' key or not first/final control (which have only 1 handle).
+ if ((!brokenIndices || !brokenIndices?.includes(controlIndex)) && handleIndex !== 1 && handleIndex !== ink.length - 2) {
+ const angle = this.angleChange(oldHandlePoint, newHandlePoint, controlPoint);
+ oppositeHandlePoint = this.rotatePoint(oppositeHandlePoint, controlPoint, angle);
+ ink[oppositeHandleIndex] = oppositeHandlePoint;
+ }
+ return ink;
+ })
} \ No newline at end of file
diff --git a/src/client/views/InkingStroke.scss b/src/client/views/InkingStroke.scss
deleted file mode 100644
index 30ab1967e..000000000
--- a/src/client/views/InkingStroke.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.inkingStroke {
- mix-blend-mode: multiply;
- stroke-linejoin: round;
- stroke-linecap: round;
- overflow: visible !important;
- transform-origin: top left;
-
- svg:not(:root) {
- overflow: visible !important;
- }
-} \ No newline at end of file
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 449019ca8..21059b330 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -1,4 +1,5 @@
-import { action } from "mobx";
+import React = require("react");
+import { action, observable } from "mobx";
import { observer } from "mobx-react";
import { Doc } from "../../fields/Doc";
import { documentSchema } from "../../fields/documentSchemas";
@@ -10,180 +11,107 @@ import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../Utils";
import { CognitiveServices } from "../cognitive_services/CognitiveServices";
import { InteractionUtils } from "../util/InteractionUtils";
import { Scripting } from "../util/Scripting";
-import { UndoManager } from "../util/UndoManager";
import { ContextMenu } from "./ContextMenu";
import { ViewBoxBaseComponent } from "./DocComponent";
-import "./InkingStroke.scss";
+import "./InkStroke.scss";
import { FieldView, FieldViewProps } from "./nodes/FieldView";
-import React = require("react");
import { InkStrokeProperties } from "./InkStrokeProperties";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
+import { InkControls } from "./InkControls";
+import { InkHandles } from "./InkHandles";
+import { Colors } from "./global/globalEnums";
type InkDocument = makeInterface<[typeof documentSchema]>;
const InkDocument = makeInterface(documentSchema);
@observer
export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocument>(InkDocument) {
- private _controlUndo?: UndoManager.Batch;
+ static readonly MaskDim = 50000;
+ @observable private _properties?: InkStrokeProperties;
+
+ constructor(props: FieldViewProps & InkDocument) {
+ super(props);
+
+ this._properties = InkStrokeProperties.Instance;
+ }
- public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); }
+ public static LayoutString(fieldStr: string) {
+ return FieldView.LayoutString(InkingStroke, fieldStr);
+ }
- private analyzeStrokes = () => {
+ analyzeStrokes() {
const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], [data]);
}
- public static toggleMask = action((inkDoc: Doc) => {
+ @action
+ public static toggleMask = (inkDoc: Doc) => {
inkDoc.isInkMask = !inkDoc.isInkMask;
inkDoc._backgroundColor = inkDoc.isInkMask ? "rgba(0,0,0,0.7)" : undefined;
inkDoc.mixBlendMode = inkDoc.isInkMask ? "hard-light" : undefined;
inkDoc.color = "#9b9b9bff";
inkDoc._stayInCollection = inkDoc.isInkMask ? true : undefined;
- });
-
- @action
- onControlDown = (e: React.PointerEvent, controlNum: number): void => {
- if (InkStrokeProperties.Instance) {
- InkStrokeProperties.Instance.control(0, 0, 1);
- const controlUndo = UndoManager.StartBatch("DocDecs set radius");
- const screenScale = this.props.ScreenToLocalTransform().Scale;
- setupMoveUpEvents(this, e,
- (e: PointerEvent, down: number[], delta: number[]) => {
- InkStrokeProperties.Instance?.control(-delta[0] * screenScale, -delta[1] * screenScale, controlNum);
- return false;
- },
- () => controlUndo?.end(), emptyFunction);
- }
}
- @action
- changeCurrPoint = (i: number) => {
- if (InkStrokeProperties.Instance) {
- InkStrokeProperties.Instance._currPoint = i;
- document.addEventListener("keydown", this.delPts, true);
+ /**
+ * Handles the movement of the entire ink object when the user clicks and drags.
+ */
+ onPointerDown = (e: React.PointerEvent) => {
+ if (this.props.isSelected(true)) {
+ setupMoveUpEvents(this, e, returnFalse, emptyFunction,
+ action((e: PointerEvent, doubleTap: boolean | undefined) =>
+ doubleTap && this._properties && (this._properties._controlButton = true))
+ );
}
}
+ /**
+ * Ensures the ink controls and handles aren't rendered when the current ink stroke is reselected.
+ */
@action
- delPts = (e: KeyboardEvent) => {
- if (["-", "Backspace", "Delete"].includes(e.key)) {
- if (InkStrokeProperties.Instance?.deletePoints()) e.stopPropagation();
+ toggleControlButton = () => {
+ if (!this.props.isSelected() && this._properties) {
+ this._properties._controlButton = false;
}
}
- onPointerDown = (e: React.PointerEvent) => {
- if (this.props.isSelected(true)) {
- setupMoveUpEvents(this, e, returnFalse, emptyFunction, action((e: PointerEvent, doubleTap: boolean | undefined) =>
- doubleTap && InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = true)));
- }
- }
-
- public static MaskDim = 50000;
render() {
TraceMobx();
- const formatInstance = InkStrokeProperties.Instance;
- if (!formatInstance) return (null);
+ this.toggleControlButton();
+ // Extracting the ink data and formatting information of the current ink stroke.
const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
- // const strokeWidth = Number(StrCast(this.layoutDoc.strokeWidth, ActiveInkWidth()));
+ const inkDoc: Doc = this.layoutDoc;
const strokeWidth = Number(this.layoutDoc.strokeWidth);
- const xs = data.map(p => p.X);
- const ys = data.map(p => p.Y);
- const lineTop = Math.min(...ys);
- const lineBot = Math.max(...ys);
- const lineLft = Math.min(...xs);
- const lineRgt = Math.max(...xs);
- const left = lineLft - strokeWidth / 2;
+ const lineTop = Math.min(...data.map(p => p.Y));
+ const lineBottom = Math.max(...data.map(p => p.Y));
+ const lineLeft = Math.min(...data.map(p => p.X));
+ const lineRight = Math.max(...data.map(p => p.X));
+ const left = lineLeft - strokeWidth / 2;
const top = lineTop - strokeWidth / 2;
- const right = lineRgt + strokeWidth / 2;
- const bottom = lineBot + strokeWidth / 2;
+ const right = lineRight + strokeWidth / 2;
+ const bottom = lineBottom + strokeWidth / 2;
const width = Math.max(1, right - left);
const height = Math.max(1, bottom - top);
const scaleX = width === strokeWidth ? 1 : (this.props.PanelWidth() - strokeWidth) / (width - strokeWidth);
const scaleY = height === strokeWidth ? 1 : (this.props.PanelHeight() - strokeWidth) / (height - strokeWidth);
const strokeColor = StrCast(this.layoutDoc.color, "");
-
- const points = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth,
- StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"),
- StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker),
- StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5 && lineBot - lineTop > 1 && lineRgt - lineLft > 1, false);
-
- const hpoints = InteractionUtils.CreatePolyline(data, left, top,
- this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, (strokeWidth + 15),
- StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"),
- "none", "none", undefined, scaleX, scaleY, "", this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted", false, true);
-
- //points for adding
- const apoints = InteractionUtils.CreatePoints(data, left, top, strokeColor, strokeWidth, strokeWidth,
- StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"),
- StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker),
- StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
-
- const controlPoints: { X: number, Y: number, I: number }[] = [];
- const handlePoints: { X: number, Y: number, I: number, dot1: number, dot2: number }[] = [];
- const handleLine: { X1: number, Y1: number, X2: number, Y2: number, X3: number, Y3: number, dot1: number, dot2: number }[] = [];
- if (data.length >= 4) {
- for (var i = 0; i <= data.length - 4; i += 4) {
- controlPoints.push({ X: data[i].X, Y: data[i].Y, I: i });
- controlPoints.push({ X: data[i + 3].X, Y: data[i + 3].Y, I: i + 3 });
- handlePoints.push({ X: data[i + 1].X, Y: data[i + 1].Y, I: i + 1, dot1: i, dot2: i === 0 ? i : i - 1 });
- handlePoints.push({ X: data[i + 2].X, Y: data[i + 2].Y, I: i + 2, dot1: i + 3, dot2: i === data.length ? i + 3 : i + 4 });
- }
-
- handleLine.push({ X1: data[0].X, Y1: data[0].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: data[1].Y, dot1: 0, dot2: 0 });
- for (var i = 2; i < data.length - 4; i += 4) {
-
- handleLine.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 1].X, Y2: data[i + 1].Y, X3: data[i + 3].X, Y3: data[i + 3].Y, dot1: i + 1, dot2: i + 2 });
-
- }
- handleLine.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[data.length - 1].X, Y2: data[data.length - 1].Y, X3: data[data.length - 1].X, Y3: data[data.length - 1].Y, dot1: data.length - 1, dot2: data.length - 1 });
-
- for (var i = 0; i <= data.length - 4; i += 4) {
- handlePoints.push({ X: data[i + 1].X, Y: data[i + 1].Y, I: i + 1, dot1: i, dot2: i === 0 ? i : i - 1 });
- handlePoints.push({ X: data[i + 2].X, Y: data[i + 2].Y, I: i + 2, dot1: i + 3, dot2: i === data.length ? i + 3 : i + 4 });
- }
- }
- // if (data.length <= 4) {
- // handlePoints = [];
- // handleLine = [];
- // controlPoints = [];
- // for (var i = 0; i < data.length; i++) {
- // controlPoints.push({ X: data[i].X, Y: data[i].Y, I: i });
- // }
-
- // }
const dotsize = Math.max(width * scaleX, height * scaleY) / 40;
- const addpoints = apoints.map((pts, i) =>
- <svg height="10" width="10" key={`add${i}`}>
- <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={strokeWidth / 2} stroke="invisible" strokeWidth={dotsize / 2} fill="invisible"
- onPointerDown={(e) => { formatInstance.addPoints(pts.X, pts.Y, apoints, i, controlPoints); }} pointerEvents="all" cursor="all-scroll"
- />
- </svg>);
- const handles = handlePoints.map((pts, i) =>
- <svg height="10" width="10" key={`hdl${i}`}>
- <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={strokeWidth} strokeWidth={0} fill="green"
- onPointerDown={(e) => this.onControlDown(e, pts.I)} pointerEvents="all" cursor="default" display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} />
- </svg>);
-
- const controls = controlPoints.map((pts, i) =>
- <svg height="10" width="10" key={`ctrl${i}`}>
- <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={strokeWidth / 2} strokeWidth={0} fill="red"
- onPointerDown={(e) => { this.changeCurrPoint(pts.I); this.onControlDown(e, pts.I); }} pointerEvents="all" cursor="default"
- />
- </svg>);
- const handleLines = handleLine.map((pts, i) =>
- <svg height="100" width="100" key={`line${i}`} >
- <line x1={(pts.X1 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y1={(pts.Y1 - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
- x2={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y2={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} stroke="green" strokeWidth={dotsize / 6}
- display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} />
- <line x1={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y1={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
- x2={(pts.X3 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y2={(pts.Y3 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} stroke="green" strokeWidth={dotsize / 6}
- display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} />
- </svg>);
-
+ // Visually renders the polygonal line made by the user.
+ const inkLine = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth, StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"), StrCast(this.layoutDoc.strokeStartMarker),
+ StrCast(this.layoutDoc.strokeEndMarker), StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5 && lineBottom - lineTop > 1 && lineRight - lineLeft > 1, false);
+ // Thin blue line indicating that the current ink stroke is selected.
+ const selectedLine = InteractionUtils.CreatePolyline(data, left - strokeWidth / 3, top - strokeWidth / 3, Colors.MEDIUM_BLUE, strokeWidth / 6, strokeWidth / 6, StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"),
+ StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker), StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5 && lineBottom - lineTop > 1 && lineRight - lineLeft > 1, false);
+ // Invisible polygonal line that enables the ink to be selected by the user.
+ const clickableLine = InteractionUtils.CreatePolyline(data, left, top, this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, strokeWidth + 15, StrCast(this.layoutDoc.strokeBezier),
+ StrCast(this.layoutDoc.fillColor, "none"), "none", "none", undefined, scaleX, scaleY, "", this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted", false, true);
+ // Set of points rendered upon the ink that can be added if a user clicks on one.
+ const addedPoints = InteractionUtils.CreatePoints(data, left, top, strokeColor, strokeWidth, strokeWidth, StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"), StrCast(this.layoutDoc.strokeStartMarker),
+ StrCast(this.layoutDoc.strokeEndMarker), StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
return (
- <svg className="inkingStroke"
+ <svg className="inkStroke"
style={{
pointerEvents: this.props.Document.isInkMask && this.props.layerProvider?.(this.props.Document) !== false ? "all" : "none",
transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
@@ -196,19 +124,28 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
if (cm) {
!Doc.UserDoc().noviceMode && cm.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" });
cm.addItem({ description: "Toggle Mask", event: () => InkingStroke.toggleMask(this.rootDoc), icon: "paint-brush" });
- cm.addItem({ description: "Edit Points", event: action(() => formatInstance._controlBtn = !formatInstance._controlBtn), icon: "paint-brush" });
- //cm.addItem({ description: "Format Shape...", event: this.formatShape, icon: "paint-brush" });
+ cm.addItem({ description: "Edit Points", event: action(() => { if (this._properties) { this._properties._controlButton = !this._properties._controlButton; } }), icon: "paint-brush" });
}
}}
- ><defs>
- </defs>
- {hpoints}
- {points}
- {formatInstance._controlBtn && this.props.isSelected() ? addpoints : ""}
- {formatInstance._controlBtn && this.props.isSelected() ? handleLines : ""}
- {formatInstance._controlBtn && this.props.isSelected() ? handles : ""}
- {formatInstance._controlBtn && this.props.isSelected() ? controls : ""}
-
+ >
+
+ {clickableLine}
+ {inkLine}
+ {this.props.isSelected() ? selectedLine : ""}
+ {this.props.isSelected() && this._properties?._controlButton ?
+ <>
+ <InkControls
+ inkDoc={inkDoc}
+ data={data}
+ addedPoints={addedPoints}
+ format={[left, top, scaleX, scaleY, strokeWidth]}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform} />
+ <InkHandles
+ inkDoc={inkDoc}
+ data={data}
+ format={[left, top, scaleX, scaleY, strokeWidth]}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform} />
+ </> : ""}
</svg>
);
}
diff --git a/src/client/views/LightboxView.scss b/src/client/views/LightboxView.scss
index 4ea2dc2d6..5d42cd97f 100644
--- a/src/client/views/LightboxView.scss
+++ b/src/client/views/LightboxView.scss
@@ -1,3 +1,32 @@
+
+ .lightboxView-navBtn {
+ margin: auto;
+ position: absolute;
+ right: 10;
+ top: 10;
+ background: transparent;
+ border-radius: 8;
+ color:white;
+ opacity: 0.7;
+ width: 35;
+ &:hover {
+ opacity: 1;
+ }
+ }
+ .lightboxView-tabBtn {
+ margin: auto;
+ position: absolute;
+ right: 35;
+ top: 10;
+ background: transparent;
+ border-radius: 8;
+ color:white;
+ opacity: 0.7;
+ width: 35;
+ &:hover {
+ opacity: 1;
+ }
+ }
.lightboxView-frame {
position: absolute;
top: 0; left: 0;
@@ -15,7 +44,6 @@
position: relative;
background: transparent;
border-radius: 8;
- color:white;
opacity: 0.7;
width: 35;
&:hover {
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index ce36d9182..88739fe91 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -1,19 +1,20 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, trace } from 'mobx';
+import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import "normalize.css";
import * as React from 'react';
import { Doc, DocListCast, Opt } from '../../fields/Doc';
import { Cast, NumCast, StrCast } from '../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue, returnFalse } from '../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils';
import { DocUtils } from '../documents/Documents';
import { DocumentManager } from '../util/DocumentManager';
import { LinkManager } from '../util/LinkManager';
import { SelectionManager } from '../util/SelectionManager';
import { Transform } from '../util/Transform';
+import { CollectionDockingView } from './collections/CollectionDockingView';
import { TabDocView } from './collections/TabDocView';
import "./LightboxView.scss";
-import { DocumentView, ViewAdjustment } from './nodes/DocumentView';
+import { DocumentView } from './nodes/DocumentView';
import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider';
interface LightboxViewProps {
@@ -160,7 +161,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
const { doc, target } = LightboxView._history?.lastElement();
const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc);
if (docView) {
- LightboxView._docTarget = undefined;
+ LightboxView._docTarget = target;
const focusSpeed = 1000;
doc._viewTransition = `transform ${focusSpeed}ms`;
if (!target) docView.ComponentView?.shrinkWrap?.();
@@ -197,7 +198,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
TabDocView.PinDoc(coll, { hidePresBox: true });
}
}
- setTimeout(LightboxView.Next);
}
future = () => LightboxView._future;
@@ -228,7 +228,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target);
if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.();
else target && targetView?.focus(target, { willZoom: true, scale: 0.9, instant: true });
- LightboxView._docTarget = undefined;
}));
})}
Document={LightboxView.LightboxDoc}
@@ -270,7 +269,16 @@ export class LightboxView extends React.Component<LightboxViewProps> {
LightboxView.Next();
})}
<LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} tourMap={this.tourMap} />
- <div className="lightboxView-navBtn" title={"toggle fit width"} style={{ position: "absolute", right: 10, top: 10, color: "white" }}
+ <div className="lightboxView-tabBtn" title={"open in tab"}
+ onClick={e => {
+ e.stopPropagation();
+ CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, "onRight");
+ SelectionManager.DeselectAll();
+ LightboxView.SetLightboxDoc(undefined);
+ }}>
+ <FontAwesomeIcon icon={"file-download"} size="2x" />
+ </div>
+ <div className="lightboxView-navBtn" title={"toggle fit width"}
onClick={e => { e.stopPropagation(); LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth; }}>
<FontAwesomeIcon icon={"arrows-alt-h"} size="2x" />
</div>
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index b1ad4868c..c8e64b5c4 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -1,4 +1,4 @@
-@import "globalCssVariables";
+@import "global/globalCssVariables";
@import "nodeModuleOverrides";
:root {
@@ -54,7 +54,7 @@ button {
background: black;
outline: none;
border: 0px;
- color: $light-color;
+ color: $white;
text-transform: uppercase;
letter-spacing: 2px;
font-size: 75%;
@@ -63,7 +63,7 @@ button {
}
button:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.05);
cursor: pointer;
}
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 3f04a0f3a..d913f2069 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -1,4 +1,4 @@
-@import "globalCssVariables";
+@import "global/globalCssVariables";
@import "nodeModuleOverrides";
@@ -22,10 +22,6 @@
height: 100%;
}
-.mainContent-div-flyout {
- left: calc(-1 * var(--flyoutHandleWidth));
-}
-
// add nodes menu. Note that the + button is actually an input label, not an actual button.
.mainView-docButtons {
position: absolute;
@@ -56,50 +52,50 @@
touch-action: none;
.searchBox-container {
- background: lightgray;
+ background: $light-gray;
}
}
.mainView-container {
- color: black;
+ color: $dark-gray;
.lm_title {
- background: #cacaca;
- color: black;
+ background: $light-gray;
+ color: $dark-gray;
}
}
.mainView-container-dark {
- color: lightgray;
+ color: $light-gray;
.lm_goldenlayout {
- background: dimgray;
+ background: $medium-gray;
}
.lm_title {
- background: black;
+ background: $dark-gray;
color: unset;
}
.marquee {
- border-color: white;
+ border-color: $white;
}
#search-input {
- background: lightgray;
+ background: $light-gray;
}
.searchBox-container {
- background: rgb(45, 45, 45);
+ background: $dark-gray;
}
.contextMenu-cont,
.contextMenu-item {
- background: dimGray;
+ background: $medium-gray;
}
.contextMenu-item:hover {
- background: gray;
+ background: $medium-gray;
}
}
@@ -111,14 +107,21 @@
user-select: none;
}
+.properties-container {
+ height: 100%;
+ position: relative;
+ left: 100%;
+ top: calc(-100% - 36px);
+ z-index: 3000;
+}
+
.mainView-propertiesDragger {
//background-color: rgb(140, 139, 139);
- background-color: lightgrey;
+ background-color: $light-gray;
height: 55px;
width: 17px;
position: absolute;
top: 50%;
- border: 1px black solid;
border-radius: 0;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
@@ -141,18 +144,6 @@
}
}
-.mainiView-propertiesView {
- display: flex;
- flex-direction: column;
- height: 100%;
- position: absolute;
- right: 0;
- top: 0;
- border-left: solid 1px;
- z-index: 100000;
- cursor: auto;
-}
-
.mainView-innerContent, .mainView-innerContent-dark {
display: contents;
flex-direction: row;
@@ -163,43 +154,43 @@
flex-direction: column;
position: relative;
height: 100%;
- background: dimgray;
+ background: $medium-gray;
.documentView-node-topmost {
- background: lightgrey;
+ background: $light-gray;
}
}
.propertiesView {
- right: 0;
+ left: 0;
position: absolute;
z-index: 2;
- background-color: rgb(159, 159, 159);
+ background-color: $light-gray;
.editable-title {
- background-color: lightgrey;
+ background-color: $light-gray;
}
}
}
.mainView-libraryHandle {
- background-color: lightgrey;
+ background-color: $light-gray;
}
.mainView-innerContent-dark
{
.propertiesView {
background-color: #252525;
input {
- background-color: dimgrey;
+ background-color: $medium-gray;
}
.propertiesView-sharingTable
{
- background-color: dimgrey;
+ background-color: $medium-gray;
}
.editable-title {
- background-color: dimgrey;
+ background-color: $medium-gray;
}
.propertiesView-field {
- background-color: dimgrey;
+ background-color: $medium-gray;
}
}
.mainView-propertiesDragger,
@@ -209,17 +200,18 @@
}
.mainView-container-dark {
.contextMenu-cont {
- background: dimgrey;
- color: white;
+ background: $medium-gray;
+ color: $white;
input::placeholder {
- color:white;
+ color:$white;
}
}
}
.mainView-menuPanel {
min-width: var(--menuPanelWidth);
- background-color: #121721;
+ background-color: $dark-gray;
+ border-right: $standard-border;
.collectionStackingView {
scrollbar-width: none;
@@ -233,13 +225,13 @@
padding: 7px;
padding-left: 7px;
width: 100%;
- background: black;
+ background: $dark-gray;
.mainView-menuPanel-button-wrap {
width: 45px;
/* padding: 5px; */
touch-action: none;
- background: black;
+ background: $dark-gray;
transform-origin: top left;
/* margin-bottom: 5px; */
margin-top: 5px;
@@ -247,7 +239,7 @@
border-radius: 8px;
&:hover {
- background: rgb(61, 61, 61);
+ background: $black;
cursor: pointer;
}
}
@@ -419,31 +411,4 @@
display: block;
width: 500px;
height: 1000px;
-}
-
-.lm_drag_tab {
- padding: 0;
- width: 15px !important;
- height: 15px !important;
- position: relative !important;
- display: inline-flex !important;
- align-items: center;
- top: 0 !important;
- right: unset !important;
- left: 0 !important;
-}
-.lm_close_tab {
- padding: 0;
- width: 15px !important;
- height: 15px !important;
- position: relative !important;
- display: inline-flex !important;
- align-items: center;
- top: 0 !important;
- right: unset !important;
- left: 0 !important;
-}
-.lm_tab, .lm_tab_active {
- display: flex !important;
- padding-right: 0 !important;
} \ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a064f7fe8..005e46836 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -40,7 +40,7 @@ import { ContextMenu } from './ContextMenu';
import { DictationOverlay } from './DictationOverlay';
import { DocumentDecorations } from './DocumentDecorations';
import { GestureOverlay } from './GestureOverlay';
-import { MENU_PANEL_WIDTH, SEARCH_PANEL_HEIGHT } from './globalCssVariables.scss';
+import { MENU_PANEL_WIDTH, SEARCH_PANEL_HEIGHT } from './global/globalCssVariables.scss';
import { KeyManager } from './GlobalKeyHandler';
import { InkStrokeProperties } from './InkStrokeProperties';
import { LightboxView } from './LightboxView';
@@ -63,6 +63,8 @@ import { PreviewCursor } from './PreviewCursor';
import { PropertiesView } from './PropertiesView';
import { SearchBox } from './search/SearchBox';
import { DefaultStyleProvider, DashboardStyleProvider, StyleProp } from './StyleProvider';
+import { TopBar } from './topbar/TopBar';
+import { Colors } from './global/globalEnums';
const _global = (window /* browser */ || global /* node */) as any;
@observer
@@ -79,7 +81,7 @@ export class MainView extends React.Component {
@observable private _sidebarContent: any = this.userDoc?.sidebar;
@observable private _flyoutWidth: number = 0;
- @computed private get topOffset() { return (CollectionMenu.Instance?.Pinned ? 35 : 0) + Number(SEARCH_PANEL_HEIGHT.replace("px", "")); }
+ @computed private get topOffset() { return Number(SEARCH_PANEL_HEIGHT.replace("px", "")); } //TODO remove
@computed private get leftOffset() { return this.menuPanelWidth() - 2; }
@computed private get userDoc() { return Doc.UserDoc(); }
@computed private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
@@ -180,12 +182,12 @@ export class MainView extends React.Component {
const targets = document.elementsFromPoint(e.x, e.y);
if (targets.length) {
const targClass = targets[0].className.toString();
- if (SearchBox.Instance._searchbarOpen || SearchBox.Instance.open) {
- const check = targets.some((thing) =>
- (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" ||
- thing.className === "collectionSchema-header-menuOptions"));
- !check && SearchBox.Instance.resetSearch(true);
- }
+ // if (SearchBox.Instance._searchbarOpen || SearchBox.Instance.open) {
+ // const check = targets.some((thing) =>
+ // (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" ||
+ // thing.className === "collectionSchema-header-menuOptions"));
+ // !check && SearchBox.Instance.resetSearch(true);
+ // }
!targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu();
!["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu();
}
@@ -194,7 +196,7 @@ export class MainView extends React.Component {
initEventListeners = () => {
window.addEventListener("drop", e => e.preventDefault(), false); // prevent default behavior of navigating to a new web page
window.addEventListener("dragover", e => e.preventDefault(), false);
- document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined));
+ // document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined));
document.addEventListener("pointerdown", this.globalPointerDown);
document.addEventListener("click", (e: MouseEvent) => {
if (!e.cancelBubble) {
@@ -244,8 +246,9 @@ export class MainView extends React.Component {
}
getPWidth = () => this._panelWidth - this.propertiesWidth();
- getPHeight = () => this._panelHeight;
+ getPHeight = () => this._panelHeight - (CollectionMenu.Instance?.Pinned ? 35 : 0);
getContentsHeight = () => this._panelHeight;
+ getMenuPanelHeight = () => this._panelHeight + (CollectionMenu.Instance?.Pinned ? 35 : 0);
@computed get mainDocView() {
return <DocumentView key="main"
@@ -277,10 +280,12 @@ export class MainView extends React.Component {
@computed get dockingContent() {
return <div key="docking" className={`mainContent-div${this._flyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }}
+ // style={{ minWidth: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`, width: `calc(100% - ${this._flyoutWidth + this.propertiesWidth()}px)` }}>
+ // FIXME update with property panel width
style={{
minWidth: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`,
transform: LightboxView.LightboxDoc ? "scale(0.0001)" : undefined,
- width: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`
+ //TODO:glr width: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`
}}>
{!this.mainContainer ? (null) : this.mainDocView}
</div>;
@@ -360,7 +365,7 @@ export class MainView extends React.Component {
removeDocument={returnFalse}
ScreenToLocalTransform={this.sidebarScreenToLocal}
PanelWidth={this.menuPanelWidth}
- PanelHeight={this.getContentsHeight}
+ PanelHeight={this.getMenuPanelHeight}
renderDepth={0}
docViewPath={returnEmptyDoclist}
focus={DocUtils.DefaultFocus}
@@ -403,20 +408,27 @@ export class MainView extends React.Component {
}
@computed get mainInnerContent() {
+ const width = this.propertiesWidth() + this._flyoutWidth + this.menuPanelWidth();
+ const transform = this._flyoutWidth ? 'translate(-28px, 0px)' : undefined;
return <>
{this.menuPanel}
<div key="inner" className={`mainView-innerContent${this.darkScheme ? "-dark" : ""}`}>
{this.flyout}
- <div className="mainView-libraryHandle" style={{ display: !this._flyoutWidth ? "none" : undefined, }} onPointerDown={this.onFlyoutPointerDown} >
+ <div className="mainView-libraryHandle" style={{ display: !this._flyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
<FontAwesomeIcon icon="chevron-left" color={this.darkScheme ? "white" : "black"} style={{ opacity: "50%" }} size="sm" />
</div>
+ <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)`, transform: transform }}>
+ <CollectionMenu />
- {this.dockingContent}
+ {this.dockingContent}
- <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
- <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.darkScheme ? "white" : "black"} size="sm" />
+ <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this._flyoutWidth ? 0 : this.propertiesWidth() - 1 }}>
+ <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.darkScheme ? Colors.WHITE : Colors.BLACK} size="sm" />
+ </div>
+ <div className="properties-container">
+ {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.getContentsHeight()} />}
+ </div>
</div>
- {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.getContentsHeight()} />}
</div>
</>;
}
@@ -527,35 +539,8 @@ export class MainView extends React.Component {
@computed get search() {
TraceMobx();
- return <div className="mainView-searchPanel">
- <SearchBox Document={CurrentUserUtils.MySearchPanelDoc}
- DataDoc={CurrentUserUtils.MySearchPanelDoc}
- fieldKey="data"
- dropAction="move"
- isSelected={returnTrue}
- isContentActive={returnTrue}
- select={returnTrue}
- setHeight={returnFalse}
- addDocument={undefined}
- addDocTab={this.addDocTabFunc}
- pinToPres={emptyFunction}
- rootSelected={returnTrue}
- styleProvider={DefaultStyleProvider}
- layerProvider={undefined}
- removeDocument={undefined}
- ScreenToLocalTransform={Transform.Identity}
- PanelWidth={this.getPWidth}
- PanelHeight={this.getPHeight}
- renderDepth={0}
- focus={DocUtils.DefaultFocus}
- docViewPath={returnEmptyDoclist}
- whenChildContentsActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
- docFilters={returnEmptyFilter}
- docRangeFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined} />
+ return <div className="mainView-topbar">
+ <TopBar />
</div>;
}
@@ -607,7 +592,6 @@ export class MainView extends React.Component {
<GoogleAuthenticationManager />
<DocumentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset} />
{this.search}
- <CollectionMenu />
{LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
{DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)}
{LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 717bd0768..805cda95c 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -120,9 +120,11 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
return marqueeAnno;
}
- const textRegionAnno = Docs.Create.HTMLAnchorDocument([], { annotationOn: this.props.rootDoc, title: "Selection on " + this.props.rootDoc.title, _width: 1, _height: 1 });
+ const textRegionAnno = Docs.Create.HTMLAnchorDocument([], { annotationOn: this.props.rootDoc, backgroundColor: "transparent", title: "Selection on " + this.props.rootDoc.title });
+ let minX = Number.MAX_VALUE;
let maxX = -Number.MAX_VALUE;
let minY = Number.MAX_VALUE;
+ let maxY = -Number.MIN_VALUE;
const annoDocs: Doc[] = [];
savedAnnoMap.forEach((value: HTMLDivElement[], key: number) => value.map(anno => {
const textRegion = new Doc();
@@ -135,12 +137,16 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
annoDocs.push(textRegion);
anno.remove();
minY = Math.min(NumCast(textRegion.y), minY);
+ minX = Math.min(NumCast(textRegion.x), minX);
+ maxY = Math.max(NumCast(textRegion.y) + NumCast(textRegion._height), maxY);
maxX = Math.max(NumCast(textRegion.x) + NumCast(textRegion._width), maxX);
}));
const textRegionAnnoProto = Doc.GetProto(textRegionAnno);
textRegionAnnoProto.y = Math.max(minY, 0);
- textRegionAnnoProto.x = Math.max(maxX, 0);
+ textRegionAnnoProto.x = Math.max(minX, 0);
+ textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0);
+ textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0);
// mainAnnoDocProto.text = this._selectionText;
textRegionAnnoProto.textInlineAnnotations = new List<Doc>(annoDocs);
savedAnnoMap.clear();
diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss
index 29d2bfcb7..484522bc7 100644
--- a/src/client/views/PropertiesButtons.scss
+++ b/src/client/views/PropertiesButtons.scss
@@ -1,4 +1,4 @@
-@import "globalCssVariables";
+@import "global/globalCssVariables";
$linkGap : 3px;
@@ -7,13 +7,13 @@ $linkGap : 3px;
}
.propertiesButtons-linkButton-empty:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.05);
cursor: pointer;
}
.propertiesButtons-linkButton-nonempty:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.05);
cursor: pointer;
}
@@ -24,7 +24,7 @@ $linkGap : 3px;
width: 29px;
border-radius: 6px;
pointer-events: auto;
- background-color: #121721;
+ background-color: $dark-gray;
color: #fcfbf7;
text-transform: uppercase;
letter-spacing: 2px;
@@ -38,18 +38,18 @@ $linkGap : 3px;
margin-left: 4px;
&:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.05);
cursor: pointer;
}
}
.propertiesButtons-linkButton-empty.toggle-on {
background-color: white;
- color: black;
+ color: $dark-gray;
}
.propertiesButtons-linkButton-empty.toggle-hover {
background-color: gray;
- color: black;
+ color: $dark-gray;
}
.propertiesButtons-linkButton-empty.toggle-off {
color: white;
@@ -111,7 +111,7 @@ $linkGap : 3px;
margin-left: 4px;
&:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.05);
cursor: pointer;
}
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index fa45a065d..321b83f52 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -1,6 +1,8 @@
+@import "./global/globalCssVariables.scss";
+
.propertiesView {
height: 100%;
- font-family: "Noto Sans";
+ font-family: "Roboto";
cursor: auto;
overflow-x: hidden;
@@ -28,9 +30,7 @@
color: grey;
cursor: pointer;
}
-
}
-
}
.propertiesView-name {
@@ -80,7 +80,6 @@
padding-bottom: 10px;
padding-top: 8px;
}
-
}
.propertiesView-sharing {
@@ -140,8 +139,6 @@
}
}
-
-
.change-buttons {
display: flex;
@@ -216,7 +213,6 @@
}
}
-
.propertiesView-appearance {
//border-bottom: 1px solid black;
//padding: 8.5px;
@@ -305,7 +301,7 @@
.notify-button-icon {
width: 6px;
height: 6.5px;
- margin-left: .5px;
+ margin-left: 0.5px;
}
&:hover {
@@ -331,7 +327,6 @@
}
.propertiesView-sharingTable {
-
// whatever's commented out - add it back in when adding the buttons
// border: 1.5px solid black;
@@ -347,7 +342,6 @@
width: 92%;
.propertiesView-sharingTable-item {
-
display: flex;
// padding: 5px;
padding: 3px;
@@ -421,7 +415,6 @@
cursor: pointer;
}
}
-
}
.propertiesView-fields-checkbox {
@@ -468,7 +461,6 @@
}
.propertiesView-contexts {
-
.propertiesView-contexts-title {
font-weight: bold;
font-size: 12.5px;
@@ -499,11 +491,9 @@
overflow: hidden;
padding: 10px;
}
-
}
.propertiesView-layout {
-
.propertiesView-layout-title {
font-weight: bold;
font-size: 12.5px;
@@ -534,7 +524,6 @@
overflow: hidden;
padding: 10px;
}
-
}
.propertiesView-presTrails {
@@ -576,7 +565,6 @@
}
.inking-button {
-
display: flex;
.inking-button-points {
@@ -635,7 +623,6 @@
}
.inputBox {
-
margin-top: 10px;
display: flex;
height: 19px;
@@ -658,7 +645,6 @@
}
.inputBox-button {
-
.inputBox-button-up {
background-color: #333333;
height: 9px;
@@ -690,7 +676,6 @@
cursor: pointer;
}
}
-
}
}
@@ -767,7 +752,6 @@
}
.widthAndDash {
-
.width {
.width-top {
display: flex;
@@ -792,13 +776,11 @@
}
.arrows {
-
display: flex;
margin-bottom: 3px;
margin-left: 4px;
.arrows-head {
-
display: flex;
margin-right: 35px;
@@ -827,7 +809,6 @@
}
.dashed {
-
display: flex;
margin-left: 64px;
margin-bottom: 6px;
@@ -844,19 +825,15 @@
}
.editable-title {
- border: none;
padding: 6px;
padding-bottom: 2px;
- background: #eeeeee;
- border-top: 1px solid;
- border-left: 1px solid;
+ border: solid 1px $dark-gray;
&:hover {
- border: 0.75px solid rgb(122, 28, 28);
+ border: 0.75px solid $medium-blue;
}
}
-
.properties-flyout {
grid-column: 2/4;
-} \ No newline at end of file
+}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index d09d949ff..8136edf04 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -24,7 +24,7 @@ import { EditableView } from "./EditableView";
import { InkStrokeProperties } from "./InkStrokeProperties";
import { DocumentView, StyleProviderFunc } from "./nodes/DocumentView";
import { KeyValueBox } from "./nodes/KeyValueBox";
-import { PresBox } from "./nodes/PresBox";
+import { PresBox } from "./nodes/trails/PresBox";
import { PropertiesButtons } from "./PropertiesButtons";
import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector";
import "./PropertiesView.scss";
@@ -86,7 +86,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@observable openSlideOptions: boolean = false;
@observable inOptions: boolean = false;
- @observable _controlBtn: boolean = false;
+ @observable _controlButton: boolean = false;
@observable _lock: boolean = false;
componentDidMount() {
@@ -540,7 +540,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
const formatInstance = InkStrokeProperties.Instance;
return !formatInstance ? (null) : <div className="inking-button">
<Tooltip title={<div className="dash-tooltip">{"Edit points"}</div>}>
- <div className="inking-button-points" onPointerDown={action(() => formatInstance._controlBtn = !formatInstance._controlBtn)} style={{ backgroundColor: formatInstance._controlBtn ? "black" : "" }}>
+ <div className="inking-button-points" onPointerDown={action(() => formatInstance._controlButton = !formatInstance._controlButton)} style={{ backgroundColor: formatInstance._controlButton ? "black" : "" }}>
<FontAwesomeIcon icon="bezier-curve" color="white" size="lg" />
</div>
</Tooltip>
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 6493e5d54..010418be5 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -84,7 +84,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
sidebarStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) => {
if (property === StyleProp.ShowTitle) {
- return doc === this.props.rootDoc ? 0 : StrCast(this.props.layoutDoc["sidebar-childShowTitle"], "title");
+ return doc === this.props.rootDoc ? undefined : StrCast(this.props.layoutDoc["sidebar-childShowTitle"], "title");
}
return this.props.styleProvider?.(doc, props, property);
}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 47a4a192c..c9e532745 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -1,4 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Colors } from './global/globalEnums';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
@@ -97,16 +98,16 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
case StyleProp.Color:
const docColor: Opt<string> = StrCast(doc?.[fieldKey + "color"], StrCast(doc?._color));
if (docColor) return docColor;
- const backColor = backgroundCol();// || (darkScheme() ? "black" : "white");
+ const backColor = backgroundCol();
if (!backColor) return undefined;
const nonAlphaColor = backColor.startsWith("#") ? (backColor as string).substring(0, 7) :
- backColor.startsWith("rgba") ? backColor.replace(/,.[^,]*\)/, ")").replace("rgba", "rgb") : backColor
+ backColor.startsWith("rgba") ? backColor.replace(/,.[^,]*\)/, ")").replace("rgba", "rgb") : backColor;
const col = Color(nonAlphaColor).rgb();
const colsum = (col.red() + col.green() + col.blue());
- if (colsum / col.alpha() > 400 || col.alpha() < 0.25) return "black";
- return "white";
+ if (colsum / col.alpha() > 400 || col.alpha() < 0.25) return Colors.DARK_GRAY;
+ return Colors.WHITE;
case StyleProp.Hidden: return BoolCast(doc?._hidden);
- case StyleProp.BorderRounding: return StrCast(doc?.[fieldKey + "borderRounding"]);
+ case StyleProp.BorderRounding: return StrCast(doc?.[fieldKey + "borderRounding"], doc?._viewType === CollectionViewType.Pile ? "50%" : "");
case StyleProp.TitleHeight: return 15;
case StyleProp.BorderPath: return comicStyle() && props?.renderDepth ? { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, .08), width: 3 } : { path: undefined, width: 0 };
case StyleProp.JitterRotation: return comicStyle() ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0;
@@ -114,38 +115,38 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
doc?.type === DocumentType.RTF) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
case StyleProp.BackgroundColor: {
let docColor: Opt<string> = StrCast(doc?.[fieldKey + "backgroundColor"], StrCast(doc?._backgroundColor, isCaption ? "rgba(0,0,0,0.4)" : ""));
- if (MainView.Instance.LastButton === doc) return darkScheme() ? "dimgrey" : "lightgrey";
+ if (MainView.Instance.LastButton === doc) return darkScheme() ? Colors.MEDIUM_GRAY : Colors.LIGHT_GRAY;
switch (doc?.type) {
case DocumentType.PRESELEMENT: docColor = docColor || (darkScheme() ? "" : ""); break;
- case DocumentType.PRES: docColor = docColor || (darkScheme() ? "#3e3e3e" : "white"); break;
- case DocumentType.FONTICON: docColor = docColor || "black"; break;
- case DocumentType.RTF: docColor = docColor || (darkScheme() ? "#2d2d2d" : "#f1efeb"); break;
- case DocumentType.FILTER: docColor = docColor || (darkScheme() ? "#2d2d2d" : "rgba(105, 105, 105, 0.432)"); break;
+ case DocumentType.PRES: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
+ case DocumentType.FONTICON: docColor = docColor || Colors.DARK_GRAY; break;
+ case DocumentType.RTF: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
+ case DocumentType.FILTER: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : "rgba(105, 105, 105, 0.432)"); break;
case DocumentType.INK: docColor = doc?.isInkMask ? "rgba(0,0,0,0.7)" : undefined; break;
case DocumentType.SLIDER: break;
case DocumentType.EQUATION: docColor = docColor || "transparent"; break;
case DocumentType.LABEL: docColor = docColor || (doc.annotationOn !== undefined ? "rgba(128, 128, 128, 0.18)" : undefined); break;
- case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? "#2d2d2d" : "lightgray"); break;
- case DocumentType.LINKANCHOR: docColor = isAnchor ? "lightblue" : "transparent"; break;
+ case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
+ case DocumentType.LINKANCHOR: docColor = isAnchor ? Colors.LIGHT_BLUE : "transparent"; break;
case DocumentType.LINK: docColor = (isAnchor ? docColor : "") || "transparent"; break;
case DocumentType.IMG:
case DocumentType.WEB:
case DocumentType.PDF:
case DocumentType.SCREENSHOT:
- case DocumentType.VID: docColor = docColor || (darkScheme() ? "#2d2d2d" : "lightgray"); break;
+ case DocumentType.VID: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
case DocumentType.COL:
if (StrCast(Doc.LayoutField(doc)).includes("SliderBox")) break;
- docColor = docColor ? docColor :
- doc?._isGroup ? "#00000004" : // very faint highlight to show bounds of group
- (Doc.IsSystem(doc) ? (darkScheme() ? "rgb(62,62,62)" : "lightgrey") : // system docs (seen in treeView) get a grayish background
+ docColor = docColor ||
+ (doc?._isGroup ? "#00000004" : // very faint highlight to show bounds of group
+ (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
isBackground() ? "cyan" : // ?? is there a good default for a background collection
doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
StrCast((props?.renderDepth || 0) > 0 ?
Doc.UserDoc().activeCollectionNestedBackground :
- Doc.UserDoc().activeCollectionBackground));
+ Doc.UserDoc().activeCollectionBackground)));
break;
//if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
- default: docColor = docColor || (darkScheme() ? "black" : "white"); break;
+ default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
}
if (docColor && (!doc || props?.layerProvider?.(doc) === false)) docColor = Color(docColor.toLowerCase()).fade(0.5).toString();
return docColor;
@@ -158,8 +159,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
switch (doc?.type) {
case DocumentType.COL:
return StrCast(doc?.boxShadow,
- isBackground() || doc?._isGroup || docProps?.LayoutTemplateString ? undefined : // groups have no drop shadow -- they're supposed to be "invisible". LayoutString's imply collection is being rendered as something else (e.g., title of a Slide)
- `${darkScheme() ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`);
+ doc?._viewType === CollectionViewType.Pile ? "4px 4px 10px 2px" :
+ isBackground() || doc?._isGroup || docProps?.LayoutTemplateString ? undefined : // groups have no drop shadow -- they're supposed to be "invisible". LayoutString's imply collection is being rendered as something else (e.g., title of a Slide)
+ `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`);
case DocumentType.LABEL:
if (doc?.annotationOn !== undefined) return "black 2px 2px 1px";
@@ -172,6 +174,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
}
}
case StyleProp.PointerEvents:
+ if (doc?.type === DocumentType.MARKER) return "none";
if (props?.pointerEvents === "none") return "none";
const layer = doc && props?.layerProvider?.(doc);
if (opacity() === 0 || (doc?.type === DocumentType.INK && !docProps?.treeViewDoc) || doc?.isInkMask) return "none";
diff --git a/src/client/views/TemplateMenu.scss b/src/client/views/TemplateMenu.scss
index bbed8cd96..f81cbdaab 100644
--- a/src/client/views/TemplateMenu.scss
+++ b/src/client/views/TemplateMenu.scss
@@ -1,4 +1,4 @@
-@import "globalCssVariables";
+@import "global/globalCssVariables";
.templating-menu {
position: absolute;
pointer-events: auto;
@@ -24,7 +24,7 @@
cursor: pointer;
&:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.05);
}
}
@@ -32,7 +32,7 @@
.template-list {
font-family: $sans-serif;
font-size: 12px;
- background-color: $light-color-secondary;
+ background-color: $light-gray;
padding: 2px 12px;
list-style: none;
position: relative;
diff --git a/src/client/views/_nodeModuleOverrides.scss b/src/client/views/_nodeModuleOverrides.scss
index b8a7db034..fd0ac9d5c 100644
--- a/src/client/views/_nodeModuleOverrides.scss
+++ b/src/client/views/_nodeModuleOverrides.scss
@@ -1,8 +1,50 @@
+@import "./global/globalCssVariables";
// this file is for overriding all the css from installed node modules
// goldenlayout stuff
div .lm_header {
- background: $dark-color;
+ background: $dark-gray;
+ overflow: hidden;
+ height: 27px !important;
+}
+
+/* Width */
+.lm_header::-webkit-scrollbar {
+ -webkit-appearance: none;
+ display: none;
+}
+
+/* Width */
+.lm_header:hover::-webkit-scrollbar {
+ -webkit-appearance: none;
+ display: block;
+ height: 0px;
+}
+
+/* Track */
+.lm_header:hover::-webkit-scrollbar-track {
+ -webkit-appearance: none;
+ display: none;
+}
+
+/* Handle */
+.lm_header:hover::-webkit-scrollbar-thumb {
+ -webkit-appearance: none;
+ background: $dark-gray;
+}
+
+/* Handle on hover */
+.lm_header:hover::-webkit-scrollbar-thumb:hover {
+ -webkit-appearance: none;
+ background: $dark-gray;
+}
+
+.lm_tabs {
+ display: flex;
+ position: absolute;
+ width: calc(100% - 60px);
+ overflow: scroll;
+ background: $dark-gray;
}
.lm_tab {
@@ -15,7 +57,14 @@ div .lm_header {
}
.lm_header .lm_controls {
- right: 1em !important;
+ align-items: center;
+ position: absolute;
+ background-color: $dark-gray;
+ border-radius: 5px;
+ display: flex;
+ justify-content: space-evenly;
+ height: 23px;
+ width: 65px;
}
// @TODO the ril__navgiation buttons in the img gallery are a lil messed up but I can't figure out
diff --git a/src/client/views/animationtimeline/Keyframe.scss b/src/client/views/animationtimeline/Keyframe.scss
index 84c8de287..38eb103c6 100644
--- a/src/client/views/animationtimeline/Keyframe.scss
+++ b/src/client/views/animationtimeline/Keyframe.scss
@@ -1,4 +1,4 @@
-@import "./../globalCssVariables.scss";
+@import "./../global/globalCssVariables.scss";
$timelineColor: #9acedf;
@@ -15,11 +15,11 @@ $timelineDark: #77a1aa;
height: 200px;
top: 50%;
position: relative;
- background-color: $light-color;
+ background-color: $white;
.menutable {
tr:nth-child(odd) {
- background-color: $light-color-secondary;
+ background-color: $light-gray;
}
}
}
@@ -67,7 +67,7 @@ $timelineDark: #77a1aa;
height: 100%;
position: absolute;
pointer-events: none;
- background: linear-gradient(to left, $timelineColor 10%, $light-color);
+ background: linear-gradient(to left, $timelineColor 10%, $white);
}
.fadeRight {
@@ -75,7 +75,7 @@ $timelineDark: #77a1aa;
height: 100%;
position: absolute;
pointer-events: none;
- background: linear-gradient(to right, $timelineColor 10%, $light-color);
+ background: linear-gradient(to right, $timelineColor 10%, $white);
}
.divider {
diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx
index e84022366..82b0218bf 100644
--- a/src/client/views/animationtimeline/Keyframe.tsx
+++ b/src/client/views/animationtimeline/Keyframe.tsx
@@ -1,7 +1,7 @@
import * as React from "react";
import "./Keyframe.scss";
import "./Timeline.scss";
-import "../globalCssVariables.scss";
+import "../global/globalCssVariables.scss";
import { observer } from "mobx-react";
import { observable, reaction, action, IReactionDisposer, observe, computed, runInAction, trace } from "mobx";
import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc";
diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss
index f90249771..48422b789 100644
--- a/src/client/views/animationtimeline/Timeline.scss
+++ b/src/client/views/animationtimeline/Timeline.scss
@@ -1,4 +1,4 @@
-@import "./../globalCssVariables.scss";
+@import "./../global/globalCssVariables.scss";
$timelineColor: #9acedf;
$timelineDark: #77a1aa;
@@ -161,7 +161,7 @@ $timelineDark: #77a1aa;
width: 100%;
height: 300px;
position: absolute;
- background-color: $light-color-secondary;
+ background-color: $light-gray;
border-bottom: 2px solid $timelineDark;
transition: transform 500ms ease;
@@ -251,7 +251,7 @@ $timelineDark: #77a1aa;
top: 0px;
width: 100px;
height: 30%;
- border: 1px solid $dark-color;
+ border: 1px solid $dark-gray;
font-size: 12px;
line-height: 11px;
background-color: $timelineDark;
diff --git a/src/client/views/animationtimeline/TimelineMenu.scss b/src/client/views/animationtimeline/TimelineMenu.scss
index 7ee0a43d5..43a89419e 100644
--- a/src/client/views/animationtimeline/TimelineMenu.scss
+++ b/src/client/views/animationtimeline/TimelineMenu.scss
@@ -1,10 +1,10 @@
-@import "./../globalCssVariables.scss";
+@import "./../global/globalCssVariables.scss";
.timeline-menu-container{
position: absolute;
display: flex;
- box-shadow: $intermediate-color 0.2vw 0.2vw 0.4vw;
+ box-shadow: $medium-gray 0.2vw 0.2vw 0.4vw;
flex-direction: column;
background: whitesmoke;
z-index: 10000;
@@ -39,7 +39,7 @@
border-top-left-radius: 15px;
border-top-right-radius: 15px;
text-transform: uppercase;
- background: $dark-color;
+ background: $dark-gray;
letter-spacing: 2px;
.timeline-menu-header-desc{
@@ -79,10 +79,10 @@
.timeline-menu-item:hover {
border-width: .11px;
border-style: none;
- border-color: $intermediate-color;
+ border-color: $medium-gray;
border-bottom-style: solid;
border-top-style: solid;
- background: $darker-alt-accent;
+ background: $medium-blue;
}
.timeline-menu-desc {
diff --git a/src/client/views/animationtimeline/TimelineOverview.scss b/src/client/views/animationtimeline/TimelineOverview.scss
index 283163ea7..c8d96c399 100644
--- a/src/client/views/animationtimeline/TimelineOverview.scss
+++ b/src/client/views/animationtimeline/TimelineOverview.scss
@@ -1,4 +1,4 @@
-@import "./../globalCssVariables.scss";
+@import "./../global/globalCssVariables.scss";
$timelineColor: #9acedf;
$timelineDark: #77a1aa;
diff --git a/src/client/views/animationtimeline/Track.scss b/src/client/views/animationtimeline/Track.scss
index aec587a79..f45e0556d 100644
--- a/src/client/views/animationtimeline/Track.scss
+++ b/src/client/views/animationtimeline/Track.scss
@@ -1,4 +1,4 @@
-@import "./../globalCssVariables.scss";
+@import "./../global/globalCssVariables.scss";
.track-container {
@@ -6,8 +6,8 @@
.inner {
top: 0px;
width: calc(100%);
- background-color: $light-color;
- border: 1px solid $dark-color;
+ background-color: $white;
+ border: 1px solid $dark-gray;
position: relative;
z-index: 100;
}
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index f4736eb29..77e7b86ea 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -1,40 +1,46 @@
-@import "../../views/globalCssVariables.scss";
+@import "../global/globalCssVariables.scss";
.lm_title {
- margin-top: 3px;
- border-radius: 5px;
- border: solid 0px dimgray;
- border-width: 2px 2px 0px;
- height: 20px;
- transform: translate(0px, -3px);
+ -webkit-appearance: none;
+ display: inline-block;
+ align-self: center;
+ align-items: center;
+ height: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ background: transparent;
+ border: solid 0px transparent;
cursor: grab;
+ color: $black;
}
.lm_title.focus-visible {
+ -webkit-appearance: none;
cursor: text;
}
.lm_title_wrap {
overflow: hidden;
- height: 19px;
- margin-top: -2px;
- display: inline-block;
+ align-items: center;
+ align-self: center;
+ background: transparent;
+ width: max-content;
+ height: 100%;
+ display: flex;
}
.lm_active .lm_title {
- border: solid 1px lightgray;
-}
-
-.lm_header .lm_tab .lm_close_tab {
- position: absolute;
- text-align: center;
+ -webkit-appearance: none;
+ // font-weight: 700;
}
.lm_header .lm_tab {
- padding-right: 20px;
- margin-top: -1px;
- border-bottom: 1px black;
+ padding: 0px;
+ opacity: 0.7;
+ box-shadow: none;
+ height: 24px;
+ // border-bottom: 1px black;
.collectionDockingView-gear {
display: none;
@@ -42,9 +48,13 @@
}
.lm_header .lm_tab.lm_active {
- padding-right: 20px;
- margin-top: 1px;
- border-bottom: unset;
+ padding: 0;
+ opacity: 1;
+ margin: 0;
+ box-shadow: none;
+ height: 27px;
+ margin-right: 2px;
+ // border-bottom: unset;
.collectionDockingView-gear {
display: inline-block;
@@ -55,6 +65,41 @@
display: inline;
}
+.lm_drag_tab {
+ padding: 0;
+ width: 15px !important;
+ height: 15px !important;
+ position: relative !important;
+ display: inline-flex !important;
+ align-items: center;
+ top: 0 !important;
+ right: unset !important;
+ left: 0 !important;
+}
+
+.lm_close_tab {
+ padding: 0;
+ align-self: center;
+ margin-right: 5px;
+ background-color: black;
+ border-radius: 3px;
+ opacity: 1 !important;
+ width: 15px !important;
+ height: 15px !important;
+ position: relative !important;
+ display: inline-flex !important;
+ align-items: center;
+ top: 0 !important;
+ right: unset !important;
+ left: 0 !important;
+}
+
+.lm_tab,
+.lm_tab_active {
+ display: flex !important;
+ padding-right: 0 !important;
+}
+
.collectiondockingview-container {
width: 100%;
height: 100%;
@@ -82,16 +127,17 @@
}
.lm_content {
- background: white;
+ background: $white;
}
.lm_controls>li {
- opacity: 0.6;
- transform: scale(1.2);
+ opacity: 1;
+ transform: scale(1);
}
.lm_controls .lm_popout {
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAUCAAAAABHICnvAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAHdElNRQfkCBsXMgbrEyzaAAAAT0lEQVQY02NgIAcIu8tgEW3/u4IDQ5B14/8LQlhFhckVFfCJjIyIOfP/QWpEZGSQJFS05s9fIPj3/z+YmseCTxS7CZS7DI+PsYcOjpAkDAA6H0KZxzDzlgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMC0wOC0yN1QyMzo1MDowNi0wNDowMDvgVpQAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjAtMDgtMjdUMjM6NTA6MDYtMDQ6MDBKve4oAAAAAElFTkSuQmCC)
+ transform: rotate(45deg);
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAQUlEQVR4nHXOQQ4AMAgCQeT/f6aXpsGK3jSTuCVJAAr7iBdoAwCKd0nwfaAdHbYERw5b44+E8JoBjEYGMBq5gAYP3usUDu2IvoUAAAAASUVORK5CYII=);
}
.lm_maximised .lm_controls .lm_maximise {
@@ -110,7 +156,7 @@
}
.flexlayout__splitter {
- background-color: black;
+ background-color: $dark-gray;
}
.flexlayout__splitter:hover {
@@ -179,7 +225,7 @@
position: absolute;
box-sizing: border-box;
background-color: #222;
- color: black;
+ color: $dark-gray;
}
.flexlayout__tab_button {
@@ -268,7 +314,7 @@
}
.flexlayout__tab_header_outer {
- background-color: black;
+ background-color: $dark-gray;
position: absolute;
left: 0;
right: 0;
@@ -311,8 +357,6 @@
background: transparent url("../../../../node_modules/flexlayout-react/images/restore.png") no-repeat center;
}
- .flexlayout__popup_menu {}
-
.flexlayout__popup_menu_item {
padding: 2px 10px 2px 10px;
color: #ddd;
@@ -332,28 +376,28 @@
}
.flexlayout__border_top {
- background-color: black;
+ background-color: $dark-gray;
border-bottom: 1px solid #ddd;
box-sizing: border-box;
overflow: hidden;
}
.flexlayout__border_bottom {
- background-color: black;
+ background-color: $dark-gray;
border-top: 1px solid #333;
box-sizing: border-box;
overflow: hidden;
}
.flexlayout__border_left {
- background-color: black;
+ background-color: $dark-gray;
border-right: 1px solid #333;
box-sizing: border-box;
overflow: hidden;
}
.flexlayout__border_right {
- background-color: black;
+ background-color: $dark-gray;
border-left: 1px solid #333;
box-sizing: border-box;
overflow: hidden;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 2e44b65e6..c0d39b2a2 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -469,4 +469,4 @@ Scripting.addGlobal(function openInLightbox(doc: any) { LightboxView.AddDocTab(d
"opens up document in a lightbox", "(doc: any)");
Scripting.addGlobal(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); },
"opens up document in tab on right side of the screen", "(doc: any)");
-Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); });
+Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); }); \ No newline at end of file
diff --git a/src/client/views/collections/CollectionLinearView.scss b/src/client/views/collections/CollectionLinearView.scss
index ca72b98a5..46e40489b 100644
--- a/src/client/views/collections/CollectionLinearView.scss
+++ b/src/client/views/collections/CollectionLinearView.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
@import "../_nodeModuleOverrides";
.collectionLinearView-outer {
@@ -12,59 +12,65 @@
align-items: center;
>span {
- background: $dark-color;
- color: $light-color;
+ background: $dark-gray;
+ color: $white;
border-radius: 18px;
margin-right: 6px;
cursor: pointer;
}
.bottomPopup-background {
- padding-right: 14px;
+ background: $medium-blue;
+ display: flex;
+ border-radius: 10px;
height: 35;
- transform: translate3d(6px, 5px, 0px);
- padding-top: 6.5px;
- padding-bottom: 7px;
- padding-left: 5px;
+ transform: translate3d(6px, 0px, 0px);
+ align-content: center;
+ justify-content: center;
+ align-items: center;
}
.bottomPopup-text {
+ color: $white;
display: inline;
white-space: nowrap;
padding-left: 8px;
- padding-right: 4px;
+ padding-right: 20px;
vertical-align: middle;
font-size: 12.5px;
}
.bottomPopup-descriptions {
+ cursor:pointer;
display: inline;
white-space: nowrap;
padding-left: 8px;
padding-right: 8px;
vertical-align: middle;
- background-color: lightgrey;
- border-radius: 5.5px;
+ background-color: $light-gray;
+ border-radius: 3px;
color: black;
margin-right: 5px;
}
.bottomPopup-exit {
+ cursor:pointer;
display: inline;
white-space: nowrap;
+ margin-right: 10px;
padding-left: 8px;
padding-right: 8px;
vertical-align: middle;
- background-color: lightgrey;
- border-radius: 5.5px;
+ background-color: $close-red;
+ border-radius: 3px;
color: black;
}
>label {
margin-top: "auto";
margin-bottom: "auto";
- background: $dark-color;
- color: $light-color;
+ background: $dark-gray;
+ color: $white;
display: inline-block;
border-radius: 18px;
font-size: 12.5px;
@@ -82,7 +88,7 @@
}
label:hover {
- background: $main-accent;
+ background: $medium-gray;
transform: scale(1.15);
}
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index e0b90304b..52c836556 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -167,24 +167,22 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
})}
</div>
{DocumentLinksButton.StartLink ? <span className="bottomPopup-background" style={{
- background: backgroundColor === color ? "black" : backgroundColor,
pointerEvents: "all"
}}
onPointerDown={e => e.stopPropagation()} >
<span className="bottomPopup-text" >
- Creating link from: {DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'}
+ Creating link from: <b>{DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'}</b>
</span>
- <Tooltip title={<><div className="dash-tooltip">{LinkDescriptionPopup.showDescriptions ? "Turn off description pop-up" :
- "Turn on description pop-up"} </div></>} placement="top">
+ <Tooltip title={<><div className="dash-tooltip">{"Toggle description pop-up"} </div></>} placement="top">
<span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"}
</span>
</Tooltip>
- <Tooltip title={<><div className="dash-tooltip">Exit link clicking mode </div></>} placement="top">
+ <Tooltip title={<><div className="dash-tooltip">Exit linking mode</div></>} placement="top">
<span className="bottomPopup-exit" onClick={this.exitLongLinks}>
- Clear
+ Stop
</span>
</Tooltip>
diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index dc5231a3a..f04b19ef7 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -1,5 +1,4 @@
-@import "../globalCssVariables";
-
+@import "../global/globalCssVariables";
.collectionMenu-cont {
position: relative;
@@ -8,8 +7,8 @@
opacity: 0.9;
z-index: 901;
transition: top .5s;
- background: #323232;
- color: white;
+ background: $dark-gray;
+ color: $white;
transform-origin: top left;
top: 0;
width: 100%;
@@ -18,7 +17,7 @@
border-radius: 100%;
width: 18px;
height: 18px;
- border: solid 1px #f5f5f5;
+ border: solid 1px $white;
display: flex;
align-items: center;
justify-content: center;
@@ -28,7 +27,7 @@
border-radius: 100%;
width: 70%;
height: 70%;
- background: white;
+ background: $white;
}
.collectionMenu {
@@ -39,11 +38,11 @@
border: unset;
.collectionMenu-divider {
- height: 85%;
+ height: 100%;
margin-left: 3px;
margin-right: 3px;
- width: 1.5px;
- background-color: #656060;
+ width: 2px;
+ background-color: $medium-gray;
}
.collectionViewBaseChrome {
@@ -51,11 +50,11 @@
align-items: center;
.collectionViewBaseChrome-viewPicker {
- font-size: 75%;
- outline-color: black;
- color: white;
+ font-size: $small-text;
+ outline-color: $black;
+ color: $white;
border: none;
- background: #323232;
+ background: $dark-gray;
}
.collectionViewBaseChrome-viewPicker:focus {
@@ -64,16 +63,16 @@
}
.collectionViewBaseChrome-viewPicker:active {
- outline-color: black;
+ outline-color: $black;
}
.collectionViewBaseChrome-button {
- font-size: 75%;
+ font-size: $small-text;
text-transform: uppercase;
letter-spacing: 2px;
- background: rgb(238, 238, 238);
- color: purple;
- outline-color: black;
+ background: $white;
+ color: $pink;
+ outline-color: $black;
border: none;
padding: 12px 10px 11px 10px;
margin-left: 10px;
@@ -82,11 +81,11 @@
.collectionViewBaseChrome-cmdPicker {
margin-left: 3px;
margin-right: 0px;
- font-size: 75%;
+ font-size: $small-text;
text-transform: capitalize;
- color: white;
+ color: $white;
border: none;
- background: #323232;
+ background: $dark-gray;
}
.collectionViewBaseChrome-cmdPicker:focus {
@@ -105,7 +104,7 @@
overflow: hidden;
.commandEntry-drop {
- color: white;
+ color: $white;
width: 30px;
margin-top: auto;
margin-bottom: auto;
@@ -113,11 +112,11 @@
}
.commandEntry-outerDiv:hover{
- background-color: rgba(0,0,0,0.2);
+ background-color: $drop-shadow;
.collectionViewBaseChrome-viewPicker,
.collectionViewBaseChrome-cmdPicker{
- background: rgb(41,41,41);
+ background: $dark-gray;
}
}
@@ -142,7 +141,7 @@
height: 100%;
display: flex;
background: transparent;
- color: grey;
+ color: $medium-gray;
justify-content: center;
}
@@ -150,31 +149,31 @@
margin-left: 5px;
display: grid;
border: none;
- border-right: solid gray 1px;
+ border-right: solid $medium-gray 1px;
.collectionViewBaseChrome-filterIcon {
position: relative;
display: flex;
margin: auto;
- background: #323232;
- color: white;
+ background: $dark-gray;
+ color: $white;
width: 30px;
height: 30px;
align-items: center;
justify-content: center;
border: none;
- border-right: solid gray 1px;
+ border-right: solid $medium-gray 1px;
}
.collectionViewBaseChrome-viewSpecsInput {
padding: 12px 10px 11px 10px;
border: 0px;
- color: grey;
+ color: $medium-gray;
text-align: center;
letter-spacing: 2px;
- outline-color: black;
- font-size: 75%;
- background: rgb(238, 238, 238);
+ outline-color: $black;
+ font-size: $small-text;
+ background: $white;
height: 100%;
width: 75px;
}
@@ -187,8 +186,8 @@
z-index: 100;
display: flex;
flex-direction: column;
- background: rgb(238, 238, 238);
- box-shadow: grey 2px 2px 4px;
+ background: $white;
+ box-shadow: $medium-gray 2px 2px 4px;
.qs-datepicker {
left: unset;
@@ -204,13 +203,13 @@
.collectionViewBaseChrome-viewSpecsMenu-rowLeft,
.collectionViewBaseChrome-viewSpecsMenu-rowMiddle,
.collectionViewBaseChrome-viewSpecsMenu-rowRight {
- font-size: 75%;
+ font-size: $small-text;
letter-spacing: 2px;
- color: grey;
+ color: $medium-gray;
margin-left: 10px;
padding: 5px;
border: none;
- outline-color: black;
+ outline-color: $black;
}
}
@@ -236,19 +235,19 @@
margin-left: 10;
.collectionGridViewChrome-viewPicker {
- font-size: 75%;
+ font-size: $small-text;
//text-transform: uppercase;
//letter-spacing: 2px;
- background: #121721;
- color: white;
- outline-color: black;
- color: white;
+ background: $dark-gray;
+ color: $white;
+ outline-color: $black;
+ color: $white;
border: none;
- border-right: solid gray 1px;
+ border-right: solid $medium-gray 1px;
}
.collectionGridViewChrome-viewPicker:active {
- outline-color: black;
+ outline-color: $black;
}
.grid-control {
@@ -268,11 +267,11 @@
.collectionGridViewChrome-entryBox {
width: 50%;
- color: black;
+ color: $black;
}
.collectionGridViewChrome-columnButton {
- color: black;
+ color: $black;
}
}
}
@@ -302,7 +301,7 @@
align-items: center;
display: flex;
grid-auto-columns: auto;
- font-size: 75%;
+ font-size: $small-text;
letter-spacing: 2px;
.collectionStackingViewChrome-pivotField-label,
@@ -311,7 +310,7 @@
grid-column: 1;
margin-right: 7px;
user-select: none;
- font-family: 'Roboto';
+ font-family: $sans-serif;
letter-spacing: normal;
}
@@ -329,13 +328,13 @@
}
.collectionStackingViewChrome-sortIcon:hover {
- background-color: rgba(0,0,0,0.2);
+ background-color: $drop-shadow;
}
.collectionStackingViewChrome-pivotField,
.collectionTreeViewChrome-pivotField,
.collection3DCarouselViewChrome-scrollSpeed {
- color: white;
+ color: $white;
grid-column: 2;
grid-row: 1;
width: 90%;
@@ -344,7 +343,7 @@
height: 80%;
border-radius: 7px;
align-items: center;
- background: #eeeeee;
+ background: $white;
.editable-view-input,
input,
@@ -352,16 +351,16 @@
.editableView-container-editing {
margin: auto;
border: 0px;
- color: grey !important;
+ color: $light-gray !important;
text-align: center;
letter-spacing: 2px;
- outline-color: black;
+ outline-color: $black;
height: 100%;
}
.react-autosuggest__container {
margin: 0;
- color: grey;
+ color: $medium-gray;
padding: 0px;
}
}
@@ -407,11 +406,11 @@
}
.switchToText {
- color: $main-accent;
+ color: $medium-gray;
}
.switchToText:hover {
- color: $dark-color;
+ color: $dark-gray;
}
}
@@ -424,11 +423,11 @@
.collectionMenu-urlInput {
padding: 12px 10px 11px 10px;
border: 0px;
- color: black;
- font-size: 10px;
+ color: $black;
+ font-size: $small-text;
letter-spacing: 2px;
- outline-color: black;
- background: rgb(238, 238, 238);
+ outline-color: $black;
+ background: $white;
width: 100%;
min-width: 350px;
margin-right: 10px;
@@ -477,10 +476,10 @@
width: 20;
height: 30;
bottom: 0;
- background: #323232;
+ background: $dark-gray;
display: inline-flex;
align-items: center;
- color: white;
+ color: $white;
}
.backKeyframe {
@@ -502,13 +501,13 @@
margin: auto;
}
- border-right: solid gray 1px;
+ border-right: solid $medium-gray 1px;
}
}
.collectionSchemaViewChrome-cont {
display: flex;
- font-size: 10.5px;
+ font-size: $small-text;
.collectionSchemaViewChrome-toggle {
display: flex;
@@ -527,19 +526,19 @@
.collectionSchemaViewChrome-toggler {
width: 100px;
height: 35px;
- background-color: black;
+ background-color: $black;
position: relative;
}
.collectionSchemaViewChrome-togglerButton {
width: 47px;
height: 30px;
- background-color: $light-color-secondary;
+ background-color: $light-gray;
// position: absolute;
transition: all 0.5s ease;
// top: 3px;
margin-top: 3px;
- color: gray;
+ color: $medium-gray;
letter-spacing: 2px;
text-transform: uppercase;
display: flex;
@@ -579,7 +578,7 @@
}
.react-autosuggest__input {
- border: 1px solid #aaa;
+ border: 1px solid $light-gray;
border-radius: 4px;
width: 100%;
}
@@ -603,11 +602,11 @@
overflow-y: auto;
max-height: 400px;
width: 180px;
- border: 1px solid #aaa;
- background-color: #fff;
- font-family: Helvetica, sans-serif;
+ border: 1px solid $light-gray;
+ background-color: $white;
+ font-family: $sans-serif;
font-weight: 300;
- font-size: 16px;
+ font-size: $large-header;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
z-index: 2;
@@ -625,5 +624,5 @@
}
.react-autosuggest__suggestion--highlighted {
- background-color: #ddd;
+ background-color: $light-gray;
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 6e6fabd0d..a9b978c4e 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -29,7 +29,7 @@ import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
import { DocumentView } from "../nodes/DocumentView";
import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
-import { PresBox } from "../nodes/PresBox";
+import { PresBox } from "../nodes/trails/PresBox";
import "./CollectionMenu.scss";
import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
import { TabDocView } from "./TabDocView";
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 6baf633dd..bc1407c53 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -1,12 +1,12 @@
-import { action, computed } from "mobx";
+import { action, computed, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
import { NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, setupMoveUpEvents, returnTrue } from "../../../Utils";
+import { emptyFunction, returnTrue, setupMoveUpEvents } from "../../../Utils";
import { DocUtils } from "../../documents/Documents";
import { SelectionManager } from "../../util/SelectionManager";
import { SnappingManager } from "../../util/SnappingManager";
-import { UndoManager, undoBatch } from "../../util/UndoManager";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
import "./CollectionPileView.scss";
import { CollectionSubView } from "./CollectionSubView";
@@ -15,6 +15,7 @@ import React = require("react");
@observer
export class CollectionPileView extends CollectionSubView(doc => doc) {
_originalChrome: any = "";
+ _disposers: { [name: string]: IReactionDisposer } = {};
componentDidMount() {
if (this.layoutEngine() !== "pass" && this.layoutEngine() !== "starburst") {
@@ -22,9 +23,14 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
}
this._originalChrome = this.layoutDoc._chromeHidden;
this.layoutDoc._chromeHidden = true;
+
+ // pileups are designed to go away when they are empty.
+ this._disposers.selected = reaction(() => this.childDocs.length,
+ (num) => !num && this.props.ContainingCollectionView?.removeDocument(this.props.Document));
}
componentWillUnmount() {
this.layoutDoc._chromeHidden = this._originalChrome;
+ Object.values(this._disposers).forEach(disposer => disposer?.());
}
layoutEngine = () => StrCast(this.Document._pileLayoutEngine);
@@ -107,9 +113,6 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
this._undoBatch?.end();
this._undoBatch = undefined;
SnappingManager.SetIsDragging(false);
- if (!this.childDocs.length) {
- this.props.ContainingCollectionView?.removeDocument(this.props.Document);
- }
}, emptyFunction, e.shiftKey && this.layoutEngine() === "pass", this.layoutEngine() === "pass" && e.shiftKey); // this sets _doubleTap
}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 8f2847139..e1e04915a 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -11,12 +11,13 @@ import { listSpec } from "../../../fields/Schema";
import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { Cast, NumCast } from "../../../fields/Types";
import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, emptyPath, returnFalse, setupMoveUpEvents, returnEmptyDoclist, returnTrue } from "../../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../Utils";
+import { DocUtils } from "../../documents/Documents";
import { SelectionManager } from "../../util/SelectionManager";
import { SnappingManager } from "../../util/SnappingManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
-import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../views/globalCssVariables.scss';
+import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../views/global/globalCssVariables.scss';
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import '../DocumentDecorations.scss';
@@ -24,8 +25,7 @@ import { DocumentView } from "../nodes/DocumentView";
import { DefaultStyleProvider } from "../StyleProvider";
import "./CollectionSchemaView.scss";
import { CollectionSubView } from "./CollectionSubView";
-import { SchemaTable } from "./SchemaTable";
-import { DocUtils } from "../../documents/Documents";
+import { SchemaTable } from "../collections/collectionSchema/SchemaTable";
// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
export enum ColumnType {
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 9f56a0c0e..4b123c8b6 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.collectionMasonryView {
display: inline;
@@ -96,8 +96,8 @@
height: 2vw;
width: 100%;
font-family: $sans-serif;
- background: $dark-color;
- color: $light-color;
+ background: $dark-gray;
+ color: $white;
}
.collectionStackingView-columnDragger {
@@ -128,7 +128,7 @@
margin-left: 2px;
margin-right: 2px;
margin-top: 2px;
- background: $main-accent;
+ background: $medium-gray;
height: 5px;
&.active {
@@ -180,11 +180,11 @@
.collectionStackingView-sectionHeader {
text-align: center;
margin: auto;
- background: $main-accent;
+ background: $medium-gray;
// overflow: hidden; overflow is visible so the color menu isn't hidden -ftong
.editableView-input {
- color: black;
+ color: $dark-gray;
}
.editableView-input:hover,
@@ -205,7 +205,7 @@
display: flex;
align-items: center;
justify-content: center;
- color: black;
+ color: $dark-gray;
.editableView-container-editing-oneLine,
.editableView-container-editing {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 30f8e0112..7aa8dfd56 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -480,7 +480,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
if (value && this.columnHeaders) {
const schemaHdrField = new SchemaHeaderField(value);
this.columnHeaders.push(schemaHdrField);
- DocUtils.addFieldEnumerations(undefined, this.pivotField, [{ title: value, _backgroundColor: schemaHdrField.color }]);
+ DocUtils.addFieldEnumerations(undefined, this.pivotField, [{ title: value, _backgroundColor: "schemaHdrField.color" }]);
return true;
}
return false;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index ca45536f4..0d9b64d24 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -303,7 +303,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
} else {
const path = window.location.origin + "/doc/";
if (text.startsWith(path)) {
- const docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0];
+ const docid = text.replace(Doc.globalServerPath(), "").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
@@ -453,8 +453,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
generatedDocuments.length > 1 ? generatedDocuments.map(d => { DocUtils.iconify(d); return d; }) : [];
if (completed) completed(set);
else {
- if (isFreeformView) {
- addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!);
+ if (isFreeformView && generatedDocuments.length > 1) {
+ addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!));
} else {
generatedDocuments.forEach(addDocument);
}
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index f41043179..08b5e6bac 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -37,7 +37,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
@observable _focusRangeFilters: Opt<string[]>;
getAnchor = () => {
- const anchor = Docs.Create.TextanchorDocument({
+ const anchor = Docs.Create.HTMLAnchorDocument([], {
title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as any,
annotationOn: this.rootDoc
});
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 72ab51784..ec461ab94 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.collectionTreeView-dropTarget {
border-width: $COLLECTION_BORDER_WIDTH;
@@ -12,7 +12,7 @@
top: 0;
padding-left: 10px;
padding-right: 10px;
- background: $light-color-secondary;
+ background: $light-gray;
font-size: 13px;
overflow: auto;
user-select: none;
@@ -40,7 +40,7 @@
}
.delete-button {
- color: $intermediate-color;
+ color: $medium-gray;
// float: right;
margin-left: 15px;
// margin-top: 3px;
@@ -71,7 +71,7 @@
.collectionTreeView-subtitle {
font-style: italic;
font-size: 8pt;
- color: $intermediate-color;
+ color: $medium-gray;
}
.docContainer {
diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss
index a5aef86de..5db489c0a 100644
--- a/src/client/views/collections/CollectionView.scss
+++ b/src/client/views/collections/CollectionView.scss
@@ -1,8 +1,8 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.collectionView {
border-width: 0;
- border-color: $light-color-secondary;
+ border-color: $light-gray;
border-style: solid;
border-radius: 0 0 $border-radius $border-radius;
box-sizing: border-box;
diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss
index 9acbc4f85..a963f1cb9 100644
--- a/src/client/views/collections/TabDocView.scss
+++ b/src/client/views/collections/TabDocView.scss
@@ -1,19 +1,62 @@
input.lm_title:focus,
-input.lm_title
-{
+input.lm_title {
max-width: unset !important;
+ outline: none;
transition-delay: unset;
- width: 100%;
+ width: max-content;
cursor: text;
}
+
input.lm_title {
transition-delay: 0.35s;
- width: 100px;
+ width: max-content;
cursor: pointer;
}
-.tabDocView-drag {
- margin: auto;
+
+.lm_iconWrap {
+ display: flex;
+ color: black;
+ width: 15px;
+ height: 15px;
+ align-items: center;
+ align-self: center;
+ justify-content: center;
+ margin: 3px;
+ border-radius: 20%;
+
+ .moreInfoDot {
+ background-color: white;
+ border-radius: 100%;
+ width: 3px;
+ height: 3px;
+ margin: 0.5px;
+ }
+}
+
+.ffMenu {
+ display: grid;
+ grid-auto-rows: 35px;
+ grid-auto-columns: auto auto auto auto auto;
+ right: 10px;
+ bottom: 50px;
+ position: absolute;
+ min-height: 35px;
+ height: max-content;
+ border: solid 2px black;
+ border-radius: 5px;
+ background-color: #bddbe6;
+ width: max-content;
+ min-width: 35px;
+
+ .ffMenuButton {
+ display: flex;
+ width: 35px;
+ height: 35px;
+ align-items: center;
+ justify-content: center;
+ }
}
+
.miniMap-hidden,
.miniMap {
position: absolute;
@@ -37,6 +80,7 @@ input.lm_title {
}
}
}
+
.miniMap-hidden {
position: absolute;
bottom: 0;
@@ -46,7 +90,8 @@ input.lm_title {
transform: translate(20px, 20px) rotate(45deg);
border-radius: 30px;
padding: 2px;
- > svg {
+
+ >svg {
margin-top: 3px;
transform: translate(0px, 7px);
}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 7e2f7811e..a24f1eb7a 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -1,3 +1,4 @@
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
import 'golden-layout/src/css/goldenlayout-base.css';
@@ -9,9 +10,9 @@ import * as ReactDOM from 'react-dom';
import { DataSym, Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
import { Id } from '../../../fields/FieldSymbols';
import { FieldId } from "../../../fields/RefField";
-import { Cast, NumCast, StrCast, BoolCast } from "../../../fields/Types";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
+import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
@@ -24,15 +25,15 @@ import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from "../../util/UndoManager";
import { LightboxView } from '../LightboxView';
import { DocFocusOptions, DocumentView, DocumentViewProps } from "../nodes/DocumentView";
-import { FieldViewProps } from '../nodes/FieldView';
-import { PinProps, PresBox, PresMovement } from '../nodes/PresBox';
+import { PinProps, PresBox, PresMovement } from '../nodes/trails';
import { DefaultLayerProvider, DefaultStyleProvider, StyleLayers, StyleProp } from '../StyleProvider';
import { CollectionDockingView } from './CollectionDockingView';
import { CollectionDockingViewMenu } from './CollectionDockingViewMenu';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
-import { CollectionViewType, CollectionView } from './CollectionView';
+import { CollectionView, CollectionViewType } from './CollectionView';
import "./TabDocView.scss";
import React = require("react");
+import Color = require('color');
const _global = (window /* browser */ || global /* node */) as any;
interface TabDocViewProps {
@@ -52,6 +53,14 @@ export class TabDocView extends React.Component<TabDocViewProps> {
@computed get layoutDoc() { return this._document && Doc.Layout(this._document); }
@computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor))); }
+ @computed get tabTextColor() { return this._document?.type === DocumentType.PRES ? "black" : StrCast(this._document?._color, StrCast(this._document?.color, DefaultStyleProvider(this._document, undefined, StyleProp.Color))); }
+ // @computed get renderBounds() {
+ // const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0];
+ // const xbounds = bounds[2] - bounds[0];
+ // const ybounds = bounds[3] - bounds[1];
+ // const dim = Math.max(xbounds, ybounds);
+ // return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim };
+ // }
get stack() { return (this.props as any).glContainer.parent.parent; }
get tab() { return (this.props as any).glContainer.tab; }
@@ -65,15 +74,25 @@ export class TabDocView extends React.Component<TabDocViewProps> {
tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true);
tab.DashDoc = doc;
CollectionDockingView.Instance.tabMap.add(tab);
-
+ const iconType: IconProp = Doc.toIcon(doc);
// setup the title element and set its size according to the # of chars in the title. Show the full title when clicked.
const titleEle = tab.titleElement[0];
+ const iconWrap = document.createElement("div");
+ const closeWrap = document.createElement("div");
+
+
titleEle.size = StrCast(doc.title).length + 3;
titleEle.value = doc.title;
titleEle.onchange = undoBatch(action((e: any) => {
titleEle.size = e.currentTarget.value.length + 3;
Doc.GetProto(doc).title = e.currentTarget.value;
}));
+
+ const dragBtnDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, emptyFunction);
+ };
+
+
if (tab.element[0].children[1].children.length === 1) {
const toggle = document.createElement("div");
toggle.style.width = "10px";
@@ -83,18 +102,42 @@ export class TabDocView extends React.Component<TabDocViewProps> {
toggle.style.borderTopRightRadius = "7px";
toggle.style.position = "relative";
toggle.style.display = "inline-block";
- toggle.style.background = "gray";
- toggle.style.borderLeft = "solid 1px black";
+ toggle.style.background = "transparent";
toggle.onclick = (e: MouseEvent) => {
if (tab.contentItem === tab.header.parent.getActiveContentItem()) {
tab.DashDoc.activeLayer = tab.DashDoc.activeLayer ? undefined : StyleLayers.Background;
}
};
- tab.element[0].style.borderTopRightRadius = "8px";
- tab.element[0].children[1].appendChild(toggle);
- tab._disposers.layerDisposer = reaction(() =>
- ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
- ({ layer, color }) => toggle.style.background = !layer ? color : "dimgrey", { fireImmediately: true });
+ iconWrap.className = "lm_iconWrap";
+ iconWrap.id = "lm_iconWrap";
+ closeWrap.className = "lm_iconWrap";
+ closeWrap.id = "lm_closeWrap";
+ closeWrap.onclick = (e: MouseEvent) => {
+ tab.header.parent.contentItem.remove();
+ Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
+ };
+ const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
+ const closeIcon = <FontAwesomeIcon icon={"times"} />;
+ ReactDOM.render(docIcon, iconWrap);
+ ReactDOM.render(closeIcon, closeWrap);
+ // tab.element[0].append(closeWrap);
+ tab.element[0].prepend(iconWrap);
+ tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
+ ({ layer, color }) => {
+ const textColor = lightOrDark(this.tabColor); //not working with StyleProp.Color
+ titleEle.style.color = textColor;
+ titleEle.style.backgroundColor = "transparent";
+ iconWrap.style.color = textColor;
+ closeWrap.style.color = textColor;
+ moreInfoDrag.style.backgroundColor = textColor;
+ tab.element[0].style.background = !layer ? color : "dimgrey";
+ }, { fireImmediately: true });
+ // TODO:glr fix
+ // tab.element[0].style.borderTopRightRadius = "8px";
+ // tab.element[0].children[1].appendChild(toggle);
+ // tab._disposers.layerDisposer = reaction(() =>
+ // ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
+ // ({ layer, color }) => toggle.style.background = !layer ? color : "dimgrey", { fireImmediately: true });
}
// shifts the focus to this tab when another tab is dragged over it
tab.element[0].onmouseenter = (e: MouseEvent) => {
@@ -103,13 +146,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
tab.setActive(true);
}
};
- const dragBtnDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([dragHdl], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, emptyFunction);
- };
+
// select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected
titleEle.onpointerdown = action((e: any) => {
- if (e.target.className !== "lm_close_tab") {
+ if (e.target.className !== "lm_iconWrap") {
if (this.view) SelectionManager.SelectView(this.view, false);
else this._activated = true;
if (Date.now() - titleEle.lastClick < 1000) titleEle.select();
@@ -123,25 +164,30 @@ export class TabDocView extends React.Component<TabDocViewProps> {
const toggle = tab.element[0].children[1].children[0] as HTMLInputElement;
selected && tab.contentItem !== tab.header.parent.getActiveContentItem() &&
UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), "tab switch");
- toggle.style.fontWeight = selected ? "bold" : "";
- toggle.style.textTransform = selected ? "uppercase" : "";
+ // toggle.style.fontWeight = selected ? "bold" : "";
+ // toggle.style.textTransform = selected ? "uppercase" : "";
}));
//attach the selection doc buttons menu to the drag handle
- const stack = tab.contentItem.parent;
- const dragHdl = document.createElement("div");
- dragHdl.className = "lm_drag_tab";
+ const stack: HTMLDivElement = tab.contentItem.parent;
+ const header: HTMLDivElement = tab;
+ console.log("Stack: " + stack.id, stack.className)
+ stack.onscroll = action((e: any) => {
+ console.log('scrolling...')
+ })
+ const moreInfoDrag = document.createElement("div");
+ moreInfoDrag.className = "lm_iconWrap";
tab._disposers.buttonDisposer = reaction(() => this.view, view =>
- view && [ReactDOM.render(<span className="tabDocView-drag" onPointerDown={dragBtnDown}><CollectionDockingViewMenu views={() => [view]} Stack={stack} /></span>, dragHdl), tab._disposers.buttonDisposer?.()],
+ view && [ReactDOM.render(<span><CollectionDockingViewMenu views={() => [view]} Stack={stack} /></span>, moreInfoDrag), tab._disposers.buttonDisposer?.()],
{ fireImmediately: true });
- tab.reactComponents = [dragHdl];
- tab.closeElement.before(dragHdl);
+ // tab.reactComponents = [moreInfoDrag];
+ // tab.element[0].children[3].before(moreInfoDrag);
// highlight the tab when the tab document is brushed in any part of the UI
tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
titleEle.value = title;
- titleEle.style.padding = degree ? 0 : 2;
- titleEle.style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
+ // titleEle.style.padding = degree ? 0 : 2;
+ // titleEle.style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
}, { fireImmediately: true });
// clean up the tab when it is closed
@@ -221,9 +267,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
})).observe(this.props.glContainer._element[0]);
this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged(undefined);
- this._tabReaction = reaction(() => ({ selected: this.active(), title: this.tab?.titleElement[0] }),
- ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""),
- { fireImmediately: true });
+ // this._tabReaction = reaction(() => ({ selected: this.active(), title: this.tab?.titleElement[0] }),
+ // ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""),
+ // { fireImmediately: true });
}
componentWillUnmount() {
@@ -243,10 +289,10 @@ export class TabDocView extends React.Component<TabDocViewProps> {
}
// adds a tab to the layout based on the locaiton parameter which can be:
- // close[:{left,right,top,bottom}] - e.g., "close" will close the tab, "close:left" will close the left tab,
+ // close[:{left,right,top,bottom}] - e.g., "close" will close the tab, "close:left" will close the left tab,
// add[:{left,right,top,bottom}] - e.g., "add" will add a tab to the current stack, "add:right" will add a tab on the right
- // replace[:{left,right,top,bottom,<any string>}] - e.g., "replace" will replace the current stack contents,
- // "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name,
+ // replace[:{left,right,top,bottom,<any string>}] - e.g., "replace" will replace the current stack contents,
+ // "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name,
// "replace:monkeys" - will replace any tab that has the label 'monkeys', or a tab with that label will be created by default on the right
// inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack
addDocTab = (doc: Doc, location: string) => {
@@ -460,4 +506,4 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
</div>
</div>;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 3f6fc8b0c..1ebc5873e 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.treeView-label {
max-height: 1.5em;
@@ -14,7 +14,7 @@
.bullet-outline {
position: relative;
width: $TREE_BULLET_WIDTH;
- color: $intermediate-color;
+ color: $medium-gray;
transform: scale(0.5);
display: inline-flex;
align-items: center;
@@ -45,7 +45,7 @@
.bullet {
position: relative;
width: $TREE_BULLET_WIDTH;
- color: $intermediate-color;
+ color: $medium-gray;
margin-top: 3px;
transform: scale(1.3, 1.3);
border: #80808030 1px solid;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 462cf2963..e33c39d20 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -20,7 +20,7 @@ import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from '../../util/UndoManager';
import { EditableView } from "../EditableView";
-import { TREE_BULLET_WIDTH } from '../globalCssVariables.scss';
+import { TREE_BULLET_WIDTH } from '../global/globalCssVariables.scss';
import { DocumentView, DocumentViewProps, StyleProviderFunc, DocumentViewInternal } from '../nodes/DocumentView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index afc1babeb..37444a9dc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -126,7 +126,8 @@ export function computerStarburstLayout(
replica: ""
});
});
- return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
+ const divider = { type: "div", color: "transparent", x: -burstRadius[0] / 3, y: 0, width: 15, height: 15, payload: undefined };
+ return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]);
}
@@ -399,7 +400,7 @@ function normalizeResults(
): ViewDefResult[] {
const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
const docEles = Array.from(docMap.entries()).map(ele => ele[1]);
- const aggBounds = aggregateBounds(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" }))).filter(e => e.zIndex !== -99), 0, 0);
+ const aggBounds = aggregateBounds(extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" })))).filter(e => e.zIndex !== -99), 0, 0);
aggBounds.r = Math.max(minWidth, aggBounds.r - aggBounds.x);
const wscale = panelDim[0] / (aggBounds.r - aggBounds.x);
let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
index c5b8fc5e8..5fa01b102 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
@@ -1,4 +1,4 @@
-@import "globalCssVariables";
+@import "global/globalCssVariables";
.collectionFreeFormRemoteCursors-cont {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index eb0538c41..79e063f7f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,4 +1,4 @@
-@import "../../globalCssVariables";
+@import "../../global/globalCssVariables";
.collectionfreeformview-none {
position: inherit;
@@ -226,7 +226,7 @@
// linear-gradient(to bottom, $light-color-secondary 1px, transparent 1px);
// background-size: 30px 30px;
// }
- border: 0px solid $light-color-secondary;
+ border: 0px solid $light-gray;
border-radius: inherit;
box-sizing: border-box;
position: absolute;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index accb80c5a..143d8e070 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -28,7 +28,7 @@ import { SelectionManager } from "../../../util/SelectionManager";
import { SnappingManager } from "../../../util/SnappingManager";
import { Transform } from "../../../util/Transform";
import { undoBatch } from "../../../util/UndoManager";
-import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss";
+import { COLLECTION_BORDER_WIDTH } from "../../../views/global/globalCssVariables.scss";
import { Timeline } from "../../animationtimeline/Timeline";
import { ContextMenu } from "../../ContextMenu";
import { DocumentDecorations } from "../../DocumentDecorations";
@@ -38,7 +38,7 @@ import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDo
import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView";
import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
import { pageSchema } from "../../nodes/ImageBox";
-import { PresBox } from "../../nodes/PresBox";
+import { PresBox } from "../../nodes/trails/PresBox";
import { StyleLayers, StyleProp } from "../../StyleProvider";
import { CollectionDockingView } from "../CollectionDockingView";
import { CollectionSubView } from "../CollectionSubView";
@@ -48,6 +48,7 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso
import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
+import { DocumentType } from "../../../documents/DocumentTypes";
export const panZoomSchema = createSchema({
_panX: "number",
@@ -1486,7 +1487,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
onDragOver={e => e.preventDefault()}
onContextMenu={this.onContextMenu}
style={{
- pointerEvents: this.backgroundEvents ? "all" : this.props.pointerEvents as any,
+ pointerEvents: this.props.Document.type === DocumentType.MARKER ? "none" : // bcz: ugh.. this is here to prevent markers, which render as freeform views, from grabbing events -- need a better approach.
+ this.backgroundEvents ? "all" : this.props.pointerEvents as any,
transform: `scale(${this.contentScaling || 1})`,
width: `${100 / (this.contentScaling || 1)}%`,
height: this.isAnnotationOverlay && this.Document.scrollHeight ? this.Document.scrollHeight : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b1f2750c3..846d28214 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -19,7 +19,8 @@ import { Transform } from "../../../util/Transform";
import { undoBatch, UndoManager } from "../../../util/UndoManager";
import { ContextMenu } from "../../ContextMenu";
import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
-import { PresBox, PresMovement } from "../../nodes/PresBox";
+import { PresBox } from "../../nodes/trails/PresBox";
+import { PresMovement } from "../../nodes/trails/PresEnums";
import { PreviewCursor } from "../../PreviewCursor";
import { CollectionDockingView } from "../CollectionDockingView";
import { SubCollectionViewProps } from "../CollectionSubView";
@@ -368,8 +369,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
SelectionManager.DeselectAll();
selected.forEach(d => this.props.removeDocument?.(d));
const newCollection = DocUtils.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2);
- this.props.addDocument?.(newCollection!);
- this.props.selectDocuments([newCollection!]);
+ this.props.addDocument?.(newCollection);
+ this.props.selectDocuments([newCollection]);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
index f75179cea..fd99abce5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
@@ -26,7 +26,7 @@ import { SnappingManager } from "../../../util/SnappingManager";
import { undoBatch } from "../../../util/UndoManager";
import '../../../views/DocumentDecorations.scss';
import { EditableView } from "../../EditableView";
-import { MAX_ROW_HEIGHT } from '../../globalCssVariables.scss';
+import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss';
import { DocumentIconContainer } from "../../nodes/DocumentIcon";
import { OverlayView } from "../../OverlayView";
import "./CollectionSchemaView.scss";
@@ -103,6 +103,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
this.props.setPreviewDoc(this.props.rowProps.original);
+ console.log("click cell");
let url: string;
if (url = StrCast(this.props.rowProps.row.href)) {
try {
@@ -246,13 +247,13 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
} else {
// check if the input is a number
let inputIsNum = true;
- for (let s of value) {
- if (isNaN(parseInt(s)) && !(s == ".") && !(s == ",")) {
+ for (const s of value) {
+ if (isNaN(parseInt(s)) && !(s === ".") && !(s === ",")) {
inputIsNum = false;
}
}
// check if the input is a boolean
- let inputIsBool: boolean = value == "false" || value == "true";
+ const inputIsBool: boolean = value === "false" || value === "true";
// what to do in the case
if (!inputIsNum && !inputIsBool && !value.startsWith("=")) {
// if it's not a number, it's a string, and should be processed as such
@@ -263,12 +264,12 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
const vsqLength = valueSansQuotes.length;
// get rid of outer quotes
valueSansQuotes = valueSansQuotes.substring(value.startsWith("\"") ? 1 : 0,
- valueSansQuotes.charAt(vsqLength - 1) == "\"" ? vsqLength - 1 : vsqLength);
+ valueSansQuotes.charAt(vsqLength - 1) === "\"" ? vsqLength - 1 : vsqLength);
}
let inputAsString = '"';
// escape any quotes in the string
for (const i of valueSansQuotes) {
- if (i == '"') {
+ if (i === '"') {
inputAsString += '\\"';
} else {
inputAsString += i;
@@ -278,7 +279,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
inputAsString += '"';
//two options here: we can strip off outer quotes or we can figure out what's going on with the script
const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
- const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length
+ const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length;
script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
// handle numbers and expressions
} else if (inputIsNum || value.startsWith("=")) {
@@ -286,18 +287,18 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
const inputscript = value.substring(value.startsWith("=") ? 1 : 0);
// if commas are not stripped, the parser only considers the numbers after the last comma
let inputSansCommas = "";
- for (let s of inputscript) {
- if (!(s == ",")) {
+ for (const s of inputscript) {
+ if (!(s === ",")) {
inputSansCommas += s;
}
}
const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
- const changeMade = value.length !== value.length || value.length - 2 !== value.length
+ const changeMade = value.length !== value.length || value.length - 2 !== value.length;
script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
// handle booleans
} else if (inputIsBool) {
const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
- const changeMade = value.length !== value.length || value.length - 2 !== value.length
+ const changeMade = value.length !== value.length || value.length - 2 !== value.length;
script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
}
}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index b57fee0e4..40cdcd14b 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -1,7 +1,7 @@
-@import "../../globalCssVariables";
+@import "../../global/globalCssVariables.scss";
.collectionSchemaView-container {
border-width: $COLLECTION_BORDER_WIDTH;
- border-color: $intermediate-color;
+ border-color: $medium-gray;
border-style: solid;
border-radius: $border-radius;
box-sizing: border-box;
@@ -33,13 +33,13 @@
cursor: col-resize;
}
// .documentView-node:first-child {
- // background: $light-color;
+ // background: $white;
// }
}
.collectionSchemaView-searchContainer {
border-width: $COLLECTION_BORDER_WIDTH;
- border-color: $intermediate-color;
+ border-color: $medium-gray;
border-style: solid;
border-radius: $border-radius;
box-sizing: border-box;
@@ -72,7 +72,7 @@
cursor: col-resize;
}
// .documentView-node:first-child {
- // background: $light-color;
+ // background: $white;
// }
}
@@ -245,7 +245,7 @@ button.add-column {
}
}
label {
- color: $main-accent;
+ color: $medium-gray;
font-weight: normal;
letter-spacing: 2px;
text-transform: uppercase;
@@ -260,11 +260,11 @@ button.add-column {
background-color: white;
transition: background-color 0.2s;
&:hover {
- background-color: $light-color-secondary;
+ background-color: $light-gray;
}
&.active {
font-weight: bold;
- border: 2px solid $light-color-secondary;
+ border: 2px solid $light-gray;
}
svg {
color: gray;
@@ -277,7 +277,7 @@ button.add-column {
//width: 100%;
background-color: white;
input {
- border: 2px solid $light-color-secondary;
+ border: 2px solid $light-gray;
padding: 3px;
height: 28px;
font-weight: bold;
@@ -303,7 +303,7 @@ button.add-column {
border-top: 0;
}
&:hover {
- background-color: $light-color-secondary;
+ background-color: $light-gray;
}
}
}
@@ -329,7 +329,7 @@ button.add-column {
height: 100%;
background-color: white;
&.row-focused .rt-td {
- background-color: #bfffc0; //$light-color-secondary;
+ background-color: #bfffc0; //$light-gray;
}
&.row-wrapped {
.rt-td {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index ef28f75c8..585cda729 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -11,21 +11,21 @@ import { listSpec } from "../../../../fields/Schema";
import { PastelSchemaPalette, SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
import { Cast, NumCast } from "../../../../fields/Types";
import { TraceMobx } from "../../../../fields/util";
-import { emptyFunction, emptyPath, returnFalse, setupMoveUpEvents, returnEmptyDoclist, returnTrue } from "../../../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils";
+import { DocUtils } from "../../../documents/Documents";
import { SelectionManager } from "../../../util/SelectionManager";
import { SnappingManager } from "../../../util/SnappingManager";
import { Transform } from "../../../util/Transform";
import { undoBatch } from "../../../util/UndoManager";
-import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../globalCssVariables.scss';
+import '../../../views/DocumentDecorations.scss';
import { ContextMenu } from "../../ContextMenu";
import { ContextMenuProps } from "../../ContextMenuItem";
-import '../../../views/DocumentDecorations.scss';
+import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
import { DocumentView } from "../../nodes/DocumentView";
import { DefaultStyleProvider } from "../../StyleProvider";
-import "./CollectionSchemaView.scss";
import { CollectionSubView } from "../CollectionSubView";
+import "./CollectionSchemaView.scss";
import { SchemaTable } from "./SchemaTable";
-import { DocUtils } from "../../../documents/Documents";
// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
export enum ColumnType {
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index 0d5c9e077..de08c327a 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -21,7 +21,7 @@ import { DocumentType } from "../../../documents/DocumentTypes";
import { CompileScript, Transformer, ts } from "../../../util/Scripting";
import { Transform } from "../../../util/Transform";
import { undoBatch } from "../../../util/UndoManager";
-import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../globalCssVariables.scss';
+import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
import { ContextMenu } from "../../ContextMenu";
import '../../../views/DocumentDecorations.scss';
import { DocumentView } from "../../nodes/DocumentView";
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss
index ccc9306c4..7556f8b8a 100644
--- a/src/client/views/globalCssVariables.scss
+++ b/src/client/views/global/globalCssVariables.scss
@@ -1,41 +1,55 @@
-@import url("https://fonts.googleapis.com/css?family=Noto+Sans:400,700|Crimson+Text:400,400i,700");
+@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap");
// colors
-$light-color: #fcfbf7;
-$light-color-secondary:#f1efeb;
-//$main-accent: #61aaa3;
-$main-accent: #aaaaa3;
-// $alt-accent: #cdd5ec;
-// $alt-accent: #cdeceb;
-//$alt-accent: #59dff7;
-$alt-accent: #c2c2c5;
-$lighter-alt-accent: rgb(207, 220, 240);
-$darker-alt-accent: #b2cef8;
-$intermediate-color: #9c9396;
-$dark-color: #121721;
-$link-color: #add8e6;
-$antimodemenu-height: 35px;
+$white: #ffffff;
+$light-gray: #dfdfdf;
+$medium-gray: #9f9f9f;
+$dark-gray: #323232;
+$black: #000000;
+
+$light-blue: #bdddf5;
+$medium-blue: #4476f7;
+$pink: #e0217d;
+$yellow: #f5d747;
+
+$close-red: #e48282;
+
+$drop-shadow: "#32323215";
+
+//padding
+$minimum-padding: 4px;
+$medium-padding: 16px;
+$large-padding: 32px;
+
+//icon sizes
+$icon-size: 28px;
+
// fonts
-$sans-serif: "Noto Sans",
-sans-serif;
+$sans-serif: "Roboto", sans-serif;
+$large-header: 16px;
+$body-text: 12px;
+$small-text: 9px;
// $sans-serif: "Roboto Slab", sans-serif;
-$serif: "Crimson Text",
-serif;
+
// misc values
$border-radius: 0.3em;
-//
$search-thumnail-size: 130;
+$topbar-height: 32px;
+$antimodemenu-height: 36px;
// dragged items
$contextMenu-zindex: 100000; // context menu shows up over everything
$radialMenu-zindex: 100000; // context menu shows up over everything
+// borders
+$standard-border: solid 1px #9f9f9f;
+
$searchpanel-height: 32px;
$mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc
$docDecorations-zindex: 998; // then doc decorations appear over everything else
$remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right?
$COLLECTION_BORDER_WIDTH: 0;
$SCHEMA_DIVIDER_WIDTH: 4;
-$MINIMIZED_ICON_SIZE:25;
+$MINIMIZED_ICON_SIZE: 24;
$MAX_ROW_HEIGHT: 44px;
$DFLT_IMAGE_NATIVE_DIM: 900px;
$MENU_PANEL_WIDTH: 60px;
@@ -53,4 +67,4 @@ $TREE_BULLET_WIDTH: 20px;
DFLT_IMAGE_NATIVE_DIM: $DFLT_IMAGE_NATIVE_DIM;
MENU_PANEL_WIDTH: $MENU_PANEL_WIDTH;
TREE_BULLET_WIDTH: $TREE_BULLET_WIDTH;
-} \ No newline at end of file
+}
diff --git a/src/client/views/globalCssVariables.scss.d.ts b/src/client/views/global/globalCssVariables.scss.d.ts
index 11e62e1eb..11e62e1eb 100644
--- a/src/client/views/globalCssVariables.scss.d.ts
+++ b/src/client/views/global/globalCssVariables.scss.d.ts
diff --git a/src/client/views/global/globalEnums.tsx b/src/client/views/global/globalEnums.tsx
new file mode 100644
index 000000000..2aeb8e338
--- /dev/null
+++ b/src/client/views/global/globalEnums.tsx
@@ -0,0 +1,38 @@
+export enum Colors {
+ BLACK = "#000000",
+ DARK_GRAY = "#323232",
+ MEDIUM_GRAY = "#9F9F9F",
+ LIGHT_GRAY = "#DFDFDF",
+ WHITE = "#FFFFFF",
+ MEDIUM_BLUE = "#4476F7",
+ LIGHT_BLUE = "#BDDDF5",
+ PINK = "#E0217D",
+ YELLOW = "#F5D747",
+ DROP_SHADOW = "#32323215",
+}
+
+export enum FontSizes {
+ //Bolded
+ LARGE_HEADER = "16px",
+
+ //Bolded or unbolded
+ BODY_TEXT = "12px",
+
+ //Bolded
+ SMALL_TEXT = "9px",
+}
+
+export enum Padding {
+ MINIMUM_PADDING = "4px",
+ SMALL_PADDING = "8px",
+ MEDIUM_PADDING = "16px",
+ LARGE_PADDING = "32px",
+}
+
+export enum IconSizes {
+ ICON_SIZE = "28px",
+}
+
+export enum Borders {
+ STANDARD = "solid 1px #9F9F9F"
+} \ No newline at end of file
diff --git a/src/client/views/linking/LinkEditor.scss b/src/client/views/linking/LinkEditor.scss
index 7e6999cdc..e45a91d57 100644
--- a/src/client/views/linking/LinkEditor.scss
+++ b/src/client/views/linking/LinkEditor.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.linkEditor {
width: 100%;
@@ -20,9 +20,9 @@
}
.linkEditor-info {
- //border-bottom: 0.5px solid $light-color-secondary;
+ //border-bottom: 0.5px solid $light-gray;
//padding-bottom: 1px;
- padding-top: 5px;
+ padding: 12px;
padding-left: 5px;
//margin-bottom: 6px;
display: flex;
@@ -61,7 +61,7 @@
}
.linkEditor-description {
- padding-left: 6.5px;
+ padding-left: 26px;
padding-right: 6.5px;
padding-bottom: 3.5px;
@@ -107,9 +107,9 @@
}
.linkEditor-followingDropdown {
- padding-left: 6.5px;
+ padding-left: 26px;
padding-right: 6.5px;
- padding-bottom: 6px;
+ padding-bottom: 15px;
&:hover {
cursor: pointer;
@@ -195,7 +195,7 @@
}
.linkEditor-group {
- background-color: $light-color-secondary;
+ background-color: $light-gray;
padding: 6px;
margin: 3px 0;
border-radius: 3px;
@@ -254,8 +254,8 @@
}
.linkEditor-option {
- background-color: $light-color-secondary;
- border: 1px solid $intermediate-color;
+ background-color: $light-gray;
+ border: 1px solid $medium-gray;
border-top: 0;
padding: 3px;
cursor: pointer;
@@ -272,7 +272,7 @@
.linkEditor-typeButton {
background-color: transparent;
- color: $dark-color;
+ color: $dark-gray;
height: 20px;
padding: 0 3px;
padding-bottom: 2px;
@@ -285,7 +285,7 @@
width: calc(100% - 40px);
&:hover {
- background-color: $light-color;
+ background-color: $white;
}
}
diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss
index a90bf8b0a..19c6463d3 100644
--- a/src/client/views/linking/LinkMenu.scss
+++ b/src/client/views/linking/LinkMenu.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.linkMenu {
width: auto;
@@ -7,20 +7,19 @@
z-index: 2001;
.linkMenu-list,
- .linkMenu-listEditor
- {
+ .linkMenu-listEditor {
display: inline-block;
position: relative;
- border: 1px solid black;
- box-shadow: 3px 3px 1.5px grey;
+ border: 1px solid #e4e4e4;
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
background: white;
-
min-width: 170px;
- max-height: 170px;
+ max-height: 230px;
overflow-y: scroll;
z-index: 10;
- }
- .linkMenu-list {
+ }
+
+ .linkMenu-list {
white-space: nowrap;
overflow-x: hidden;
width: 240px;
@@ -46,13 +45,13 @@
}
.linkMenu-group-name {
+ padding: 10px;
&:hover {
- p {
- background-color: lightgray;
-
- }
+ // p {
+ // background-color: lightgray;
+ // }
p.expand-one {
width: calc(100% + 20px);
@@ -65,10 +64,9 @@
p {
width: 100%;
- //padding: 4px 6px;
line-height: 12px;
border-radius: 5px;
- font-weight: bold;
+ text-transform: capitalize;
}
.linkEditor-tableButton {
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index c7888c5ee..6fc860447 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -15,6 +15,9 @@ interface Props {
changeFlyout: () => void;
}
+/**
+ * the outermost component for the link menu of a node that contains a list of its linked nodes
+ */
@observer
export class LinkMenu extends React.Component<Props> {
private _editorRef = React.createRef<HTMLDivElement>();
@@ -36,6 +39,11 @@ export class LinkMenu extends React.Component<Props> {
}
}
+ /**
+ * maps each link to a JSX element to be rendered
+ * @param groups LinkManager containing info of all of the links
+ * @returns list of link JSX elements if there at least one linked element
+ */
renderAllGroups = (groups: Map<string, Array<Doc>>): Array<JSX.Element> => {
const linkItems = Array.from(groups.entries()).map(group =>
<LinkMenuGroup
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index 74af78234..c7586a467 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -40,7 +40,7 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
return (
<div className="linkMenu-group" ref={this._menuRef}>
<div className="linkMenu-group-name">
- <p className={this.props.groupType === "*" || this.props.groupType === "" ? "" : "expand-one"} > {this.props.groupType}:</p>
+ <p className={this.props.groupType === "*" || this.props.groupType === "" ? "" : "expand-one"}> {this.props.groupType}:</p>
</div>
<div className="linkMenu-group-wrapper">
{groupItems}
diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss
index 4e13ef8c8..90722daf9 100644
--- a/src/client/views/linking/LinkMenuItem.scss
+++ b/src/client/views/linking/LinkMenuItem.scss
@@ -1,10 +1,10 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.linkMenu-item {
- // border-top: 0.5px solid $main-accent;
+ // border-top: 0.5px solid $medium-gray;
position: relative;
display: flex;
- border-bottom: 0.5px solid black;
+ border-top: 0.5px solid #cdcdcd;
padding-left: 6.5px;
padding-right: 2px;
@@ -55,8 +55,8 @@
.linkMenu-destination-title {
text-decoration: none;
- color: rgb(85, 120, 196);
- font-size: 14px;
+ color: #4476F7;
+ font-size: 16px;
padding-bottom: 2px;
padding-right: 4px;
margin-right: 4px;
@@ -76,7 +76,7 @@
text-decoration: none;
font-style: italic;
color: rgb(95, 97, 102);
- font-size: 10px;
+ font-size: 9px;
margin-left: 20px;
max-width: 125px;
height: auto;
@@ -102,7 +102,7 @@
.link-metadata {
padding: 0 10px 0 16px;
margin-bottom: 4px;
- color: $main-accent;
+ color: $medium-gray;
font-style: italic;
font-size: 10.5px;
}
@@ -143,8 +143,8 @@
padding-right: 6px;
border-radius: 50%;
pointer-events: auto;
- background-color: $dark-color;
- color: $light-color;
+ background-color: $dark-gray;
+ color: $white;
font-size: 65%;
transition: transform 0.2s;
text-align: center;
@@ -162,7 +162,7 @@
}
&:hover {
- background: $main-accent;
+ background: $medium-gray;
cursor: pointer;
}
}
diff --git a/src/client/views/linking/LinkPopup.scss b/src/client/views/linking/LinkPopup.scss
new file mode 100644
index 000000000..8ae65158d
--- /dev/null
+++ b/src/client/views/linking/LinkPopup.scss
@@ -0,0 +1,45 @@
+.linkPopup-container {
+ background: white;
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
+ top: 35px;
+ height: 200px;
+ width: 200px;
+ position: absolute;
+ padding: 15px;
+ border-radius: 3px;
+
+ input {
+ border: 1px solid #b9b9b9;
+ border-radius: 20px;
+ height: 25px;
+ width: 100%;
+ padding-left: 10px;
+ }
+
+ .divider {
+ margin: 10px 0;
+ height: 20px;
+ width: 100%;
+
+ .line {
+ height: 1px;
+ background-color: #b9b9b9;
+ width: 100%;
+ position: relative;
+ top: 12px;
+ }
+
+ .divider-text {
+ width: 20px;
+ background-color: white;
+ text-align: center;
+ position: relative;
+ margin: auto;
+ }
+ }
+
+
+ .searchBox-container {
+ background: pink;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
new file mode 100644
index 000000000..2c4b718f4
--- /dev/null
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -0,0 +1,114 @@
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, observable, runInAction } from 'mobx';
+import { observer } from "mobx-react";
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { Cast, StrCast } from '../../../fields/Types';
+import { WebField } from '../../../fields/URLField';
+import { emptyFunction, setupMoveUpEvents, returnFalse, returnTrue, returnEmptyDoclist, returnEmptyFilter } from '../../../Utils';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager } from '../../util/DragManager';
+import { Hypothesis } from '../../util/HypothesisUtils';
+import { LinkManager } from '../../util/LinkManager';
+import { undoBatch } from '../../util/UndoManager';
+import { DocumentLinksButton } from '../nodes/DocumentLinksButton';
+import { DocumentView, DocumentViewSharedProps } from '../nodes/DocumentView';
+import { LinkDocPreview } from '../nodes/LinkDocPreview';
+import './LinkPopup.scss';
+import React = require("react");
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { DefaultStyleProvider } from '../StyleProvider';
+import { Transform } from '../../util/Transform';
+import { DocUtils } from '../../documents/Documents';
+import { SearchBox } from '../search/SearchBox';
+import { EditorView } from 'prosemirror-view';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+
+interface LinkPopupProps {
+ showPopup: boolean;
+ // groupType: string;
+ // linkDoc: Doc;
+ // docView: DocumentView;
+ // sourceDoc: Doc;
+}
+
+/**
+ * Popup component for creating links from text to Dash documents
+ */
+
+@observer
+export class LinkPopup extends React.Component<LinkPopupProps> {
+ @observable private linkURL: string = "";
+ @observable public view?: EditorView;
+
+
+
+ // TODO: should check for valid URL
+ @undoBatch
+ makeLinkToURL = (target: string, lcoation: string) => {
+ ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, "onRadd:rightight", target, target);
+ }
+
+ @action
+ onLinkChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this.linkURL = e.target.value;
+ console.log(this.linkURL)
+ }
+
+
+ getPWidth = () => 500;
+ getPHeight = () => 500;
+
+ render() {
+ const popupVisibility = this.props.showPopup ? "block" : "none";
+ return (
+ <div className="linkPopup-container" style={{ display: popupVisibility }}>
+ <div className="linkPopup-url-container">
+ <input autoComplete="off" type="text" value={this.linkURL} placeholder="Enter URL..." onChange={this.onLinkChange} />
+ <button onPointerDown={e => this.makeLinkToURL(this.linkURL, "add:right")}
+ style={{ display: "block", margin: "10px auto", }}>Apply hyperlink</button>
+ </div>
+ <div className="divider">
+ <div className="line"></div>
+ <p className="divider-text">or</p>
+ </div>
+ <div className="linkPopup-document-search-container">
+ {/* <i></i>
+ <input defaultValue={""} autoComplete="off" type="text" placeholder="Search for Document..." id="search-input"
+ className="linkPopup-searchBox searchBox-input" /> */}
+
+ <SearchBox Document={CurrentUserUtils.MySearchPanelDoc}
+ DataDoc={CurrentUserUtils.MySearchPanelDoc}
+ fieldKey="data"
+ dropAction="move"
+ isSelected={returnTrue}
+ isContentActive={returnTrue}
+ select={returnTrue}
+ setHeight={returnFalse}
+ addDocument={undefined}
+ addDocTab={returnTrue}
+ pinToPres={emptyFunction}
+ rootSelected={returnTrue}
+ styleProvider={DefaultStyleProvider}
+ layerProvider={undefined}
+ removeDocument={undefined}
+ ScreenToLocalTransform={Transform.Identity}
+ PanelWidth={this.getPWidth}
+ PanelHeight={this.getPHeight}
+ renderDepth={0}
+ focus={DocUtils.DefaultFocus}
+ docViewPath={returnEmptyDoclist}
+ whenChildContentsActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined} />
+ </div>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index a2e36f12e..82bad971d 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -196,7 +196,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this._recorder.ondataavailable = async (e: any) => {
const [{ result }] = await Networking.UploadFilesToServer(e.data);
if (!(result instanceof Error)) {
- this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client));
+ this.props.Document[this.props.fieldKey] = new AudioField(result.accessPaths.agnostic.client);
}
};
this._recordStart = new Date().getTime();
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index a0a40becb..3d2cdf5a4 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -11,7 +11,7 @@ import { CollectionFreeFormView } from "../collections/collectionFreeForm/Collec
import { CollectionSchemaView } from "../collections/collectionSchema/CollectionSchemaView";
import { CollectionView } from "../collections/CollectionView";
import { InkingStroke } from "../InkingStroke";
-import { PresElementBox } from "../presentationview/PresElementBox";
+import { PresElementBox } from "../nodes/trails/PresElementBox";
import { SearchBox } from "../search/SearchBox";
import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo";
import { YoutubeBox } from "./../../apis/youtube/YoutubeBox";
@@ -32,7 +32,7 @@ import { LabelBox } from "./LabelBox";
import { LinkAnchorBox } from "./LinkAnchorBox";
import { LinkBox } from "./LinkBox";
import { PDFBox } from "./PDFBox";
-import { PresBox } from "./PresBox";
+import { PresBox } from "./trails/PresBox";
import { ScreenshotBox } from "./ScreenshotBox";
import { ScriptingBox } from "./ScriptingBox";
import { SliderBox } from "./SliderBox";
@@ -64,6 +64,7 @@ interface HTMLtagProps {
htmltag: string;
onClick?: ScriptField;
onInput?: ScriptField;
+ scaling: number;
}
//"<HTMLdiv borderRadius='100px' onClick={this.bannerColor=this.bannerColor==='red'?'green':'red'} overflow='hidden' position='absolute' width='100%' height='100%' transform='rotate({2*this.x+this.y}deg)'> <ImageBox {...props} fieldKey={'data'}/> <HTMLspan width='200px' top='0' height='35px' textAlign='center' paddingTop='10px' transform='translate(-40px, 45px) rotate(-45deg)' position='absolute' color='{this.bannerColor===`green`?`light`:`dark`}blue' backgroundColor='{this.bannerColor===`green`?`dark`:`light`}blue'> {this.title}</HTMLspan></HTMLdiv>"
@@ -82,7 +83,7 @@ interface HTMLtagProps {
export class HTMLtag extends React.Component<HTMLtagProps> {
click = (e: React.MouseEvent) => {
const clickScript = (this.props as any).onClick as Opt<ScriptField>;
- clickScript?.script.run({ this: this.props.Document, self: this.props.RootDoc });
+ clickScript?.script.run({ this: this.props.Document, self: this.props.RootDoc, scale: this.props.scaling });
}
onInput = (e: React.FormEvent<HTMLDivElement>) => {
const onInputScript = (this.props as any).onInput as Opt<ScriptField>;
@@ -90,9 +91,9 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
}
render() {
const style: { [key: string]: any } = {};
- const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "Document", "key", "onInput", "onClick", "__proto__"]).omit;
+ const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "scaling", "Document", "key", "onInput", "onClick", "__proto__"]).omit;
const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a propery expression string: { script } into a value
- return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ self: this.props.RootDoc, this: this.props.Document }).result as string || "";
+ return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.props.RootDoc, this: this.props.Document, scale: this.props.scaling }).result as string || "";
};
Object.keys(divKeys).map((prop: string) => {
const p = (this.props as any)[prop] as string;
@@ -184,7 +185,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
// replace HTML<tag> with corresponding HTML tag as in: <HTMLdiv> becomes <HTMLtag Document={props.Document} htmltag='div'>
const replacer2 = (match: any, p1: string, offset: any, string: any) => {
- return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} htmltag='${p1}'`;
+ return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} scaling='${this.props.scaling?.() || 1}' htmltag='${p1}'`;
};
layoutFrame = layoutFrame.replace(/<HTML([a-zA-Z0-9_-]+)/g, replacer2);
@@ -200,7 +201,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
if (splits.length > 1) {
const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] });
layoutFrame = splits[0] + ` ${func}={props.${func}} ` + splits[1].substring(code[1].end + 1);
- return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, value: "string" });
+ return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, scale: "number", value: "string" });
}
return undefined;
// add input function to props
diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss
index 735aa669f..b37b68249 100644
--- a/src/client/views/nodes/DocumentLinksButton.scss
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -1,16 +1,27 @@
-@import "../globalCssVariables.scss";
+@import "../global/globalCssVariables.scss";
+.documentLinksButton-menu {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ display: flex;
+ align-content: center;
+ justify-content: center;
+ align-items: center;
+}
+
.documentLinksButton-cont {
min-width: 20;
min-height: 20;
position: absolute;
}
+
.documentLinksButton,
.documentLinksButton-endLink,
.documentLinksButton-startLink {
- height: 20px;
- width: 20px;
+ height: 25px;
+ width: 25px;
position: absolute;
border-radius: 50%;
opacity: 0.9;
@@ -33,27 +44,43 @@
}
.documentLinksButton {
- background-color: black;
+ background-color: $dark-gray;
+ color: $white;
font-weight: bold;
+ width: 80%;
+ height: 80%;
+ font-size: 100%;
+ transition: 0.2s ease all;
&:hover {
- background: $main-accent;
- transform: scale(1.05);
- cursor: pointer;
+ background-color: $black;
}
}
-.documentLinksButton-endLink {
- border: red solid 2px;
+.documentLinksButton.startLink {
+ background-color: $medium-blue;
+ color: $white;
+ font-weight: bold;
+ width: 80%;
+ height: 80%;
+ font-size: 100%;
+ transition: 0.2s ease all;
&:hover {
- background: deepskyblue;
- transform: scale(1.05);
- cursor: pointer;
+ background-color: $black;
}
}
-.documentLinksButton-startLink {
- border: red solid 2px;
- background-color: rgba(255, 192, 203, 0.5);
+.documentLinksButton-endLink {
+ border: $medium-blue 2px dashed;
+ color: $medium-blue;
+ background-color: none !important;
+ width: 80%;
+ height: 80%;
+ font-size: 100%;
+ transition: 0.2s ease all;
+
+ &:hover {
+ background-color: $light-blue;
+ }
} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index a6d07374a..b63174e54 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -20,6 +20,7 @@ import './DocumentLinksButton.scss';
import { DocServer } from "../../DocServer";
import { LightboxView } from "../LightboxView";
import { cat } from "shelljs";
+import { Colors } from "../global/globalEnums";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
@@ -30,12 +31,12 @@ interface DocumentLinksButtonProps {
Offset?: (number | undefined)[];
AlwaysOn?: boolean;
InMenu?: boolean;
- StartLink?: boolean;
+ StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed)
}
@observer
export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
private _linkButton = React.createRef<HTMLDivElement>();
- @observable public static StartLink: Doc | undefined;
+ @observable public static StartLink: Doc | undefined; //origin's Doc, if defined
@observable public static StartLinkView: DocumentView | undefined;
@observable public static AnnotationId: string | undefined;
@observable public static AnnotationUri: string | undefined;
@@ -45,6 +46,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
public static invisibleWebRef = React.createRef<HTMLDivElement>();
@action public static ClearLinkEditor() { DocumentLinksButton.LinkEditorDocView = undefined; }
+
@action @undoBatch
onLinkButtonMoved = (e: PointerEvent) => {
if (this.props.InMenu && this.props.StartLink) {
@@ -66,6 +68,40 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
return false;
}
+ onLinkMenuOpen = (e: React.PointerEvent): void => {
+ setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
+ if (doubleTap) {
+ const rootDoc = this.props.View.rootDoc;
+ const docid = Doc.CurrentUserEmail + Doc.GetProto(rootDoc)[Id] + "-pivotish";
+ DocServer.GetRefField(docid).then(async docx => {
+ const rootAlias = () => {
+ const rootAlias = Doc.MakeAlias(rootDoc);
+ rootAlias.x = rootAlias.y = 0;
+ return rootAlias;
+ };
+ let wid = rootDoc[WidthSym]();
+ const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([rootAlias()], { title: this.props.View.Document.title + "-pivot", _width: 500, _height: 500, }, docid);
+ const docs = await DocListCastAsync(Doc.GetProto(target).data);
+ if (!target.pivotFocusish) (Doc.GetProto(target).pivotFocusish = target);
+ DocListCast(rootDoc.links).forEach(link => {
+ const other = LinkManager.getOppositeAnchor(link, rootDoc);
+ const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other;
+ if (otherdoc && !docs?.some(d => Doc.AreProtosEqual(d, otherdoc))) {
+ const alias = Doc.MakeAlias(otherdoc);
+ alias.x = wid;
+ alias.y = 0;
+ alias._lockedPosition = false;
+ wid += otherdoc[WidthSym]();
+ Doc.AddDocToList(Doc.GetProto(target), "data", alias);
+ }
+ });
+ LightboxView.SetLightboxDoc(target);
+ });
+ }
+ else DocumentLinksButton.LinkEditorDocView = this.props.View;
+ }));
+ }
+
@undoBatch
onLinkButtonDown = (e: React.PointerEvent): void => {
setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
@@ -78,37 +114,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
DocumentLinksButton.StartLink = this.props.View.props.Document;
DocumentLinksButton.StartLinkView = this.props.View;
}
- } else if (!this.props.InMenu) {
- if (doubleTap) {
- const rootDoc = this.props.View.rootDoc;
- const docid = Doc.CurrentUserEmail + Doc.GetProto(rootDoc)[Id] + "-pivotish";
- DocServer.GetRefField(docid).then(async docx => {
- const rootAlias = () => {
- const rootAlias = Doc.MakeAlias(rootDoc);
- rootAlias.x = rootAlias.y = 0;
- return rootAlias;
- };
- let wid = rootDoc[WidthSym]();
- const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([rootAlias()], { title: this.props.View.Document.title + "-pivot", _width: 500, _height: 500, }, docid);
- const docs = await DocListCastAsync(Doc.GetProto(target).data);
- if (!target.pivotFocusish) (Doc.GetProto(target).pivotFocusish = target);
- DocListCast(rootDoc.links).forEach(link => {
- const other = LinkManager.getOppositeAnchor(link, rootDoc);
- const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other;
- if (otherdoc && !docs?.some(d => Doc.AreProtosEqual(d, otherdoc))) {
- const alias = Doc.MakeAlias(otherdoc);
- alias.x = wid;
- alias.y = 0;
- alias._lockedPosition = false;
- wid += otherdoc[WidthSym]();
- Doc.AddDocToList(Doc.GetProto(target), "data", alias);
- }
- });
- LightboxView.SetLightboxDoc(target);
- });
- }
- else DocumentLinksButton.LinkEditorDocView = this.props.View;
- }
+ };
}));
}
@@ -120,17 +126,17 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
DocumentLinksButton.StartLinkView = undefined;
- } else {
+ } else { //if this LinkButton's Document is undefined
DocumentLinksButton.StartLink = this.props.View.props.Document;
DocumentLinksButton.StartLinkView = this.props.View;
}
-
//action(() => Doc.BrushDoc(this.props.View.Document));
} else if (!this.props.InMenu) {
DocumentLinksButton.LinkEditorDocView = this.props.View;
}
}
+
completeLink = (e: React.PointerEvent): void => {
setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action((e, doubleTap) => {
if (doubleTap && !this.props.StartLink) {
@@ -141,7 +147,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
} else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
const sourceDoc = DocumentLinksButton.StartLink;
const targetDoc = this.props.View.ComponentView?.getAnchor?.() || this.props.View.Document;
- const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, "long drag");
+ const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, "links"); //why is long drag here when this is used for completing links by clicking?
LinkManager.currentLink = linkDoc;
@@ -184,7 +190,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
} else if (startLink !== endLink) {
endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink;
startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink;
- const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag", undefined, undefined, true);
+ const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "link", undefined, undefined, true);
LinkManager.currentLink = linkDoc;
@@ -192,7 +198,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
- const dashHyperlink = Utils.prepend("/doc/" + (startIsAnnotation ? endLink[Id] : startLink[Id]));
+ const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink);
Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId,
(startIsAnnotation ? startLink : endLink)); // edit annotation to add a Dash hyperlink to the linked doc
}
@@ -242,45 +248,50 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
return results;
}
+ /**
+ * gets the JSX of the link button (btn used to start/complete links) OR the link-view button (btn on bottom left of each linked node)
+ *
+ * todo:glr / anh seperate functionality such as onClick onPointerDown of link menu button
+ */
@computed get linkButtonInner() {
- const btnDim = this.props.InMenu ? "20px" : "30px";
+ const btnDim = "30px";
const link = <img style={{ width: "22px", height: "16px" }} src={`/assets/${"link.png"}`} />;
-
- return <div className="documentLinksButton-cont" ref={this._linkButton}
- style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }}
- >
- <div className={"documentLinksButton"}
- onPointerDown={this.onLinkButtonDown} onClick={this.onLinkClick}
- style={{
- backgroundColor: this.props.InMenu ? "" : "#add8e6",
- color: this.props.InMenu ? "white" : "black",
- width: btnDim,
- height: btnDim,
- }} >
- {this.props.InMenu ?
- this.props.StartLink ?
- <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />
- : link
- : Array.from(this.filteredLinks).length}
- </div>
- {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ?
- <div className={"documentLinksButton-endLink"}
+ const isActive = (DocumentLinksButton.StartLink === this.props.View.props.Document) && this.props.StartLink;
+ return (!this.props.InMenu ?
+ <div className="documentLinksButton-cont"
+ style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }}
+ >
+ <div className={"documentLinksButton"}
+ onPointerDown={this.onLinkMenuOpen} onClick={this.onLinkClick}
style={{
- width: btnDim, height: btnDim,
- backgroundColor: DocumentLinksButton.StartLink ? "" : "grey",
- opacity: DocumentLinksButton.StartLink ? "" : "50%",
- border: DocumentLinksButton.StartLink ? "" : "none",
- cursor: DocumentLinksButton.StartLink ? "pointer" : "default"
- }}
- onPointerDown={DocumentLinksButton.StartLink && this.completeLink}
- onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)} />
- : (null)
- }
- {DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.InMenu && this.props.StartLink ?
- <div className={"documentLinksButton-startLink"} onPointerDown={this.clearLinks} onClick={this.clearLinks} style={{ width: btnDim, height: btnDim }} />
- : (null)
- }
- </div >;
+ backgroundColor: Colors.LIGHT_BLUE,
+ color: Colors.BLACK,
+ width: btnDim,
+ height: btnDim,
+ }}>
+ {Array.from(this.filteredLinks).length}
+ </div>
+ </div>
+ :
+ <div className="documentLinksButton-menu" ref={this._linkButton}>
+ {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? //if the origin node is not this node
+ <div className={"documentLinksButton-endLink"}
+ onPointerDown={DocumentLinksButton.StartLink && this.completeLink}
+ onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
+ </div>
+ : (null)
+ }
+ {
+ this.props.InMenu && this.props.StartLink ? //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again
+ <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} onPointerDown={isActive ? undefined : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
+ </div>
+ :
+ (null)
+ }
+ </div>
+ )
}
render() {
@@ -290,6 +301,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
const buttonTitle = "Tap to view links; double tap to open link collection";
const title = this.props.InMenu ? menuTitle : buttonTitle;
+ //render circular tooltip if it isn't set to invisible and show the number of doc links the node has, and render inner-menu link button for starting/stopping links if currently in menu
return !Array.from(this.filteredLinks).length && !this.props.AlwaysOn ? (null) :
this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink) ?
<Tooltip title={<div className="dash-tooltip">{title}</div>}>
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index bdbece621..7f164ca48 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.documentView-effectsWrapper {
border-radius: inherit;
@@ -22,7 +22,7 @@
transition: outline .3s linear;
cursor: grab;
- // background: $light-color; //overflow: hidden;
+ // background: $white; //overflow: hidden;
transform-origin: left top;
&.minimized {
@@ -147,7 +147,7 @@
.documentView-titleWrapper,
.documentView-titleWrapper-hover {
overflow: hidden;
- color: white;
+ color: $black;
transform-origin: top left;
top: 0;
width: 100%;
@@ -218,6 +218,6 @@
.documentView-node:first-child {
position: relative;
- background: "#B59B66"; //$light-color;
+ background: "#B59B66"; //$white;
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a4645ed8d..f716eb7b1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -43,7 +43,7 @@ import { DocumentLinksButton } from './DocumentLinksButton';
import "./DocumentView.scss";
import { LinkAnchorBox } from './LinkAnchorBox';
import { LinkDocPreview } from "./LinkDocPreview";
-import { PresBox } from './PresBox';
+import { PresBox } from './trails/PresBox';
import { RadialMenu } from './RadialMenu';
import React = require("react");
import { ScriptingBox } from "./ScriptingBox";
@@ -746,7 +746,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
}
- moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
+ moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: "fingerprint" });
}
}
@@ -757,7 +757,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const help = cm.findByDescription("Help...");
const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
!Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
- helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
+ helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
!Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
!Doc.UserDoc().novice && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
@@ -884,7 +884,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
recorder.ondataavailable = async (e: any) => {
const [{ result }] = await Networking.UploadFilesToServer(e.data);
if (!(result instanceof Error)) {
- const audioDoc = Docs.Create.AudioDocument(Utils.prepend(result.accessPaths.agnostic.client), { title: "audio test", _width: 200, _height: 32 });
+ const audioDoc = Docs.Create.AudioDocument(result.accessPaths.agnostic.client, { title: "audio test", _width: 200, _height: 32 });
audioDoc.treeViewExpandedView = "layout";
const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + "-audioAnnotations"], listSpec(Doc));
if (audioAnnos === undefined) {
@@ -970,7 +970,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
const highlightColor = (CurrentUserUtils.ActiveDashboard?.darkScheme ?
["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
- ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"])[highlightIndex];
+ ["transparent", "#4476F7", "#4476F7", "yellow", "magenta", "cyan", "orange"])[highlightIndex];
const highlightStyle = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"][highlightIndex];
const excludeTypes = !this.props.treeViewDoc ? [DocumentType.FONTICON, DocumentType.INK] : [DocumentType.FONTICON];
let highlighting = !this.props.disableDocBrushing && highlightIndex && !excludeTypes.includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear;
@@ -978,7 +978,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const borderPath = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath) || { path: undefined };
const internal = PresBox.EffectsProvider(this.layoutDoc, this.renderDoc) || this.renderDoc;
- const boxShadow = highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
+ const boxShadow = this.props.treeViewDoc ? null : highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
this.boxShadow || (this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined);
return <div className={DocumentView.ROOT_DIV} ref={this._mainCont}
onContextMenu={this.onContextMenu}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 86250c9d1..ebbc1138a 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -64,9 +64,9 @@ export class FieldView extends React.Component<FieldViewProps> {
// else if (field instaceof PresBox) {
// return <PresBox {...this.props} />;
// }
- else if (field instanceof VideoField) {
- return <VideoBox {...this.props} />;
- }
+ // else if (field instanceof VideoField) {
+ // return <VideoBox {...this.props} />;
+ // }
// else if (field instanceof AudioField) {
// return <AudioBox {...this.props} />;
//}
diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss
index 33ac85a0e..718af2c16 100644
--- a/src/client/views/nodes/FontIconBox.scss
+++ b/src/client/views/nodes/FontIconBox.scss
@@ -1,5 +1,7 @@
+@import "../global/globalCssVariables";
+
.fontIconBox-label {
- color: white;
+ color: $white;
margin-right: 4px;
margin-top: 1px;
position: relative;
@@ -22,8 +24,8 @@
position: absolute;
top: -10px;
right: -10px;
- color: white;
- background: #f44b42;
+ color: $white;
+ background: $pink;
font-weight: 300;
border-radius: 100%;
width: 25px;
@@ -37,7 +39,7 @@
.menuButton-circle,
.menuButton-round {
border-radius: 100%;
- background-color: black;
+ background-color: $dark-gray;
padding: 0;
.fontIconBox-label {
@@ -47,13 +49,14 @@
}
&:hover {
- background-color: #aaaaa3;
+ background-color: $light-gray;
}
}
.menuButton-square {
padding-top: 3px;
padding-bottom: 3px;
+ background-color: $dark-gray;
.fontIconBox-label {
border-radius: 0px;
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 6ae4b9726..0d415e238 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -14,6 +14,7 @@ import { DocComponent } from '../DocComponent';
import { StyleProp } from '../StyleProvider';
import { FieldView, FieldViewProps } from './FieldView';
import './FontIconBox.scss';
+import { Colors } from '../global/globalEnums';
const FontIconSchema = createSchema({
icon: "string",
});
@@ -47,7 +48,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
const icon = StrCast(this.dataDoc.icon, "user") as any;
const presSize = shape === 'round' ? 25 : 30;
const presTrailsIcon = <img src={`/assets/${"presTrails.png"}`}
- style={{ width: presSize, height: presSize, filter: `invert(${color === "white" ? "100%" : "0%"})`, marginBottom: "5px" }} />;
+ style={{ width: presSize, height: presSize, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})`, marginBottom: "5px" }} />;
const button = <button className={`menuButton-${shape}`} onContextMenu={this.specificContextMenu}
style={{ backgroundColor: backgroundColor, }}>
<div className="menuButton-wrap">
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d876ae818..2c0106960 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -238,7 +238,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
let succeeded = true;
let data: ImageField | undefined;
try {
- data = new ImageField(Utils.prepend(accessPaths.agnostic.client));
+ data = new ImageField(accessPaths.agnostic.client);
} catch {
succeeded = false;
}
@@ -340,7 +340,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@action
finishMarquee = () => {
this._marqueeing = undefined;
- this.props.select(false)
+ this.props.select(false);
}
render() {
diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss
index eb7c2f32b..ffcba4981 100644
--- a/src/client/views/nodes/KeyValueBox.scss
+++ b/src/client/views/nodes/KeyValueBox.scss
@@ -1,10 +1,10 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.keyValueBox-cont {
overflow-y: scroll;
width:100%;
height: 100%;
- background-color: $light-color;
- border: 1px solid $intermediate-color;
+ background-color: $white;
+ border: 1px solid $medium-gray;
border-radius: $border-radius;
box-sizing: border-box;
display: inline-block;
@@ -56,8 +56,8 @@ $header-height: 30px;
width:100%;
position: relative;
display: inline-block;
- background: $intermediate-color;
- color: $light-color;
+ background: $medium-gray;
+ color: $white;
text-transform: uppercase;
letter-spacing: 2px;
font-size: 12px;
@@ -66,7 +66,7 @@ $header-height: 30px;
th {
font-weight: normal;
&:first-child {
- border-right: 1px solid $light-color;
+ border-right: 1px solid $white;
}
}
}
@@ -76,9 +76,9 @@ $header-height: 30px;
display: flex;
width:100%;
height:$header-height;
- background: $light-color;
+ background: $white;
.formattedTextBox-cont {
- background: $light-color;
+ background: $white;
}
}
.keyValueBox-cont {
@@ -116,8 +116,8 @@ $header-height: 30px;
display: flex;
width:100%;
height:30px;
- background: $light-color-secondary;
+ background: $light-gray;
.formattedTextBox-cont {
- background: $light-color-secondary;
+ background: $light-gray;
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss
index f78767234..5b660e582 100644
--- a/src/client/views/nodes/KeyValuePair.scss
+++ b/src/client/views/nodes/KeyValuePair.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.keyValuePair-td-key {
diff --git a/src/client/views/nodes/LinkDescriptionPopup.scss b/src/client/views/nodes/LinkDescriptionPopup.scss
index d92823ccc..a8db5d360 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.scss
+++ b/src/client/views/nodes/LinkDescriptionPopup.scss
@@ -1,9 +1,13 @@
+@import "../global/globalCssVariables.scss";
+
.linkDescriptionPopup {
display: flex;
-
- border: 1px solid rgb(170, 26, 26);
-
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ border: 2px solid $medium-blue;
+ background-color: $white;
width: auto;
position: absolute;
@@ -11,17 +15,11 @@
z-index: 10000;
border-radius: 10px;
font-size: 12px;
- //white-space: nowrap;
-
- background-color: rgba(250, 250, 250, 0.95);
- padding-top: 9px;
- padding-bottom: 9px;
- padding-left: 9px;
- padding-right: 9px;
+ gap: 5px;
+ padding: 9px;
.linkDescriptionPopup-input {
float: left;
- background-color: rgba(250, 250, 250, 0.95);
color: rgb(100, 100, 100);
border: none;
min-width: 160px;
@@ -30,46 +28,29 @@
.linkDescriptionPopup-btn {
float: right;
-
justify-content: center;
vertical-align: middle;
-
.linkDescriptionPopup-btn-dismiss {
- background-color: white;
- color: black;
+ cursor: pointer;
display: inline;
- right: 0;
- border-radius: 10px;
- border: 1px solid black;
- padding: 3px;
- font-size: 9px;
- text-align: center;
- position: relative;
- margin-right: 4px;
- justify-content: center;
-
- &:hover{
- cursor: pointer;
- }
+ white-space: nowrap;
+ padding: 5px;
+ vertical-align: middle;
+ background-color: $close-red;
+ border-radius: 3px;
+ color: black;
}
.linkDescriptionPopup-btn-add {
- background-color: black;
- color: white;
+ cursor: pointer;
display: inline;
- right: 0;
- border-radius: 10px;
- border: 1px solid black;
- padding: 3px;
- font-size: 9px;
- text-align: center;
- position: relative;
- justify-content: center;
-
- &:hover{
- cursor: pointer;
- }
+ white-space: nowrap;
+ padding: 5px;
+ vertical-align: middle;
+ background-color: $light-blue;
+ border-radius: 3px;
+ color: black;
}
}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index b73fb10df..126a37eb8 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -72,14 +72,14 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
@computed get href() {
if (this.props.hrefs?.length) {
const href = this.props.hrefs[this._hrefInd];
- if (href.indexOf(Utils.prepend("/doc/")) !== 0) { // link to a web page URL -- try to show a preview
+ if (href.indexOf(Doc.localServerPath()) !== 0) { // link to a web page URL -- try to show a preview
if (href.startsWith("https://en.wikipedia.org/wiki/")) {
wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500))));
} else {
setTimeout(action(() => this._toolTipText = "url => " + href));
}
} else { // hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated
- const anchorDoc = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+ const anchorDoc = href.replace(Doc.localServerPath(), "").split("?")[0];
anchorDoc && DocServer.GetRefField(anchorDoc).then(action(anchor => {
if (anchor instanceof Doc && DocListCast(anchor.links).length) {
this._linkDoc = DocListCast(anchor.links)[0];
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 0f46da294..72dec6e4c 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -7,7 +7,7 @@
overflow: hidden;
cursor: auto;
transform-origin: top left;
- z-index: 0;
+ //z-index: 0;
.pdfBox-ui {
position: absolute;
@@ -30,6 +30,7 @@
justify-content: center;
border-radius: 3px;
pointer-events: all;
+ z-index: 1; // so it appears on top of the document's title, if shown
}
.pdfBox-pageNums {
@@ -223,7 +224,7 @@
.pdfBox {
width: 100%;
height: 100%;
- pointer-events: none;
+ //pointer-events: none;
.pdfViewerDash-text {
.textLayer {
display: none;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 8f61e252b..0b451e2b4 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -53,30 +53,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) runInAction(() => this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href));
else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action(pdf => this._pdf = pdf));
}
-
- const backup = "oldPath";
- const href = this.pdfUrl?.url.href;
- if (href) {
- 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(this.props.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?.(); }
diff --git a/src/client/views/nodes/RadialMenu.scss b/src/client/views/nodes/RadialMenu.scss
index daa620d12..312b51013 100644
--- a/src/client/views/nodes/RadialMenu.scss
+++ b/src/client/views/nodes/RadialMenu.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.radialMenu-cont {
position: absolute;
@@ -53,7 +53,7 @@ s
transition: all .1s;
border-width: .11px;
border-style: none;
- border-color: $intermediate-color; // rgb(187, 186, 186);
+ border-color: $medium-gray; // rgb(187, 186, 186);
// padding: 10px 0px 10px 0px;
white-space: nowrap;
font-size: 13px;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 700f8a7d3..0e235a62d 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -227,7 +227,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
this._audioRec.onstop = async (e: any) => {
const [{ result }] = await Networking.UploadFilesToServer(aud_chunks);
if (!(result instanceof Error)) {
- this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client));
+ this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(result.accessPaths.agnostic.client);
}
};
this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
@@ -244,7 +244,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
this.layoutDoc._fitWidth = undefined;
- this.dataDoc[this.props.fieldKey] = new VideoField(Utils.prepend(result.accessPaths.agnostic.client));
+ this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
} else alert("video conversion failed");
};
this._audioRec.start();
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index fc08a2302..ce45c01e6 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -75,10 +75,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined) || this.rootDoc;
}
- choosePath(url: string) {
- return url.indexOf(window.location.origin) === -1 ? Utils.CorsProxy(url) : url;
- }
-
videoLoad = () => {
const aspect = this.player!.videoWidth / this.player!.videoHeight;
Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth);
@@ -182,8 +178,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
}
- private createRealSummaryLink = (relative: string, downX?: number, downY?: number) => {
- const url = this.choosePath(Utils.prepend(relative));
+ private createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
+ const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
const width = this.layoutDoc._width || 1;
const height = this.layoutDoc._height || 0;
const imageSummary = Docs.Create.ImageDocument(url, {
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index ca82c049c..19b69ff5a 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables.scss";
+@import "../global/globalCssVariables.scss";
.webBox {
@@ -17,6 +17,7 @@
justify-content: center;
border-radius: 3px;
pointer-events: all;
+ z-index: 1; // so it appears on top of the document's title, if shown
}
.pdfViewerDash-dragAnnotationBox {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 88e38712a..f5b1f96f2 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -162,7 +162,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
scrollFocus = (doc: Doc, smooth: boolean) => {
if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
if (doc !== this.rootDoc && this._outerRef.current) {
- const scrollTo = doc.type === DocumentType.TEXTANCHOR ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
+ const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
+ const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * .1);
if (scrollTo !== undefined) {
const focusSpeed = smooth ? 500 : 0;
this._initialScroll !== undefined && (this._initialScroll = scrollTo);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 53aceb533..3cedab1a4 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -1,4 +1,4 @@
-@import "../../globalCssVariables";
+@import "../../global/globalCssVariables";
.ProseMirror {
width: 100%;
@@ -31,7 +31,7 @@ audiotag:hover {
padding: 0;
border-width: 0px;
border-radius: inherit;
- border-color: $intermediate-color;
+ border-color: $medium-gray;
box-sizing: border-box;
background-color: inherit;
border-style: solid;
@@ -363,7 +363,7 @@ footnote::after {
@media only screen and (max-width: 1000px) {
- @import "../../globalCssVariables";
+ @import "../../global/globalCssVariables";
.ProseMirror {
width: 100%;
@@ -381,7 +381,7 @@ footnote::after {
padding: 0;
border-width: 0px;
border-radius: inherit;
- border-color: $intermediate-color;
+ border-color: $medium-gray;
box-sizing: border-box;
background-color: inherit;
border-style: solid;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 95d8f555c..f7e9ee028 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -71,6 +71,7 @@ export interface FormattedTextBoxProps {
xPadding?: number; // used to override document's settings for xMargin --- see CollectionCarouselView
yPadding?: number;
noSidebar?: boolean;
+ dontScale?: boolean;
dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded (and mark as not being associated with scrollTop document field)
}
export const GoogleRef = "googleDocId";
@@ -126,7 +127,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
@computed get scrollHeight() { return NumCast(this.rootDoc[this.fieldKey + "-scrollHeight"]); }
@computed get sidebarHeight() { return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + "-height"]); }
@computed get titleHeight() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0; }
- @computed get autoHeightMargins() { return this.titleHeight + (this.layoutDoc._autoHeightMargins && !this.props.dontSelectOnLoad ? NumCast(this.layoutDoc._autoHeightMargins) : 0); }
+ @computed get autoHeightMargins() { return this.titleHeight + NumCast(this.layoutDoc._autoHeightMargins); }
@computed get _recording() { return this.dataDoc?.mediaState === "recording"; }
set _recording(value) {
!this.dataDoc.recordingSource && (this.dataDoc.mediaState = value ? "recording" : undefined);
@@ -214,6 +215,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
AnchorMenu.Instance.Status = "marquee";
AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => {
this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView.state, this._editorView?.dispatch);
+ console.log("highlight")
return undefined;
});
/**
@@ -369,7 +371,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
const anchor = Docs.Create.TextanchorDocument();
const alink = DocUtils.MakeLink({ doc: anchor }, { doc: target }, "automatic")!;
- const allAnchors = [{ href: Utils.prepend("/doc/" + anchor[Id]), title: "a link", anchorId: anchor[Id] }];
+ const allAnchors = [{ href: Doc.localServerPath(anchor), title: "a link", anchorId: anchor[Id] }];
const link = this._editorView!.state.schema.marks.linkAnchor.create({ allAnchors, title: "auto link", location });
tr = tr.addMark(flattened[i].from, flattened[i].to, link);
});
@@ -703,7 +705,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
let tr = state.tr.addMark(sel.from, sel.to, splitter);
if (sel.from !== sel.to) {
const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: this._editorView?.state.doc.textBetween(sel.from, sel.to) });
- const href = targetHref ?? Utils.prepend("/doc/" + anchor[Id]);
+ const href = targetHref ?? Doc.localServerPath(anchor);
if (anchor !== anchorDoc) this.addDocument(anchor);
tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
@@ -786,7 +788,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
componentDidMount() {
- this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
+ !this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
this._cachedLinks = DocListCast(this.Document.links);
this._disposers.breakupDictation = reaction(() => DocumentManager.Instance.RecordingEvent, this.breakupDictation);
this._disposers.autoHeight = reaction(() => this.autoHeight, autoHeight => autoHeight && this.tryUpdateScrollHeight());
@@ -1040,7 +1042,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
const marks = [...node.marks];
const linkIndex = marks.findIndex(mark => mark.type.name === "link");
- const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }];
+ const allLinks = [{ href: Doc.globalServerPath(linkId), title, linkId }];
const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "add:right", title, docref: true });
marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link);
return node.mark(marks);
@@ -1524,10 +1526,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
<div className="formattedTextBox-cont"
onWheel={e => this.isContentActive() && e.stopPropagation()}
style={{
- transform: `scale(${scale})`,
- transformOrigin: "top left",
- width: `${100 / scale}%`,
- height: `${100 / scale}%`,
+ transform: this.props.dontScale ? undefined : `scale(${scale})`,
+ transformOrigin: this.props.dontScale ? undefined : "top left",
+ width: this.props.dontScale ? undefined : `${100 / scale}%`,
+ height: this.props.dontScale ? undefined : `${100 / scale}%`,
// overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined,
...this.styleFromLayoutString(scale) // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
}}>
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.scss b/src/client/views/nodes/formattedText/RichTextMenu.scss
index 1d24d6833..c94e93541 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.scss
+++ b/src/client/views/nodes/formattedText/RichTextMenu.scss
@@ -1,4 +1,4 @@
-@import "../../globalCssVariables";
+@import "../../global/globalCssVariables";
.button-dropdown-wrapper {
position: relative;
@@ -24,7 +24,7 @@
top: 35px;
left: 0;
background-color: #323232;
- color: $light-color-secondary;
+ color: $light-gray;
border: 1px solid #4d4d4d;
border-radius: 0 6px 6px 6px;
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 59b2d3753..82ad2b7db 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -821,8 +821,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
if (link) {
const href = link.attrs.allAnchors.length > 0 ? link.attrs.allAnchors[0].href : undefined;
if (href) {
- if (href.indexOf(Utils.prepend("/doc/")) === 0) {
- const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+ if (href.indexOf(Doc.localServerPath()) === 0) {
+ const linkclicked = href.replace(Doc.localServerPath(), "").split("?")[0];
if (linkclicked) {
const linkDoc = await DocServer.GetRefField(linkclicked);
if (linkDoc instanceof Doc) {
@@ -852,6 +852,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@undoBatch
makeLinkToURL = (target: string, lcoation: string) => {
((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, "onRadd:rightight", target, target);
+ console.log((this.view as any)?.TextView);
}
@undoBatch
@@ -863,8 +864,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const allAnchors = linkAnchor.attrs.allAnchors.slice();
this.TextView.RemoveAnchorFromSelection(allAnchors);
// bcz: Argh ... this will remove the link from the document even it's anchored somewhere else in the text which happens if only part of the anchor text was selected.
- allAnchors.filter((aref: any) => aref?.href.indexOf(Utils.prepend("/doc/")) === 0).forEach((aref: any) => {
- const anchorId = aref.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+ allAnchors.filter((aref: any) => aref?.href.indexOf(Doc.localServerPath()) === 0).forEach((aref: any) => {
+ const anchorId = aref.href.replace(Doc.localServerPath(), "").split("?")[0];
anchorId && DocServer.GetRefField(anchorId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc));
});
}
@@ -963,7 +964,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.createHighlighterButton(),
this.createLinkButton(),
this.createBrushButton(),
- <div className="richTextMenu-divider" key="divider 2" />,
+ <div className="collectionMenu-divider" key="divider 2" />,
this.createButton("align-left", "Align Left", this.activeAlignment === "left", this.alignLeft),
this.createButton("align-center", "Align Center", this.activeAlignment === "center", this.alignCenter),
this.createButton("align-right", "Align Right", this.activeAlignment === "right", this.alignRight),
@@ -976,7 +977,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const row2 = <div className="antimodeMenu-row row-2" key="row2">
{this.collapsed ? this.getDragger() : (null)}
<div key="row 2" style={{ display: this.collapsed ? "none" : undefined }}>
- <div className="richTextMenu-divider" key="divider 3" />
+ <div className="collectionMenu-divider" key="divider 3" />
{[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => {
this.activeFontSize = val;
SelectionManager.Views().map(dv => dv.props.Document._fontSize = val);
@@ -985,12 +986,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.activeFontFamily = val;
SelectionManager.Views().map(dv => dv.props.Document._fontFamily = val);
})),
- <div className="richTextMenu-divider" key="divider 4" />,
+ <div className="collectionMenu-divider" key="divider 4" />,
this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})),
this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer),
this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),
- this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule),
- <div className="richTextMenu-divider" key="divider 5" />,]}
+ this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule)
+ ]}
</div>
{/* <div key="collapser">
{<div key="collapser">
diff --git a/src/client/views/nodes/formattedText/TooltipTextMenu.scss b/src/client/views/nodes/formattedText/TooltipTextMenu.scss
index 0e4b752ac..8c4d77da9 100644
--- a/src/client/views/nodes/formattedText/TooltipTextMenu.scss
+++ b/src/client/views/nodes/formattedText/TooltipTextMenu.scss
@@ -1,4 +1,4 @@
-@import "../views/globalCssVariables";
+@import "../views/global/globalCssVariables";
.ProseMirror-menu-dropdown-wrap {
display: inline-block;
position: relative;
@@ -50,7 +50,7 @@
padding: 3px;
&:hover {
- background-color: $light-color-secondary;
+ background-color: $light-gray;
}
}
}
@@ -294,9 +294,9 @@
top: 31px;
background-color: #323232;
border: 1px solid #4d4d4d;
- color: $light-color-secondary;
+ color: $light-gray;
// border: none;
- // border: 1px solid $light-color-secondary;
+ // border: 1px solid $light-gray;
border-radius: 0 6px 6px 6px;
padding: 3px;
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
@@ -323,7 +323,7 @@
}
.separated-button {
- border-top: 1px solid $light-color-secondary;
+ border-top: 1px solid $light-gray;
padding-top: 6px;
}
diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss
index 1ba86232b..06932d145 100644
--- a/src/client/views/nodes/PresBox.scss
+++ b/src/client/views/nodes/trails/PresBox.scss
@@ -1,7 +1,4 @@
-$light-blue: #AEDDF8;
-$dark-blue: #5B9FDD;
-$light-background: #ececec;
-$dark-grey: #656565;
+@import "../../global/globalCssVariables";
.presBox-cont {
cursor: auto;
@@ -10,7 +7,6 @@ $dark-grey: #656565;
pointer-events: inherit;
z-index: 2;
font-family: Roboto;
- box-shadow: #AAAAAA .2vw .2vw .4vw;
width: 100%;
min-width: 20px;
height: 100%;
@@ -47,8 +43,8 @@ $dark-grey: #656565;
align-items: center;
height: 30px;
width: 100%;
- color: white;
- background-color: #323232;
+ color: $white;
+ background-color: $dark-gray;
.toolbar-button {
cursor: pointer;
@@ -110,7 +106,7 @@ $dark-grey: #656565;
}
.toolbar-divider {
- border-left: solid #ffffff70 0.5px;
+ border-left: solid $medium-gray 0.5px;
height: 20px;
}
}
@@ -118,7 +114,7 @@ $dark-grey: #656565;
.dropdown {
font-size: 10;
margin-left: 5px;
- color: darkgrey;
+ color: $medium-gray;
transition: 0.5s ease;
}
@@ -174,7 +170,7 @@ $dark-grey: #656565;
.ribbon-colorBox {
cursor: pointer;
- border: solid 1px black;
+ border: solid 1px $black;
display: flex;
margin-left: 5px;
margin-top: 5px;
@@ -191,9 +187,9 @@ $dark-grey: #656565;
font-size: 11;
font-weight: 200;
height: 20;
- background-color: #ececec;
- color: black;
- border: solid 1px black;
+ background-color: $white;
+ color: $black;
+ border: solid 1px $black;
display: flex;
margin-left: 5px;
margin-top: 5px;
@@ -220,11 +216,11 @@ $dark-grey: #656565;
align-items: center;
height: 100%;
width: 100%;
- background: black;
+ background: $black;
}
.ribbon-propertyUpDownItem:hover {
- background: darkgrey;
+ background: $medium-gray;
transform: scale(1.05);
}
}
@@ -239,7 +235,7 @@ $dark-grey: #656565;
.multiThumb-slider {
display: grid;
- background-color: $light-background;
+ background-color: $white;
height: 10px;
border-radius: 10px;
overflow: hidden;
@@ -257,8 +253,8 @@ $dark-grey: #656565;
-webkit-appearance: none;
height: 10px;
cursor: ew-resize;
- background: $dark-blue;
- box-shadow: -100vw 0 0 100vw $light-background;
+ background: $medium-blue;
+ box-shadow: -100vw 0 0 100vw $white;
}
.toolbar-slider.end::-webkit-slider-thumb {
@@ -267,7 +263,7 @@ $dark-grey: #656565;
-webkit-appearance: none;
height: 10px;
cursor: ew-resize;
- background: $dark-blue;
+ background: $medium-blue;
box-shadow: -100vw 0 0 100vw $light-blue;
}
}
@@ -282,7 +278,7 @@ $dark-grey: #656565;
height: 10px;
border-radius: 10px;
-webkit-appearance: none;
- background-color: $light-background;
+ background-color: $white;
}
.toolbar-slider:focus {
@@ -301,7 +297,7 @@ $dark-grey: #656565;
-webkit-appearance: none;
height: 10px;
cursor: ew-resize;
- background: $dark-blue;
+ background: $medium-blue;
box-shadow: -100vw 0 0 100vw $light-blue;
}
@@ -318,7 +314,7 @@ $dark-grey: #656565;
width: 15px;
min-width: 15px;
cursor: pointer;
- background: $light-background;
+ background: $white;
}
.presBox-checkbox:focus {
@@ -326,7 +322,7 @@ $dark-grey: #656565;
}
.presBox-checkbox:hover {
- background: #c0c0c0;
+ background: $light-gray;
}
.presBox-checkbox:checked {
@@ -381,9 +377,9 @@ $dark-grey: #656565;
text-align: center;
font-size: 16;
width: 90%;
- color: black;
+ color: $black;
transform: translate(5%, 0px);
- border-bottom: solid 2px darkgrey;
+ border-bottom: solid 2px $medium-gray;
}
@@ -396,8 +392,8 @@ $dark-grey: #656565;
justify-self: left;
margin-top: 5px;
padding-left: 10px;
- background-color: $light-background;
- border: solid 1px black;
+ background-color: $white;
+ border: solid 1px $black;
min-width: 80px;
max-width: 200px;
width: 100%;
@@ -416,7 +412,7 @@ $dark-grey: #656565;
}
.ribbon-frameSelector {
- border: black solid 1px;
+ border: $black solid 1px;
width: 60px;
height: 20px;
margin-top: 5px;
@@ -433,12 +429,12 @@ $dark-grey: #656565;
cursor: pointer;
position: relative;
height: 100%;
- background: $light-background;
+ background: $white;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
- color: black;
+ color: $black;
}
.numKeyframe {
@@ -446,7 +442,7 @@ $dark-grey: #656565;
font-size: 10;
font-weight: 600;
position: relative;
- color: black;
+ color: $black;
display: flex;
width: 100%;
height: 100%;
@@ -489,7 +485,7 @@ $dark-grey: #656565;
padding-left: 10;
padding-right: 10;
border-radius: 10px;
- background-color: #979797;
+ background-color: $medium-gray;
}
.ribbon-final-button:hover {
@@ -508,13 +504,13 @@ $dark-grey: #656565;
align-items: center;
margin-bottom: 5px;
height: 25px;
- color: lightgrey;
+ color: $light-gray;
width: 100%;
max-width: 120;
padding-left: 10;
padding-right: 10;
border-radius: 10px;
- background-color: black;
+ background-color: $black;
}
.ribbon-final-button-hidden:hover {
@@ -525,15 +521,15 @@ $dark-grey: #656565;
.ribbon-frameList {
width: calc(100% - 5px);
height: 50px;
- background-color: #ececec;
- border: 1px solid #9f9f9f;
+ background-color: $white;
+ border: 1px solid $medium-gray;
grid-template-rows: max-content;
.frameList-header {
display: grid;
width: 100%;
height: 20px;
- background-color: #9f9f9f;
+ background-color: $medium-gray;
.frameList-headerButtons {
display: flex;
@@ -588,7 +584,7 @@ $dark-grey: #656565;
font-size: 10.5;
font-weight: 300;
height: 20;
- background-color: #979797;
+ background-color: $medium-gray;
color: white;
display: flex;
margin-top: 5px;
@@ -607,8 +603,8 @@ $dark-grey: #656565;
transition: all 0.4s;
font-weight: 400;
opacity: 1;
- color: white;
- background-color: black;
+ color: $white;
+ background-color: $black;
}
.ribbon-toggle {
@@ -616,10 +612,10 @@ $dark-grey: #656565;
font-size: 10.5;
font-weight: 200;
height: 20;
- background-color: $light-background;
+ background-color: $white;
border: solid 1px rgba(0, 0, 0, 0.5);
display: flex;
- color: black;
+ color: $black;
margin-top: 5px;
margin-bottom: 5px;
border-radius: 5px;
@@ -660,13 +656,13 @@ $dark-grey: #656565;
position: relative;
font-size: 13;
padding-bottom: 10px;
- border-bottom: solid 1px $dark-grey;
+ border-bottom: solid 1px $dark-gray;
.presBox-dropdown:hover {
- border: solid 1px $dark-blue;
+ border: solid 1px $medium-blue;
.presBox-dropdownIcon {
- color: $dark-blue;
+ color: $medium-blue;
}
}
@@ -675,12 +671,12 @@ $dark-grey: #656565;
display: grid;
grid-template-columns: auto 20%;
position: relative;
- border: solid 1px black;
- background-color: $light-background;
+ border: solid 1px $black;
+ background-color: $light-gray;
border-radius: 5px;
font-size: 10;
height: 25;
- color: black;
+ color: $black;
padding-left: 5px;
align-items: center;
margin-top: 5px;
@@ -744,7 +740,7 @@ $dark-grey: #656565;
height: 100px;
padding-top: 5px;
padding-bottom: 5px;
- border: solid 1px black;
+ border: solid 1px $black;
// overflow: auto;
::-webkit-scrollbar {
@@ -794,7 +790,7 @@ $dark-grey: #656565;
cursor: pointer;
position: relative;
text-align: center;
- border-left: solid 1px darkgrey;
+ border-left: solid 1px $medium-gray;
width: 20%;
height: 100%;
display: flex;
@@ -825,7 +821,7 @@ $dark-grey: #656565;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.8);
z-index: 200;
background-color: white;
- color: black;
+ color: $black;
position: absolute;
overflow: hidden;
}
@@ -841,12 +837,12 @@ $dark-grey: #656565;
align-items: center;
justify-content: center;
transform: translate(0px, -1px);
- background-color: $light-background;
+ background-color: $white;
width: 40px;
height: 15px;
align-self: center;
justify-self: center;
- border: solid 1px black;
+ border: solid 1px $black;
border-top: 0px;
border-bottom-right-radius: 7px;
border-bottom-left-radius: 7px;
@@ -855,15 +851,15 @@ $dark-grey: #656565;
.layout-container {
padding: 5px;
display: grid;
- background-color: $light-background;
+ background-color: $white;
grid-template-columns: repeat(auto-fit, minmax(90px, 100px));
width: 100%;
- border: solid 1px black;
+ border: solid 1px $black;
min-width: 100px;
overflow: hidden;
.layout:hover {
- border: solid 2px #5c9edd;
+ border: solid 2px $medium-blue;
}
.layout {
@@ -878,7 +874,7 @@ $dark-grey: #656565;
width: 90px;
overflow: hidden;
background-color: white;
- border: solid darkgrey 1px;
+ border: solid $medium-gray 1px;
display: grid;
grid-template-rows: auto;
align-items: center;
@@ -893,7 +889,7 @@ $dark-grey: #656565;
height: 13;
font-size: 12;
display: flex;
- background-color: #f1efec;
+ background-color: $white;
}
.subtitle {
@@ -906,7 +902,7 @@ $dark-grey: #656565;
height: 13;
font-size: 9;
display: flex;
- background-color: #f1efec;
+ background-color: $white;
}
.content {
@@ -919,7 +915,7 @@ $dark-grey: #656565;
height: 13;
font-size: 10;
display: flex;
- background-color: #f1efec;
+ background-color: $white;
height: 33;
text-align: left;
font-size: 8px;
@@ -930,7 +926,7 @@ $dark-grey: #656565;
.presBox-buttons {
position: relative;
width: 100%;
- background: gray;
+ background: $medium-gray;
min-height: 35px;
padding-top: 5px;
padding-bottom: 5px;
@@ -960,8 +956,8 @@ $dark-grey: #656565;
select {
- background: #323232;
- color: white;
+ background: $dark-gray;
+ color: $white;
}
.presBox-button {
@@ -975,8 +971,8 @@ $dark-grey: #656565;
text-align: center;
letter-spacing: normal;
width: inherit;
- background: #323232;
- color: white;
+ background: $dark-gray;
+ color: $white;
}
.presBox-button.active {
@@ -984,7 +980,7 @@ $dark-grey: #656565;
}
.presBox-button.active:hover {
- background-color: #233163;
+ background-color: $medium-blue;
}
.presBox-button.edit {
@@ -1053,8 +1049,8 @@ $dark-grey: #656565;
font-size: 100;
display: flex;
align-items: center;
- background: #323232;
- color: white;
+ background: $dark-gray;
+ color: $white;
}
.presBox-viewPicker {
@@ -1086,7 +1082,7 @@ $dark-grey: #656565;
top: 10;
opacity: 0.1;
transition: all 0.4s;
- color: white;
+ color: $white;
}
.miniPres:hover {
@@ -1094,8 +1090,8 @@ $dark-grey: #656565;
}
.presPanelOverlay {
- background-color: #323232;
- color: white;
+ background-color: $dark-gray;
+ color: $white;
border-radius: 5px;
grid-template-rows: 100%;
height: 25;
@@ -1129,7 +1125,7 @@ $dark-grey: #656565;
.presPanel-divider {
width: 0.5px;
height: 80%;
- border-right: solid 1px #5a5a5a;
+ border-right: solid 1px $medium-gray;
}
.presPanel-button-frame {
@@ -1161,12 +1157,12 @@ $dark-grey: #656565;
}
.presPanel-button:hover {
- background-color: #5a5a5a;
+ background-color: $medium-gray;
transform: scale(1.2);
}
.presPanel-button-text:hover {
- background-color: #5a5a5a;
+ background-color: $medium-gray;
}
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index f3fb6ff17..5cb9866f8 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -5,67 +5,33 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
import { observer } from "mobx-react";
import { ColorState, SketchPicker } from "react-color";
import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
-import { Doc, DocListCast, DocListCastAsync, FieldResult } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
-import { InkTool } from "../../../fields/InkField";
-import { List } from "../../../fields/List";
-import { PrefetchProxy } from "../../../fields/Proxy";
-import { listSpec, makeInterface } from "../../../fields/Schema";
-import { ScriptField } from "../../../fields/ScriptField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { returnFalse, returnOne, returnTrue, emptyFunction } from '../../../Utils';
-import { Docs } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DocumentManager } from "../../util/DocumentManager";
-import { Scripting } from "../../util/Scripting";
-import { SelectionManager } from "../../util/SelectionManager";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionView, CollectionViewType } from "../collections/CollectionView";
-import { TabDocView } from "../collections/TabDocView";
-import { ViewBoxBaseComponent } from "../DocComponent";
-import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
-import { FieldView, FieldViewProps } from './FieldView';
+import { Doc, DocListCast, DocListCastAsync, FieldResult } from "../../../../fields/Doc";
+import { documentSchema } from "../../../../fields/documentSchemas";
+import { InkTool } from "../../../../fields/InkField";
+import { List } from "../../../../fields/List";
+import { PrefetchProxy } from "../../../../fields/Proxy";
+import { listSpec, makeInterface } from "../../../../fields/Schema";
+import { ScriptField } from "../../../../fields/ScriptField";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
+import { emptyFunction, returnFalse, returnOne, returnTrue } from '../../../../Utils';
+import { Docs } from "../../../documents/Documents";
+import { DocumentType } from "../../../documents/DocumentTypes";
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
+import { DocumentManager } from "../../../util/DocumentManager";
+import { Scripting } from "../../../util/Scripting";
+import { SelectionManager } from "../../../util/SelectionManager";
+import { undoBatch, UndoManager } from "../../../util/UndoManager";
+import { CollectionDockingView } from "../../collections/CollectionDockingView";
+import { CollectionView, CollectionViewType } from "../../collections/CollectionView";
+import { TabDocView } from "../../collections/TabDocView";
+import { ViewBoxBaseComponent } from "../../DocComponent";
+import { Colors } from "../../global/globalEnums";
+import { LightboxView } from "../../LightboxView";
+import { CollectionFreeFormDocumentView } from "../CollectionFreeFormDocumentView";
+import { FieldView, FieldViewProps } from '../FieldView';
import "./PresBox.scss";
import Color = require("color");
-import { LightboxView } from "../LightboxView";
-
-export enum PresMovement {
- Zoom = "zoom",
- Pan = "pan",
- Jump = "jump",
- None = "none",
-}
-
-export enum PresEffect {
- Zoom = "Zoom",
- Lightspeed = "Lightspeed",
- Fade = "Fade in",
- Flip = "Flip",
- Rotate = "Rotate",
- Bounce = "Bounce",
- Roll = "Roll",
- None = "None",
- Left = "left",
- Right = "right",
- Center = "center",
- Top = "top",
- Bottom = "bottom"
-}
-
-enum PresStatus {
- Autoplay = "auto",
- Manual = "manual",
- Edit = "edit"
-}
-
-export enum PresColor {
- LightBlue = "#AEDDF8",
- DarkBlue = "#5B9FDD",
- LightBackground = "#ececec",
- SlideBackground = "#d5dce2",
-}
+import { PresEffect, PresStatus, PresMovement } from "./PresEnums";
export class PinProps {
audioRange?: boolean;
@@ -1198,9 +1164,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
{this.scrollable ? "Scroll to pinned view" : !isPinWithView ? "No movement" : "Pan & Zoom to pinned view"}
</div>
:
- <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${PresColor.DarkBlue}` : 'solid 1px black' }}>
+ <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
{this.setMovementName(activeItem.presMovement, activeItem)}
- <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? PresColor.DarkBlue : 'black' }} icon={"angle-down"} />
+ <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={"angle-down"} />
<div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? "grid" : "none" }}>
<div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None)}>None</div>
<div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom)}>Pan {"&"} Zoom</div>
@@ -1245,7 +1211,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-doubleButton">
{isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideBefore ? "active" : ""}`} onClick={() => this.updateHideBefore(activeItem)}>Hide before</div></Tooltip>}
{isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideAfter ? "active" : ""}`} onClick={() => this.updateHideAfter(activeItem)}>Hide after</div></Tooltip>}
- <Tooltip title={<><div className="dash-tooltip">{"Open in lightbox view"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? PresColor.LightBlue : "" }} onClick={() => this.updateOpenDoc(activeItem)}>Lightbox</div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Open in lightbox view"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? Colors.LIGHT_BLUE : "" }} onClick={() => this.updateOpenDoc(activeItem)}>Lightbox</div></Tooltip>
</div>
{(type === DocumentType.AUDIO || type === DocumentType.VID) ? (null) : <>
<div className="ribbon-doubleButton" >
@@ -1280,9 +1246,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
{isPresCollection ? (null) : <div className="ribbon-box">
Effects
- <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openEffectDropdown = !this.openEffectDropdown; })} style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${PresColor.DarkBlue}` : 'solid 1px black' }}>
+ <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openEffectDropdown = !this.openEffectDropdown; })} style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
{effect}
- <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openEffectDropdown ? PresColor.DarkBlue : 'black' }} icon={"angle-down"} />
+ <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={"angle-down"} />
<div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this.openEffectDropdown ? "grid" : "none" }} onPointerDown={e => e.stopPropagation()}>
<div className={`presBox-dropdownOption ${targetDoc.presEffect === PresEffect.None || !targetDoc.presEffect ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.None)}>None</div>
<div className={`presBox-dropdownOption ${targetDoc.presEffect === PresEffect.Fade ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Fade)}>Fade In</div>
@@ -1299,11 +1265,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
</div>
<div className="effectDirection" style={{ display: effect === 'None' ? "none" : "grid", width: 40 }}>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from left"}</div></>}><div style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Left ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Left)}><FontAwesomeIcon icon={"angle-right"} /></div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from right"}</div></>}><div style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Right ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Right)}><FontAwesomeIcon icon={"angle-left"} /></div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from top"}</div></>}><div style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Top ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Top)}><FontAwesomeIcon icon={"angle-down"} /></div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from bottom"}</div></>}><div style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Bottom ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Bottom)}><FontAwesomeIcon icon={"angle-up"} /></div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from center"}</div></>}><div style={{ gridColumn: 2, gridRow: 2, width: 10, height: 10, alignSelf: 'center', justifySelf: 'center', border: targetDoc.presEffectDirection === PresEffect.Center || !targetDoc.presEffectDirection ? `solid 2px ${PresColor.LightBlue}` : "solid 2px black", borderRadius: "100%", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Center)}></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from left"}</div></>}><div style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Left ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Left)}><FontAwesomeIcon icon={"angle-right"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from right"}</div></>}><div style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Right ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Right)}><FontAwesomeIcon icon={"angle-left"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from top"}</div></>}><div style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Top ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Top)}><FontAwesomeIcon icon={"angle-down"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from bottom"}</div></>}><div style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Bottom ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Bottom)}><FontAwesomeIcon icon={"angle-up"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from center"}</div></>}><div style={{ gridColumn: 2, gridRow: 2, width: 10, height: 10, alignSelf: 'center', justifySelf: 'center', border: targetDoc.presEffectDirection === PresEffect.Center || !targetDoc.presEffectDirection ? `solid 2px ${Colors.LIGHT_BLUE}` : "solid 2px black", borderRadius: "100%", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Center)}></div></Tooltip>
</div>
</div>}
<div className="ribbon-final-box">
@@ -1356,7 +1322,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<>
{this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : (null)}
<div className="ribbon-doubleButton">
- <Tooltip title={<><div className="dash-tooltip">{activeItem.presPinView ? "Turn off pin with view" : "Turn on pin with view"}</div></>}><div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? PresColor.LightBlue : "" }}
+ <Tooltip title={<><div className="dash-tooltip">{activeItem.presPinView ? "Turn off pin with view" : "Turn on pin with view"}</div></>}><div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? Colors.LIGHT_BLUE : "" }}
onClick={() => {
activeItem.presPinView = !activeItem.presPinView;
targetDoc.presPinView = activeItem.presPinView;
@@ -1496,7 +1462,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="slider-text" style={{ fontWeight: 500 }}>
Start time (s)
</div>
- <div id={"startTime"} className="slider-number" style={{ backgroundColor: PresColor.LightBackground }}>
+ <div id={"startTime"} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
<input className="presBox-input"
style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }}
type="number" value={NumCast(activeItem.presStartTime)}
@@ -1508,7 +1474,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="slider-text" style={{ fontWeight: 500 }}>
Duration (s)
</div>
- <div className="slider-number" style={{ backgroundColor: PresColor.LightBlue }}>
+ <div className="slider-number" style={{ backgroundColor: Colors.LIGHT_BLUE }}>
{Math.round((NumCast(activeItem.presEndTime) - NumCast(activeItem.presStartTime)) * 10) / 10}
</div>
</div>
@@ -1516,7 +1482,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="slider-text" style={{ fontWeight: 500 }}>
End time (s)
</div>
- <div id={"endTime"} className="slider-number" style={{ backgroundColor: PresColor.LightBackground }}>
+ <div id={"endTime"} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
<input className="presBox-input"
style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }}
type="number" value={NumCast(activeItem.presEndTime)}
@@ -1534,16 +1500,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this._batch = UndoManager.StartBatch("presEndTime");
const endBlock = document.getElementById("endTime");
if (endBlock) {
- endBlock.style.color = PresColor.LightBackground;
- endBlock.style.backgroundColor = PresColor.DarkBlue;
+ endBlock.style.color = Colors.LIGHT_GRAY;
+ endBlock.style.backgroundColor = Colors.MEDIUM_BLUE;
}
}}
onPointerUp={() => {
this._batch?.end();
const endBlock = document.getElementById("endTime");
if (endBlock) {
- endBlock.style.color = "black";
- endBlock.style.backgroundColor = PresColor.LightBackground;
+ endBlock.style.color = Colors.BLACK;
+ endBlock.style.backgroundColor = Colors.LIGHT_GRAY;
}
}}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
@@ -1558,16 +1524,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this._batch = UndoManager.StartBatch("presStartTime");
const startBlock = document.getElementById("startTime");
if (startBlock) {
- startBlock.style.color = PresColor.LightBackground;
- startBlock.style.backgroundColor = PresColor.DarkBlue;
+ startBlock.style.color = Colors.LIGHT_GRAY;
+ startBlock.style.backgroundColor = Colors.MEDIUM_BLUE;
}
}}
onPointerUp={() => {
this._batch?.end();
const startBlock = document.getElementById("startTime");
if (startBlock) {
- startBlock.style.color = "black";
- startBlock.style.backgroundColor = PresColor.LightBackground;
+ startBlock.style.color = Colors.BLACK;
+ startBlock.style.backgroundColor = Colors.LIGHT_GRAY;
}
}}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
@@ -1651,15 +1617,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div>
<div className={'presBox-toolbar-dropdown'} style={{ display: this.newDocumentTools && this.layoutDoc.presStatus === "edit" ? "inline-flex" : "none" }} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
<div className="layout-container" style={{ height: 'max-content' }}>
- <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'blank'; this.createNewSlide(this.layout); })} />
- <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'title'; this.createNewSlide(this.layout); })}>
+ <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'blank'; this.createNewSlide(this.layout); })} />
+ <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'title'; this.createNewSlide(this.layout); })}>
<div className="title">Title</div>
<div className="subtitle">Subtitle</div>
</div>
- <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'header'; this.createNewSlide(this.layout); })}>
+ <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'header'; this.createNewSlide(this.layout); })}>
<div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div>
</div>
- <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'content'; this.createNewSlide(this.layout); })}>
+ <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'content'; this.createNewSlide(this.layout); })}>
<div className="title" style={{ alignSelf: 'center' }}>Title</div>
<div className="content">Text goes here</div>
</div>
@@ -1691,26 +1657,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-box">
Choose type:
<div className="ribbon-doubleButton">
- <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "" : PresColor.LightBlue }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div>
- <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? PresColor.LightBlue : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div>
+ <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "" : Colors.LIGHT_BLUE }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div>
+ <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? Colors.LIGHT_BLUE : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div>
</div>
</div>
<div className="ribbon-box" style={{ display: this.addFreeform ? "grid" : "none" }}>
Preset layouts:
<div className="layout-container" style={{ height: this.openLayouts ? 'max-content' : '75px' }}>
- <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'blank')} />
- <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'title')}>
+ <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'blank')} />
+ <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'title')}>
<div className="title">Title</div>
<div className="subtitle">Subtitle</div>
</div>
- <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'header')}>
+ <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'header')}>
<div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div>
</div>
- <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'content')}>
+ <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'content')}>
<div className="title" style={{ alignSelf: 'center' }}>Title</div>
<div className="content">Text goes here</div>
</div>
- <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'twoColumns')}>
+ <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'twoColumns')}>
<div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>Title</div>
<div className="content" style={{ gridColumn: 1, gridRow: 2 }}>Column one text</div>
<div className="content" style={{ gridColumn: 2, gridRow: 2 }}>Column two text</div>
@@ -1869,8 +1835,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-box">
{this.stringType} selected
<div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
- <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? PresColor.LightBlue : "" }} onClick={this.progressivizeChild}>Contents</div>
- <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? PresColor.LightBlue : "" }} onClick={this.editProgressivize}>Edit</div>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeChild}>Contents</div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editProgressivize}>Edit</div>
</div>
<div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}>
<div className="presBox-subheading">Active text color</div>
@@ -1885,12 +1851,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
{this.viewedColorPicker}
<div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}>
- <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? PresColor.LightBlue : "" }} onClick={this.progressivizeZoom}>Zoom</div>
- <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? PresColor.LightBlue : "" }} onClick={this.editZoomProgressivize}>Edit</div>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeZoom}>Zoom</div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editZoomProgressivize}>Edit</div>
</div>
<div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
- <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? PresColor.LightBlue : "" }} onClick={this.progressivizeScroll}>Scroll</div>
- <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? PresColor.LightBlue : "" }} onClick={this.editScrollProgressivize}>Edit</div>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeScroll}>Scroll</div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editScrollProgressivize}>Edit</div>
</div>
</div>
<div className="ribbon-final-box">
@@ -1900,7 +1866,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div key="back" title="back frame" className="backKeyframe" onClick={e => { e.stopPropagation(); this.prevKeyframe(targetDoc, activeItem); }}>
<FontAwesomeIcon icon={"caret-left"} size={"lg"} />
</div>
- <div key="num" title="toggle view all" className="numKeyframe" style={{ color: targetDoc.keyFrameEditing ? "white" : "black", backgroundColor: targetDoc.keyFrameEditing ? PresColor.DarkBlue : PresColor.LightBlue }}
+ <div key="num" title="toggle view all" className="numKeyframe" style={{ color: targetDoc.keyFrameEditing ? "white" : "black", backgroundColor: targetDoc.keyFrameEditing ? Colors.MEDIUM_BLUE : Colors.LIGHT_BLUE }}
onClick={action(() => targetDoc.keyFrameEditing = !targetDoc.keyFrameEditing)} >
{NumCast(targetDoc._currentFrame)}
</div>
@@ -1914,7 +1880,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
{this.frameListHeader}
{this.frameList}
</div>
- <div className="ribbon-toggle" style={{ height: 20, backgroundColor: PresColor.LightBlue }} onClick={() => console.log(" TODO: play frames")}>Play</div>
+ <div className="ribbon-toggle" style={{ height: 20, backgroundColor: Colors.LIGHT_BLUE }} onClick={() => console.log(" TODO: play frames")}>Play</div>
</div>
</div>
</div>
@@ -2130,7 +2096,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? "block" : "none" }}>{this.checkMovementLists(doc, doc["x-indexed"], doc["y-indexed"])}</div>);
}
tags.push(
- <div className="progressivizeButton" key={index} onPointerLeave={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? PresColor.LightBlue : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}>
+ <div className="progressivizeButton" key={index} onPointerLeave={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? Colors.LIGHT_BLUE : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}>
<div className="progressivizeButton-prev"><FontAwesomeIcon icon={"caret-left"} size={"lg"} onClick={e => { e.stopPropagation(); this.prevAppearFrame(doc, index); }} /></div>
<div className="progressivizeButton-frame">{doc.appearFrame}</div>
<div className="progressivizeButton-next"><FontAwesomeIcon icon={"caret-right"} size={"lg"} onClick={e => { e.stopPropagation(); this.nextAppearFrame(doc, index); }} /></div>
@@ -2213,6 +2179,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
const isMini: boolean = this.toolbarWidth <= 100;
const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation);
+ const activeColor = Colors.LIGHT_BLUE;
+ const inactiveColor = Colors.WHITE;
return (mode === CollectionViewType.Carousel3D) ? (null) : (
<div id="toolbarContainer" className={'presBox-toolbar'}>
{/* <Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}>
@@ -2220,7 +2188,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<FontAwesomeIcon className={`dropdown ${this.newDocumentTools ? "active" : ""}`} icon={"angle-down"} />
</div></Tooltip> */}
<Tooltip title={<><div className="dash-tooltip">{"View paths"}</div></>}>
- <div style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? PresColor.DarkBlue : 'white', width: isMini ? "100%" : undefined }} className={"toolbar-button"} onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}>
+ <div style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? "100%" : undefined }} className={"toolbar-button"} onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}>
<FontAwesomeIcon icon={"exchange-alt"} />
</div>
</Tooltip>
@@ -2229,7 +2197,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="toolbar-divider" />
{/* <Tooltip title={<><div className="dash-tooltip">{this._expandBoolean ? "Minimize all" : "Expand all"}</div></>}>
<div className={"toolbar-button"}
- style={{ color: this._expandBoolean ? PresColors.DarkBlue : 'white' }}
+ style={{ color: this._expandBoolean ? Colors.MEDIUM_BLUE : 'white' }}
onClick={this.toggleExpandMode}>
<FontAwesomeIcon icon={"eye"} />
</div>
@@ -2237,12 +2205,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="toolbar-divider" /> */}
<Tooltip title={<><div className="dash-tooltip">{presKeyEvents ? "Keys are active" : "Keys are not active - click anywhere on the presentation trail to activate keys"}</div></>}>
<div className="toolbar-button" style={{ cursor: presKeyEvents ? 'default' : 'pointer', position: 'absolute', right: 30, fontSize: 16 }}>
- <FontAwesomeIcon className={"toolbar-thumbtack"} icon={"keyboard"} style={{ color: presKeyEvents ? PresColor.DarkBlue : 'white' }} />
+ <FontAwesomeIcon className={"toolbar-thumbtack"} icon={"keyboard"} style={{ color: presKeyEvents ? activeColor : inactiveColor }} />
</div>
</Tooltip>
<Tooltip title={<><div className="dash-tooltip">{propTitle}</div></>}>
<div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}>
- <FontAwesomeIcon className={"toolbar-thumbtack"} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? PresColor.DarkBlue : 'white' }} />
+ <FontAwesomeIcon className={"toolbar-thumbtack"} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? activeColor : inactiveColor }} />
</div>
</Tooltip>
</>
@@ -2379,7 +2347,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
// Case 1: There are still other frames and should go through all frames before going to next slide
return (<div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}>
- <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? PresColor.DarkBlue : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
<div className="presPanel-divider"></div>
<div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onClick={() => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-left"} /></div>
<Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? "pause" : "play"} /></div></Tooltip>
@@ -2418,8 +2386,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
return CurrentUserUtils.OverlayDocs.includes(this.rootDoc) ?
<div className="miniPres">
- <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + PresColor.DarkBlue : undefined }}>
- <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? PresColor.DarkBlue : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
+ <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
+ <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
<div className="presPanel-divider"></div>
<div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onClick={() => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-left"} /></div>
<Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip>
diff --git a/src/client/views/presentationview/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss
index 1ad4b820e..1ad4b820e 100644
--- a/src/client/views/presentationview/PresElementBox.scss
+++ b/src/client/views/nodes/trails/PresElementBox.scss
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index f15d51764..5e713c3cf 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -2,27 +2,29 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tooltip } from "@material-ui/core";
import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
import { observer } from "mobx-react";
-import { DataSym, Doc, Opt } from "../../../fields/Doc";
-import { documentSchema } from '../../../fields/documentSchemas';
-import { Id } from "../../../fields/FieldSymbols";
-import { createSchema, makeInterface } from '../../../fields/Schema';
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, emptyPath, returnEmptyDoclist } from "../../../Utils";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DocumentManager } from "../../util/DocumentManager";
-import { DragManager } from "../../util/DragManager";
-import { Transform } from "../../util/Transform";
-import { undoBatch } from "../../util/UndoManager";
-import { ViewBoxBaseComponent } from '../DocComponent';
-import { EditableView } from "../EditableView";
-import { DocumentView, DocumentViewProps } from "../nodes/DocumentView";
-import { FieldView, FieldViewProps } from '../nodes/FieldView';
-import { PresBox, PresColor, PresMovement } from "../nodes/PresBox";
-import { StyleProp } from "../StyleProvider";
+import { DataSym, Doc, Opt } from "../../../../fields/Doc";
+import { documentSchema } from '../../../../fields/documentSchemas';
+import { Id } from "../../../../fields/FieldSymbols";
+import { createSchema, makeInterface } from '../../../../fields/Schema';
+import { Cast, NumCast, StrCast } from "../../../../fields/Types";
+import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, emptyPath, returnEmptyDoclist } from "../../../../Utils";
+import { DocumentType } from "../../../documents/DocumentTypes";
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
+import { DocumentManager } from "../../../util/DocumentManager";
+import { DragManager } from "../../../util/DragManager";
+import { Transform } from "../../../util/Transform";
+import { undoBatch } from "../../../util/UndoManager";
+import { ViewBoxBaseComponent } from '../../DocComponent';
+import { EditableView } from "../../EditableView";
+import { DocumentView, DocumentViewProps } from "../../nodes/DocumentView";
+import { FieldView, FieldViewProps } from '../../nodes/FieldView';
+import { PresBox } from "./PresBox";
+import { Colors } from "../../global/globalEnums";
+import { StyleProp } from "../../StyleProvider";
import "./PresElementBox.scss";
import React = require("react");
-import { DocUtils } from "../../documents/Documents";
+import { DocUtils } from "../../../documents/Documents";
+import { PresMovement } from "./PresEnums";
export const presSchema = createSchema({
presentationTargetDoc: Doc,
@@ -210,11 +212,11 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
const height = slide.clientHeight;
const halfLine = height / 2;
if (y <= halfLine) {
- slide.style.borderTop = "solid 2px #5B9FDD";
+ slide.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
slide.style.borderBottom = "0px";
} else if (y > halfLine) {
slide.style.borderTop = "0px";
- slide.style.borderBottom = "solid 2px #5B9FDD";
+ slide.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
}
}
document.removeEventListener("pointermove", this.onPointerMove);
@@ -292,7 +294,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
const miniView: boolean = this.toolbarWidth <= 110;
const presBox: Doc = this.presBox; //presBox
const presBoxColor: string = StrCast(presBox._backgroundColor);
- const presColorBool: boolean = presBoxColor ? (presBoxColor !== "white" && presBoxColor !== "transparent") : false;
+ const presColorBool: boolean = presBoxColor ? (presBoxColor !== Colors.WHITE && presBoxColor !== "transparent") : false;
const targetDoc: Doc = this.targetDoc;
const activeItem: Doc = this.rootDoc;
return (
@@ -300,7 +302,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
key={this.props.Document[Id] + this.indexInPres}
ref={this._itemRef}
style={{
- backgroundColor: presColorBool ? isSelected ? "rgba(250,250,250,0.3)" : "transparent" : isSelected ? "#AEDDF8" : "transparent",
+ backgroundColor: presColorBool ? isSelected ? "rgba(250,250,250,0.3)" : "transparent" : isSelected ? Colors.LIGHT_BLUE : "transparent",
opacity: this._dragging ? 0.3 : 1
}}
onClick={e => {
@@ -356,7 +358,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
style={{
zIndex: 1000 - this.indexInPres,
fontWeight: 700,
- backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : PresColor.DarkBlue : undefined,
+ backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : Colors.MEDIUM_BLUE : undefined,
height: activeItem.groupWithUp ? 53 : 18,
transform: activeItem.groupWithUp ? "translate(0, -17px)" : undefined
}}>
diff --git a/src/client/views/nodes/trails/PresEnums.ts b/src/client/views/nodes/trails/PresEnums.ts
new file mode 100644
index 000000000..93ab323fb
--- /dev/null
+++ b/src/client/views/nodes/trails/PresEnums.ts
@@ -0,0 +1,28 @@
+export enum PresMovement {
+ Zoom = "zoom",
+ Pan = "pan",
+ Jump = "jump",
+ None = "none",
+}
+
+export enum PresEffect {
+ Zoom = "Zoom",
+ Lightspeed = "Lightspeed",
+ Fade = "Fade in",
+ Flip = "Flip",
+ Rotate = "Rotate",
+ Bounce = "Bounce",
+ Roll = "Roll",
+ None = "None",
+ Left = "left",
+ Right = "right",
+ Center = "center",
+ Top = "top",
+ Bottom = "bottom"
+}
+
+export enum PresStatus {
+ Autoplay = "auto",
+ Manual = "manual",
+ Edit = "edit"
+} \ No newline at end of file
diff --git a/src/client/views/nodes/trails/index.ts b/src/client/views/nodes/trails/index.ts
new file mode 100644
index 000000000..8f3f7b03a
--- /dev/null
+++ b/src/client/views/nodes/trails/index.ts
@@ -0,0 +1,3 @@
+export * from "./PresBox";
+export * from "./PresElementBox";
+export * from "./PresEnums"; \ No newline at end of file
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index c24c4eaaf..55816ed52 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -10,6 +10,7 @@ import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu";
import { ButtonDropdown } from "../nodes/formattedText/RichTextMenu";
import "./AnchorMenu.scss";
import { SelectionManager } from "../../util/SelectionManager";
+import { LinkPopup } from "../linking/LinkPopup";
@observer
export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -38,6 +39,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable private _valueValue: string = "";
@observable private _added: boolean = false;
@observable private highlightColor: string = "rgba(245, 230, 95, 0.616)";
+ @observable private _showLinkPopup: boolean = false;
@observable public _colorBtn = false;
@observable public Highlighting: boolean = false;
@@ -80,6 +82,13 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
}
}
+ @action
+ toggleLinkPopup = (e: React.MouseEvent) => {
+ //ignore the potential null type error because this method cannot be called unless the user selects text and clicks the link button
+ //change popup visibility field to visible
+ this._showLinkPopup = !this._showLinkPopup;
+ }
+
@computed get highlighter() {
const button =
<button className="antimodeMenu-button color-preview-button" title="" key="highlighter-button" onClick={this.highlightClicked}>
@@ -136,6 +145,14 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
<FontAwesomeIcon icon="comment-alt" size="lg" />
</button>
</Tooltip>,
+
+ //NOTE: link popup is currently incomplete
+ // <Tooltip key="link" title={<div className="dash-tooltip">{"Link selected text to document or URL"}</div>}>
+ // <button className="antimodeMenu-button link" onPointerDown={this.toggleLinkPopup} style={{}}>
+ // <FontAwesomeIcon icon="link" size="lg" />
+ // </button>
+ // </Tooltip>,
+ // <LinkPopup showPopup={this._showLinkPopup} />
] : [
<Tooltip key="trash" title={<div className="dash-tooltip">{"Remove Link Anchor"}</div>}>
<button className="antimodeMenu-button" onPointerDown={this.Delete}>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 4a50dccf3..e8c7a4ab0 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -184,7 +184,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
const mainCont = this._mainCont.current;
let focusSpeed: Opt<number>;
if (doc !== this.props.rootDoc && mainCont && this._pdfViewer) {
- const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
+ const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
+ const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight);
if (scrollTo !== undefined) {
focusSpeed = 500;
diff --git a/src/client/views/search/CheckBox.scss b/src/client/views/search/CheckBox.scss
index cc858bec6..2a0085ade 100644
--- a/src/client/views/search/CheckBox.scss
+++ b/src/client/views/search/CheckBox.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.checkboxfilter {
display: flex;
@@ -13,7 +13,7 @@
margin-top: 0px;
.check-container:hover~.check-box {
- background-color: $darker-alt-accent;
+ background-color: $medium-blue;
}
.check-container {
@@ -40,7 +40,7 @@
overflow: visible;
background-color: transparent;
border-style: solid;
- border-color: $alt-accent;
+ border-color: $medium-gray;
border-width: 2px;
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
diff --git a/src/client/views/search/CollectionFilters.scss b/src/client/views/search/CollectionFilters.scss
index b54cdcbd1..845b16f67 100644
--- a/src/client/views/search/CollectionFilters.scss
+++ b/src/client/views/search/CollectionFilters.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.collection-filters {
display: flex;
diff --git a/src/client/views/search/IconBar.scss b/src/client/views/search/IconBar.scss
index 013dcd57e..6aaf7918d 100644
--- a/src/client/views/search/IconBar.scss
+++ b/src/client/views/search/IconBar.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.icon-bar {
display: flex;
diff --git a/src/client/views/search/IconButton.scss b/src/client/views/search/IconButton.scss
index 4ec03c7c9..3cb08d756 100644
--- a/src/client/views/search/IconButton.scss
+++ b/src/client/views/search/IconButton.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.type-outer {
display: flex;
@@ -9,7 +9,7 @@
.type-icon {
height: 30px;
width: 30px;
- color: $light-color;
+ color: $white;
// background-color: rgb(194, 194, 197);
background-color: gray;
border-radius: 50%;
@@ -43,7 +43,7 @@
.type-icon:hover {
transform: scale(1.1);
- background-color: $darker-alt-accent;
+ background-color: $medium-blue;
opacity: 1;
+.filter-description {
diff --git a/src/client/views/search/IconButton.tsx b/src/client/views/search/IconButton.tsx
index 349690b20..2dd6b1b79 100644
--- a/src/client/views/search/IconButton.tsx
+++ b/src/client/views/search/IconButton.tsx
@@ -4,7 +4,7 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from 'mo
import { observer } from 'mobx-react';
import * as React from 'react';
import { DocumentType } from "../../documents/DocumentTypes";
-import '../globalCssVariables.scss';
+import '../global/globalCssVariables.scss';
import { IconBar } from './IconBar';
import "./IconButton.scss";
import "./SearchBox.scss";
@@ -104,7 +104,7 @@ export class IconButton extends React.Component<IconButtonProps>{
hoverStyle = {
opacity: 1,
backgroundColor: "rgb(128, 128, 128)"
- //backgroundColor: "rgb(178, 206, 248)" //$darker-alt-accent
+ //backgroundColor: "rgb(178, 206, 248)" //$medium-blue
};
render() {
diff --git a/src/client/views/search/NaviconButton.scss b/src/client/views/search/NaviconButton.scss
index c23bab461..8a70b29de 100644
--- a/src/client/views/search/NaviconButton.scss
+++ b/src/client/views/search/NaviconButton.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
$height-icon: 15px;
$width-line: 30px;
@@ -20,7 +20,7 @@ $translateX: 0;
.line {
display: block;
- background: $alt-accent;
+ background: $medium-gray;
width: $width-line;
height: $height-line;
position: absolute;
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index 4f5b7e41a..6a2fe6f19 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
@import "./NaviconButton.scss";
.searchBox-container {
@@ -20,7 +20,7 @@
display: flex;
justify-content: center;
align-items: center;
- background-color: black;
+ background-color: $dark-gray;
.searchBox-lozenges {
position: absolute;
@@ -86,7 +86,7 @@
&.searchBox-input {
margin:5px;
border-radius:20px;
- border:black;
+ border:$dark-gray;
display: block;
width: 130px;
-webkit-transition: width 0.4s;
@@ -114,7 +114,7 @@
}
&.searchBox-close {
- color: $light-color;
+ color: $white;
max-height: $searchpanel-height;
}
}
@@ -132,7 +132,7 @@
.no-result {
width: 500px;
- background: $light-color-secondary;
+ background: $light-gray;
padding: 10px;
height: 50px;
text-transform: uppercase;
diff --git a/src/client/views/search/SelectorContextMenu.scss b/src/client/views/search/SelectorContextMenu.scss
index 48cacc608..a114f679c 100644
--- a/src/client/views/search/SelectorContextMenu.scss
+++ b/src/client/views/search/SelectorContextMenu.scss
@@ -1,7 +1,7 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.parents {
- background: $lighter-alt-accent;
+ background: $light-blue;
padding: 10px;
// width: 300px;
@@ -10,7 +10,7 @@
}
.collection {
- border-color: $darker-alt-accent;
+ border-color: $medium-blue;
border-bottom-style: solid;
}
} \ No newline at end of file
diff --git a/src/client/views/search/ToggleBar.scss b/src/client/views/search/ToggleBar.scss
index 79f866acb..3a164f133 100644
--- a/src/client/views/search/ToggleBar.scss
+++ b/src/client/views/search/ToggleBar.scss
@@ -1,9 +1,9 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.toggle-title {
display: flex;
align-items: center;
- color: $light-color;
+ color: $white;
text-transform: uppercase;
flex-direction: row;
justify-content: space-around;
@@ -25,7 +25,7 @@
// height: 50px;
height: 30px;
width: 100px;
- background-color: $alt-accent;
+ background-color: $medium-gray;
border-radius: 10px;
padding: 5px;
display: flex;
@@ -36,6 +36,6 @@
width: 40px;
height: 100%;
border-radius: 10px;
- background-color: $light-color;
+ background-color: $white;
}
} \ No newline at end of file
diff --git a/src/client/views/topbar/TopBar.scss b/src/client/views/topbar/TopBar.scss
new file mode 100644
index 000000000..164cc29cd
--- /dev/null
+++ b/src/client/views/topbar/TopBar.scss
@@ -0,0 +1,217 @@
+@import "../global/globalCssVariables";
+
+.topbar-container {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ position: relative;
+ font-size: 10px;
+ line-height: 1;
+ overflow-y: auto;
+ overflow-x: visible;
+ background: $dark-gray;
+ overflow: visible;
+ z-index: 1000;
+
+ .topbar-bar {
+ height: $topbar-height;
+ display: grid;
+ grid-auto-columns: 33.3% 33.3% 33.3%;
+ align-items: center;
+ background-color: $dark-gray;
+
+ .topBar-icon {
+ cursor: pointer;
+ font-size: 12px;
+ font-family: 'Roboto';
+ width: fit-content;
+ display: flex;
+ justify-content: center;
+ gap: 4px;
+ align-items: center;
+ justify-self: center;
+ align-self: center;
+ border-radius: 5px;
+ padding: 5px;
+ transition: linear 0.1s;
+ color: $black;
+ background-color: $light-gray;
+ }
+
+ .topBar-icon:hover {
+ background-color: $light-blue;
+ }
+
+
+ .topbar-center {
+ grid-column: 2;
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ gap: 5px;
+
+ .topbar-dashboards {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .topbar-lozenge-dashboard {
+ display: flex;
+
+
+
+ .topbar-dashSelect {
+ border: none;
+ background-color: $dark-gray;
+ color: $white;
+ font-family: 'Roboto';
+ font-size: 17;
+ font-weight: 500;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+ }
+
+
+ .topbar-right {
+ grid-column: 3;
+ position: relative;
+ display: flex;
+ justify-content: flex-end;
+ gap: 5px;
+ margin-right: 5px;
+ }
+
+ .topbar-left {
+ grid-column: 1;
+ color: black;
+ font-family: 'Roboto';
+ position: relative;
+ display: flex;
+ width: 450;
+ gap: 5px;
+
+ .topBar-icon:hover {
+ background-color: $close-red;
+ }
+
+ .topbar-lozenge-user,
+ .topbar-lozenge {
+ height: 23;
+ font-size: 12;
+ color: white;
+ font-family: 'Roboto';
+ font-weight: 400;
+ padding: 4px;
+ align-self: center;
+ margin-left: 7px;
+ display: flex;
+ align-items: center;
+
+ .topbar-dashSelect {
+ border: none;
+ background-color: transparent;
+ color: black;
+ font-family: 'Roboto';
+ font-size: 17;
+ font-weight: 500;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+
+ .topbar-logoff {
+ border-radius: 3px;
+ background: olivedrab;
+ color: white;
+ display: none;
+ margin-left: 5px;
+ padding: 1px 2px 1px 2px;
+ cursor: pointer;
+ }
+
+ .topbar-logoff {
+ background: red;
+ }
+
+ .topbar-lozenge-user:hover {
+ .topbar-logoff {
+ display: inline-block;
+ }
+ }
+ }
+
+ .topbar-barChild {
+
+ &.topbar-collection {
+ flex: 0 1 auto;
+ margin-left: 2px;
+ margin-right: 2px
+ }
+
+ &.topbar-input {
+ margin:5px;
+ border-radius:20px;
+ border:$dark-gray;
+ display: block;
+ width: 130px;
+ -webkit-transition: width 0.4s;
+ transition: width 0.4s;
+ /* align-self: stretch; */
+ outline: none;
+
+ &:focus {
+ width: 500px;
+ outline: none;
+ }
+ }
+
+ &.topbar-filter {
+ align-self: stretch;
+
+ button {
+ transform: none;
+
+ &:hover {
+ transform: none;
+ }
+ }
+ }
+
+ &.topbar-submit {
+ margin-left: 2px;
+ margin-right: 2px
+ }
+
+ &.topbar-close {
+ color: $white;
+ max-height: $topbar-height;
+ }
+ }
+ }
+}
+
+.topbar-results {
+ display: flex;
+ flex-direction: column;
+ top: 300px;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ overflow: visible;
+
+ .no-result {
+ width: 500px;
+ background: $light-gray;
+ padding: 10px;
+ height: 50px;
+ text-transform: uppercase;
+ text-align: left;
+ font-weight: bold;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
new file mode 100644
index 000000000..05edb975c
--- /dev/null
+++ b/src/client/views/topbar/TopBar.tsx
@@ -0,0 +1,66 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { observer } from "mobx-react";
+import * as React from 'react';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { StrCast } from '../../../fields/Types';
+import { Utils } from '../../../Utils';
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { SettingsManager } from "../../util/SettingsManager";
+import { undoBatch } from "../../util/UndoManager";
+import { Borders, Colors } from "../global/globalEnums";
+import "./TopBar.scss";
+
+/**
+ * ABOUT: This is the topbar in Dash, which included the current Dashboard as well as access to information on the user
+ * and settings and help buttons. Future scope for this bar is to include the collaborators that are on the same Dashboard.
+ */
+@observer
+export class TopBar extends React.Component {
+ render() {
+ const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+ return (
+ //TODO:glr Add support for light / dark mode
+ <div style={{ pointerEvents: "all" }} className="topbar-container">
+ <div className="topbar-bar" style={{ background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }}>
+ <div className="topbar-left">
+ <div className="topbar-lozenge-user">
+ {`${Doc.CurrentUserEmail}`}
+ </div>
+ <div className="topbar-icon" onClick={() => window.location.assign(Utils.prepend("/logout"))}>
+ {"Sign out"}
+ </div>
+ </div>
+ <div className="topbar-center" >
+ <div className="topbar-lozenge-dashboard">
+ <select className="topbar-dashSelect" onChange={e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)])}
+ value={myDashboards.indexOf(CurrentUserUtils.ActiveDashboard)}
+ style={{ color: Colors.WHITE }}>
+ {myDashboards.map((dash, i) => <option key={dash[Id]} value={i}> {StrCast(dash.title)} </option>)}
+ </select>
+ </div>
+ <div className="topbar-dashboards">
+ <div className="topbar-icon" onClick={undoBatch(() => CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}
+ >
+ {"New"}<FontAwesomeIcon icon="plus"></FontAwesomeIcon>
+ </div>
+ {Doc.UserDoc().noviceMode ? (null) : <div className="topbar-icon" onClick={undoBatch(() => CurrentUserUtils.snapshotDashboard(Doc.UserDoc()))}
+ >
+ {"Snapshot"}<FontAwesomeIcon icon="camera"></FontAwesomeIcon>
+ </div>}
+ </div>
+ </div>
+ <div className="topbar-right" >
+ <div className="topbar-icon">
+ {"Help"}<FontAwesomeIcon icon="question-circle"></FontAwesomeIcon>
+ </div>
+ <div className="topbar-icon" onClick={() => SettingsManager.Instance.open()}>
+ {"Settings"}<FontAwesomeIcon icon="cog"></FontAwesomeIcon>
+ </div>
+
+ </div>
+ </div>
+ </div >
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/webcam/DashWebRTCVideo.scss b/src/client/views/webcam/DashWebRTCVideo.scss
index 41307a808..249aee9d6 100644
--- a/src/client/views/webcam/DashWebRTCVideo.scss
+++ b/src/client/views/webcam/DashWebRTCVideo.scss
@@ -1,4 +1,4 @@
-@import "../globalCssVariables";
+@import "../global/globalCssVariables";
.webcam-cont {
background: whitesmoke;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index f5825fa66..6dcf34a3a 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -24,6 +24,7 @@ import { AudioField, ImageField, PdfField, VideoField, WebField } from "./URLFie
import { deleteProperty, GetEffectiveAcl, getField, getter, inheritParentAcls, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util";
import JSZip = require("jszip");
import { CurrentUserUtils } from "../client/util/CurrentUserUtils";
+import { IconProp } from "@fortawesome/fontawesome-svg-core";
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
@@ -61,10 +62,10 @@ export type FieldWaiting<T extends RefField = RefField> = T extends undefined ?
export type FieldResult<T extends Field = Field> = Opt<T> | FieldWaiting<Extract<T, RefField>>;
/**
- * Cast any field to either a List of Docs or undefined if the given field isn't a List of Docs.
- * If a default value is given, that will be returned instead of undefined.
- * If a default value is given, the returned value should not be modified as it might be a temporary value.
- * If no default value is given, and the returned value is not undefined, it can be safely modified.
+ * Cast any field to either a List of Docs or undefined if the given field isn't a List of Docs.
+ * If a default value is given, that will be returned instead of undefined.
+ * If a default value is given, the returned value should not be modified as it might be a temporary value.
+ * If no default value is given, and the returned value is not undefined, it can be safely modified.
*/
export function DocListCastAsync(field: FieldResult): Promise<Doc[] | undefined>;
export function DocListCastAsync(field: FieldResult, defaultValue: Doc[]): Promise<Doc[]>;
@@ -366,13 +367,13 @@ export namespace Doc {
/**
* This function is intended to model Object.assign({}, {}) [https://mzl.la/1Mo3l21], which copies
* the values of the properties of a source object into the target.
- *
+ *
* This is just a specific, Dash-authored version that serves the same role for our
* Doc class.
- *
- * @param doc the target document into which you'd like to insert the new fields
+ *
+ * @param doc the target document into which you'd like to insert the new fields
* @param fields the fields to project onto the target. Its type signature defines a mapping from some string key
- * to a potentially undefined field, where each entry in this mapping is optional.
+ * to a potentially undefined field, where each entry in this mapping is optional.
*/
export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<Field>>>, skipUndefineds: boolean = false, isInitializing = false) {
isInitializing && (doc[Initializing] = true);
@@ -399,7 +400,7 @@ export namespace Doc {
}
// Gets the data document for the document. Note: this is mis-named -- it does not specifically
- // return the doc's proto, but rather recursively searches through the proto inheritance chain
+ // return the doc's proto, but rather recursively searches through the proto inheritance chain
// and returns the document who's proto is undefined or whose proto is marked as a base prototype ('isPrototype').
export function GetProto(doc: Doc): Doc {
if (doc instanceof Promise) {
@@ -521,30 +522,30 @@ export namespace Doc {
return alias;
}
- export async function makeClone(doc: Doc, cloneMap: Map<string, Doc>, rtfs: { copy: Doc, key: string, field: RichTextField }[], exclusions: string[], dontCreate: boolean, asBranch: boolean): Promise<Doc> {
+ export async function makeClone(doc: Doc, cloneMap: Map<string, Doc>, linkMap: Map<Doc, Doc>, rtfs: { copy: Doc, key: string, field: RichTextField }[], exclusions: string[], dontCreate: boolean, asBranch: boolean): Promise<Doc> {
if (Doc.IsBaseProto(doc)) return doc;
if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!;
const copy = dontCreate ? asBranch ? (Cast(doc.branchMaster, Doc, null) || doc) : doc : new Doc(undefined, true);
cloneMap.set(doc[Id], copy);
- if (LinkManager.Instance.getAllLinks().includes(doc) && LinkManager.Instance.getAllLinks().indexOf(copy) === -1) LinkManager.Instance.addLink(copy);
- const filter = [...exclusions, ...Cast(doc.cloneFieldFilter, listSpec("string"), [])];
- await Promise.all([...Object.keys(doc), "links"].map(async key => {
+ const fieldExclusions = doc.type === DocumentType.MARKER ? exclusions.filter(ex => ex !== "annotationOn") : exclusions;
+ const filter = [...fieldExclusions, ...Cast(doc.cloneFieldFilter, listSpec("string"), [])];
+ await Promise.all(Object.keys(doc).map(async key => {
if (filter.includes(key)) return;
const assignKey = (val: any) => !dontCreate && (copy[key] = val);
const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
- const field = key === "links" && Doc.IsPrototype(doc) ? doc[key] : ProxyField.WithoutProxy(() => doc[key]);
+ const field = ProxyField.WithoutProxy(() => doc[key]);
const copyObjectField = async (field: ObjectField) => {
const list = Cast(doc[key], listSpec(Doc));
const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc);
if (docs !== undefined && docs.length) {
- const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, rtfs, exclusions, dontCreate, asBranch)));
+ const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch)));
!dontCreate && assignKey(new List<Doc>(clones));
} else if (doc[key] instanceof Doc) {
- assignKey(key.includes("layout[") ? undefined : key.startsWith("layout") ? doc[key] as Doc : await Doc.makeClone(doc[key] as Doc, cloneMap, rtfs, exclusions, dontCreate, asBranch)); // reference documents except copy documents that are expanded template fields
+ assignKey(key.includes("layout[") ? undefined : key.startsWith("layout") ? doc[key] as Doc : await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch)); // reference documents except copy documents that are expanded template fields
} else {
!dontCreate && assignKey(ObjectField.MakeCopy(field));
if (field instanceof RichTextField) {
- if (field.Data.includes('"docid":') || field.Data.includes('"targetId":') || field.Data.includes('"linkId":')) {
+ if (field.Data.includes('"audioId":') || field.Data.includes('"textId":') || field.Data.includes('"anchorId":')) {
rtfs.push({ copy, key, field });
}
}
@@ -552,14 +553,17 @@ export namespace Doc {
};
if (key === "proto") {
if (doc[key] instanceof Doc) {
- assignKey(await Doc.makeClone(doc[key]!, cloneMap, rtfs, exclusions, dontCreate, asBranch));
+ assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch));
+ }
+ } else if (key === "anchor1" || key === "anchor2") {
+ if (doc[key] instanceof Doc) {
+ assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, true, asBranch));
}
} else {
if (field instanceof RefField) {
assignKey(field);
} else if (cfield instanceof ComputedField) {
!dontCreate && assignKey(ComputedField.MakeFunction(cfield.script.originalScript));
- (key === "links" && field instanceof ObjectField) && await copyObjectField(field);
} else if (field instanceof ObjectField) {
await copyObjectField(field);
} else if (field instanceof Promise) {
@@ -569,6 +573,10 @@ export namespace Doc {
}
}
}));
+ for (const link of Array.from(doc[DirectLinksSym])) {
+ const linkClone = await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch);
+ linkMap.set(link, linkClone);
+ }
if (!dontCreate) {
Doc.SetInPlace(copy, "title", (asBranch ? "BRANCH: " : "CLONE: ") + doc.title, true);
asBranch ? (copy.branchOf = doc) : (copy.cloneOf = doc);
@@ -581,8 +589,10 @@ export namespace Doc {
}
export async function MakeClone(doc: Doc, dontCreate: boolean = false, asBranch = false) {
const cloneMap = new Map<string, Doc>();
+ const linkMap = new Map<Doc, Doc>();
const rtfMap: { copy: Doc, key: string, field: RichTextField }[] = [];
- const copy = await Doc.makeClone(doc, cloneMap, rtfMap, ["context", "annotationOn", "cloneOf", "branches", "branchOf"], dontCreate, asBranch);
+ const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ["context", "annotationOn", "cloneOf", "branches", "branchOf"], dontCreate, asBranch);
+ Array.from(linkMap.entries()).map((links: Doc[]) => LinkManager.Instance.addLink(links[1], true));
rtfMap.map(({ copy, key, field }) => {
const replacer = (match: any, attr: string, id: string, offset: any, string: any) => {
const mapped = cloneMap.get(id);
@@ -592,9 +602,9 @@ export namespace Doc {
const mapped = cloneMap.get(id);
return href + (mapped ? mapped[Id] : id);
};
- const regex = `(${Utils.prepend("/doc/")})([^"]*)`;
+ const regex = `(${Doc.localServerPath()})([^"]*)`;
const re = new RegExp(regex, "g");
- copy[key] = new RichTextField(field.Data.replace(/("docid":|"targetId":|"linkId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text);
+ copy[key] = new RichTextField(field.Data.replace(/("textId":|"audioId":|"anchorId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text);
});
return { clone: copy, map: cloneMap };
}
@@ -662,14 +672,14 @@ export namespace Doc {
const _pendingMap: Map<string, boolean> = new Map();
//
// Returns an expanded template layout for a target data document if there is a template relationship
- // between the two. If so, the layoutDoc is expanded into a new document that inherits the properties
+ // between the two. If so, the layoutDoc is expanded into a new document that inherits the properties
// of the original layout while allowing for individual layout properties to be overridden in the expanded layout.
// templateArgs should be equivalent to the layout key that generates the template since that's where the template parameters are stored in ()'s at the end of the key.
// NOTE: the template will have references to "@params" -- the template arguments will be assigned to the '@params' field
// so that when the @params key is accessed, it will be rewritten as the key that is stored in the 'params' field and
// the derefence will then occur on the rootDocument (the original document).
// in the future, field references could be written as @<someparam> and then arguments would be passed in the layout key as:
- // layout_mytemplate(somparam=somearg).
+ // layout_mytemplate(somparam=somearg).
// then any references to @someparam would be rewritten as accesses to 'somearg' on the rootDocument
export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc, templateArgs?: string) {
const args = templateArgs?.match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1].replace("()", "") || StrCast(templateLayoutDoc.PARAMS);
@@ -770,7 +780,7 @@ export namespace Doc {
copy[key] = cfield[Copy]();// ComputedField.MakeFunction(cfield.script.originalScript);
} else if (field instanceof ObjectField) {
copy[key] = doc[key] instanceof Doc ?
- key.includes("layout[") ? undefined : doc[key] : // reference documents except remove documents that are expanded teplate fields
+ key.includes("layout[") ? undefined : doc[key] : // reference documents except remove documents that are expanded teplate fields
ObjectField.MakeCopy(field);
} else if (field instanceof Promise) {
debugger; //This shouldn't happend...
@@ -808,6 +818,27 @@ export namespace Doc {
return undefined;
}
+ // Makes a delegate of a document by first creating a delegate where data should be stored
+ // (ie, the 'data' doc), and then creates another delegate of that (ie, the 'layout' doc).
+ // This is appropriate if you're trying to create a document that behaves like all
+ // regularly created documents (e.g, text docs, pdfs, etc which all have data/layout docs)
+ export function MakeDelegateWithProto(doc: Doc, id?: string, title?: string): Doc {
+ const delegateProto = new Doc();
+ delegateProto[Initializing] = true;
+ delegateProto.proto = doc;
+ delegateProto.author = Doc.CurrentUserEmail;
+ delegateProto.isPrototype = true;
+ title && (delegateProto.title = title);
+ const delegate = new Doc(id, true);
+ delegate[Initializing] = true;
+ delegate.proto = delegateProto;
+ delegate.author = Doc.CurrentUserEmail;
+ Doc.AddDocToList(delegateProto[DataSym], "aliases", delegate);
+ delegate[Initializing] = false;
+ delegateProto[Initializing] = false;
+ return delegate;
+ }
+
let _applyCount: number = 0;
export function ApplyTemplate(templateDoc: Doc) {
if (templateDoc) {
@@ -871,6 +902,16 @@ export namespace Doc {
return true;
}
+
+ // converts a document id to a url path on the server
+ export function globalServerPath(doc: Doc | string = ""): string {
+ return Utils.prepend("/doc/" + (doc instanceof Doc ? doc[Id] : doc));
+ }
+ // converts a document id to a url path on the server
+ export function localServerPath(doc?: Doc): string {
+ return "/doc/" + (doc ? doc[Id] : "");
+ }
+
export function overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) {
const doc2Layout = Doc.Layout(doc2);
const doc1Layout = Doc.Layout(doc1);
@@ -902,7 +943,7 @@ export namespace Doc {
}
// the document containing the view layout information - will be the Document itself unless the Document has
- // a layout field or 'layout' is given.
+ // a layout field or 'layout' is given.
export function Layout(doc: Doc, layout?: Doc): Doc {
const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, "data")}-layout[` + layout[Id] + "]"], Doc, null);
return overrideLayout || doc[LayoutSym] || doc;
@@ -1081,7 +1122,7 @@ export namespace Doc {
}
// filters document in a container collection:
- // all documents with the specified value for the specified key are included/excluded
+ // all documents with the specified value for the specified key are included/excluded
// based on the modifiers :"check", "x", undefined
export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: "remove" | "match" | "check" | "x", toggle?: boolean, fieldSuffix?: string, append: boolean = true) {
if (!container) return;
@@ -1158,8 +1199,7 @@ export namespace Doc {
return ndoc;
}
export function delegateDragFactory(dragFactory: Doc) {
- const ndoc = Doc.MakeDelegate(dragFactory);
- ndoc.isPrototype = true;
+ const ndoc = Doc.MakeDelegateWithProto(dragFactory);
if (ndoc && dragFactory["dragFactory-count"] !== undefined) {
dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1;
Doc.GetProto(ndoc).title = ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString();
@@ -1172,7 +1212,10 @@ export namespace Doc {
case DocumentType.IMG: return "image";
case DocumentType.COMPARISON: return "columns";
case DocumentType.RTF: return "sticky-note";
- case DocumentType.COL: return !doc?.isFolder ? "folder" + (isOpen ? "-open" : "") : "chevron-" + (isOpen ? "down" : "right");
+ case DocumentType.COL:
+ const folder: IconProp = isOpen ? "folder-open" : "folder";
+ const chevron: IconProp = isOpen ? "chevron-down" : "chevron-right"
+ return !doc?.isFolder ? folder : chevron;
case DocumentType.WEB: return "globe-asia";
case DocumentType.SCREENSHOT: return "photo-video";
case DocumentType.WEBCAM: return "video";
@@ -1206,39 +1249,39 @@ export namespace Doc {
/**
* This function takes any valid JSON(-like) data, i.e. parsed or unparsed, and at arbitrarily
* deep levels of nesting, converts the data and structure into nested documents with the appropriate fields.
- *
+ *
* After building a hierarchy within / below a top-level document, it then returns that top-level parent.
- *
+ *
* If we've received a string, treat it like valid JSON and try to parse it into an object. If this fails, the
* string is invalid JSON, so we should assume that the input is the result of a JSON.parse()
* call that returned a regular string value to be stored as a Field.
- *
+ *
* If we've received something other than a string, since the caller might also pass in the results of a
* JSON.parse() call, valid input might be an object, an array (still typeof object), a boolean or a number.
* Anything else (like a function, etc. passed in naively as any) is meaningless for this operation.
- *
+ *
* All TS/JS objects get converted directly to documents, directly preserving the key value structure. Everything else,
* lacking the key value structure, gets stored as a field in a wrapper document.
- *
+ *
* @param data for convenience and flexibility, either a valid JSON string to be parsed,
* or the result of any JSON.parse() call.
* @param title an optional title to give to the highest parent document in the hierarchy.
* If whether this function creates a new document or appendToExisting is specified and that document already has a title,
* because this title field can be left undefined for the opposite behavior, including a title will overwrite the existing title.
* @param appendToExisting **if specified**, there are two cases, both of which return the target document:
- *
+ *
* 1) the json to be converted can be represented as a document, in which case the target document will act as the root
* of the tree and receive all the conversion results as new fields on itself
* 2) the json can't be represented as a document, in which case the function will assign the field-level conversion
* results to either the specified key on the target document, or to its "json" key by default.
- *
+ *
* If not specified, the function creates and returns a new entirely generic document (different from the Doc.Create calls)
* to act as the root of the tree.
- *
+ *
* One might choose to specify this field if you want to write to a document returned from a Document.Create function call,
* say a TreeView document that will be rendered, not just an untyped, identityless doc that would otherwise be created
* from a default call to new Doc.
- *
+ *
* @param excludeEmptyObjects whether non-primitive objects (TypeScript objects and arrays) should be converted even
* if they contain no data. By default, empty objects and arrays are ignored.
*/
@@ -1274,7 +1317,7 @@ export namespace Doc {
* For each value of the object, recursively convert it to its appropriate field value
* and store the field at the appropriate key in the document if it is not undefined
* @param object the object to convert
- * @returns the object mapped from JSON to field values, where each mapping
+ * @returns the object mapped from JSON to field values, where each mapping
* might involve arbitrary recursion (since toField might itself call convertObject)
*/
const convertObject = (object: any, excludeEmptyObjects: boolean, title?: string, target?: Doc): Opt<Doc> => {
@@ -1298,10 +1341,10 @@ export namespace Doc {
};
/**
- * For each element in the list, recursively convert it to a document or other field
+ * For each element in the list, recursively convert it to a document or other field
* and push the field to the list if it is not undefined
* @param list the list to convert
- * @returns the list mapped from JSON to field values, where each mapping
+ * @returns the list mapped from JSON to field values, where each mapping
* might involve arbitrary recursion (since toField might itself call convertList)
*/
const convertList = (list: Array<any>, excludeEmptyObjects: boolean): Opt<List<Field>> => {
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index dbe51b24a..1270a2dab 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -4,6 +4,7 @@ import { ObjectField } from "./ObjectField";
import { Copy, ToScriptString, ToString, Update } from "./FieldSymbols";
import { Scripting } from "../client/util/Scripting";
+// Helps keep track of the current ink tool in use.
export enum InkTool {
None = "none",
Pen = "pen",
@@ -12,13 +13,41 @@ export enum InkTool {
Stamp = "stamp"
}
+
+// Defines a point in an ink as a pair of x- and y-coordinates.
export interface PointData {
X: number;
Y: number;
}
+// Defines an ink as an array of points.
export type InkData = Array<PointData>;
+export interface ControlPoint {
+ X: number;
+ Y: number;
+ I: number;
+}
+
+export interface HandlePoint {
+ X: number;
+ Y: number;
+ I: number;
+ dot1: number;
+ dot2: number;
+}
+
+export interface HandleLine {
+ X1: number;
+ Y1: number;
+ X2: number;
+ Y2: number;
+ X3: number;
+ Y3: number;
+ dot1: number;
+ dot2: number;
+}
+
const pointSchema = createSimpleSchema({
X: true, Y: true
});
@@ -32,8 +61,6 @@ const strokeDataSchema = createSimpleSchema({
export class InkField extends ObjectField {
@serializable(list(object(strokeDataSchema)))
readonly inkData: InkData;
- // inkData: InkData;
-
constructor(data: InkData) {
super();
diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts
index fb71160ca..d96e8a70a 100644
--- a/src/fields/URLField.ts
+++ b/src/fields/URLField.ts
@@ -3,14 +3,17 @@ import { serializable, custom } from "serializr";
import { ObjectField } from "./ObjectField";
import { ToScriptString, ToString, Copy } from "./FieldSymbols";
import { Scripting, scriptingGlobal } from "../client/util/Scripting";
+import { Utils } from "../Utils";
function url() {
return custom(
function (value: URL) {
- return value.href;
+ return value.origin === window.location.origin ?
+ value.pathname :
+ value.href;
},
function (jsonValue: string) {
- return new URL(jsonValue);
+ return new URL(jsonValue, window.location.origin);
}
);
}
@@ -24,15 +27,21 @@ export abstract class URLField extends ObjectField {
constructor(url: URL | string) {
super();
if (typeof url === "string") {
- url = new URL(url);
+ url = url.startsWith("http") ? new URL(url) : new URL(url, window.location.origin);
}
this.url = url;
}
[ToScriptString]() {
+ if (Utils.prepend(this.url.pathname) === this.url.href) {
+ return `new ${this.constructor.name}("${this.url.pathname}")`;
+ }
return `new ${this.constructor.name}("${this.url.href}")`;
}
[ToString]() {
+ if (Utils.prepend(this.url.pathname) === this.url.href) {
+ return this.url.pathname;
+ }
return this.url.href;
}
diff --git a/src/mobile/AudioUpload.scss b/src/mobile/AudioUpload.scss
index 6e64d9e2e..dce0c724f 100644
--- a/src/mobile/AudioUpload.scss
+++ b/src/mobile/AudioUpload.scss
@@ -1,4 +1,4 @@
-@import "../client/views/globalCssVariables.scss";
+@import "../client/views/global/globalCssVariables.scss";
.audioUpload_cont {
display: flex;
diff --git a/src/mobile/ImageUpload.scss b/src/mobile/ImageUpload.scss
index 890258918..6669a3d21 100644
--- a/src/mobile/ImageUpload.scss
+++ b/src/mobile/ImageUpload.scss
@@ -1,4 +1,4 @@
-@import "../client/views/globalCssVariables.scss";
+@import "../client/views/global/globalCssVariables.scss";
.imgupload_cont {
display: flex;
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index 2183d2172..f910d765e 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -5,7 +5,7 @@ import * as rp from 'request-promise';
import { DocServer } from '../client/DocServer';
import { Docs } from '../client/documents/Documents';
import { Networking } from '../client/Network';
-import { DFLT_IMAGE_NATIVE_DIM } from '../client/views/globalCssVariables.scss';
+import { DFLT_IMAGE_NATIVE_DIM } from '../client/views/global/globalCssVariables.scss';
import { MainViewModal } from '../client/views/MainViewModal';
import { Doc, Opt } from '../fields/Doc';
import { List } from '../fields/List';
@@ -50,7 +50,7 @@ export class Uploader extends React.Component<ImageUploadProps> {
if (result instanceof Error) {
return;
}
- const path = Utils.prepend(result.accessPaths.agnostic.client);
+ const path = result.accessPaths.agnostic.client;
let doc = null;
// Case 1: File is a video
if (file.type === "video/mp4") {
diff --git a/src/server/DashSession/Session/agents/server_worker.ts b/src/server/DashSession/Session/agents/server_worker.ts
index 6a19bfa5d..84d35b40e 100644
--- a/src/server/DashSession/Session/agents/server_worker.ts
+++ b/src/server/DashSession/Session/agents/server_worker.ts
@@ -1,10 +1,10 @@
-import { ExitHandler } from "./applied_session_agent";
import { isMaster } from "cluster";
-import { manage, ErrorLike } from "./promisified_ipc_manager";
-import IPCMessageReceiver from "./process_message_router";
-import { red, green, white, yellow } from "colors";
+import { green, red, white, yellow } from "colors";
import { get } from "request-promise";
+import { ExitHandler } from "./applied_session_agent";
import { Monitor } from "./monitor";
+import IPCMessageReceiver from "./process_message_router";
+import { ErrorLike, manage } from "./promisified_ipc_manager";
/**
* Effectively, each worker repairs the connection to the server by reintroducing a consistent state
@@ -21,7 +21,6 @@ export class ServerWorker extends IPCMessageReceiver {
private pollTarget: string;
private serverPort: number;
private isInitialized = false;
-
public static Create(work: Function) {
if (isMaster) {
console.error(red("cannot create a worker on the monitor process."));
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 5ce69999a..a617571ae 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -1,4 +1,4 @@
-import { red, green } from 'colors';
+import { green, red } from 'colors';
import { ExifImage } from 'exif';
import { File } from 'formidable';
import { createWriteStream, existsSync, readFileSync, rename, unlinkSync, writeFile } from 'fs';
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index e40f2b8e5..0f4a067fc 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -142,8 +142,9 @@ function registerCorsProxy(server: express.Express) {
const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
server.use("/corsProxy", async (req, res) => {
- const requrl = decodeURIComponent(req.url.substring(1));
const referer = req.headers.referer ? decodeURIComponent(req.headers.referer) : "";
+ const requrlraw = decodeURIComponent(req.url.substring(1));
+ const requrl = requrlraw.startsWith("/") ? referer + requrlraw : requrlraw;
// cors weirdness here...
// if the referer is a cors page and the cors() route (I think) redirected to /corsProxy/<path> and the requested url path was relative,
// then we redirect again to the cors referer and just add the relative path.