aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorusodhi <61431818+usodhi@users.noreply.github.com>2020-06-29 17:00:41 +0530
committerusodhi <61431818+usodhi@users.noreply.github.com>2020-06-29 17:00:41 +0530
commit9c0a250fd412c46861f72d8bb7cc5535b101c0fa (patch)
treeff24809b94f06b3fb12ce47dbd94e14cdc615baa /src
parent6ad7fac6342204bc489ef49cb3ba8f450bfa3824 (diff)
parent29f9cedd937697c56ce3b400931b26c82a23721d (diff)
merge conflicts resolved
Diffstat (limited to 'src')
-rw-r--r--src/client/DocServer.ts15
-rw-r--r--src/client/apis/google_docs/GooglePhotosClientUtils.ts1
-rw-r--r--src/client/documents/Documents.ts67
-rw-r--r--src/client/util/CurrentUserUtils.ts49
-rw-r--r--src/client/util/DocumentManager.ts2
-rw-r--r--src/client/util/DragManager.ts6
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx2
-rw-r--r--src/client/util/InteractionUtils.scss4
-rw-r--r--src/client/util/InteractionUtils.tsx86
-rw-r--r--src/client/util/LinkManager.ts3
-rw-r--r--src/client/util/SelectionManager.ts13
-rw-r--r--src/client/views/.DS_Storebin6148 -> 6148 bytes
-rw-r--r--src/client/views/DocumentButtonBar.tsx2
-rw-r--r--src/client/views/DocumentDecorations.scss59
-rw-r--r--src/client/views/DocumentDecorations.tsx103
-rw-r--r--src/client/views/EditableView.tsx60
-rw-r--r--src/client/views/GestureOverlay.tsx50
-rw-r--r--src/client/views/InkingStroke.scss4
-rw-r--r--src/client/views/InkingStroke.tsx48
-rw-r--r--src/client/views/Main.tsx2
-rw-r--r--src/client/views/MainView.tsx49
-rw-r--r--src/client/views/animationtimeline/Timeline.tsx4
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.scss107
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx197
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx10
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx16
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx77
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx18
-rw-r--r--src/client/views/collections/CollectionSubView.tsx10
-rw-r--r--src/client/views/collections/CollectionTreeView.scss3
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx394
-rw-r--r--src/client/views/collections/CollectionView.scss14
-rw-r--r--src/client/views/collections/CollectionView.tsx21
-rw-r--r--src/client/views/collections/CollectionViewChromes.scss17
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx49
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx5
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss7
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx35
-rw-r--r--src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss7
-rw-r--r--src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx235
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx8
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx34
-rw-r--r--src/client/views/nodes/DocumentView.scss11
-rw-r--r--src/client/views/nodes/DocumentView.tsx64
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx4
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx10
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx4
-rw-r--r--src/client/views/nodes/PDFBox.scss6
-rw-r--r--src/client/views/nodes/PDFBox.tsx37
-rw-r--r--src/client/views/nodes/PresBox.tsx4
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx4
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss58
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx19
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts30
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts4
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts2
-rw-r--r--src/client/views/pdf/PDFViewer.scss24
-rw-r--r--src/client/views/pdf/PDFViewer.tsx54
-rw-r--r--src/client/views/search/SearchItem.tsx2
-rw-r--r--src/fields/Doc.ts3
-rw-r--r--src/fields/InkField.ts4
-rw-r--r--src/fields/List.ts7
-rw-r--r--src/fields/ObjectField.ts4
-rw-r--r--src/fields/Schema.ts5
-rw-r--r--src/fields/documentSchemas.ts10
-rw-r--r--src/fields/util.ts8
-rw-r--r--src/scraping/buxton/scraper.py3
-rw-r--r--src/server/ApiManagers/PDFManager.ts7
68 files changed, 1566 insertions, 715 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index c7dfb0b23..2a7a7c59a 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -31,16 +31,19 @@ export namespace DocServer {
export enum WriteMode {
Default = 0, //Anything goes
- Playground = 1,
- LiveReadonly = 2,
- LivePlayground = 3,
+ Playground = 1, //Playground (write own/no read)
+ LiveReadonly = 2,//Live Readonly (no write/read others)
+ LivePlayground = 3,//Live Playground (write own/read others)
}
-
- export let AclsMode = WriteMode.Default;
-
const fieldWriteModes: { [field: string]: WriteMode } = {};
const docsWithUpdates: { [field: string]: Set<Doc> } = {};
+ export var PlaygroundFields: string[];
+ export function setPlaygroundFields(livePlayougroundFields: string[]) {
+ DocServer.PlaygroundFields = livePlayougroundFields;
+ livePlayougroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground));
+ }
+
export function setFieldWriteMode(field: string, writeMode: WriteMode) {
fieldWriteModes[field] = writeMode;
if (writeMode !== WriteMode.Playground) {
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index a604c7de1..13bfb3a91 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -130,7 +130,6 @@ export namespace GooglePhotos {
const uploads = await Transactions.WriteMediaItemsToServer(response);
const children = uploads.map((upload: Transactions.UploadInformation) => {
const document = Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean));
- document.fillColumn = true;
document.contentSize = upload.contentSize;
return document;
});
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f10abfff2..9feee0d47 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -13,17 +13,19 @@ import { Cast, NumCast, StrCast } from "../../fields/Types";
import { AudioField, ImageField, PdfField, VideoField, WebField, YoutubeField } from "../../fields/URLField";
import { MessageStore } from "../../server/Message";
import { OmitKeys, Utils } from "../../Utils";
+import { YoutubeBox } from "../apis/youtube/YoutubeBox";
import { DocServer } from "../DocServer";
+import { DocumentManager } from "../util/DocumentManager";
import { dropActionType } from "../util/DragManager";
+import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox";
import { LinkManager } from "../util/LinkManager";
import { Scripting } from "../util/Scripting";
import { UndoManager } from "../util/UndoManager";
-import { DocumentType } from "./DocumentTypes";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
import { ContextMenu } from "../views/ContextMenu";
import { ContextMenuProps } from "../views/ContextMenuItem";
-import { ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from "../views/InkingStroke";
+import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from "../views/InkingStroke";
import { AudioBox } from "../views/nodes/AudioBox";
import { ColorBox } from "../views/nodes/ColorBox";
import { ComparisonBox } from "../views/nodes/ComparisonBox";
@@ -45,9 +47,7 @@ import { WebBox } from "../views/nodes/WebBox";
import { PresElementBox } from "../views/presentationview/PresElementBox";
import { RecommendationsBox } from "../views/RecommendationsBox";
import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
-import { YoutubeBox } from "../apis/youtube/YoutubeBox";
-import { DocumentManager } from "../util/DocumentManager";
-import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox";
+import { DocumentType } from "./DocumentTypes";
const path = require('path');
export interface DocumentOptions {
@@ -62,7 +62,7 @@ export interface DocumentOptions {
_dimUnit?: string; // "px" or "*" (default = "*")
_fitWidth?: boolean;
_fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
- _LODdisable?: boolean;
+ _freeformLOD?: boolean; // whether to use LOD to render a freeform document
_showTitleHover?: string; //
_showTitle?: string; // which field to display in the title area. leave empty to have no title
_showCaption?: string; // which field to display in the caption area. leave empty to have no caption
@@ -92,14 +92,14 @@ export interface DocumentOptions {
label?: string; // short form of title for use as an icon label
style?: string;
page?: number;
- scale?: number;
+ _viewScale?: number;
isDisplayPanel?: boolean; // whether the panel functions as GoldenLayout "stack" used to display documents
forceActive?: boolean;
layout?: string | Doc; // default layout string for a document
childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view)
childLayoutString?: string; // template string for collection to use to render its children
hideFilterView?: boolean; // whether to hide the filter popout on collections
- hideHeadings?: boolean; // whether stacking view column headings should be hidden
+ _columnsHideIfEmpty?: boolean; // whether stacking view column headings should be hidden
isTemplateForField?: string; // the field key for which the containing document is a rendering template
isTemplateDoc?: boolean;
targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc)
@@ -119,7 +119,7 @@ export interface DocumentOptions {
defaultBackgroundColor?: string;
isBackground?: boolean;
isLinkButton?: boolean;
- columnWidth?: number;
+ _columnWidth?: number;
_fontSize?: number;
_fontFamily?: string;
curPage?: number;
@@ -137,7 +137,8 @@ export interface DocumentOptions {
"onCheckedClick-rawScript"?: string; // onChecked script in raw text form
"onCheckedClick-params"?: List<string>; // parameter list for onChecked treeview functions
_pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views
- schemaColumns?: List<SchemaHeaderField>;
+ _columnHeaders?: List<SchemaHeaderField>; // headers for stacking views
+ _schemaHeaders?: List<SchemaHeaderField>; // headers for schema view
dockingConfig?: string;
annotationOn?: Doc;
removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
@@ -168,6 +169,7 @@ export interface DocumentOptions {
treeViewOpen?: boolean; // whether this document is expanded in a tree view
treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view
treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked
+ treeViewTruncateTitleWidth?: number;
limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents
// [key: string]: Opt<Field>;
pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown
@@ -227,7 +229,7 @@ export namespace Docs {
}],
[DocumentType.COL, {
layout: { view: CollectionView, dataField: defaultDataKey },
- options: { _panX: 0, _panY: 0, scale: 1 } // , _width: 500, _height: 500 }
+ options: { _panX: 0, _panY: 0, _viewScale: 1 } // , _width: 500, _height: 500 }
}],
[DocumentType.KVP, {
layout: { view: KeyValueBox, dataField: defaultDataKey },
@@ -442,8 +444,7 @@ export namespace Docs {
const parent = TreeDocument([loading], {
title: "The Buxton Collection",
_width: 400,
- _height: 400,
- _LODdisable: true
+ _height: 400
});
const parentProto = Doc.GetProto(parent);
const { _socket } = DocServer;
@@ -479,7 +480,7 @@ export namespace Docs {
return imageDoc;
});
// the main document we create
- const doc = StackingDocument(deviceImages, { title, _LODdisable: true, hero: new ImageField(constructed[0].url) });
+ 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) } });
@@ -541,6 +542,8 @@ export namespace Docs {
const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey);
const viewDoc = Doc.MakeDelegate(dataDoc, delegId);
+ proto.links = ComputedField.MakeFunction("links(self)");
+
viewDoc.author = Doc.CurrentUserEmail;
viewDoc.type !== DocumentType.LINK && DocUtils.MakeLinkToActiveAudio(viewDoc);
@@ -660,13 +663,17 @@ export namespace Docs {
return doc;
}
- export function InkDocument(color: string, tool: string, strokeWidth: string, strokeBezier: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
+ export function InkDocument(color: string, tool: string, strokeWidth: string, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
const I = new Doc();
I.type = DocumentType.INK;
I.layout = InkingStroke.LayoutString("data");
I.color = color;
I.strokeWidth = strokeWidth;
I.strokeBezier = strokeBezier;
+ I.fillColor = fillColor;
+ I.arrowStart = arrowStart;
+ I.arrowEnd = arrowEnd;
+ I.dash = dash;
I.tool = tool;
I.title = "ink";
I.x = options.x;
@@ -706,15 +713,15 @@ export namespace Docs {
}
export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Freeform }, id);
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Freeform }, id);
}
export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Pile }, id);
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", ...options, _viewType: CollectionViewType.Pile }, id);
}
export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Linear }, id);
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", ...options, _viewType: CollectionViewType.Linear }, id);
}
export function MapDocument(documents: Array<Doc>, options: DocumentOptions = {}) {
@@ -722,31 +729,35 @@ export namespace Docs {
}
export function CarouselDocument(documents: Array<Doc>, options: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Carousel });
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Carousel });
+ }
+
+ export function Carousel3DDocument(documents: Array<Doc>, options: DocumentOptions) {
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Carousel3D });
}
- export function SchemaDocument(schemaColumns: SchemaHeaderField[], documents: Array<Doc>, options: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List(schemaColumns.length ? schemaColumns : [new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Schema });
+ export function SchemaDocument(schemaHeaders: SchemaHeaderField[], documents: Array<Doc>, options: DocumentOptions) {
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", _schemaHeaders: schemaHeaders.length ? new List(schemaHeaders) : undefined, ...options, _viewType: CollectionViewType.Schema });
}
export function TreeDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Tree }, id);
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Tree }, id);
}
export function StackingDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Stacking }, id);
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Stacking }, id);
}
export function MulticolumnDocument(documents: Array<Doc>, options: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Multicolumn });
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Multicolumn });
}
export function MultirowDocument(documents: Array<Doc>, options: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Multirow });
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Multirow });
}
export function MasonryDocument(documents: Array<Doc>, options: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Masonry });
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Masonry });
}
export function LabelDocument(options?: DocumentOptions) {
@@ -888,7 +899,7 @@ export namespace DocUtils {
created = Docs.Create.AudioDocument((field).url.href, resolved);
layout = AudioBox.LayoutString;
} else if (field instanceof InkField) {
- created = Docs.Create.InkDocument(ActiveInkColor(), Doc.GetSelectedTool(), ActiveInkWidth(), ActiveInkBezierApprox(), (field).inkData, resolved);
+ created = Docs.Create.InkDocument(ActiveInkColor(), Doc.GetSelectedTool(), ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved);
layout = InkingStroke.LayoutString;
} else if (field instanceof List && field[0] instanceof Doc) {
created = Docs.Create.StackingDocument(DocListCast(field), resolved);
@@ -1058,7 +1069,7 @@ export namespace DocUtils {
});
});
if (x !== undefined && y !== undefined) {
- const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100, _LODdisable: true });
+ const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100 });
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;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 4a188384c..1366d0df0 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -43,7 +43,7 @@ export class CurrentUserUtils {
const queryTemplate = Docs.Create.MulticolumnDocument(
[
Docs.Create.QueryDocument({ title: "query", _height: 200 }),
- Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true })
+ Docs.Create.FreeformDocument([], { title: "data", _height: 100 })
],
{ _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true }
);
@@ -136,9 +136,9 @@ export class CurrentUserUtils {
if (doc["template-button-switch"] === undefined) {
const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create;
- const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _LODdisable: true, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40 });
+ const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40 });
const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1 });
- const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, _LODdisable: true });
+ const no = FreeformDocument([], { title: "no", _height: 100, _width: 100 });
const labelTemplate = {
doc: {
type: "doc", content: [{
@@ -193,10 +193,10 @@ export class CurrentUserUtils {
const shared = { _chromeStatus: "disabled", _autoHeight: true, _xMargin: 0 };
const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: 12 };
- const descriptionWrapperOpts = { title: "descriptions", _height: 300, columnWidth: -1, treeViewHideTitle: true, _pivotField: "title" };
+ const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTitle: true, _pivotField: "title" };
const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts });
- descriptionWrapper.sectionHeaders = new List<SchemaHeaderField>([
+ descriptionWrapper._columnHeaders = new List<SchemaHeaderField>([
new SchemaHeaderField("[A Short Description]", "dimGray", undefined, undefined, undefined, false),
new SchemaHeaderField("[Long Description]", "dimGray", undefined, undefined, undefined, true),
new SchemaHeaderField("[Details]", "dimGray", undefined, undefined, undefined, true),
@@ -225,7 +225,7 @@ export class CurrentUserUtils {
if (doc["template-buttons"] === undefined) {
doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument(requiredTypes, {
title: "Advanced Item Prototypes", _xMargin: 0, _showTitle: "title",
- _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+ _autoHeight: true, _width: 500, _columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
}));
} else {
@@ -306,21 +306,17 @@ export class CurrentUserUtils {
// setup templates for different document types when they are iconified from Document Decorations
static setupDefaultIconTemplates(doc: Doc) {
if (doc["template-icon-view"] === undefined) {
- const iconView = Docs.Create.TextDocument("", {
- title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)")
+ const iconView = Docs.Create.LabelDocument({
+ title: "icon", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("title"), _backgroundColor: "dimGray",
+ _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)")
});
- Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', "");
+ // Docs.Create.TextDocument("", {
+ // title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)")
+ // });
+ // Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', "");
iconView.isTemplateDoc = makeTemplate(iconView);
doc["template-icon-view"] = new PrefetchProxy(iconView);
}
- if (doc["template-icon-view-pdf"] === undefined) {
- const iconPdfView = Docs.Create.LabelDocument({
- title: "icon_" + DocumentType.PDF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("title"), _backgroundColor: "dimGray",
- _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)")
- });
- iconPdfView.isTemplateDoc = makeTemplate(iconPdfView, true, "icon_" + DocumentType.PDF);
- doc["template-icon-view-pdf"] = new PrefetchProxy(iconPdfView);
- }
if (doc["template-icon-view-rtf"] === undefined) {
const iconRtfView = Docs.Create.LabelDocument({
title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"),
@@ -362,11 +358,11 @@ export class CurrentUserUtils {
}[] {
if (doc.emptyPresentation === undefined) {
doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
- { title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias", _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
+ { title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias", _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
}
if (doc.emptyCollection === undefined) {
doc.emptyCollection = Docs.Create.FreeformDocument([],
- { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" });
+ { _nativeWidth: undefined, _nativeHeight: undefined, _width: 150, _height: 100, title: "freeform" });
}
if (doc.emptyDocHolder === undefined) {
doc.emptyDocHolder = Docs.Create.DocumentDocument(
@@ -434,7 +430,7 @@ export class CurrentUserUtils {
if (dragCreatorSet === undefined) {
doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
title: "Basic Item Creators", _showTitle: "title", _xMargin: 0,
- _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+ _autoHeight: true, _width: 500, _columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
}));
} else {
@@ -497,7 +493,7 @@ export class CurrentUserUtils {
static setupMobileDoc(userDoc: Doc) {
return userDoc.activeMoble ?? Docs.Create.MasonryDocument(CurrentUserUtils.setupMobileButtons(userDoc), {
- columnWidth: 100, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons", _autoHeight: true, _yMargin: 5
+ _columnWidth: 100, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons", _autoHeight: true, _yMargin: 5
});
}
@@ -605,6 +601,7 @@ export class CurrentUserUtils {
if (doc["tabs-button-library"] === undefined) {
const libraryStack = new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], {
title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
+ treeViewTruncateTitleWidth: 150,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same"
})) as any as Doc;
doc["tabs-button-library"] = new PrefetchProxy(Docs.Create.ButtonDocument({
@@ -658,7 +655,7 @@ export class CurrentUserUtils {
// Finally, setup the list of buttons to display in the sidebar
if (doc["tabs-buttons"] === undefined) {
doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([searchBtn, libraryBtn, toolsBtn], {
- _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", hideHeadings: true, ignoreClick: true, _chromeStatus: "view-mode",
+ _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", _columnsHideIfEmpty: true, ignoreClick: true, _chromeStatus: "view-mode",
title: "sidebar btn row stack", backgroundColor: "dimGray",
}));
(toolsBtn.onClick as ScriptField).script.run({ this: toolsBtn });
@@ -707,7 +704,7 @@ export class CurrentUserUtils {
if (doc.activePresentation === undefined) {
doc.activePresentation = Docs.Create.PresDocument(new List<Doc>(), {
title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias",
- _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0"
+ _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0"
});
}
}
@@ -762,7 +759,11 @@ export class CurrentUserUtils {
doc.activeInkPen = doc;
doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)");
doc.activeInkWidth = StrCast(doc.activeInkWidth, "1");
- doc.activeInkBezier = StrCast(doc.activeInkBezier, "");
+ doc.activeInkBezier = StrCast(doc.activeInkBezier, "0");
+ doc.activeFillColor = StrCast(doc.activeFillColor, "none");
+ doc.activeArrowStart = StrCast(doc.activeArrowStart, "none");
+ doc.activeArrowEnd = StrCast(doc.activeArrowEnd, "none");
+ doc.activeDash = StrCast(doc.activeDash, "0");
doc.fontSize = NumCast(doc.fontSize, 12);
doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); //
doc["constants-dragThreshold"] = NumCast(doc["constants-dragThreshold"], 4); //
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 40d6b039a..fb5d1717e 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -170,7 +170,7 @@ export class DocumentManager {
const targetDocContextView = getFirstDocView(targetDocContext);
targetDocContext._scrollY = 0; // this will force PDFs to activate and load their annotations / allow scrolling
if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first..
- targetDocContext.panTransformType = "Ease";
+ targetDocContext._viewTransition = "transform 500ms";
targetDocContextView.props.focus(targetDocContextView.props.Document, willZoom);
// now find the target document within the context
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 417ddf989..0db3963b2 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -19,8 +19,6 @@ export function SetupDrag(
docFunc: () => Doc | Promise<Doc> | undefined,
moveFunc?: DragManager.MoveFunction,
dropAction?: dropActionType,
- treeViewId?: string,
- dontHideOnDrop?: boolean,
dragStarted?: () => void
) {
const onRowMove = async (e: PointerEvent) => {
@@ -34,8 +32,6 @@ export function SetupDrag(
const dragData = new DragManager.DocumentDragData([doc]);
dragData.dropAction = dropAction;
dragData.moveDocument = moveFunc;
- dragData.treeViewId = treeViewId;
- dragData.dontHideOnDrop = dontHideOnDrop;
DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y);
dragStarted?.();
}
@@ -128,7 +124,7 @@ export namespace DragManager {
draggedDocuments: Doc[];
droppedDocuments: Doc[];
dragDivName?: string;
- treeViewId?: string;
+ treeViewDoc?: Doc;
dontHideOnDrop?: boolean;
offset: number[];
dropAction: dropActionType;
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index af6c57e68..77f13e9f4 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -161,7 +161,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
importContainer = Docs.Create.SchemaDocument(headers, docs, options);
}
runInAction(() => this.phase = 'External: uploading files to Google Photos...');
- importContainer.singleColumn = false;
+ importContainer._columnsStack = false;
await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer });
Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
!this.persistent && this.props.removeDocument && this.props.removeDocument(doc);
diff --git a/src/client/util/InteractionUtils.scss b/src/client/util/InteractionUtils.scss
new file mode 100644
index 000000000..6707157d4
--- /dev/null
+++ b/src/client/util/InteractionUtils.scss
@@ -0,0 +1,4 @@
+.halo {
+ opacity: 0.2;
+ stroke: black;
+} \ No newline at end of file
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index df792c9c0..edeb461e0 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -1,6 +1,8 @@
import React = require("react");
import * as beziercurve from 'bezier-curve';
import * as fitCurve from 'fit-curve';
+import "./InteractionUtils.scss";
+import { Utils } from "../../Utils";
export namespace InteractionUtils {
export const MOUSETYPE = "mouse";
@@ -89,15 +91,17 @@ export namespace InteractionUtils {
return myTouches;
}
- export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: number, strokeWidth: number, bezier: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean) {
+ export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number,
+ color: string, width: number, strokeWidth: number, bezier: string, fill: string, arrowStart: string, arrowEnd: string,
+ dash: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean, nodefs: boolean) {
let pts: { X: number; Y: number; }[] = [];
if (shape) { //if any of the shape are true
pts = makePolygon(shape, points);
}
else if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y === points[0].Y) {
//pointer is up (first and last points are the same)
- points.pop();
const newPoints = points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]);
+ newPoints.pop();
const bezierCurves = fitCurve(newPoints, parseInt(bezier));
for (const curve of bezierCurves) {
@@ -111,25 +115,55 @@ export namespace InteractionUtils {
}
const strpts = pts.reduce((acc: string, pt: { X: number, Y: number }) => acc +
`${(pt.X - left - width / 2) * scalex + width / 2},
- ${(pt.Y - top - width / 2) * scaley + width / 2} `, "");
+ ${(pt.Y - top - width / 2) * scaley + width / 2} `, "");
+ const dashArray = String(Number(width) * Number(dash));
+ const defGuid = Utils.GenerateGuid();
+ const arrowDim = Math.max(0.5, 8 / Math.log(Math.max(2, strokeWidth)));
+ return (<svg fill={fill === "none" ? color : fill}> {/* setting the svg fill sets the arrowhead fill */}
+ {nodefs ? (null) : <defs>
+ {arrowStart !== "dot" && arrowEnd !== "dot" ? (null) : <marker id={`dot${defGuid}`} orient="auto" overflow="visible">
+ <circle r={1} fill="context-stroke" />
+ </marker>}
+ {arrowStart !== "arrowHead" && arrowEnd !== "arrowHead" ? (null) : <marker id={`arrowHead${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7">
+ <polygon points={`${arrowDim} ${-Math.max(1, arrowDim / 2)}, ${arrowDim} ${Math.max(1, arrowDim / 2)}, -1 0`} />
+ </marker>}
+ {arrowStart !== "arrowEnd" && arrowEnd !== "arrowEnd" ? (null) : <marker id={`arrowEnd${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7">
+ <polygon points={`${2 - arrowDim} ${-Math.max(1, arrowDim / 2)}, ${2 - arrowDim} ${Math.max(1, arrowDim / 2)}, 3 0`} />
+ </marker>}
+ </defs>}
- return (
<polyline
points={strpts}
style={{
- filter: drawHalo ? "url(#dangerShine)" : undefined,
- fill: "none",
+ filter: drawHalo ? "url(#inkSelectionHalo)" : undefined,
+ fill,
opacity: strokeWidth !== width ? 0.5 : undefined,
pointerEvents: pevents as any,
stroke: color ?? "rgb(0, 0, 0)",
strokeWidth: strokeWidth,
strokeLinejoin: "round",
- strokeLinecap: "round"
+ strokeLinecap: "round",
+ strokeDasharray: dashArray
}}
+ markerStart={`url(#${arrowStart + defGuid})`}
+ markerEnd={`url(#${arrowEnd + defGuid})`}
/>
- );
+
+ </svg>);
}
+ // export function makeArrow() {
+ // return (
+ // InkOptionsMenu.Instance.getColors().map(color => {
+ // const id1 = "arrowHeadTest" + color;
+ // console.log(color);
+ // <marker id={id1} orient="auto" overflow="visible" refX="0" refY="1" markerWidth="10" markerHeight="7">
+ // <polygon points="0 0, 3 1, 0 2" fill={"#" + color} />
+ // </marker>;
+ // })
+ // );
+ // }
+
export function makePolygon(shape: string, points: { X: number, Y: number }[]) {
if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y + 1 === points[0].Y) {
//pointer is up (first and last points are the same)
@@ -199,24 +233,24 @@ export namespace InteractionUtils {
}
points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
return points;
- case "arrow":
- const x1 = left;
- const y1 = top;
- const x2 = right;
- const y2 = bottom;
- const L1 = Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + (Math.pow(Math.abs(y1 - y2), 2)));
- const L2 = L1 / 5;
- const angle = 0.785398;
- const x3 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) + (y1 - y2) * Math.sin(angle));
- const y3 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) - (x1 - x2) * Math.sin(angle));
- const x4 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle));
- const y4 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) + (x1 - x2) * Math.sin(angle));
- points.push({ X: x1, Y: y1 });
- points.push({ X: x2, Y: y2 });
- points.push({ X: x3, Y: y3 });
- points.push({ X: x4, Y: y4 });
- points.push({ X: x2, Y: y2 });
- return points;
+ // case "arrow":
+ // const x1 = left;
+ // const y1 = top;
+ // const x2 = right;
+ // const y2 = bottom;
+ // const L1 = Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + (Math.pow(Math.abs(y1 - y2), 2)));
+ // const L2 = L1 / 5;
+ // const angle = 0.785398;
+ // const x3 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) + (y1 - y2) * Math.sin(angle));
+ // const y3 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) - (x1 - x2) * Math.sin(angle));
+ // const x4 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle));
+ // const y4 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) + (x1 - x2) * Math.sin(angle));
+ // points.push({ X: x1, Y: y1 });
+ // points.push({ X: x2, Y: y2 });
+ // points.push({ X: x3, Y: y3 });
+ // points.push({ X: x4, Y: y4 });
+ // points.push({ X: x2, Y: y2 });
+ // return points;
case "line":
points.push({ X: left, Y: top });
points.push({ X: right, Y: bottom });
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 94a0da985..9b4dc2630 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -63,6 +63,9 @@ export class LinkManager {
const protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, null));
return protomatch1 || protomatch2 || Doc.AreProtosEqual(link, anchor);
});
+ DocListCast(anchor[Doc.LayoutFieldKey(anchor) + "-annotations"]).map(anno => {
+ related.push(...LinkManager.Instance.getAllRelatedLinks(anno));
+ });
return related;
}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index eb905d237..024532f90 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -3,6 +3,8 @@ import { Doc } from "../../fields/Doc";
import { DocumentView } from "../views/nodes/DocumentView";
import { computedFn } from "mobx-utils";
import { List } from "../../fields/List";
+import { Scripting } from "./Scripting";
+import { DocumentManager } from "./DocumentManager";
export namespace SelectionManager {
@@ -10,17 +12,12 @@ export namespace SelectionManager {
@observable IsDragging: boolean = false;
SelectedDocuments: ObservableMap<DocumentView, boolean> = new ObservableMap();
- clearSelection() {
- if (window.getSelection) { window.getSelection()?.removeAllRanges(); }
- else if (document.getSelection()) { document.getSelection()?.empty(); }
- }
@action
SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
// if doc is not in SelectedDocuments, add it
if (!manager.SelectedDocuments.get(docView)) {
if (!ctrlPressed) {
this.DeselectAll();
- this.clearSelection();
}
manager.SelectedDocuments.set(docView, true);
@@ -86,3 +83,9 @@ export namespace SelectionManager {
}
}
+
+Scripting.addGlobal(function selectDoc(doc: any) {
+ const view = DocumentManager.Instance.getDocumentView(doc);
+ view && SelectionManager.SelectDoc(view, false);
+ //Doc.UserDoc().activeSelection = new List([doc]);
+}); \ No newline at end of file
diff --git a/src/client/views/.DS_Store b/src/client/views/.DS_Store
index 5008ddfcf..3717a2923 100644
--- a/src/client/views/.DS_Store
+++ b/src/client/views/.DS_Store
Binary files differ
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index fcaea0f0b..c05ca33fb 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -271,7 +271,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
const considerPush = isText && this.considerGoogleDocsPush;
return <div className="documentButtonBar">
<div className="documentButtonBar-button">
- <DocumentLinksButton View={this.view0!} AlwaysOn={true} />
+ <DocumentLinksButton View={this.view0} AlwaysOn={true} />
</div>
<div className="documentButtonBar-button">
{this.templateButton}
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 11aaaaf8c..5948ada88 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -22,11 +22,20 @@ $linkGap : 3px;
}
+ .documentDecorations-rotation {
+ pointer-events: auto;
+ // cursor: grabbing;
+ cursor: ns-resize;
+ width: 10px;
+ height: 10px;
+ }
+
.documentDecorations-resizer {
pointer-events: auto;
background: $alt-accent;
opacity: 0.1;
}
+
.documentDecorations-resizer:hover {
opacity: 1;
}
@@ -87,14 +96,17 @@ $linkGap : 3px;
background: unset;
opacity: 1;
}
+
#documentDecorations-topLeftResizer {
- border-left: 2px solid;
- border-top: solid 2px;
+ border-left: 2px solid;
+ border-top: solid 2px;
}
+
#documentDecorations-bottomRightResizer {
- border-right: 2px solid;
- border-bottom: solid 2px;
+ border-right: 2px solid;
+ border-bottom: solid 2px;
}
+
#documentDecorations-topLeftResizer:hover,
#documentDecorations-bottomRightResizer:hover {
opacity: 1;
@@ -110,14 +122,17 @@ $linkGap : 3px;
background: unset;
opacity: 1;
}
+
#documentDecorations-topRightResizer {
- border-right: 2px solid;
- border-top: 2px solid;
+ border-right: 2px solid;
+ border-top: 2px solid;
}
+
#documentDecorations-bottomLeftResizer {
- border-left: 2px solid;
- border-bottom: 2px solid;
+ border-left: 2px solid;
+ border-bottom: 2px solid;
}
+
#documentDecorations-topRightResizer:hover,
#documentDecorations-bottomLeftResizer:hover {
cursor: nesw-resize;
@@ -139,10 +154,11 @@ $linkGap : 3px;
width: 25px;
height: calc(100% + 8px); // 8px for the height of the top resizer bar
grid-column-start: 2;
- grid-column-end : 2;
+ grid-column-end: 2;
pointer-events: all;
padding-left: 5px;
}
+
.documentDecorations-title {
opacity: 1;
grid-column-start: 3;
@@ -150,14 +166,16 @@ $linkGap : 3px;
pointer-events: auto;
overflow: hidden;
text-align: center;
- display: flex;
+ display: flex;
border-bottom: solid 1px;
- margin-left:10px;
+ margin-left: 10px;
width: calc(100% - 10px);
}
+
.focus-visible {
- margin-left:0px;
+ margin-left: 0px;
}
+
.publishBox {
width: 20px;
height: 22px;
@@ -175,10 +193,10 @@ $linkGap : 3px;
.documentDecorations-iconifyButton {
opacity: 1;
grid-column-start: 4;
- grid-column-end: 6;
+ grid-column-end: 5;
pointer-events: all;
text-align: center;
- left: -20px;
+ left: -25px;
top: -2px;
cursor: pointer;
position: absolute;
@@ -189,10 +207,13 @@ $linkGap : 3px;
.documentDecorations-closeButton {
opacity: 1;
grid-column-start: 4;
- grid-column-end: 6;
+ grid-column-end: 5;
pointer-events: all;
text-align: center;
cursor: pointer;
+ width: 15px;
+ margin-left: -8px;
+ margin-top: auto;
}
.documentDecorations-minimizeButton {
@@ -208,8 +229,9 @@ $linkGap : 3px;
width: 8px;
height: $MINIMIZED_ICON_SIZE;
max-height: 20px;
- > svg {
- margin:0;
+
+ >svg {
+ margin: 0;
}
}
@@ -348,7 +370,8 @@ $linkGap : 3px;
padding: 2px 12px;
list-style: none;
- .templateToggle, .chromeToggle {
+ .templateToggle,
+ .chromeToggle {
text-align: left;
}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d7b0ab7a9..4fda10926 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,5 +1,5 @@
import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faTextHeight, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons';
+import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faTextHeight, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes, faAngleLeft, faAngleRight, faAngleDoubleLeft, faAngleDoubleRight, faPause } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
@@ -17,11 +17,14 @@ import { DocumentButtonBar } from './DocumentButtonBar';
import './DocumentDecorations.scss';
import { DocumentView } from "./nodes/DocumentView";
import React = require("react");
-import { Id } from '../../fields/FieldSymbols';
+import { Id, Copy, Update } from '../../fields/FieldSymbols';
import e = require('express');
import { CollectionDockingView } from './collections/CollectionDockingView';
import { SnappingManager } from '../util/SnappingManager';
import { HtmlField } from '../../fields/HtmlField';
+import { InkData, InkField, InkTool } from "../../fields/InkField";
+import { update } from 'serializr';
+import { Transform } from "../util/Transform";
library.add(faCaretUp);
library.add(faObjectGroup);
@@ -38,6 +41,11 @@ library.add(faCheckCircle);
library.add(faCloudUploadAlt);
library.add(faSyncAlt);
library.add(faShare);
+library.add(faAngleDoubleLeft);
+library.add(faAngleDoubleRight);
+library.add(faAngleLeft);
+library.add(faAngleRight);
+library.add(faPause);
@observer
export class DocumentDecorations extends React.Component<{}, { value: string }> {
@@ -50,6 +58,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
private _resizeUndo?: UndoManager.Batch;
private _offX = 0; _offY = 0; // offset from click pt to inner edge of resize border
private _snapX = 0; _snapY = 0; // last snapped location of resize border
+ private _prevX = 0;
+ private _prevY = 0;
+ private _centerPoints: { X: number, Y: number }[] = [];
+
@observable private _accumulatedTitle = "";
@observable private _titleControlString: string = "#title";
@observable private _edtingTitle = false;
@@ -70,7 +82,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
get Bounds(): { x: number, y: number, b: number, r: number } {
return SelectionManager.SelectedDocuments().reduce((bounds, documentView) => {
if (documentView.props.renderDepth === 0 ||
- documentView.props.treeViewId ||
+ documentView.props.treeViewDoc ||
Doc.AreProtosEqual(documentView.props.Document, Doc.UserDoc())) {
return bounds;
}
@@ -238,6 +250,82 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
return false;
}
+ @action
+ onRotateDown = (e: React.PointerEvent): void => {
+ setupMoveUpEvents(this, e, this.onRotateMove, this.onRotateUp, (e) => { });
+ this._prevX = e.clientX;
+ this._prevY = e.clientY;
+ SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
+ const doc = Document(element.rootDoc);
+ if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
+ const ink = Cast(doc.data, InkField)?.inkData;
+ if (ink) {
+ const xs = ink.map(p => p.X);
+ const ys = ink.map(p => p.Y);
+ const left = Math.min(...xs);
+ const top = Math.min(...ys);
+ const right = Math.max(...xs);
+ const bottom = Math.max(...ys);
+ // this._centerPoints.push({ X: ((right - left) / 2) + left, Y: ((bottom - top) / 2) + bottom });
+ this._centerPoints.push({ X: left, Y: top });
+ }
+ }
+ }));
+
+ }
+ @action
+ onRotateMove = (e: PointerEvent, down: number[]): boolean => {
+
+ // const distance = Math.sqrt((this._prevY - e.clientY) * (this._prevY - e.clientY) + (this._prevX - e.clientX) * (this._prevX - e.clientX));
+ const distance = Math.abs(this._prevY - e.clientY);
+ var angle = 0;
+ //think of a better condition later...
+ // if ((down[0] < e.clientX && this._prevY < e.clientY) || (down[0] > e.clientX && this._prevY > e.clientY)) {
+ if (e.clientY > this._prevY) {
+ angle = distance * (Math.PI / 180);
+ // } else if ((down[0] < e.clientX && this._prevY > e.clientY) || (down[0] > e.clientX && this._prevY <= e.clientY)) {
+ } else if (e.clientY < this._prevY) {
+ angle = - distance * (Math.PI / 180);
+ }
+ this._prevX = e.clientX;
+ this._prevY = e.clientY;
+ var index = 0;
+ SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
+ const doc = Document(element.rootDoc);
+ if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
+ const ink = Cast(doc.data, InkField)?.inkData;
+ if (ink) {
+
+ const newPoints: { X: number, Y: number }[] = [];
+ for (var i = 0; i < ink.length; i++) {
+ const newX = Math.cos(angle) * (ink[i].X - this._centerPoints[index].X) - Math.sin(angle) * (ink[i].Y - this._centerPoints[index].Y) + this._centerPoints[index].X;
+ const newY = Math.sin(angle) * (ink[i].X - this._centerPoints[index].X) + Math.cos(angle) * (ink[i].Y - this._centerPoints[index].Y) + this._centerPoints[index].Y;
+ newPoints.push({ X: newX, Y: newY });
+ }
+ doc.data = new InkField(newPoints);
+ const xs = newPoints.map(p => p.X);
+ const ys = newPoints.map(p => p.Y);
+ const left = Math.min(...xs);
+ const top = Math.min(...ys);
+ const right = Math.max(...xs);
+ const bottom = Math.max(...ys);
+
+ doc._height = (bottom - top) * element.props.ScreenToLocalTransform().Scale;
+ doc._width = (right - left) * element.props.ScreenToLocalTransform().Scale;
+
+ }
+ index++;
+ }
+ }));
+ return false;
+ }
+
+ onRotateUp = (e: PointerEvent) => {
+ this._centerPoints = [];
+ }
+
+
+
_initialAutoHeight = false;
_dragHeights = new Map<Doc, number>();
@action
@@ -520,12 +608,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}}>
{maximizeIcon}
{titleArea}
- <div className="documentDecorations-iconifyButton" title={`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`} onPointerDown={this.onIconifyDown}>
- {"_"}
- </div>
+ {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
+ <div className="documentDecorations-iconifyButton" title={`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`} onPointerDown={this.onIconifyDown}>
+ {"_"}
+ </div>}
<div className="documentDecorations-closeButton" title="Open Document in Tab" onPointerDown={this.onMaximizeDown}>
{SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."}
</div>
+ <div id="documentDecorations-rotation" className="documentDecorations-rotation"
+ onPointerDown={this.onRotateDown}> ⟲ </div>
<div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer"
onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-topResizer" className="documentDecorations-resizer"
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index ee3ce1cf3..bab3a1634 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -31,6 +31,7 @@ export interface EditableProps {
fontStyle?: string;
fontSize?: number;
height?: number | "auto";
+ sizeToContent?: boolean;
maxHeight?: number;
display?: string;
autosuggestProps?: {
@@ -60,13 +61,11 @@ export interface EditableProps {
*/
@observer
export class EditableView extends React.Component<EditableProps> {
- public static loadId = "";
@observable _editing: boolean = false;
constructor(props: EditableProps) {
super(props);
this._editing = this.props.editing ? true : false;
- EditableView.loadId = "";
}
// @action
@@ -150,37 +149,44 @@ export class EditableView extends React.Component<EditableProps> {
@action
setIsFocused = (value: boolean) => {
const wasFocused = this._editing;
- this._editing = value;
+ //this._editing = value;
return wasFocused !== this._editing;
}
_ref = React.createRef<HTMLDivElement>();
+ renderEditor() {
+ return this.props.autosuggestProps
+ ? <Autosuggest
+ {...this.props.autosuggestProps.autosuggestProps}
+ inputProps={{
+ className: "editableView-input",
+ onKeyDown: this.onKeyDown,
+ autoFocus: true,
+ onBlur: e => this.finalizeEdit(e.currentTarget.value, false, true),
+ onPointerDown: this.stopPropagation,
+ onClick: this.stopPropagation,
+ onPointerUp: this.stopPropagation,
+ value: this.props.autosuggestProps.value,
+ onChange: this.props.autosuggestProps.onChange
+ }}
+ />
+ : <input className="editableView-input"
+ defaultValue={this.props.GetValue()}
+ onKeyDown={this.onKeyDown}
+ autoFocus={true}
+ onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true)}
+ onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation}
+ style={{ display: this.props.display, fontSize: this.props.fontSize, minWidth: 20 }}
+ placeholder={this.props.placeholder}
+ />;
+ }
render() {
if (this._editing && this.props.GetValue() !== undefined) {
- return this.props.autosuggestProps
- ? <Autosuggest
- {...this.props.autosuggestProps.autosuggestProps}
- inputProps={{
- className: "editableView-input",
- onKeyDown: this.onKeyDown,
- autoFocus: true,
- onBlur: e => this.finalizeEdit(e.currentTarget.value, false, true),
- onPointerDown: this.stopPropagation,
- onClick: this.stopPropagation,
- onPointerUp: this.stopPropagation,
- value: this.props.autosuggestProps.value,
- onChange: this.props.autosuggestProps.onChange
- }}
- />
- : <input className="editableView-input"
- defaultValue={this.props.GetValue()}
- onKeyDown={this.onKeyDown}
- autoFocus={true}
- onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true)}
- onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation}
- style={{ display: this.props.display, fontSize: this.props.fontSize }}
- placeholder={this.props.placeholder}
- />;
+ return this.props.sizeToContent ?
+ <div style={{ display: "grid", minWidth: 100 }}>
+ <div style={{ display: "inline-block", position: "relative", height: 0, width: "100%", overflow: "hidden" }}>{this.props.GetValue()}</div>
+ {this.renderEditor()}
+ </div> : this.renderEditor();
} else {
this.props.autosuggestProps?.resetValue();
return (this.props.contents instanceof ObjectField ? (null) :
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index aeac1d4a9..85695bbac 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -18,12 +18,13 @@ import { LinkManager } from "../util/LinkManager";
import { Scripting } from "../util/Scripting";
import { Transform } from "../util/Transform";
import "./GestureOverlay.scss";
-import { ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from "./InkingStroke";
+import { ActiveInkBezierApprox, ActiveArrowStart, ActiveArrowEnd, ActiveFillColor, ActiveInkColor, ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth, SetActiveFillColor, SetActiveArrowStart, SetActiveArrowEnd, ActiveDash, SetActiveDash } from "./InkingStroke";
import { DocumentView } from "./nodes/DocumentView";
import { RadialMenu } from "./nodes/RadialMenu";
import HorizontalPalette from "./Palette";
import { Touchable } from "./Touchable";
import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
+import HeightLabel from "./collections/collectionMulticolumn/MultirowHeightLabel";
@observer
export default class GestureOverlay extends Touchable {
@@ -32,6 +33,10 @@ export default class GestureOverlay extends Touchable {
@observable public InkShape: string = "";
@observable public SavedColor?: string;
@observable public SavedWidth?: string;
+ @observable public SavedFill?: string;
+ @observable public SavedArrowStart: string = "none";
+ @observable public SavedArrowEnd: string = "none";
+ @observable public SavedDash: String = "0";
@observable public Tool: ToolglassTools = ToolglassTools.None;
@observable private _thumbX?: number;
@@ -627,7 +632,9 @@ export default class GestureOverlay extends Touchable {
this.makePolygon(this.InkShape, false);
this.dispatchGesture(GestureUtils.Gestures.Stroke);
this._points = [];
- this.InkShape = "";
+ if (this.InkShape !== "noRec") {
+ this.InkShape = "";
+ }
}
// if we're not drawing in a toolglass try to recognize as gesture
else {
@@ -677,6 +684,10 @@ export default class GestureOverlay extends Touchable {
} else {
this._points = [];
}
+ SetActiveArrowStart("none");
+ GestureOverlay.Instance.SavedArrowStart = ActiveArrowStart();
+ SetActiveArrowEnd("none");
+ GestureOverlay.Instance.SavedArrowEnd = ActiveArrowEnd();
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
}
@@ -688,7 +699,9 @@ export default class GestureOverlay extends Touchable {
var left = Math.min(...xs);
var bottom = Math.max(...ys);
var top = Math.min(...ys);
-
+ if (shape === "noRec") {
+ return;
+ }
if (!gesture) {
//if shape options is activated in inkOptionMenu
//take second to last point because _point[length-1] is _points[0]
@@ -747,7 +760,7 @@ export default class GestureOverlay extends Touchable {
case "line":
this._points.push({ X: left, Y: top });
this._points.push({ X: right, Y: bottom });
- this._points.push({ X: right, Y: bottom - 1 });
+ // this._points.push({ X: right, Y: bottom - 1 });
break;
case "arrow":
const x1 = left;
@@ -766,7 +779,7 @@ export default class GestureOverlay extends Touchable {
this._points.push({ X: x3, Y: y3 });
this._points.push({ X: x4, Y: y4 });
this._points.push({ X: x2, Y: y2 });
- this._points.push({ X: x1, Y: y1 - 1 });
+ // this._points.push({ X: x1, Y: y1 - 1 });
}
}
@@ -802,19 +815,28 @@ export default class GestureOverlay extends Touchable {
}
@computed get elements() {
- const B = this.svgBounds;
const width = Number(ActiveInkWidth());
+ const B = this.svgBounds;
+ B.left = B.left - width / 2;
+ B.right = B.right + width / 2;
+ B.top = B.top - width / 2;
+ B.bottom = B.bottom + width / 2;
+ B.width += width;
+ B.height += width;
return [
this.props.children,
this._palette,
[this._strokes.map((l, i) => {
const b = this.getBounds(l);
return <svg key={i} width={b.width} height={b.height} style={{ transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000, overflow: "visible" }}>
- {InteractionUtils.CreatePolyline(l, b.left, b.top, ActiveInkColor(), width, width, ActiveInkBezierApprox(), 1, 1, this.InkShape, "none", false)}
+ {InteractionUtils.CreatePolyline(l, b.left, b.top, ActiveInkColor(), width, width,
+ ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(),
+ ActiveDash(), 1, 1, this.InkShape, "none", false, false)}
</svg>;
}),
- this._points.length <= 1 ? (null) : <svg key="svg" width={B.width} height={B.height} style={{ transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000, overflow: "visible" }}>
- {InteractionUtils.CreatePolyline(this._points, B.left, B.top, ActiveInkColor(), width, width, ActiveInkBezierApprox(), 1, 1, this.InkShape, "none", false)}
+ this._points.length <= 1 ? (null) : <svg key="svg" width={B.width} height={B.height}
+ style={{ transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000, overflow: "visible" }}>
+ {InteractionUtils.CreatePolyline(this._points, B.left, B.top, ActiveInkColor(), width, width, ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), 1, 1, this.InkShape, "none", false, false)}
</svg>]
];
}
@@ -903,12 +925,20 @@ Scripting.addGlobal("GestureOverlay", GestureOverlay);
Scripting.addGlobal(function setToolglass(tool: any) {
runInAction(() => GestureOverlay.Instance.Tool = tool);
});
-Scripting.addGlobal(function setPen(width: any, color: any) {
+Scripting.addGlobal(function setPen(width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) {
runInAction(() => {
GestureOverlay.Instance.SavedColor = ActiveInkColor();
SetActiveInkColor(color);
GestureOverlay.Instance.SavedWidth = ActiveInkWidth();
SetActiveInkWidth(width);
+ GestureOverlay.Instance.SavedFill = ActiveFillColor();
+ SetActiveFillColor(fill);
+ GestureOverlay.Instance.SavedArrowStart = ActiveArrowStart();
+ SetActiveArrowStart(arrowStart);
+ GestureOverlay.Instance.SavedArrowEnd = ActiveArrowEnd();
+ SetActiveArrowStart(arrowEnd);
+ GestureOverlay.Instance.SavedDash = ActiveDash();
+ SetActiveDash(dash);
});
});
Scripting.addGlobal(function resetPen() {
diff --git a/src/client/views/InkingStroke.scss b/src/client/views/InkingStroke.scss
index 433433a42..30ab1967e 100644
--- a/src/client/views/InkingStroke.scss
+++ b/src/client/views/InkingStroke.scss
@@ -4,4 +4,8 @@
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 cdabd213b..c32e76cec 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -4,7 +4,7 @@ import { observer } from "mobx-react";
import { documentSchema } from "../../fields/documentSchemas";
import { InkData, InkField, InkTool } from "../../fields/InkField";
import { makeInterface } from "../../fields/Schema";
-import { Cast, StrCast, BoolCast } from "../../fields/Types";
+import { Cast, StrCast } from "../../fields/Types";
import { TraceMobx } from "../../fields/util";
import { CognitiveServices } from "../cognitive_services/CognitiveServices";
import { InteractionUtils } from "../util/InteractionUtils";
@@ -54,10 +54,13 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
const scaleY = (this.props.PanelHeight() - strokeWidth) / (height - strokeWidth);
const strokeColor = StrCast(this.layoutDoc.color, ActiveInkColor());
const points = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth,
- StrCast(this.layoutDoc.strokeBezier, ActiveInkBezierApprox()), scaleX, scaleY, "", "none", (this.props.isSelected() || BoolCast(this.props.backgroundHalo?.())) && strokeWidth <= 5);
+ StrCast(this.layoutDoc.strokeBezier, ActiveInkBezierApprox()), StrCast(this.layoutDoc.fillColor, ActiveFillColor()),
+ StrCast(this.layoutDoc.arrowStart, ActiveArrowStart()), StrCast(this.layoutDoc.arrowEnd, ActiveArrowEnd()),
+ StrCast(this.layoutDoc.dash, ActiveDash()), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
const hpoints = InteractionUtils.CreatePolyline(data, left, top,
this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, (strokeWidth + 15),
- StrCast(this.layoutDoc.strokeBezier, ActiveInkBezierApprox()), scaleX, scaleY, "", this.props.active() ? "visiblestroke" : "none", false);
+ StrCast(this.layoutDoc.strokeBezier, ActiveInkBezierApprox()), StrCast(this.layoutDoc.fillColor, ActiveFillColor()),
+ "none", "none", "0", scaleX, scaleY, "", this.props.active() ? "visiblepainted" : "none", false, true);
return (
<svg className="inkingStroke"
width={width}
@@ -65,29 +68,14 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
style={{
pointerEvents: this.props.Document.isInkMask ? "all" : "none",
transform: this.props.Document.isInkMask ? "translate(2500px, 2500px)" : undefined,
- mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset"
+ mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset",
+ overflow: "visible",
}}
onContextMenu={() => {
ContextMenu.Instance.addItem({ description: "Analyze Stroke", event: this.analyzeStrokes, icon: "paint-brush" });
ContextMenu.Instance.addItem({ description: "Make Mask", event: this.makeMask, icon: "paint-brush" });
}}
><defs>
- <filter id="dangerShine">
- <feColorMatrix type="matrix"
- result="color"
- values="1 0 0 0 0
- 0 0 0 0 0
- 0 0 0 0 0
- 0 0 0 1 0">
- </feColorMatrix>
- <feGaussianBlur in="color" stdDeviation="4" result="blur"></feGaussianBlur>
- <feOffset in="blur" dx="0" dy="0" result="offset"></feOffset>
- <feMerge>
- <feMergeNode in="bg"></feMergeNode>
- <feMergeNode in="offset"></feMergeNode>
- <feMergeNode in="SourceGraphic"></feMergeNode>
- </feMerge>
- </filter>
</defs>
{hpoints}
{points}
@@ -100,17 +88,33 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
export function SetActiveInkWidth(width: string): void { !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width); }
export function SetActiveBezierApprox(bezier: string): void { ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? "" : bezier); }
export function SetActiveInkColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeInkColor = value); }
+export function SetActiveFillColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeFillColor = value); }
+export function SetActiveArrowStart(value: string) { ActiveInkPen() && (ActiveInkPen().activeArrowStart = value); }
+export function SetActiveArrowEnd(value: string) { ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value); }
+export function SetActiveDash(dash: string): void { !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); }
export function ActiveInkPen(): Doc { return Cast(Doc.UserDoc().activeInkPen, Doc, null); }
export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, "black"); }
+export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, "none"); }
+export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, "none"); }
+export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, "none"); }
+export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, "0"); }
export function ActiveInkWidth(): string { return StrCast(ActiveInkPen()?.activeInkWidth, "1"); }
export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); }
-Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any) {
+Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) {
Doc.SetSelectedTool(pen ? InkTool.Highlighter : InkTool.None);
SetActiveInkWidth(width);
SetActiveInkColor(color);
+ SetActiveFillColor(fill);
+ SetActiveArrowStart(arrowStart);
+ SetActiveArrowEnd(arrowEnd);
+ SetActiveDash(dash);
});
Scripting.addGlobal(function activateEraser(pen: any) { return Doc.SetSelectedTool(pen ? InkTool.Eraser : InkTool.None); });
Scripting.addGlobal(function activateStamp(pen: any) { return Doc.SetSelectedTool(pen ? InkTool.Stamp : InkTool.None); });
Scripting.addGlobal(function deactivateInk() { return Doc.SetSelectedTool(InkTool.None); });
Scripting.addGlobal(function setInkWidth(width: any) { return SetActiveInkWidth(width); });
-Scripting.addGlobal(function setInkColor(color: any) { return SetActiveInkColor(color); }); \ No newline at end of file
+Scripting.addGlobal(function setInkColor(color: any) { return SetActiveInkColor(color); });
+Scripting.addGlobal(function setFillColor(fill: any) { return SetActiveFillColor(fill); });
+Scripting.addGlobal(function setActiveArrowStart(arrowStart: any) { return SetActiveArrowStart(arrowStart); });
+Scripting.addGlobal(function setActiveArrowEnd(arrowEnd: any) { return SetActiveArrowStart(arrowEnd); });
+Scripting.addGlobal(function setActiveDash(dash: any) { return SetActiveDash(dash); });
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 6878658a8..77e37834d 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -6,12 +6,14 @@ import * as React from 'react';
import { DocServer } from "../DocServer";
import { AssignAllExtensions } from "../../extensions/General/Extensions";
import { Networking } from "../Network";
+import { CollectionView } from "./collections/CollectionView";
AssignAllExtensions();
export let resolvedPorts: { server: number, socket: number };
(async () => {
+ window.location.search.includes("safe") && CollectionView.SetSafeMode(true);
const info = await CurrentUserUtils.loadCurrentUser();
resolvedPorts = JSON.parse(await Networking.FetchFromServer("/resolvedPorts"));
DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, info.email);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 055e39669..cea664543 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -5,7 +5,7 @@ import {
faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote,
- faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faQuoteLeft, faSortAmountDown
+ faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown
} from '@fortawesome/free-solid-svg-icons';
import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -82,7 +82,10 @@ export class MainView extends React.Component {
public isPointerDown = false;
+
componentDidMount() {
+ DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_viewType"]); // can play with these fields on someone else's
+
const tag = document.createElement('script');
const proto = DocServer.GetRefField("rtfProto").then(proto => {
@@ -139,7 +142,7 @@ export class MainView extends React.Component {
faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell,
- faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faQuoteLeft, faSortAmountDown);
+ faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown);
this.initEventListeners();
this.initAuthenticationRouters();
}
@@ -204,7 +207,6 @@ export class MainView extends React.Component {
_width: this._panelWidth * .7,
_height: this._panelHeight,
title: "Collection " + workspaceCount,
- _LODdisable: true
};
const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
@@ -353,15 +355,6 @@ export class MainView extends React.Component {
}
@action
- pointerOverDragger = () => {
- // if (this.flyoutWidth === 0) {
- // this.flyoutWidth = 250;
- // this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30;
- // this._flyoutTranslate = false;
- // }
- }
-
- @action
pointerLeaveDragger = () => {
if (!this._flyoutTranslate) {
this.flyoutWidth = 0;
@@ -373,13 +366,13 @@ export class MainView extends React.Component {
onPointerMove = (e: PointerEvent) => {
this.flyoutWidth = Math.max(e.clientX, 0);
Math.abs(this.flyoutWidth - this._flyoutSizeOnDown) > 6 && (this._canClick = false);
- this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30;
+ this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30;
}
@action
onPointerUp = (e: PointerEvent) => {
if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4 && this._canClick) {
this.flyoutWidth = this.flyoutWidth < 15 ? 250 : 0;
- this.flyoutWidth && (this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30);
+ this.flyoutWidth && (this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30);
}
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
@@ -473,7 +466,7 @@ export class MainView extends React.Component {
}} >
<div style={{ display: "contents", flexDirection: "row", position: "relative" }}>
<div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
- <div className="mainView-libraryHandle" onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}
+ <div className="mainView-libraryHandle" onPointerDown={this.onPointerDown}
style={{ backgroundColor: this.defaultBackgroundColors(sidebar) }}>
<span title="library View Dragger" style={{
width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw",
@@ -500,7 +493,7 @@ export class MainView extends React.Component {
public static expandFlyout = action(() => {
MainView.Instance._flyoutTranslate = true;
MainView.Instance.flyoutWidth = (MainView.Instance.flyoutWidth || 250);
- MainView.Instance.sidebarButtonsDoc.columnWidth = MainView.Instance.flyoutWidth / 3 - 30;
+ MainView.Instance.sidebarButtonsDoc._columnWidth = MainView.Instance.flyoutWidth / 3 - 30;
});
@computed get expandButton() {
@@ -575,8 +568,32 @@ export class MainView extends React.Component {
</div>;
}
+ @computed get inkResources() {
+ return <svg width={0} height={0}>
+ <defs>
+ <filter id="inkSelectionHalo">
+ <feColorMatrix type="matrix"
+ result="color"
+ values="1 0 0 0 0
+ 0 0 0 0 0
+ 0 0 0 0 0
+ 0 0 0 1 0">
+ </feColorMatrix>
+ <feGaussianBlur in="color" stdDeviation="4" result="blur"></feGaussianBlur>
+ <feOffset in="blur" dx="0" dy="0" result="offset"></feOffset>
+ <feMerge>
+ <feMergeNode in="bg"></feMergeNode>
+ <feMergeNode in="offset"></feMergeNode>
+ <feMergeNode in="SourceGraphic"></feMergeNode>
+ </feMerge>
+ </filter>
+ </defs>
+ </svg>;
+ }
+
render() {
return (<div className={"mainView-container" + (this.darkScheme ? "-dark" : "")} ref={this._mainViewRef}>
+ {this.inkResources}
<DictationOverlay />
<SharingManager />
<SettingsManager />
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index ab984f727..f54bd3aff 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -288,13 +288,13 @@ export class Timeline extends React.Component<FieldViewProps> {
resetView(doc: Doc) {
doc._panX = doc._customOriginX ?? 0;
doc._panY = doc._customOriginY ?? 0;
- doc.scale = doc._customOriginScale ?? 1;
+ doc._viewScale = doc._customOriginScale ?? 1;
}
setView(doc: Doc) {
doc._customOriginX = doc._panX;
doc._customOriginY = doc._panY;
- doc._customOriginScale = doc.scale;
+ doc._customOriginScale = doc._viewScale;
}
/**
* zooming mechanism (increment and spacing changes)
diff --git a/src/client/views/collections/CollectionCarousel3DView.scss b/src/client/views/collections/CollectionCarousel3DView.scss
new file mode 100644
index 000000000..5f8895c1f
--- /dev/null
+++ b/src/client/views/collections/CollectionCarousel3DView.scss
@@ -0,0 +1,107 @@
+.collectionCarousel3DView-outer {
+ height: 100%;
+ position: relative;
+ background-color: white;
+}
+
+.carousel-wrapper {
+ display: flex;
+ position: absolute;
+ top: 15%;
+ align-items: center;
+ transition: transform 0.3s cubic-bezier(0.455, 0.03, 0.515, 0.955);
+
+ .collectionCarousel3DView-item,
+ .collectionCarousel3DView-item-active {
+ flex: 1;
+ transition: opacity 0.3s linear, transform 0.5s cubic-bezier(0.455, 0.03, 0.515, 0.955);
+ pointer-events: none;
+ }
+
+ .collectionCarousel3DView-item-active {
+ pointer-events: unset;
+ }
+}
+
+.dot-bar {
+ display: flex;
+ position: absolute;
+ justify-content: center;
+ bottom: 5%;
+ width: 100%;
+
+ .dot,
+ .dot-active {
+ height: 10px;
+ width: 10px;
+ border-radius: 50%;
+ margin: 3px;
+ display: inline-block;
+ background-color: lightgrey;
+ cursor: pointer;
+ }
+
+ .dot-active {
+ background-color: grey;
+ }
+}
+
+.carousel3DView-back,
+.carousel3DView-fwd,
+.carousel3DView-back-scroll,
+.carousel3DView-fwd-scroll,
+.carousel3DView-back-scroll-hidden,
+.carousel3DView-fwd-scroll-hidden {
+ position: absolute;
+ display: flex;
+ width: 30;
+ height: 30;
+ align-items: center;
+ border-radius: 5px;
+ justify-content: center;
+ background: rgba(255, 255, 255, 0.46);
+ cursor: pointer;
+}
+
+.carousel3DView-fwd,
+.carousel3DView-back {
+ top: 50%;
+}
+
+.carousel3DView-fwd-scroll,
+.carousel3DView-back-scroll,
+.carousel3DView-fwd-scroll-hidden,
+.carousel3DView-back-scroll-hidden {
+ top: calc(50% - 30px);
+}
+
+.carousel3DView-fwd,
+.carousel3DView-fwd-scroll,
+.carousel3DView-fwd-scroll-hidden {
+ right: 0;
+}
+
+.carousel3DView-back,
+.carousel3DView-back-scroll,
+.carousel3DView-back-scroll-hidden {
+ left: 0;
+}
+
+.carousel3DView-fwd-scroll-hidden,
+.carousel3DView-back-scroll-hidden {
+ opacity: 0;
+ transition: opacity 0.5s linear;
+ pointer-events: none;
+}
+
+.carousel3DView-fwd-scroll,
+.carousel3DView-back-scroll {
+ opacity: 1;
+}
+
+.carousel3DView-back:hover,
+.carousel3DView-fwd:hover,
+.carousel3DView-back-scroll:hover,
+.carousel3DView-fwd-scroll:hover {
+ background: lightgray;
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
new file mode 100644
index 000000000..1344b70f4
--- /dev/null
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -0,0 +1,197 @@
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { documentSchema, collectionSchema } from '../../../fields/documentSchemas';
+import { makeInterface } from '../../../fields/Schema';
+import { NumCast, StrCast, ScriptCast } from '../../../fields/Types';
+import { DragManager } from '../../util/DragManager';
+import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
+import "./CollectionCarousel3DView.scss";
+import { CollectionSubView } from './CollectionSubView';
+import { Doc } from '../../../fields/Doc';
+import { ContextMenu } from '../ContextMenu';
+import { ObjectField } from '../../../fields/ObjectField';
+import { returnFalse, Utils } from '../../../Utils';
+import { ScriptField } from '../../../fields/ScriptField';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Id } from '../../../fields/FieldSymbols';
+
+type Carousel3DDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
+const Carousel3DDocument = makeInterface(documentSchema, collectionSchema);
+
+@observer
+export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocument) {
+ @computed get scrollSpeed() {
+ return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; //default scroll speed
+ }
+
+ private _dropDisposer?: DragManager.DragDropDisposer;
+
+ componentWillUnmount() { this._dropDisposer?.(); }
+
+ protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+ this._dropDisposer?.();
+ if (ele) {
+ this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
+ }
+ }
+
+ panelWidth = () => this.props.PanelWidth() / 3;
+ panelHeight = () => this.props.PanelHeight() * 0.6;
+ @computed get content() {
+ const currentIndex = NumCast(this.layoutDoc._itemIndex);
+ const displayDoc = (childPair: { layout: Doc, data: Doc }) => {
+ return <ContentFittingDocumentView {...this.props}
+ onDoubleClick={ScriptCast(this.layoutDoc.onChildDoubleClick)}
+ onClick={ScriptField.MakeScript(
+ "child._showCaption = 'caption'",
+ { child: Doc.name },
+ { child: childPair.layout })}
+ renderDepth={this.props.renderDepth + 1}
+ LayoutTemplate={this.props.ChildLayoutTemplate}
+ LayoutTemplateString={this.props.ChildLayoutString}
+ Document={childPair.layout}
+ DataDoc={childPair.data}
+ PanelWidth={this.panelWidth}
+ PanelHeight={this.panelHeight}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ bringToFront={returnFalse}
+ parentActive={this.props.active}
+ />;
+ };
+
+ return (this.childLayoutPairs.map((childPair, index) => {
+ return (
+ <div key={childPair.layout[Id]}
+ className={`collectionCarousel3DView-item${index === currentIndex ? "-active" : ""} ${index}`}
+ style={index === currentIndex ?
+ { opacity: '1', transform: 'scale(1.3)' } :
+ { opacity: '0.5', transform: 'scale(0.6)', userSelect: 'none' }}>
+ {displayDoc(childPair)}
+ </div>);
+ }));
+ }
+
+ changeSlide = (direction: number) => {
+ this.layoutDoc._itemIndex = (NumCast(this.layoutDoc._itemIndex) + direction + this.childLayoutPairs.length) % this.childLayoutPairs.length;
+ }
+
+ onArrowClick = (e: React.MouseEvent, direction: number) => {
+ e.stopPropagation();
+ this.changeSlide(direction);
+ !this.layoutDoc.autoScrollOn && (this.layoutDoc.showScrollButton = (direction === 1) ? "fwd" : "back"); // while autoscroll is on, keep the other autoscroll button hidden
+ !this.layoutDoc.autoScrollOn && this.fadeScrollButton(); // keep pause button visible while autoscroll is on
+ }
+
+ interval?: number;
+ startAutoScroll = (direction: number) => {
+ this.interval = window.setInterval(() => {
+ console.log(this.interval, this.scrollSpeed);
+ this.changeSlide(direction);
+ }, this.scrollSpeed);
+ }
+
+ stopAutoScroll = () => {
+ window.clearInterval(this.interval);
+ this.interval = undefined;
+ this.fadeScrollButton();
+ }
+
+ toggleAutoScroll = (direction: number) => {
+ this.layoutDoc.autoScrollOn = this.layoutDoc.autoScrollOn ? false : true;
+ this.layoutDoc.autoScrollOn ? this.startAutoScroll(direction) : this.stopAutoScroll();
+ }
+
+ fadeScrollButton = () => {
+ window.setTimeout(() => {
+ !this.layoutDoc.autoScrollOn && (this.layoutDoc.showScrollButton = "none"); //fade away after 1.5s if it's not clicked.
+ }, 1500);
+ }
+
+ onContextMenu = (e: React.MouseEvent): void => {
+ // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
+ if (!e.isPropagationStopped()) {
+ ContextMenu.Instance.addItem({
+ description: "Make Hero Image", event: () => {
+ const index = NumCast(this.layoutDoc._itemIndex);
+ (this.dataDoc || Doc.GetProto(this.props.Document)).hero = ObjectField.MakeCopy(this.childLayoutPairs[index].layout.data as ObjectField);
+ }, icon: "plus"
+ });
+ }
+ }
+ _downX = 0;
+ _downY = 0;
+ onPointerDown = (e: React.PointerEvent) => {
+ this._downX = e.clientX;
+ this._downY = e.clientY;
+ console.log("CAROUSEL down");
+ document.addEventListener("pointerup", this.onpointerup);
+ }
+ private _lastTap: number = 0;
+ private _doubleTap = false;
+ onpointerup = (e: PointerEvent) => {
+ console.log("CAROUSEL up");
+ this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2);
+ this._lastTap = Date.now();
+ }
+
+ onClick = (e: React.MouseEvent) => {
+ if (this._doubleTap) {
+ e.stopPropagation();
+ this.props.Document.isLightboxOpen = true;
+ }
+ }
+
+ @computed get buttons() {
+ if (!this.props.active()) return null;
+ return <div className="arrow-buttons" >
+ <div key="back" className="carousel3DView-back" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }}
+ onClick={(e) => this.onArrowClick(e, -1)}
+ >
+ <FontAwesomeIcon icon={"angle-left"} size={"2x"} />
+ </div>
+ <div key="fwd" className="carousel3DView-fwd" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }}
+ onClick={(e) => this.onArrowClick(e, 1)}
+ >
+ <FontAwesomeIcon icon={"angle-right"} size={"2x"} />
+ </div>
+ {this.autoScrollButton}
+ </div>;
+ }
+
+ @computed get autoScrollButton() {
+ const whichButton = this.layoutDoc.showScrollButton;
+ return <>
+ <div className={`carousel3DView-back-scroll${whichButton === "back" ? "" : "-hidden"}`} style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }}
+ onClick={() => this.toggleAutoScroll(-1)}>
+ {this.layoutDoc.autoScrollOn ? <FontAwesomeIcon icon={"pause"} size={"1x"} /> : <FontAwesomeIcon icon={"angle-double-left"} size={"1x"} />}
+ </div>
+ <div className={`carousel3DView-fwd-scroll${whichButton === "fwd" ? "" : "-hidden"}`} style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }}
+ onClick={() => this.toggleAutoScroll(1)}>
+ {this.layoutDoc.autoScrollOn ? <FontAwesomeIcon icon={"pause"} size={"1x"} /> : <FontAwesomeIcon icon={"angle-double-right"} size={"1x"} />}
+ </div>
+ </>;
+ }
+
+ @computed get dots() {
+ return (this.childLayoutPairs.map((_child, index) => {
+ return <div key={Utils.GenerateGuid()} className={`dot${index === NumCast(this.layoutDoc._itemIndex) ? "-active" : ""}`}
+ onClick={() => this.layoutDoc._itemIndex = index} />;
+ }));
+ }
+
+ render() {
+ const index = NumCast(this.layoutDoc._itemIndex);
+ const translateX = (1 - index) / this.childLayoutPairs.length * 100;
+
+ return <div className="collectionCarousel3DView-outer" onClick={this.onClick} onPointerDown={this.onPointerDown} ref={this.createDashEventsTarget} onContextMenu={this.onContextMenu}>
+ <div className="carousel-wrapper" style={{ transform: `translateX(calc(${translateX}%` }}>
+ {this.content}
+ </div>
+ {this.props.Document._chromeStatus !== "replaced" ? this.buttons : (null)}
+ <div className="dot-bar">
+ {this.dots}
+ </div>
+ </div>;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index e0b53e762..627b22417 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -111,8 +111,8 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
const key = StrCast(this.props.parent.props.Document._pivotField);
const castedValue = this.getValue(value);
if (castedValue) {
- if (this.props.parent.sectionHeaders) {
- if (this.props.parent.sectionHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
+ if (this.props.parent.columnHeaders) {
+ if (this.props.parent.columnHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
return false;
}
}
@@ -151,9 +151,9 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
this._createAliasSelected = false;
const key = StrCast(this.props.parent.props.Document._pivotField);
this.props.docList.forEach(d => d[key] = undefined);
- if (this.props.parent.sectionHeaders && this.props.headingObject) {
- const index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
- this.props.parent.sectionHeaders.splice(index, 1);
+ if (this.props.parent.columnHeaders && this.props.headingObject) {
+ const index = this.props.parent.columnHeaders.indexOf(this.props.headingObject);
+ this.props.parent.columnHeaders.splice(index, 1);
}
}));
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 56a2a517c..b780da97e 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -240,10 +240,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH - this.previewWidth(); }
@computed get columns() {
- return Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField), []);
+ return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []);
}
set columns(columns: SchemaHeaderField[]) {
- this.props.Document.schemaColumns = new List<SchemaHeaderField>(columns);
+ this.props.Document._schemaHeaders = new List<SchemaHeaderField>(columns);
}
@computed get childDocs() {
@@ -375,10 +375,12 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
constructor(props: SchemaTableProps) {
super(props);
// convert old schema columns (list of strings) into new schema columns (list of schema header fields)
- const oldSchemaColumns = Cast(this.props.Document.schemaColumns, listSpec("string"), []);
- if (oldSchemaColumns && oldSchemaColumns.length && typeof oldSchemaColumns[0] !== "object") {
- const newSchemaColumns = oldSchemaColumns.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
- this.props.Document.schemaColumns = new List<SchemaHeaderField>(newSchemaColumns);
+ const oldSchemaHeaders = Cast(this.props.Document._schemaHeaders, listSpec("string"), []);
+ if (oldSchemaHeaders?.length && typeof oldSchemaHeaders[0] !== "object") {
+ const newSchemaHeaders = oldSchemaHeaders.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
+ this.props.Document._schemaHeaders = new List<SchemaHeaderField>(newSchemaHeaders);
+ } else if (this.props.Document._schemaHeaders === undefined) {
+ this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb")]);
}
}
@@ -740,7 +742,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
if(col === undefined) {
return (doc as any)[key][row + ${row}];
}
- return (doc as any)[key][row + ${row}][(doc as any).schemaColumns[col + ${col}].heading];
+ return (doc as any)[key][row + ${row}][(doc as any)._schemaHeaders[col + ${col}].heading];
}
return ${script}`;
const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: false, transformer: this.createTransformer(row, col) });
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 4aab43b08..7bdf5e7df 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -43,27 +43,26 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
@observable _heightMap = new Map<string, number>();
@observable _cursor: CursorProperty = "grab";
@observable _scroll = 0; // used to force the document decoration to update when scrolling
- @computed get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); }
- @computed get pivotField() { return StrCast(this.props.Document._pivotField); }
+ @computed get columnHeaders() { return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField)); }
+ @computed get pivotField() { return StrCast(this.layoutDoc._pivotField); }
@computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout); }
- @computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
- @computed get yMargin() { return Math.max(this.props.Document._showTitle && !this.props.Document._showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
- @computed get gridGap() { return NumCast(this.props.Document._gridGap, 10); }
- @computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
+ @computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
+ @computed get yMargin() { return Math.max(this.layoutDoc._showTitle && !this.layoutDoc._showTitleHover ? 30 : 0, NumCast(this.layoutDoc._yMargin, 0)); } // 2 * this.gridGap)); }
+ @computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); }
+ @computed get isStackingView() { return BoolCast(this.layoutDoc._columnsStack, true); }
@computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
- @computed get showAddAGroup() { return (this.pivotField && (this.props.Document._chromeStatus !== 'view-mode' && this.props.Document._chromeStatus !== 'disabled')); }
+ @computed get showAddAGroup() { return (this.pivotField && (this.layoutDoc._chromeStatus !== 'view-mode' && this.layoutDoc._chromeStatus !== 'disabled')); }
@computed get columnWidth() {
- TraceMobx();
- return Math.min(this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin,
- this.isStackingView ? Number.MAX_VALUE : this.props.Document.columnWidth === -1 ? this.props.PanelWidth() - 2 * this.xMargin : NumCast(this.props.Document.columnWidth, 250));
+ return Math.min(this.props.PanelWidth() / this.props.ContentScaling() - 2 * this.xMargin,
+ this.isStackingView ? Number.MAX_VALUE : this.layoutDoc._columnWidth === -1 ? this.props.PanelWidth() - 2 * this.xMargin : NumCast(this.layoutDoc._columnWidth, 250));
}
@computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; }
constructor(props: any) {
super(props);
- if (this.sectionHeaders === undefined) {
- this.props.Document.sectionHeaders = new List<SchemaHeaderField>();
+ if (this.columnHeaders === undefined) {
+ this.layoutDoc._columnHeaders = new List<SchemaHeaderField>();
}
}
@@ -89,14 +88,14 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
}
get Sections() {
- if (!this.pivotField || this.sectionHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
+ if (!this.pivotField || this.columnHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
- if (this.sectionHeaders === undefined) {
- setTimeout(() => this.props.Document.sectionHeaders = new List<SchemaHeaderField>(), 0);
+ if (this.columnHeaders === undefined) {
+ setTimeout(() => this.layoutDoc._columnHeaders = new List<SchemaHeaderField>(), 0);
return new Map<SchemaHeaderField, Doc[]>();
}
- const sectionHeaders = Array.from(this.sectionHeaders);
- const fields = new Map<SchemaHeaderField, Doc[]>(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
+ const columnHeaders = Array.from(this.columnHeaders);
+ const fields = new Map<SchemaHeaderField, Doc[]>(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
let changed = false;
this.filteredChildren.map(d => {
const sectionValue = (d[this.pivotField] ? d[this.pivotField] : `NO ${this.pivotField.toUpperCase()} VALUE`) as object;
@@ -105,26 +104,26 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
const castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
// look for if header exists already
- const existingHeader = sectionHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
+ const existingHeader = columnHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
if (existingHeader) {
fields.get(existingHeader)!.push(d);
}
else {
const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`);
fields.set(newSchemaHeader, [d]);
- sectionHeaders.push(newSchemaHeader);
+ columnHeaders.push(newSchemaHeader);
changed = true;
}
});
// remove all empty columns if hideHeadings is set
- if (this.props.Document.hideHeadings) {
+ if (this.layoutDoc._columnsHideIfEmpty) {
Array.from(fields.keys()).filter(key => !fields.get(key)!.length).map(header => {
fields.delete(header);
- sectionHeaders.splice(sectionHeaders.indexOf(header), 1);
+ columnHeaders.splice(columnHeaders.indexOf(header), 1);
changed = true;
});
}
- changed && setTimeout(action(() => { if (this.sectionHeaders) { this.sectionHeaders.length = 0; this.sectionHeaders.push(...sectionHeaders); } }), 0);
+ changed && setTimeout(action(() => { if (this.columnHeaders) { this.columnHeaders.length = 0; this.columnHeaders.push(...columnHeaders); } }), 0);
return fields;
}
@@ -136,7 +135,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
if (!layoutDoc._fitWidth && nw && nh) {
const aspect = nw && nh ? nh / nw : 1;
- if (!(this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
+ if (!(this.layoutDoc._columnsFill)) wid = Math.min(layoutDoc[WidthSym](), wid);
return wid * aspect;
}
return layoutDoc._fitWidth ? wid * NumCast(layoutDoc.scrollHeight, nh) / (nw || 1) : layoutDoc[HeightSym]();
@@ -147,7 +146,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
// reset section headers when a new filter is inputted
this._pivotFieldDisposer = reaction(
() => this.pivotField,
- () => this.props.Document.sectionHeaders = new List()
+ () => this.layoutDoc._columnHeaders = new List()
);
}
componentWillUnmount() {
@@ -211,7 +210,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
fitToBox={false}
dontRegisterView={this.props.dontRegisterView}
rootSelected={this.rootSelected}
- dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
+ dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
onClick={this.onChildClickHandler}
onDoubleClick={this.onChildDoubleClickHandler}
ScreenToLocalTransform={dxf}
@@ -236,7 +235,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
if (!d) return 0;
const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.());
const nw = NumCast(layoutDoc._nativeWidth);
- return Math.min(nw && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+ return Math.min(nw && !this.layoutDoc._columnsFill ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
}
getDocHeight(d?: Doc) {
if (!d) return 0;
@@ -246,7 +245,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
if (!layoutDoc._fitWidth && nw && nh) {
const aspect = nw && nh ? nh / nw : 1;
- if (!(this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
+ if (!(this.layoutDoc._columnsFill)) wid = Math.min(layoutDoc[WidthSym](), wid);
return wid * aspect;
}
return layoutDoc._fitWidth ? !nh ? this.props.PanelHeight() - 2 * this.yMargin :
@@ -259,7 +258,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
}
@action
onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
- this.layoutDoc.columnWidth = Math.max(10, this.columnWidth + delta[0]);
+ this.layoutDoc._columnWidth = Math.max(10, this.columnWidth + delta[0]);
return false;
}
@@ -341,7 +340,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
this.refList.push(ref);
const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
this.observer = new _global.ResizeObserver(action((entries: any) => {
- if (this.props.Document._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+ if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
Doc.Layout(doc)._height = Math.min(1200, Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
}
}));
@@ -388,7 +387,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
this.refList.push(ref);
const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
this.observer = new _global.ResizeObserver(action((entries: any) => {
- if (this.props.Document._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+ if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
Doc.Layout(doc)._height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
}
}));
@@ -411,9 +410,9 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
@action
addGroup = (value: string) => {
- if (value && this.sectionHeaders) {
+ if (value && this.columnHeaders) {
const schemaHdrField = new SchemaHeaderField(value);
- this.sectionHeaders.push(schemaHdrField);
+ this.columnHeaders.push(schemaHdrField);
DocUtils.addFieldEnumerations(undefined, this.pivotField, [{ title: value, _backgroundColor: schemaHdrField.color }]);
return true;
}
@@ -421,22 +420,22 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
}
sortFunc = (a: [SchemaHeaderField, Doc[]], b: [SchemaHeaderField, Doc[]]): 1 | -1 => {
- const descending = BoolCast(this.props.Document.stackingHeadersSortDescending);
+ const descending = StrCast(this.layoutDoc._columnsSort) === "descending";
const firstEntry = descending ? b : a;
const secondEntry = descending ? a : b;
return firstEntry[0].heading > secondEntry[0].heading ? 1 : -1;
}
onToggle = (checked: Boolean) => {
- this.props.Document._chromeStatus = checked ? "collapsed" : "view-mode";
+ this.layoutDoc._chromeStatus = checked ? "collapsed" : "view-mode";
}
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
if (!e.isPropagationStopped()) {
const subItems: ContextMenuProps[] = [];
- subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" });
- subItems.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
+ subItems.push({ description: `${this.layoutDoc._columnsFill ? "Variable Size" : "Autosize"} Column`, event: () => this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill, icon: "plus" });
+ subItems.push({ description: `${this.layoutDoc._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" });
}
}
@@ -446,7 +445,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
if (this.pivotField) {
const entries = Array.from(this.Sections.entries());
- sections = entries.sort(this.sortFunc);
+ sections = this.layoutDoc._columnsSort ? entries.sort(this.sortFunc) : entries;
}
return sections.map((section, i) => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1], i === 0));
}
@@ -489,10 +488,10 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
style={{ width: !this.isStackingView ? "100%" : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
<EditableView {...editableViewProps} />
</div>}
- {this.props.Document._chromeStatus !== 'disabled' && this.props.isSelected() ? <Switch
+ {this.layoutDoc._chromeStatus !== 'disabled' && this.props.isSelected() ? <Switch
onChange={this.onToggle}
onClick={this.onToggle}
- defaultChecked={this.props.Document._chromeStatus !== 'view-mode'}
+ defaultChecked={this.layoutDoc._chromeStatus !== 'view-mode'}
checkedChildren="edit"
unCheckedChildren="view"
/> : null}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index eb48d87c8..2f4a25bfe 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -96,8 +96,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
const key = StrCast(this.props.parent.props.Document._pivotField);
const castedValue = this.getValue(value);
if (castedValue) {
- if (this.props.parent.sectionHeaders) {
- if (this.props.parent.sectionHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
+ if (this.props.parent.columnHeaders) {
+ if (this.props.parent.columnHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
return false;
}
}
@@ -148,9 +148,9 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
deleteColumn = () => {
const key = StrCast(this.props.parent.props.Document._pivotField);
this.props.docList.forEach(d => d[key] = undefined);
- if (this.props.parent.sectionHeaders && this.props.headingObject) {
- const index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
- this.props.parent.sectionHeaders.splice(index, 1);
+ if (this.props.parent.columnHeaders && this.props.headingObject) {
+ const index = this.props.parent.columnHeaders.indexOf(this.props.headingObject);
+ this.props.parent.columnHeaders.splice(index, 1);
}
}
@@ -168,7 +168,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
startDrag = (e: PointerEvent, down: number[], delta: number[]) => {
const alias = Doc.MakeAlias(this.props.parent.props.Document);
- alias._width = this.props.parent.props.PanelWidth() / (Cast(this.props.parent.props.Document.sectionHeaders, listSpec(SchemaHeaderField))?.length || 1);
+ alias._width = this.props.parent.props.PanelWidth() / (Cast(this.props.parent.columnHeaders, listSpec(SchemaHeaderField))?.length || 1);
alias._pivotField = undefined;
const key = StrCast(this.props.parent.props.Document._pivotField);
let value = this.getValue(this._heading);
@@ -259,8 +259,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
}
}, icon: "compress-arrows-alt"
}));
- layoutItems.push({ description: ":freeform", event: () => this.props.parent.props.addDocument(Docs.Create.FreeformDocument([], { _width: 200, _height: 200, _LODdisable: true })), icon: "compress-arrows-alt" });
- layoutItems.push({ description: ":carousel", event: () => this.props.parent.props.addDocument(Docs.Create.CarouselDocument([], { _width: 400, _height: 200, _LODdisable: true })), icon: "compress-arrows-alt" });
+ layoutItems.push({ description: ":freeform", event: () => this.props.parent.props.addDocument(Docs.Create.FreeformDocument([], { _width: 200, _height: 200 })), icon: "compress-arrows-alt" });
+ layoutItems.push({ description: ":carousel", event: () => this.props.parent.props.addDocument(Docs.Create.CarouselDocument([], { _width: 400, _height: 200 })), icon: "compress-arrows-alt" });
layoutItems.push({ description: ":columns", event: () => this.props.parent.props.addDocument(Docs.Create.MulticolumnDocument([], { _width: 200, _height: 200 })), icon: "compress-arrows-alt" });
layoutItems.push({ description: ":image", event: () => this.props.parent.props.addDocument(Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { _width: 200, _height: 200 })), icon: "compress-arrows-alt" });
@@ -359,7 +359,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
background: this._background
}}
ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
- {this.props.parent.Document.hideHeadings ? (null) : headingView}
+ {this.props.parent.Document._columnsHideIfEmpty ? (null) : headingView}
{
this.collapsed ? (null) :
<div style={{ marginTop: 5 }}>
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 00d6d59c8..49480e759 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -19,6 +19,7 @@ import { undoBatch, UndoManager } from "../../util/UndoManager";
import { DocComponent } from "../DocComponent";
import { FieldViewProps } from "../nodes/FieldView";
import React = require("react");
+import * as rp from 'request-promise';
export interface CollectionViewProps extends FieldViewProps {
addDocument: (document: Doc | Doc[]) => boolean;
@@ -103,8 +104,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
}
docFilters = () => {
return this.props.ignoreFields?.includes("_docFilters") ? [] :
- this.props.docFilters !== returnEmptyFilter ? this.props.docFilters() :
- Cast(this.props.Document._docFilters, listSpec("string"), []);
+ [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])];
}
@computed get childDocs() {
const docFilters = this.docFilters();
@@ -341,7 +341,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const rect = "getBoundingClientRect" in focusNode ? focusNode.getBoundingClientRect() : focusNode?.parentElement.getBoundingClientRect();
const x = (rect?.x || 0);
const y = NumCast(srcWeb._scrollTop) + (rect?.y || 0);
- const anchor = Docs.Create.FreeformDocument([], { _LODdisable: true, _backgroundColor: "transparent", _width: 25, _height: 25, x, y, annotationOn: srcWeb });
+ const anchor = Docs.Create.FreeformDocument([], { _backgroundColor: "transparent", _width: 25, _height: 25, x, y, annotationOn: srcWeb });
anchor.context = srcWeb;
const key = Doc.LayoutFieldKey(srcWeb);
Doc.AddDocToList(srcWeb, key + "-annotations", anchor);
@@ -400,9 +400,9 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const item = e.dataTransfer.items[i];
if (item.kind === "string" && item.type.includes("uri")) {
const stringContents = await new Promise<string>(resolve => item.getAsString(resolve));
- const type = "html";// (await rp.head(Utils.CorsProxy(stringContents)))["content-type"];
+ const type = (await rp.head(Utils.CorsProxy(stringContents)))["content-type"];
if (type) {
- const doc = await DocUtils.DocumentFromType(type, stringContents, options);
+ const doc = await DocUtils.DocumentFromType(type, Utils.CorsProxy(stringContents), options);
doc && generatedDocuments.push(doc);
}
}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 2aac81146..50f0534bd 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -82,8 +82,6 @@
text-overflow: ellipsis;
white-space: pre-wrap;
min-width: 10px;
- // width:100%;//width: max-content;
-
}
.treeViewItem-openRight {
@@ -100,6 +98,7 @@
border-left: dashed 1px #00000042;
}
+.treeViewItem-header-editing,
.treeViewItem-header {
border: transparent 1px solid;
display: flex;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 68dc0ced2..f6f6fb7cc 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -13,7 +13,7 @@ import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
import { DocumentManager } from '../../util/DocumentManager';
import { SnappingManager } from '../../util/SnappingManager';
-import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager";
+import { DragManager, dropActionType } from "../../util/DragManager";
import { Scripting } from '../../util/Scripting';
import { SelectionManager } from '../../util/SelectionManager';
import { Transform } from '../../util/Transform';
@@ -55,7 +55,7 @@ export interface TreeViewProps {
ScreenToLocalTransform: () => Transform;
backgroundColor?: (doc: Doc) => string | undefined;
outerXf: () => { translateX: number, translateY: number };
- treeViewId: Doc;
+ treeViewDoc: Doc;
parentKey: string;
active: (outsideReaction?: boolean) => boolean;
treeViewHideHeaderFields: () => boolean;
@@ -76,31 +76,32 @@ export interface TreeViewProps {
* treeViewExpandedView : name of field whose contents are being displayed as the document's subtree
*/
class TreeView extends React.Component<TreeViewProps> {
- static _editTitleScript: ScriptField | undefined;
+ private _editTitleScript: ScriptField | undefined;
private _header?: React.RefObject<HTMLDivElement> = React.createRef();
private _treedropDisposer?: DragManager.DragDropDisposer;
private _dref = React.createRef<HTMLDivElement>();
private _tref = React.createRef<HTMLDivElement>();
private _docRef = React.createRef<DocumentView>();
+ private _uniqueId = Utils.GenerateGuid();
+ private _editMaxWidth: number | string = 0;
+ get doc() { return this.props.document; }
get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
- get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive
- get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.props.document.defaultExpandedView, this.noviceMode ? "layout" : "fields"); }
+ get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive
+ get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.doc.defaultExpandedView, this.noviceMode ? "layout" : "fields"); }
@observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
- set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = this._overrideTreeViewOpen = c; }
- @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.props.document.treeViewPreventOpen && BoolCast(this.props.document.treeViewOpen)) || this._overrideTreeViewOpen; }
- @computed get treeViewExpandedView() { return StrCast(this.props.document.treeViewExpandedView, this.defaultExpandedView); }
+ set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.doc.treeViewOpen = this._overrideTreeViewOpen = c; }
+ @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; }
+ @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.defaultExpandedView); }
@computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
- @computed get dataDoc() { return this.props.document[DataSym]; }
- @computed get fieldKey() {
- const splits = StrCast(Doc.LayoutField(this.props.document)).split("fieldKey={\'");
- return splits.length > 1 ? splits[1].split("\'")[0] : "data";
- }
+ @computed get dataDoc() { return this.doc[DataSym]; }
+ @computed get layoutDoc() { return Doc.Layout(this.doc); }
+ @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; }
childDocList(field: string) {
- const layout = Doc.LayoutField(this.props.document) instanceof Doc ? Doc.LayoutField(this.props.document) as Doc : undefined;
+ const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined;
return ((this.props.dataDoc ? DocListCast(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
(layout ? Cast(layout[field], listSpec(Doc)) : undefined) || // else if there's a layout doc, display it's fields
- Cast(this.props.document[field], listSpec(Doc))) as Doc[]; // otherwise use the document's data field
+ Cast(this.doc[field], listSpec(Doc))) as Doc[]; // otherwise use the document's data field
}
@computed get childDocs() { return this.childDocList(this.fieldKey); }
@computed get childLinks() { return this.childDocList("links"); }
@@ -109,22 +110,27 @@ class TreeView extends React.Component<TreeViewProps> {
Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
}
- @undoBatch openRight = () => this.props.addDocTab(this.props.document, "onRight", this.props.libraryPath);
+ @undoBatch openRight = () => this.props.addDocTab(this.doc, "onRight", this.props.libraryPath);
@undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
- return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc);
+ return this.doc !== target && this.props.deleteDoc(doc) && addDoc(doc);
}
@undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
- return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) =>
- flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
+ return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
}
@undoBatch @action removeDoc = (doc: Doc | Doc[]) => {
return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) =>
flg && Doc.RemoveDocFromList(this.props.containingCollection, Doc.LayoutFieldKey(this.props.containingCollection), doc), true);
}
+ constructor(props: any) {
+ super(props);
+ this._editTitleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); selectDoc(self);} `);
+ if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false);
+ }
+
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this._treedropDisposer?.();
- ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.props.document);
+ ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.doc);
}
onPointerEnter = (e: React.PointerEvent): void => {
@@ -136,7 +142,9 @@ class TreeView extends React.Component<TreeViewProps> {
}
onPointerLeave = (e: React.PointerEvent): void => {
Doc.UnBrushDoc(this.dataDoc);
- this._header!.current!.className = "treeViewItem-header";
+ if (this._header?.current?.className !== "treeViewItem-header-editing") {
+ this._header!.current!.className = "treeViewItem-header";
+ }
document.removeEventListener("pointermove", this.onDragMove, true);
}
onDragMove = (e: PointerEvent): void => {
@@ -144,7 +152,7 @@ class TreeView extends React.Component<TreeViewProps> {
const pt = [e.clientX, e.clientY];
const rect = this._header!.current!.getBoundingClientRect();
const before = pt[1] < rect.top + rect.height / 2;
- const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && DocListCast(this.dataDoc[this.fieldKey]).length);
+ const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
this._header!.current!.className = "treeViewItem-header";
if (inside) this._header!.current!.className += " treeViewItem-header-inside";
else if (before) this._header!.current!.className += " treeViewItem-header-above";
@@ -155,42 +163,38 @@ class TreeView extends React.Component<TreeViewProps> {
editableView = (key: string, style?: string) => (<EditableView
oneLine={true}
display={"inline-block"}
- editing={true /*this.dataDoc[Id] === EditableView.loadId*/}
- contents={StrCast(this.props.document[key])}
+ editing={true}
+ contents={StrCast(this.doc[key])}
height={12}
+ sizeToContent={true}
fontStyle={style}
fontSize={12}
- GetValue={() => StrCast(this.props.document[key])}
+ GetValue={() => StrCast(this.doc[key])}
SetValue={undoBatch((value: string) => {
- Doc.SetInPlace(this.props.document, key, value, false) || true;
- Doc.SetInPlace(this.props.document, "editTitle", undefined, false);
+ Doc.SetInPlace(this.doc, key, value, false) || true;
+ Doc.SetInPlace(this.doc, "editTitle", undefined, false);
})}
OnFillDown={undoBatch((value: string) => {
- Doc.SetInPlace(this.props.document, key, value, false);
- const doc = Docs.Create.FreeformDocument([], { title: "-", x: 0, y: 0, _width: 100, _height: 25, _LODdisable: true, templates: new List<string>([Templates.Title.Layout]) });
- Doc.SetInPlace(this.props.document, "editTitle", undefined, false);
- Doc.SetInPlace(doc, "editTitle", true, false);
+ Doc.SetInPlace(this.doc, key, value, false);
+ const doc = Docs.Create.FreeformDocument([], { title: "-", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
+ Doc.SetInPlace(this.doc, "editTitle", undefined, false);
+ Doc.SetInPlace(doc, "editTitle", "*", false);
return this.props.addDocument(doc);
})}
onClick={() => {
SelectionManager.DeselectAll();
- Doc.UserDoc().activeSelection = new List([this.props.document]);
+ Doc.UserDoc().activeSelection = new List([this.doc]);
return false;
}}
OnTab={undoBatch((shift?: boolean) => {
- EditableView.loadId = this.dataDoc[Id];
shift ? this.props.outdentDocument?.() : this.props.indentDocument?.();
- setTimeout(() => { // unsetting/setting brushing for this doc will recreate & refocus this editableView after all other treeview changes have been made to the Dom (which may remove focus from this document).
- Doc.UnBrushDoc(this.props.document);
- Doc.BrushDoc(this.props.document);
- EditableView.loadId = "";
- }, 0);
+ setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", "*", false), 0);
})}
/>)
preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
const dragData = de.complete.docDragData;
- dragData && (dragData.dropAction = this.props.treeViewId[Id] === dragData.treeViewId ? "same" : dragData.dropAction);
+ dragData && (dragData.dropAction = this.props.treeViewDoc === dragData.treeViewDoc ? "same" : dragData.dropAction);
}
@undoBatch
@@ -198,18 +202,18 @@ class TreeView extends React.Component<TreeViewProps> {
const pt = [de.x, de.y];
const rect = this._header!.current!.getBoundingClientRect();
const before = pt[1] < rect.top + rect.height / 2;
- const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && DocListCast(this.dataDoc[this.fieldKey]).length);
+ const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
const complete = de.complete;
if (complete.linkDragData) {
const sourceDoc = complete.linkDragData.linkSourceDocument;
- const destDoc = this.props.document;
+ const destDoc = this.doc;
DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link");
e.stopPropagation();
}
const docDragData = complete.docDragData;
if (docDragData) {
e.stopPropagation();
- if (docDragData.draggedDocuments[0] === this.props.document) return true;
+ if (docDragData.draggedDocuments[0] === this.doc) return true;
const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before);
let addDoc = parentAddDoc;
if (inside) {
@@ -222,35 +226,28 @@ class TreeView extends React.Component<TreeViewProps> {
return false;
}
- docTransform = () => {
- const { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!);
- const outerXf = this.props.outerXf();
- const offset = this.props.ScreenToLocalTransform().transformDirection((outerXf.translateX - translateX), outerXf.translateY - translateY);
- const finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
-
- return finalXf;
- }
- getTransform = () => {
- const { scale, translateX, translateY } = Utils.GetScreenTransform(this._tref.current!);
+ refTransform = (ref: HTMLDivElement) => {
+ const { scale, translateX, translateY } = Utils.GetScreenTransform(ref);
const outerXf = this.props.outerXf();
const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
- const finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
- return finalXf;
+ return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
}
+ docTransform = () => this.refTransform(this._dref.current!);
+ getTransform = () => this.refTransform(this._tref.current!);
docWidth = () => {
- const layoutDoc = Doc.Layout(this.props.document);
+ const layoutDoc = this.layoutDoc;
const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20));
return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20;
}
docHeight = () => {
- const layoutDoc = Doc.Layout(this.props.document);
+ const layoutDoc = this.layoutDoc;
const bounds = this.boundsOfCollectionDocument;
return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => {
const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
if (aspect) return this.docWidth() * aspect;
if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
- return layoutDoc._fitWidth ? (!this.props.document._nativeHeight ? NumCast(this.props.containingCollection._height) :
+ return layoutDoc._fitWidth ? (!this.doc._nativeHeight ? NumCast(this.props.containingCollection._height) :
Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth,
NumCast(this.props.containingCollection._height)))) :
NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50;
@@ -259,7 +256,7 @@ class TreeView extends React.Component<TreeViewProps> {
@computed get expandedField() {
const ids: { [key: string]: string } = {};
- const doc = this.props.document;
+ const doc = this.doc;
doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
const rows: JSX.Element[] = [];
@@ -273,13 +270,12 @@ class TreeView extends React.Component<TreeViewProps> {
const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce(
(flg, doc) => flg && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true), true);
contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
- DocListCast(contents), this.props.treeViewId, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
+ DocListCast(contents), this.props.treeViewDoc, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
[...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields);
} else {
- contentElement = <EditableView
- key="editableView"
+ contentElement = <EditableView key="editableView"
contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
height={13}
fontSize={12}
@@ -307,8 +303,8 @@ class TreeView extends React.Component<TreeViewProps> {
return rows;
}
- rtfWidth = () => Math.min(Doc.Layout(this.props.document)?.[WidthSym](), this.props.panelWidth() - 20);
- rtfHeight = () => this.rtfWidth() < Doc.Layout(this.props.document)?.[WidthSym]() ? Math.min(Doc.Layout(this.props.document)?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
+ rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20);
+ rtfHeight = () => this.rtfWidth() < this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
@computed get renderContent() {
TraceMobx();
@@ -320,32 +316,32 @@ class TreeView extends React.Component<TreeViewProps> {
const docs = expandKey === "links" ? this.childLinks : this.childDocs;
const sortKey = `${this.fieldKey}-sortAscending`;
return <ul key={expandKey + "more"} onClick={(e) => {
- this.props.document[sortKey] = (this.props.document[sortKey] ? false : (this.props.document[sortKey] === false ? undefined : true));
+ this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true));
e.stopPropagation();
}}>
{!docs ? (null) :
- TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document),
+ TreeView.GetChildElements(docs, this.props.treeViewDoc, this.layoutDoc,
this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
- StrCast(this.props.document.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
+ StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
- [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields)}
+ [...this.props.renderedIds, this.doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields)}
</ul >;
} else if (this.treeViewExpandedView === "fields") {
- return <ul><div ref={this._dref} style={{ display: "inline-block" }} key={this.props.document[Id] + this.props.document.title}>
+ return <ul><div ref={this._dref} style={{ display: "inline-block" }} key={this.doc[Id] + this.doc.title}>
{this.expandedField}
</div></ul>;
} else {
- const layoutDoc = Doc.Layout(this.props.document);
+ const layoutDoc = this.layoutDoc;
const panelHeight = layoutDoc.type === DocumentType.RTF ? this.rtfHeight : this.docHeight;
const panelWidth = layoutDoc.type === DocumentType.RTF ? this.rtfWidth : this.docWidth;
- return <div ref={this._dref} style={{ display: "inline-block", height: panelHeight() }} key={this.props.document[Id]}>
+ return <div ref={this._dref} style={{ display: "inline-block", height: panelHeight() }} key={this.doc[Id]}>
<ContentFittingDocumentView
Document={layoutDoc}
DataDoc={this.dataDoc}
LibraryPath={emptyPath}
renderDepth={this.props.renderDepth + 1}
rootSelected={returnTrue}
- treeViewId={this.props.treeViewId[Id]}
+ treeViewDoc={undefined}
backgroundColor={this.props.backgroundColor}
fitToBox={this.boundsOfCollectionDocument !== undefined}
FreezeDimensions={true}
@@ -372,17 +368,17 @@ class TreeView extends React.Component<TreeViewProps> {
}
}
- get onCheckedClick() { return this.props.onCheckedClick || ScriptCast(this.props.document.onCheckedClick); }
+ get onCheckedClick() { return this.props.onCheckedClick || ScriptCast(this.doc.onCheckedClick); }
@action
bulletClick = (e: React.MouseEvent) => {
- if (this.onCheckedClick && this.props.document.type !== DocumentType.COL) {
+ if (this.onCheckedClick && this.doc.type !== DocumentType.COL) {
// this.props.document.treeViewChecked = this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check";
this.onCheckedClick.script.run({
- this: this.props.document.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.props.document,
+ this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
heading: this.props.containingCollection.title,
- checked: this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check",
- containingTreeView: this.props.treeViewId,
+ checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? undefined : "check",
+ containingTreeView: this.props.treeViewDoc,
}, console.log);
} else {
this.treeViewOpen = !this.treeViewOpen;
@@ -392,104 +388,110 @@ class TreeView extends React.Component<TreeViewProps> {
@computed
get renderBullet() {
- const checked = this.props.document.type === DocumentType.COL ? undefined : this.onCheckedClick ? (this.props.document.treeViewChecked ? this.props.document.treeViewChecked : "unchecked") : undefined;
+ const checked = this.doc.type === DocumentType.COL ? undefined : this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined;
return <div className="bullet"
title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
onClick={this.bulletClick}
- style={{ color: StrCast(this.props.document.color, checked === "unchecked" ? "white" : "inherit"), opacity: checked === "unchecked" ? undefined : 0.4 }}>
+ style={{ color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"), opacity: checked === "unchecked" ? undefined : 0.4 }}>
{<FontAwesomeIcon icon={checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? (this.childDocs ? "caret-square-right" : "caret-right") : (this.childDocs ? "caret-square-down" : "caret-down"))} />}
</div>;
}
showContextMenu = (e: React.MouseEvent) => {
this._docRef.current?.ContentDiv && simulateMouseClick(this._docRef.current.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
- e.stopPropagation();
}
focusOnDoc = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(doc)?.props.focus(doc, true);
- contextMenuItems = () => {
- const focusScript = ScriptField.MakeFunction(`DocFocus(self)`);
- return [{ script: focusScript!, label: "Focus" }];
- }
+ contextMenuItems = () => [{ script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }];
+ truncateTitleWidth = () => NumCast(this.props.treeViewDoc.treeViewTruncateTitleWidth, 0);
+ showTitleEdit = () => ["*", this._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "");
/**
* Renders the EditableView title element for placement into the tree.
*/
@computed
get renderTitle() {
TraceMobx();
- (!TreeView._editTitleScript) && (TreeView._editTitleScript = ScriptField.MakeFunction("setInPlace(self, 'editTitle', true)"));
- const headerElements = (
+ const headerElements = this.props.treeViewHideHeaderFields() ? (null) :
<>
- <FontAwesomeIcon icon="cog" size="sm" onClick={e => this.showContextMenu(e)}></FontAwesomeIcon>
+ <FontAwesomeIcon icon="cog" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />
<span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
onPointerDown={action(() => {
if (this.treeViewOpen) {
- this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") :
- this.treeViewExpandedView === "fields" && Doc.Layout(this.props.document) ? "layout" :
- this.treeViewExpandedView === "layout" && this.props.document.links ? "links" :
+ this.doc.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") :
+ this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
+ this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
}
this.treeViewOpen = true;
})}>
{this.treeViewExpandedView}
</span>
- </>);
- const openRight = (<div className="treeViewItem-openRight" onClick={this.openRight}>
- <FontAwesomeIcon title="open in pane on right" icon="angle-right" size="lg" />
- </div>);
+ </>;
+ const view = this.showTitleEdit() ? this.editableView("title") :
+ <DocumentView
+ ref={this._docRef}
+ Document={this.doc}
+ DataDoc={undefined}
+ treeViewDoc={this.props.treeViewDoc}
+ LibraryPath={this.props.libraryPath || emptyPath}
+ addDocument={undefined}
+ addDocTab={this.props.addDocTab}
+ rootSelected={returnTrue}
+ pinToPres={emptyFunction}
+ onClick={this.props.onChildClick || this._editTitleScript}
+ dropAction={this.props.dropAction}
+ moveDocument={this.move}
+ removeDocument={this.removeDoc}
+ ScreenToLocalTransform={this.getTransform}
+ ContentScaling={returnOne}
+ PanelWidth={this.truncateTitleWidth}
+ PanelHeight={returnZero}
+ NativeHeight={returnZero}
+ NativeWidth={returnZero}
+ contextMenuItems={this.contextMenuItems}
+ renderDepth={1}
+ focus={returnTrue}
+ parentActive={returnTrue}
+ whenActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ dontRegisterView={BoolCast(this.props.treeViewDoc.dontRegisterChildViews)}
+ docFilters={returnEmptyFilter}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={this.props.containingCollection}
+ />;
return <>
<div className="docContainer" ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`}
style={{
- fontWeight: this.props.document.searchMatch ? "bold" : undefined,
- textDecoration: Doc.GetT(this.props.document, "title", "string", true) ? "underline" : undefined,
- outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
+ fontWeight: this.doc.searchMatch ? "bold" : undefined,
+ textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
+ outline: BoolCast(this.doc.workspaceBrush) ? "dashed 1px #06123232" : undefined,
pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none"
}} >
- {Doc.GetT(this.props.document, "editTitle", "boolean", true) ?
- this.editableView("title") :
- <DocumentView
- ref={this._docRef}
- Document={this.props.document}
- DataDoc={undefined}
- treeViewId={this.props.treeViewId[Id]}
- LibraryPath={this.props.libraryPath || emptyPath}
- addDocument={undefined}
- addDocTab={this.props.addDocTab}
- rootSelected={returnTrue}
- pinToPres={emptyFunction}
- onClick={this.props.onChildClick || TreeView._editTitleScript}
- dropAction={this.props.dropAction}
- moveDocument={this.move}
- removeDocument={this.removeDoc}
- ScreenToLocalTransform={this.getTransform}
- ContentScaling={returnOne}
- PanelWidth={returnZero}
- PanelHeight={returnZero}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
- contextMenuItems={this.contextMenuItems}
- renderDepth={1}
- focus={returnTrue}
- parentActive={returnTrue}
- whenActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
- dontRegisterView={BoolCast(this.props.treeViewId.dontRegisterChildViews)}
- docFilters={returnEmptyFilter}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={this.props.containingCollection}
- />}
+ {view}
</div >
- {this.props.treeViewHideHeaderFields() ? (null) : headerElements}
- {openRight}
+ {headerElements}
+ <div className="treeViewItem-openRight" onClick={this.openRight}>
+ <FontAwesomeIcon title="open in pane on right" icon="external-link-alt" size="sm" />
+ </div>
</>;
}
render() {
TraceMobx();
- const sorting = this.props.document[`${this.fieldKey}-sortAscending`];
- //setTimeout(() => runInAction(() => untracked(() => this._overrideTreeViewOpen = this.treeViewOpen)), 0);
+ const sorting = this.doc[`${this.fieldKey}-sortAscending`];
+ if (this.showTitleEdit()) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll
+ let par: any = this._header?.current;
+ if (par) {
+ while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode;
+ if (par) {
+ const par_rect = (par as HTMLElement).getBoundingClientRect();
+ const my_recct = this._docRef.current?.ContentDiv?.getBoundingClientRect();
+ this._editMaxWidth = Math.max(100, par_rect.right - (my_recct?.left || 0));
+ }
+ }
+ } else this._editMaxWidth = "";
return <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active() && SelectionManager.DeselectAll()}>
<li className="collection-child">
- <div className="treeViewItem-header" ref={this._header} onClick={e => {
+ <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
if (this.props.active(true)) {
e.stopPropagation();
e.preventDefault();
@@ -507,14 +509,14 @@ class TreeView extends React.Component<TreeViewProps> {
{this.renderTitle}
</div>
<div className="treeViewItem-border" style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
- {!this.treeViewOpen || this.props.renderedIds.indexOf(this.props.document[Id]) !== -1 ? (null) : this.renderContent}
+ {!this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent}
</div>
</li>
</div>;
}
public static GetChildElements(
childDocs: Doc[],
- treeViewId: Doc,
+ treeViewDoc: Doc,
containingCollection: Doc,
dataDoc: Doc | undefined,
key: string,
@@ -624,7 +626,7 @@ class TreeView extends React.Component<TreeViewProps> {
libraryPath={libraryPath ? [...libraryPath, containingCollection] : undefined}
containingCollection={containingCollection}
prevSibling={docs[i]}
- treeViewId={treeViewId}
+ treeViewDoc={treeViewDoc}
key={child[Id]}
indentDocument={indent}
outdentDocument={outdent}
@@ -665,21 +667,22 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
private treedropDisposer?: DragManager.DragDropDisposer;
private _mainEle?: HTMLDivElement;
- @computed get dataDoc() { return this.props.DataDoc || this.props.Document; }
+ @computed get doc() { return this.props.Document; }
+ @computed get dataDoc() { return this.props.DataDoc || this.doc; }
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this.treedropDisposer?.();
if (this._mainEle = ele) {
- this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.props.Document, this.onInternalPreDrop.bind(this));
+ this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
}
}
protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
const dragData = de.complete.docDragData;
if (dragData) {
- if (targetAction && !dragData.draggedDocuments.some(d => d.context === this.props.Document && this.childDocs.includes(d))) {
+ if (targetAction && !dragData.draggedDocuments.some(d => d.context === this.doc && this.childDocs.includes(d))) {
dragData.dropAction = targetAction;
- } else dragData.dropAction = this.props.Document[Id] === dragData?.treeViewId ? "same" : dragData.dropAction;
+ } else dragData.dropAction = this.doc === dragData?.treeViewDoc ? "same" : dragData.dropAction;
}
}
@@ -691,7 +694,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
@action
remove = (doc: Doc | Doc[]): boolean => {
const docs = doc instanceof Doc ? [doc] : doc;
- const targetDataDoc = this.props.Document[DataSym];
+ const targetDataDoc = this.doc[DataSym];
const value = DocListCast(targetDataDoc[this.props.fieldKey]);
const result = value.filter(v => !docs.includes(v));
if (result.length !== value.length) {
@@ -704,9 +707,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
addDoc = (doc: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => {
const doAddDoc = (doc: Doc | Doc[]) =>
(doc instanceof Doc ? [doc] : doc).reduce((flg, doc) =>
- flg && Doc.AddDocToList(this.props.Document[DataSym], this.props.fieldKey, doc, relativeTo, before, false, false, false), true);
- if (this.props.Document.resolvedDataDoc instanceof Promise) {
- this.props.Document.resolvedDataDoc.then((resolved: any) => doAddDoc(doc));
+ flg && Doc.AddDocToList(this.doc[DataSym], this.props.fieldKey, doc, relativeTo, before, false, false, false), true);
+ if (this.doc.resolvedDataDoc instanceof Promise) {
+ this.doc.resolvedDataDoc.then((resolved: any) => doAddDoc(doc));
} else {
doAddDoc(doc);
}
@@ -714,22 +717,23 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
}
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
- if (!e.isPropagationStopped() && this.props.Document === Doc.UserDoc().myWorkspaces) {
+ if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myWorkspaces) {
ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
- ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.props.Document), icon: "minus" });
+ ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.doc), icon: "minus" });
e.stopPropagation();
e.preventDefault();
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
- } else if (!e.isPropagationStopped() && this.props.Document === Doc.UserDoc().myRecentlyClosed) {
+ } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myRecentlyClosed) {
ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myRecentlyClosed = new List<Doc>(), icon: "plus" });
e.stopPropagation();
e.preventDefault();
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
} else {
const layoutItems: ContextMenuProps[] = [];
- layoutItems.push({ description: (this.props.Document.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.treeViewPreventOpen = !this.props.Document.treeViewPreventOpen, icon: "paint-brush" });
- layoutItems.push({ description: (this.props.Document.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.treeViewHideHeaderFields = !this.props.Document.treeViewHideHeaderFields, icon: "paint-brush" });
- layoutItems.push({ description: (this.props.Document.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.props.Document.treeViewHideTitle = !this.props.Document.treeViewHideTitle, icon: "paint-brush" });
+ layoutItems.push({ description: (this.doc.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.doc.treeViewPreventOpen = !this.doc.treeViewPreventOpen, icon: "paint-brush" });
+ layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" });
+ layoutItems.push({ description: (this.doc.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle, icon: "paint-brush" });
+ layoutItems.push({ description: (this.doc.treeViewHideLinkLines ? "Show" : "Hide") + " Link Lines", event: () => this.doc.treeViewHideLinkLines = !this.doc.treeViewHideLinkLines, icon: "paint-brush" });
ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
}
!Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({
@@ -745,7 +749,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
const fallback = ImageDocument("http://cs.brown.edu/~bcz/face.gif", { _width: 400 }); // replace with desired double click target
let pdfContent: string;
- DocListCast(this.dataDoc[this.props.fieldKey]).map(d => {
+ this.childDocs?.map(d => {
DocListCast(d.data).map((img, i) => {
const caption = (d.captions as any)[i];
if (caption) {
@@ -774,7 +778,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
onClicks.push({
- description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit"
+ description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit"
});
!existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
}
@@ -784,7 +788,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
@computed get renderClearButton() {
return <div id="toolbar" key="toolbar">
<button className="toolbar-button round-button" title="Empty"
- onClick={undoBatch(action(() => Doc.GetProto(this.props.Document)[this.props.fieldKey] = undefined))}>
+ onClick={undoBatch(action(() => Doc.GetProto(this.doc)[this.props.fieldKey] = undefined))}>
<FontAwesomeIcon icon={"trash"} size="sm" />
</button>
</div >;
@@ -795,51 +799,51 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
}
render() {
TraceMobx();
- if (!(this.props.Document instanceof Doc)) return (null);
- const dropAction = StrCast(this.props.Document.childDropAction) as dropActionType;
+ if (!(this.doc instanceof Doc)) return (null);
+ const dropAction = StrCast(this.doc.childDropAction) as dropActionType;
const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(d, target, addDoc);
const childDocs = this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs;
return !childDocs ? (null) : (
- <div className="collectionTreeView-dropTarget" id="body"
- style={{
- background: this.props.backgroundColor?.(this.props.Document),
- paddingLeft: `${NumCast(this.props.Document._xPadding, 10)}px`,
- paddingRight: `${NumCast(this.props.Document._xPadding, 10)}px`,
- paddingTop: `${NumCast(this.props.Document._yPadding, 20)}px`,
- pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
- }}
- onKeyPress={this.onKeyPress}
- onContextMenu={this.onContextMenu}
- onWheel={(e: React.WheelEvent) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
- onDrop={this.onTreeDrop}
- ref={this.createTreeDropTarget}>
- {(this.props.treeViewHideTitle || this.props.Document.treeViewHideTitle ? (null) : <EditableView
- contents={this.dataDoc.title}
- editing={false}
- display={"block"}
- maxHeight={72}
- height={"auto"}
- GetValue={() => StrCast(this.dataDoc.title)}
- SetValue={undoBatch((value: string) => Doc.SetInPlace(this.dataDoc, "title", value, false) || true)}
- OnFillDown={undoBatch((value: string) => {
- Doc.SetInPlace(this.dataDoc, "title", value, false);
- const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, _LODdisable: true, templates: new List<string>([Templates.Title.Layout]) });
- EditableView.loadId = doc[Id];
- Doc.SetInPlace(doc, "editTitle", true, false);
- this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true);
- })} />)}
- {this.props.Document.allowClear ? this.renderClearButton : (null)}
- <ul className="no-indent" style={{ width: "max-content" }} >
- {
- TreeView.GetChildElements(childDocs, this.props.Document, this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
- moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
- this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.props.Document.treeViewHideHeaderFields),
- BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick,
- this.props.onChildClick || ScriptCast(this.props.Document.onChildClick), this.props.ignoreFields)
- }
- </ul>
- </div >
+ <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}>
+ <div className="collectionTreeView-dropTarget" id="body"
+ style={{
+ background: this.props.backgroundColor?.(this.doc),
+ paddingLeft: `${NumCast(this.doc._xPadding, 10)}px`,
+ paddingRight: `${NumCast(this.doc._xPadding, 10)}px`,
+ paddingTop: `${NumCast(this.doc._yPadding, 20)}px`,
+ pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
+ }}
+ onKeyPress={this.onKeyPress}
+ onWheel={(e) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
+ onDrop={this.onTreeDrop}
+ ref={this.createTreeDropTarget}>
+ {this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? (null) : <EditableView
+ contents={this.dataDoc.title}
+ editing={false}
+ display={"block"}
+ maxHeight={72}
+ height={"auto"}
+ GetValue={() => StrCast(this.dataDoc.title)}
+ SetValue={undoBatch((value: string) => Doc.SetInPlace(this.dataDoc, "title", value, false) || true)}
+ OnFillDown={undoBatch((value: string) => {
+ Doc.SetInPlace(this.dataDoc, "title", value, false);
+ const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
+ Doc.SetInPlace(doc, "editTitle", "*", false);
+ this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true);
+ })} />}
+ {this.doc.allowClear ? this.renderClearButton : (null)}
+ <ul className="no-indent" style={{ width: "max-content" }} >
+ {
+ TreeView.GetChildElements(childDocs, this.doc, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
+ moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
+ this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
+ BoolCast(this.doc.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick,
+ this.props.onChildClick || ScriptCast(this.doc.onChildClick), this.props.ignoreFields)
+ }
+ </ul>
+ </div >
+ </div>
);
}
}
diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss
index 7877fe155..b630f9cf8 100644
--- a/src/client/views/collections/CollectionView.scss
+++ b/src/client/views/collections/CollectionView.scss
@@ -11,16 +11,18 @@
height: 100%;
overflow: hidden; // bcz: used to be 'auto' which would create scrollbars when there's a floating doc that's not visible. not sure if that's better, but the scrollbars are annoying...
- .collectionTimeView-dragger {
- background-color: lightgray;
+ .collectionView-filterDragger {
+ background-color: rgb(140, 139, 139);
height: 40px;
- width: 20px;
+ width: 10px;
position: absolute;
- border-radius: 10px;
top: 55%;
border: 1px black solid;
+ border-radius: 0;
+ border-top-left-radius: 20px;
+ border-bottom-left-radius: 20px;
+ border-right: unset;
z-index: 2;
- right: -10px;
}
.collectionTimeView-treeView {
display: flex;
@@ -30,6 +32,8 @@
position: absolute;
right: 0;
top: 0;
+ border-left: solid 1px;
+ z-index: 1;
.collectionTimeView-addfacet {
display: inline-block;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 508b9e5e7..f2ffe7835 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -28,6 +28,7 @@ import { FieldView, FieldViewProps } from '../nodes/FieldView';
import { ScriptBox } from '../ScriptBox';
import { Touchable } from '../Touchable';
import { CollectionCarouselView } from './CollectionCarouselView';
+import { CollectionCarousel3DView } from './CollectionCarousel3DView';
import { CollectionDockingView } from "./CollectionDockingView";
import { AddCustomFreeFormLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
@@ -66,6 +67,7 @@ export enum CollectionViewType {
Multirow = "multirow",
Time = "time",
Carousel = "carousel",
+ Carousel3D = "3D Carousel",
Linear = "linear",
Staff = "staff",
Map = "map",
@@ -106,7 +108,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
get collectionViewType(): CollectionViewType | undefined {
const viewField = StrCast(this.props.Document._viewType);
if (CollectionView._safeMode) {
- if (viewField === CollectionViewType.Freeform) {
+ if (viewField === CollectionViewType.Freeform || viewField === CollectionViewType.Schema) {
return CollectionViewType.Tree;
}
if (viewField === CollectionViewType.Invalid) {
@@ -191,7 +193,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
// return !allTagged ? (null) : <img id={"google-tags"} src={"/assets/google_tags.png"} />;
}
- screenToLocalTransform = () => this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth());
+ screenToLocalTransform = () => this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth());
private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
const props: SubCollectionViewProps = { ...this.props, ...renderProps, ScreenToLocalTransform: this.screenToLocalTransform, CollectionView: this, annotationsKey: "" };
switch (type) {
@@ -204,8 +206,9 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
case CollectionViewType.Linear: { return (<CollectionLinearView key="collview" {...props} />); }
case CollectionViewType.Pile: { return (<CollectionPileView key="collview" {...props} />); }
case CollectionViewType.Carousel: { return (<CollectionCarouselView key="collview" {...props} />); }
- case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView key="collview" {...props} />); }
- case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView key="collview" {...props} />); }
+ case CollectionViewType.Carousel3D: { return (<CollectionCarousel3DView key="collview" {...props} />); }
+ case CollectionViewType.Stacking: { this.props.Document._columnsStack = true; return (<CollectionStackingView key="collview" {...props} />); }
+ case CollectionViewType.Masonry: { this.props.Document._columnsStack = false; return (<CollectionStackingView key="collview" {...props} />); }
case CollectionViewType.Time: { return (<CollectionTimeView key="collview" {...props} />); }
case CollectionViewType.Map: return (<CollectionMapView key="collview" {...props} />);
case CollectionViewType.Grid: return (<CollectionGridView key="gridview" {...props} />);
@@ -244,6 +247,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
subItems.push({ description: "Multirow", event: () => func(CollectionViewType.Multirow), icon: "columns" });
subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" });
subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
+ subItems.push({ description: "3D Carousel", event: () => func(CollectionViewType.Carousel3D), icon: "columns" });
subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
@@ -356,7 +360,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
const facets = new Set<string>();
this.childDocs.filter(child => child).forEach(child => Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key)));
Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.filter(child => child).forEach(child => Object.keys(child).forEach(key => facets.add(key)));
- return Array.from(facets);
+ return Array.from(facets).filter(f => !f.startsWith("_") && !["proto", "zIndex", "isPrototype", "context", "text-noTemplate"].includes(f)).sort();
}
/**
@@ -432,7 +436,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
this._facetWidth = this.props.PanelWidth() - Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0);
return false;
- }), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0));
+ }), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0), false);
}
filterBackground = () => "rgba(105, 105, 105, 0.432)";
get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } // this makes the tree view collection ignore these filters (otherwise, the filters would filter themselves)
@@ -530,8 +534,9 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
Utils.CorsProxy(Cast(d.data, ImageField)!.url.href) : Cast(d.data, ImageField)!.url.href
:
""))}
- {!this.props.isSelected() || this.props.PanelHeight() < 100 || this.props.Document.hideFilterView ? (null) :
- <div className="collectionTimeView-dragger" title="library View Dragger" onPointerDown={this.onPointerDown} style={{ right: this.facetWidth() - 10 }} />
+ {(!this.props.isSelected() || this.props.Document.hideFilterView) && !this.props.Document.forceActive ? (null) :
+ <div className="collectionView-filterDragger" title="library View Dragger" onPointerDown={this.onPointerDown}
+ style={{ right: this.facetWidth() - 1, top: this.props.Document._viewType === CollectionViewType.Docking ? "25%" : "55%" }} />
}
{this.facetWidth() < 10 ? (null) : this.filterView}
</div>);
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 77a12ed37..2885ac763 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -169,7 +169,8 @@
}
.collectionStackingViewChrome-cont,
- .collectionTreeViewChrome-cont {
+ .collectionTreeViewChrome-cont,
+ .collection3DCarouselViewChrome-cont {
display: flex;
justify-content: space-between;
}
@@ -234,21 +235,24 @@
.collectionStackingViewChrome-pivotField-cont,
- .collectionTreeViewChrome-pivotField-cont {
+ .collectionTreeViewChrome-pivotField-cont,
+ .collection3DCarouselViewChrome-scrollSpeed-cont {
justify-self: right;
display: flex;
font-size: 75%;
letter-spacing: 2px;
.collectionStackingViewChrome-pivotField-label,
- .collectionTreeViewChrome-pivotField-label {
+ .collectionTreeViewChrome-pivotField-label,
+ .collection3DCarouselViewChrome-scrollSpeed-label {
vertical-align: center;
padding-left: 10px;
margin: auto;
}
.collectionStackingViewChrome-pivotField,
- .collectionTreeViewChrome-pivotField {
+ .collectionTreeViewChrome-pivotField,
+ .collection3DCarouselViewChrome-scrollSpeed {
color: white;
width: 100%;
min-width: 100px;
@@ -262,7 +266,7 @@
.editableView-container-editing {
margin: auto;
border: 0px;
- color: grey;
+ color: grey !important;
text-align: center;
letter-spacing: 2px;
outline-color: black;
@@ -278,7 +282,8 @@
}
.collectionStackingViewChrome-pivotField:hover,
- .collectionTreeViewChrome-pivotField:hover {
+ .collectionTreeViewChrome-pivotField:hover,
+ .collection3DCarouselViewChrome-scrollSpeed:hover {
cursor: text;
}
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index ac7136d4d..ab0df88f8 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -88,6 +88,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
case CollectionViewType.Freeform: return this._freeform_commands;
case CollectionViewType.Time: return this._freeform_commands;
case CollectionViewType.Carousel: return this._freeform_commands;
+ case CollectionViewType.Carousel3D: return this._freeform_commands;
}
return [];
}
@@ -214,6 +215,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
case CollectionViewType.Tree: return (<CollectionTreeViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
case CollectionViewType.Masonry: return (<CollectionStackingViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
+ case CollectionViewType.Carousel3D: return (<Collection3DCarouselViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
case CollectionViewType.Grid: return (<CollectionGridViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
default: return null;
}
@@ -404,7 +406,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
@observable private _currentKey: string = "";
@observable private suggestions: string[] = [];
- @computed private get descending() { return BoolCast(this.props.CollectionView.props.Document.stackingHeadersSortDescending); }
+ @computed private get descending() { return StrCast(this.props.CollectionView.props.Document._columnsSort) === "descending"; }
@computed get pivotField() { return StrCast(this.props.CollectionView.props.Document._pivotField); }
getKeySuggestions = async (value: string): Promise<string[]> => {
@@ -448,7 +450,11 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
return true;
}
- @action toggleSort = () => { this.props.CollectionView.props.Document.stackingHeadersSortDescending = !this.props.CollectionView.props.Document.stackingHeadersSortDescending; };
+ @action toggleSort = () => {
+ this.props.CollectionView.props.Document._columnsSort =
+ this.props.CollectionView.props.Document._columnsSort === "descending" ? "ascending" :
+ this.props.CollectionView.props.Document._columnsSort === "ascending" ? undefined : "descending";
+ };
@action resetValue = () => { this._currentKey = this.pivotField; };
render() {
@@ -577,6 +583,43 @@ export class CollectionTreeViewChrome extends React.Component<CollectionViewChro
}
}
+// Enter scroll speed for 3D Carousel
+@observer
+export class Collection3DCarouselViewChrome extends React.Component<CollectionViewChromeProps> {
+ @computed get scrollSpeed() {
+ return this.props.CollectionView.props.Document._autoScrollSpeed;
+ }
+
+ @action
+ setValue = (value: string) => {
+ const numValue = Number(StrCast(value));
+ if (numValue > 0) {
+ this.props.CollectionView.props.Document._autoScrollSpeed = numValue;
+ return true;
+ }
+ return false;
+ }
+
+ render() {
+ return (
+ <div className="collection3DCarouselViewChrome-cont">
+ <div className="collection3DCarouselViewChrome-scrollSpeed-cont">
+ <div className="collectionStackingViewChrome-scrollSpeed-label">
+ AUTOSCROLL SPEED:
+ </div>
+ <div className="collection3DCarouselViewChrome-scrollSpeed">
+ <EditableView
+ GetValue={() => StrCast(this.scrollSpeed)}
+ oneLine
+ SetValue={this.setValue}
+ contents={this.scrollSpeed ? this.scrollSpeed : 1000} />
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
+
/**
* Chrome for grid view.
*/
@@ -754,4 +797,4 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
</div>
);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index ae81b4b36..1a2421bfd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -10,6 +10,7 @@ import React = require("react");
import { Utils, emptyFunction } from "../../../../Utils";
import { DocumentType } from "../../../documents/DocumentTypes";
import { SnappingManager } from "../../../util/SnappingManager";
+import { Cast } from "../../../../fields/Types";
@observer
export class CollectionFreeFormLinksView extends React.Component {
@@ -30,8 +31,8 @@ export class CollectionFreeFormLinksView extends React.Component {
return drawnPairs;
}, [] as { a: DocumentView, b: DocumentView, l: Doc[] }[]);
return connections.filter(c =>
- c.a.props.Document.type === DocumentType.LINK &&
- c.a.props.pinToPres !== emptyFunction && c.b.props.pinToPres !== emptyFunction // bcz: this prevents links to be drawn to anchors in CollectionTree views -- this is a hack that should be fixed
+ c.a.props.Document.type === DocumentType.LINK
+ && !c.a.props.treeViewDoc?.treeViewHideLinkLines && !c.b.props.treeViewDoc?.treeViewHideLinkLines
).map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index d9011c9d3..92aee3776 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,7 +1,6 @@
@import "../../globalCssVariables";
-.collectionfreeformview-none,
-.collectionfreeformview-ease {
+.collectionfreeformview-none {
position: inherit;
top: 0;
left: 0;
@@ -22,10 +21,6 @@
}
}
-.collectionfreeformview-ease {
- transition: transform 500ms;
-}
-
.collectionfreeformview-none {
touch-action: none;
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6632291c2..ba20e9830 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -45,7 +45,7 @@ import React = require("react");
import { CollectionViewType } from "../CollectionView";
import { Timeline } from "../../animationtimeline/Timeline";
import { SnappingManager } from "../../../util/SnappingManager";
-import { ActiveInkColor, ActiveInkWidth, ActiveInkBezierApprox } from "../../InkingStroke";
+import { InkingStroke, ActiveArrowStart, ActiveArrowEnd, ActiveInkColor, ActiveFillColor, ActiveInkWidth, ActiveInkBezierApprox, ActiveDash } from "../../InkingStroke";
import { DocumentType } from "../../../documents/DocumentTypes";
import { DocumentLinksButton } from "../../nodes/DocumentLinksButton";
@@ -63,7 +63,7 @@ export const panZoomSchema = createSchema({
fitToBox: "boolean",
_xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
_yPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
- panTransformType: "string",
+ _viewTransition: "string",
scrollHeight: "number",
fitX: "number",
fitY: "number",
@@ -110,9 +110,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@computed get nativeWidth() { return this.fitToContent ? 0 : NumCast(this.Document._nativeWidth, this.props.NativeWidth()); }
@computed get nativeHeight() { return this.fitToContent ? 0 : NumCast(this.Document._nativeHeight, this.props.NativeHeight()); }
private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
- private get scaleFieldKey() { return this.props.scaleField || "scale"; }
+ private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; }
private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
- private easing = () => this.props.Document.panTransformType === "Ease";
private panX = () => this.fitToContent ? (this.contentBounds.x + this.contentBounds.r) / 2 : this.Document._panX || 0;
private panY = () => this.fitToContent ? (this.contentBounds.y + this.contentBounds.b) / 2 : this.Document._panY || 0;
private zoomScaling = () => (this.fitToContentScaling / this.parentScaling) * (this.fitToContent ?
@@ -463,7 +462,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
case GestureUtils.Gestures.Stroke:
const points = ge.points;
const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
- const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), Doc.GetSelectedTool(), ActiveInkWidth(), ActiveInkBezierApprox(), points,
+ const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), Doc.GetSelectedTool(), ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
{ title: "ink stroke", x: B.x - Number(ActiveInkWidth()) / 2, y: B.y - Number(ActiveInkWidth()) / 2, _width: B.width + Number(ActiveInkWidth()), _height: B.height + Number(ActiveInkWidth()) });
this.addDocument(inkDoc);
e.stopPropagation();
@@ -832,7 +831,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
}
if (!this.layoutDoc._lockedTransform || this.Document.inOverlay) {
- this.Document.panTransformType = panType;
+ this.Document._viewTransition = panType;
const scale = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) : (1 - 1 / scale) * this.nativeHeight), Math.max(0, panY));
@@ -860,7 +859,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
scaleAtPt(docpt: number[], scale: number) {
const screenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
- this.Document.panTransformType = "Ease";
+ this.Document._viewTransition = "transform 500ms";
this.layoutDoc[this.scaleFieldKey] = scale;
const newScreenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
const scrDelta = { x: screenXY[0] - newScreenXY[0], y: screenXY[1] - newScreenXY[1] };
@@ -905,14 +904,14 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
HistoryUtil.pushState(newState);
- const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document[this.scaleFieldKey], pt: this.Document.panTransformType };
+ const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document[this.scaleFieldKey], pt: this.Document._viewTransition };
// if (!willZoom && DocumentView._focusHack.length) {
// Doc.BrushDoc(this.props.Document);
// !doc.z && NumCast(this.layoutDoc.scale) < 1 && this.scaleAtPt(DocumentView._focusHack, 1); // [NumCast(doc.x), NumCast(doc.y)], 1);
// } else {
if (DocListCast(this.dataDoc[this.props.fieldKey]).includes(doc)) {
- if (!doc.z) this.setPan(newPanX, newPanY, "Ease", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
+ if (!doc.z) this.setPan(newPanX, newPanY, "transform 500ms", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
}
Doc.BrushDoc(this.props.Document);
this.props.focus(this.props.Document);
@@ -925,7 +924,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this.Document._panX = savedState.px;
this.Document._panY = savedState.py;
this.Document[this.scaleFieldKey] = savedState.s;
- this.Document.panTransformType = savedState.pt;
+ this.Document._viewTransition = savedState.pt;
}
}, 500);
}
@@ -1011,7 +1010,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const { z, color, zIndex } = params.pair.layout;
return {
x: NumCast(x), y: NumCast(y), z: Cast(z, "number"), color: StrCast(color), zIndex: Cast(zIndex, "number"),
- transition: StrCast(layoutDoc.transition), opacity: this.Document.editing ? 1 : Cast(opacity, "number", null),
+ transition: StrCast(layoutDoc.dataTransition), opacity: this.Document.editing ? 1 : Cast(opacity, "number", null),
width: Cast(layoutDoc._width, "number"), height: Cast(layoutDoc._height, "number"), pair: params.pair, replica: ""
};
}
@@ -1227,7 +1226,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
if (!Doc.UserDoc().noviceMode) {
optionItems.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
- optionItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" });
+ optionItems.push({ description: `${this.Document._freeformLOD ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._freeformLOD = !this.Document._freeformLOD, icon: "table" });
optionItems.push({
description: "Import document", icon: "upload", event: ({ x, y }) => {
const input = document.createElement("input");
@@ -1333,9 +1332,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
nudge = action((x: number, y: number) => {
if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform) { // bcz: this isn't ideal, but want to try it out...
this.setPan(NumCast(this.layoutDoc._panX) + this.props.PanelWidth() / 2 * x / this.zoomScaling(),
- NumCast(this.layoutDoc._panY) + this.props.PanelHeight() / 2 * (-y) / this.zoomScaling(), "Ease", true);
+ NumCast(this.layoutDoc._panY) + this.props.PanelHeight() / 2 * (-y) / this.zoomScaling(), "transform 500ms", true);
this._nudgeTime = Date.now();
- setTimeout(() => (Date.now() - this._nudgeTime >= 500) && (this.Document.panTransformType = undefined), 500);
+ setTimeout(() => (Date.now() - this._nudgeTime >= 500) && (this.Document._viewTransition = undefined), 500);
return true;
}
return false;
@@ -1354,8 +1353,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
<CollectionFreeFormViewPannableContents
centeringShiftX={this.centeringShiftX}
centeringShiftY={this.centeringShiftY}
- easing={this.easing}
- transition={Cast(this.layoutDoc.transition, "string", null)}
+ transition={Cast(this.layoutDoc._viewTransition, "string", null)}
viewDefDivClick={this.props.viewDefDivClick}
zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
{this.children}
@@ -1392,7 +1390,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
width: this.contentScaling ? `${100 / this.contentScaling}%` : "",
height: this.contentScaling ? `${100 / this.contentScaling}%` : this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
}}>
- {!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ?
+ {this.Document._freeformLOD && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ?
this.placeholder : this.marqueeView}
<CollectionFreeFormOverlayView elements={this.elementFunc} />
@@ -1434,7 +1432,6 @@ interface CollectionFreeFormViewPannableContentsProps {
panX: () => number;
panY: () => number;
zoomScaling: () => number;
- easing: () => boolean;
viewDefDivClick?: ScriptField;
children: () => JSX.Element[];
transition?: string;
@@ -1443,7 +1440,7 @@ interface CollectionFreeFormViewPannableContentsProps {
@observer
class CollectionFreeFormViewPannableContents extends React.Component<CollectionFreeFormViewPannableContentsProps>{
render() {
- const freeformclass = "collectionfreeformview" + (this.props.viewDefDivClick ? "-viewDef" : (this.props.easing() ? "-ease" : "-none"));
+ const freeformclass = "collectionfreeformview" + (this.props.viewDefDivClick ? "-viewDef" : "-none");
const cenx = this.props.centeringShiftX();
const ceny = this.props.centeringShiftY();
const panx = -this.props.panX();
diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss
index a7f4d4e53..a9fab4c1e 100644
--- a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss
+++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss
@@ -1,5 +1,10 @@
.antimodeMenu-button {
- .color-preview {
+ .color-previewI {
+ width: 100%;
+ height: 40%;
+ }
+
+ .color-previewII {
width: 100%;
height: 100%;
}
diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
index 4fac87ca5..f1032adaa 100644
--- a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
@@ -3,25 +3,44 @@ import AntimodeMenu from "../../AntimodeMenu";
import { observer } from "mobx-react";
import { observable, action, computed } from "mobx";
import "./InkOptionsMenu.scss";
-import { ActiveInkColor, ActiveInkBezierApprox, SetActiveInkWidth, SetActiveInkColor, SetActiveBezierApprox } from "../../InkingStroke";
+import { ActiveInkColor, ActiveInkBezierApprox, ActiveFillColor, ActiveArrowStart, ActiveArrowEnd, SetActiveInkWidth, SetActiveInkColor, SetActiveBezierApprox, SetActiveFillColor, SetActiveArrowStart, SetActiveArrowEnd, ActiveDash, SetActiveDash } from "../../InkingStroke";
import { Scripting } from "../../../util/Scripting";
import { InkTool } from "../../../../fields/InkField";
import { ColorState } from "react-color";
import { Utils } from "../../../../Utils";
import GestureOverlay from "../../GestureOverlay";
import { Doc } from "../../../../fields/Doc";
+import { SelectionManager } from "../../../util/SelectionManager";
+import { DocumentView } from "../../../views/nodes/DocumentView";
+import { Document } from "../../../../fields/documentSchemas";
+import { DocumentType } from "../../../documents/DocumentTypes";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
+import { faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSubscript, faSuperscript, faIndent, faEyeDropper, faCaretDown, faPalette, faArrowsAlt, faHighlighter, faLink, faPaintRoller, faSleigh, faBars, faFillDrip, faBrush, faPenNib, faShapes, faArrowLeft, faEllipsisH, faBezierCurve, } from "@fortawesome/free-solid-svg-icons";
+
+library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, faCaretDown, faPalette, faArrowsAlt, faHighlighter, faLink, faPaintRoller, faBars, faFillDrip, faBrush, faPenNib, faShapes, faArrowLeft, faEllipsisH, faBezierCurve);
@observer
export default class InkOptionsMenu extends AntimodeMenu {
static Instance: InkOptionsMenu;
- private _palette = ["D0021B", "F5A623", "F8E71C", "8B572A", "7ED321", "417505", "9013FE", "4A90E2", "50E3C2", "B8E986", "000000", "4A4A4A", "9B9B9B", "FFFFFF"];
- private _width = ["1", "5", "10", "100", "200", "300"];
- private _buttons = ["circle", "triangle", "rectangle", "arrow", "line"];
- private _icons = ["O", "∆", "ロ", "➜", "-"];
+ private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF", "none"];
+ private _width = ["1", "5", "10", "100"];
+ // private _buttons = ["circle", "triangle", "rectangle", "arrow", "line"];
+ // private _icons = ["O", "∆", "ロ", "➜", "-"];
+ private _buttons = ["circle", "triangle", "rectangle", "line", "noRec", "",];
+ private _icons = ["O", "∆", "ロ", "⎯", "✖︎", " "];
+ //arrowStart and arrowEnd must match and defs must exist in Inking Stroke
+ private _arrowStart = ["arrowHead", "arrowHead", "dot", "dot", "none"];
+ private _arrowEnd = ["none", "arrowEnd", "none", "dot", "none"];
+ private _arrowIcons = ["→", "↔︎", "•", "••", " "];
@observable _colorBtn = false;
@observable _widthBtn = false;
+ @observable _fillBtn = false;
+ @observable _arrowBtn = false;
+ @observable _dashBtn = false;
+ @observable _shapeBtn = false;
constructor(props: Readonly<{}>) {
super(props);
@@ -29,18 +48,106 @@ export default class InkOptionsMenu extends AntimodeMenu {
this._canFade = false; // don't let the inking menu fade away
}
+ getColors = () => {
+ return this._palette;
+ }
+
@action
- changeColor = (color: string) => {
+ changeArrow = (arrowStart: string, arrowEnd: string) => {
+ SetActiveArrowStart(arrowStart);
+ SetActiveArrowEnd(arrowEnd);
+ }
+
+ @action
+ changeColor = (color: string, type: string) => {
const col: ColorState = {
hex: color, hsl: { a: 0, h: 0, s: 0, l: 0, source: "" }, hsv: { a: 0, h: 0, s: 0, v: 0, source: "" },
rgb: { a: 0, r: 0, b: 0, g: 0, source: "" }, oldHue: 0, source: "",
};
- SetActiveInkColor(Utils.colorString(col));
+ if (type === "color") {
+ SetActiveInkColor(Utils.colorString(col));
+ } else if (type === "fill") {
+ SetActiveFillColor(Utils.colorString(col));
+ }
}
@action
+ editProperties = (value: any, field: string) => {
+ SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
+ const doc = Document(element.rootDoc);
+ if (doc.type === DocumentType.INK) {
+ switch (field) {
+ case "width":
+ doc.strokeWidth = Number(value);
+ break;
+ case "color":
+ doc.color = String(value);
+ break;
+ case "fill":
+ doc.fillColor = String(value);
+ break;
+ case "bezier":
+ // doc.strokeBezier === 300 ? doc.strokeBezier = 0 : doc.strokeBezier = 300;
+ break;
+ case "arrowStart":
+ doc.arrowStart = String(value);
+ break;
+ case "arrowEnd":
+ doc.arrowEnd = String(value);
+ break;
+ case "dash":
+ doc.dash = Number(value);
+ default:
+ break;
+ }
+ }
+ }));
+ }
+
+
+ @action
changeBezier = (e: React.PointerEvent): void => {
SetActiveBezierApprox(!ActiveInkBezierApprox() ? "300" : "");
+ this.editProperties(0, "bezier");
+ }
+ @action
+ changeDash = (e: React.PointerEvent): void => {
+ SetActiveDash(ActiveDash() === "0" ? "2" : "0");
+ this.editProperties(ActiveDash(), "dash");
+ }
+
+ @computed get arrowPicker() {
+ var currIcon;
+ for (var i = 0; i < this._arrowStart.length; i++) {
+ if (this._arrowStart[i] === ActiveArrowStart() && this._arrowEnd[i] === ActiveArrowEnd()) {
+ currIcon = this._arrowIcons[i];
+ if (this._arrowIcons[i] === " ") {
+ currIcon = "➤";
+ }
+ }
+ }
+ var arrowPicker = <button
+ className="antimodeMenu-button"
+ key="arrow"
+ onPointerDown={action(e => this._arrowBtn = !this._arrowBtn)}
+ style={{ backgroundColor: this._arrowBtn ? "121212" : "" }}>
+ {currIcon}
+ </button>;
+ if (this._arrowBtn) {
+ arrowPicker = <div className="btn2-group" key="arrows">
+ {arrowPicker}
+ {this._arrowStart.map((arrowStart, i) => {
+ return <button
+ className="antimodeMenu-button"
+ key={arrowStart}
+ onPointerDown={action(() => { SetActiveArrowStart(arrowStart); SetActiveArrowEnd(this._arrowEnd[i]); this.editProperties(arrowStart, "arrowStart"), this.editProperties(this._arrowEnd[i], "arrowEnd"); this._arrowBtn = false; })}
+ style={{ backgroundColor: this._arrowBtn ? "121212" : "" }}>
+ {this._arrowIcons[i]}
+ </button>;
+ })}
+ </div>;
+ }
+ return arrowPicker;
}
@computed get widthPicker() {
@@ -49,7 +156,7 @@ export default class InkOptionsMenu extends AntimodeMenu {
key="width"
onPointerDown={action(e => this._widthBtn = !this._widthBtn)}
style={{ backgroundColor: this._widthBtn ? "121212" : "" }}>
- W
+ <FontAwesomeIcon icon="bars" size="lg" />
</button>;
if (this._widthBtn) {
widthPicker = <div className="btn2-group" key="width">
@@ -58,7 +165,7 @@ export default class InkOptionsMenu extends AntimodeMenu {
return <button
className="antimodeMenu-button"
key={wid}
- onPointerDown={action(() => { SetActiveInkWidth(wid); this._widthBtn = false; })}
+ onPointerDown={action(() => { SetActiveInkWidth(wid); this._widthBtn = false; this.editProperties(wid, "width"); })}
style={{ backgroundColor: this._widthBtn ? "121212" : "" }}>
{wid}
</button>;
@@ -68,6 +175,8 @@ export default class InkOptionsMenu extends AntimodeMenu {
return widthPicker;
}
+
+
@computed get colorPicker() {
var colorPicker = <button
className="antimodeMenu-button"
@@ -75,7 +184,9 @@ export default class InkOptionsMenu extends AntimodeMenu {
title="colorChanger"
onPointerDown={action(e => this._colorBtn = !this._colorBtn)}
style={{ backgroundColor: this._colorBtn ? "121212" : "" }}>
- <div className="color-preview" style={{ backgroundColor: ActiveInkColor() ?? "121212" }}></div>
+ <FontAwesomeIcon icon="pen-nib" size="lg" />
+ <div className="color-previewI" style={{ backgroundColor: ActiveInkColor() ?? "121212" }}></div>
+
</button>;
if (this._colorBtn) {
colorPicker = <div className="btn-group" key="color">
@@ -84,9 +195,10 @@ export default class InkOptionsMenu extends AntimodeMenu {
return <button
className="antimodeMenu-button"
key={color}
- onPointerDown={action(() => { this.changeColor(color); this._colorBtn = false; })}
+ onPointerDown={action(() => { this.changeColor(color, "color"); this._colorBtn = false; this.editProperties(color, "color"); })}
style={{ backgroundColor: this._colorBtn ? "121212" : "" }}>
- <div className="color-preview" style={{ backgroundColor: color }}></div>
+ {/* <FontAwesomeIcon icon="pen-nib" size="lg" /> */}
+ <div className="color-previewII" style={{ backgroundColor: color }}></div>
</button>;
})}
</div>;
@@ -94,15 +206,75 @@ export default class InkOptionsMenu extends AntimodeMenu {
return colorPicker;
}
- @computed get shapeButtons() {
- return this._buttons.map((btn, i) => <button
+ @computed get fillPicker() {
+ var fillPicker = <button
className="antimodeMenu-button"
- title={`Draw ${btn}`}
- key={i}
- onPointerDown={action(e => GestureOverlay.Instance.InkShape = btn)}
- style={{ backgroundColor: btn === GestureOverlay.Instance.InkShape ? "121212" : "" }}>
- {this._icons[i]}
- </button>);
+ key="fill"
+ title="fillChanger"
+ onPointerDown={action(e => this._fillBtn = !this._fillBtn)}
+ style={{ backgroundColor: this._fillBtn ? "121212" : "" }}>
+ <FontAwesomeIcon icon="fill-drip" size="lg" />
+ <div className="color-previewI" style={{ backgroundColor: ActiveFillColor() ?? "121212" }}></div>
+ </button>;
+ if (this._fillBtn) {
+ fillPicker = <div className="btn-group" key="fill">
+ {fillPicker}
+ {this._palette.map(color => {
+ return <button
+ className="antimodeMenu-button"
+ key={color}
+ onPointerDown={action(() => { this.changeColor(color, "fill"); this._fillBtn = false; this.editProperties(color, "fill"); })}
+ style={{ backgroundColor: this._fillBtn ? "121212" : "" }}>
+ <div className="color-previewII" style={{ backgroundColor: color }}></div>
+ </button>;
+ })}
+
+ </div>;
+ }
+ return fillPicker;
+ }
+
+ @computed get shapePicker() {
+ var currIcon;
+ if (GestureOverlay.Instance.InkShape === "") {
+ currIcon = <FontAwesomeIcon icon="shapes" size="lg" />;
+ } else {
+ for (var i = 0; i < this._icons.length; i++) {
+ if (GestureOverlay.Instance.InkShape === this._buttons[i]) {
+ currIcon = this._icons[i];
+ }
+ }
+ }
+ var shapePicker = <button
+ className="antimodeMenu-button"
+ key="shape"
+ onPointerDown={action(e => this._shapeBtn = !this._shapeBtn)}
+ style={{ backgroundColor: this._shapeBtn ? "121212" : "" }}>
+ {currIcon}
+ </button>;
+ if (this._shapeBtn) {
+ shapePicker = <div className="btn2-group" key="shape">
+ {shapePicker}
+ {this._buttons.map((btn, i) => {
+ var ttl = btn;
+ if (btn === "") {
+ ttl = "no shape";
+ }
+ if (btn === "noRec") {
+ ttl = "disable shape recognition";
+ }
+ return <button
+ className="antimodeMenu-button"
+ title={`Draw ${btn}`}
+ key={ttl}
+ onPointerDown={action((e) => { GestureOverlay.Instance.InkShape = btn; this._shapeBtn = false; })}
+ style={{ backgroundColor: this._shapeBtn ? "121212" : "" }}>
+ {this._icons[i]}
+ </button>;
+ })}
+ </div>;
+ }
+ return shapePicker;
}
@computed get bezierButton() {
@@ -112,16 +284,35 @@ export default class InkOptionsMenu extends AntimodeMenu {
key="bezier"
onPointerDown={e => this.changeBezier(e)}
style={{ backgroundColor: ActiveInkBezierApprox() ? "121212" : "" }}>
- B
+ <FontAwesomeIcon icon="bezier-curve" size="lg" />
+
+ </button>;
+ }
+
+ @computed get dashButton() {
+ return <button
+ className="antimodeMenu-button"
+ title="dash changer"
+ key="dash"
+ onPointerDown={e => this.changeDash(e)}
+ style={{ backgroundColor: ActiveDash() !== "0" ? "121212" : "" }}>
+ <FontAwesomeIcon icon="ellipsis-h" size="lg" />
+
</button>;
}
render() {
const buttons = [
- ...this.shapeButtons,
+ // <button className="antimodeMenu-button" title="Drag" key="drag" onPointerDown={e => this.dragStart(e)}>
+ // <FontAwesomeIcon icon="arrows-alt" size="lg" />
+ // </button>,
+ this.shapePicker,
this.bezierButton,
this.widthPicker,
this.colorPicker,
+ this.fillPicker,
+ this.arrowPicker,
+ this.dashButton,
];
return this.getElement(buttons);
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 1bc7c6fb5..97ed74c10 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -20,8 +20,6 @@ import { CollectionView } from "../CollectionView";
import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
import "./MarqueeView.scss";
import React = require("react");
-import { DateField } from "../../../../fields/DateField";
-import { DocServer } from "../../../DocServer";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -260,6 +258,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
e.preventDefault();
}
}
+ clearSelection() {
+ if (window.getSelection) { window.getSelection()?.removeAllRanges(); }
+ else if (document.getSelection()) { document.getSelection()?.empty(); }
+ }
setPreviewCursor = action((x: number, y: number, drag: boolean) => {
if (drag) {
@@ -275,6 +277,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this._downX = x;
this._downY = y;
PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge);
+ this.clearSelection();
}
});
@@ -349,7 +352,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : isBackground ? "cyan" : undefined,
_width: bounds.width,
_height: bounds.height,
- _LODdisable: true,
title: "a nested collection",
});
selected.forEach(d => d.context = newCollection);
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 404d69730..a3020f912 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -22,7 +22,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
zIndex?: number;
highlight?: boolean;
jitterRotation: number;
- transition?: string;
+ dataTransition?: string;
fitToBox?: boolean;
replica: string;
}
@@ -60,10 +60,10 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
const someView = Cast(this.props.Document.someView, Doc);
const minimap = Cast(this.props.Document.minimap, Doc);
if (someView instanceof Doc && minimap instanceof Doc) {
- const x = (NumCast(someView._panX) - NumCast(someView._width) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap._width) - NumCast(minimap._width) / 2;
- const y = (NumCast(someView._panY) - NumCast(someView._height) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap._height) - NumCast(minimap._height) / 2;
- const w = NumCast(someView._width) / NumCast(someView.scale) / NumCast(minimap.fitW) * NumCast(minimap.width);
- const h = NumCast(someView._height) / NumCast(someView.scale) / NumCast(minimap.fitH) * NumCast(minimap.height);
+ const x = (NumCast(someView._panX) - NumCast(someView._width) / 2 / NumCast(someView._viewScale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap._width) - NumCast(minimap._width) / 2;
+ const y = (NumCast(someView._panY) - NumCast(someView._height) / 2 / NumCast(someView._viewScale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap._height) - NumCast(minimap._height) / 2;
+ const w = NumCast(someView._width) / NumCast(someView._viewScale) / NumCast(minimap.fitW) * NumCast(minimap.width);
+ const h = NumCast(someView._height) / NumCast(someView._viewScale) / NumCast(minimap.fitH) * NumCast(minimap.height);
return { x: x, y: y, width: w, height: h };
}
}
@@ -81,9 +81,15 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static setValues(time: number, d: Doc, x?: number, y?: number, opacity?: number) {
const timecode = Math.round(time);
- Cast(d["x-indexed"], listSpec("number"), [])[timecode] = x as any as number;
- Cast(d["y-indexed"], listSpec("number"), [])[timecode] = y as any as number;
- Cast(d["opacity-indexed"], listSpec("number"), null)[timecode] = opacity as any as number;
+ const xindexed = Cast(d["x-indexed"], listSpec("number"), []).slice();
+ const yindexed = Cast(d["y-indexed"], listSpec("number"), []).slice();
+ const oindexed = Cast(d["opacity-indexed"], listSpec("number"), []).slice();
+ xindexed[timecode] = x as any as number;
+ yindexed[timecode] = y as any as number;
+ oindexed[timecode] = opacity as any as number;
+ d["x-indexed"] = new List<number>(xindexed);
+ d["y-indexed"] = new List<number>(yindexed);
+ d["opacity-indexed"] = new List<number>(oindexed);
}
public static updateKeyframe(docs: Doc[], time: number) {
const timecode = Math.round(time);
@@ -94,14 +100,14 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
xindexed?.length <= timecode + 1 && xindexed.push(undefined as any as number);
yindexed?.length <= timecode + 1 && yindexed.push(undefined as any as number);
opacityindexed?.length <= timecode + 1 && opacityindexed.push(undefined as any as number);
- doc.transition = "all 1s";
+ doc.dataTransition = "all 1s";
});
- setTimeout(() => docs.forEach(doc => doc.transition = "inherit"), 1010);
+ setTimeout(() => docs.forEach(doc => doc.dataTransition = "inherit"), 1010);
}
public static gotoKeyframe(docs: Doc[]) {
- docs.forEach(doc => doc.transition = "all 1s");
- setTimeout(() => docs.forEach(doc => doc.transition = "inherit"), 1010);
+ docs.forEach(doc => doc.dataTransition = "all 1s");
+ setTimeout(() => docs.forEach(doc => doc.dataTransition = "inherit"), 1010);
}
public static setupKeyframes(docs: Doc[], timecode: number, progressivize: boolean = false) {
@@ -119,7 +125,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
doc.x = ComputedField.MakeInterpolated("x", "activeFrame");
doc.y = ComputedField.MakeInterpolated("y", "activeFrame");
doc.opacity = ComputedField.MakeInterpolated("opacity", "activeFrame");
- doc.transition = "inherit";
+ doc.dataTransition = "inherit";
});
}
@@ -150,7 +156,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
borderRadius: StrCast(Doc.Layout(this.layoutDoc).borderRounding),
outline: this.Highlight ? "orange solid 2px" : "",
transform: this.transform,
- transition: this.props.transition ? this.props.transition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.transition),
+ transition: this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition),
width: this.props.Document.isInkMask ? 5000 : this.width,
height: this.props.Document.isInkMask ? 5000 : this.height,
zIndex: this.ZInd,
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index b7726f7ba..b978f6245 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -42,6 +42,17 @@
width:10px !important;
}
}
+ .documentView-treeView {
+ max-height: 1.5em;
+ text-overflow: ellipsis;
+ display: inline-block;
+ white-space: pre;
+ width: 100%;
+ overflow: hidden;
+ > .documentView-node {
+ position: absolute;
+ }
+ }
.documentView-lock {
width: 20;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8178c8aa7..21b6d8310 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -69,7 +69,7 @@ export interface DocumentViewProps {
onDoubleClick?: ScriptField;
onPointerDown?: ScriptField;
onPointerUp?: ScriptField;
- treeViewId?: string;
+ treeViewDoc?: Doc;
dropAction?: dropActionType;
dragDivName?: string;
nudge?: (x: number, y: number) => void;
@@ -123,7 +123,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@computed get freezeDimensions() { return this.props.FreezeDimensions; }
@computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
@computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight, this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
- @computed get onClickHandler() { return this.props.onClick || Cast(this.layoutDoc.onClick, ScriptField, null); }
+ @computed get onClickHandler() { return this.props.onClick || Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
@computed get onDoubleClickHandler() { return this.props.onDoubleClick || Cast(this.layoutDoc.onDoubleClick, ScriptField, null) || this.Document.onDoubleClick; }
@computed get onPointerDownHandler() { return this.props.onPointerDown ? this.props.onPointerDown : this.Document.onPointerDown; }
@computed get onPointerUpHandler() { return this.props.onPointerUp ? this.props.onPointerUp : this.Document.onPointerUp; }
@@ -237,7 +237,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
dragData.removeDocument = this.props.removeDocument;
dragData.moveDocument = this.props.moveDocument;// this.layoutDoc.onDragStart ? undefined : this.props.moveDocument;
dragData.dragDivName = this.props.dragDivName;
- dragData.treeViewId = this.props.treeViewId;
+ dragData.treeViewDoc = this.props.treeViewDoc;
DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.layoutDoc.onDragStart });
}
}
@@ -374,7 +374,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this._downX = touch.clientX;
this._downY = touch.clientY;
if (!e.nativeEvent.cancelBubble) {
- if ((this.active || this.layoutDoc.onDragStart || this.layoutDoc.onClick) && !e.ctrlKey && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) e.stopPropagation();
+ if ((this.active || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) e.stopPropagation();
this.removeMoveListeners();
this.addMoveListeners();
this.removeEndListeners();
@@ -389,11 +389,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (e.cancelBubble && this.active) {
this.removeMoveListeners();
}
- else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart || this.layoutDoc.onClick) && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) {
+ else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) {
const touch = me.touchEvent.changedTouches.item(0);
if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) {
- if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.layoutDoc.onClick)) {
+ if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler)) {
this.cleanUpInteractions();
this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined);
}
@@ -582,12 +582,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch
toggleLinkButtonBehavior = (): void => {
- if (this.Document.isLinkButton || this.layoutDoc.onClick || this.Document.ignoreClick) {
+ if (this.Document.isLinkButton || this.onClickHandler || this.Document.ignoreClick) {
this.Document.isLinkButton = false;
const first = DocListCast(this.Document.links).find(d => d instanceof Doc);
first && (first.hidden = false);
this.Document.ignoreClick = false;
- this.layoutDoc.onClick = undefined;
+ this.Document.onClick = this.layoutDoc.onClick = undefined;
} else {
this.Document.isLinkButton = true;
const first = DocListCast(this.Document.links).find(d => d instanceof Doc);
@@ -742,7 +742,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.props.contextMenuItems?.().forEach(item =>
cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
- let options = cm.findByDescription("Options...");
+ const options = cm.findByDescription("Options...");
const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
templateDoc && optionItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" });
@@ -752,11 +752,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const existingOnClick = cm.findByDescription("OnClick...");
const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
- onClicks.push({ description: "Toggle Detail", event: () => this.layoutDoc.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "window-restore" });
+ onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "window-restore" });
onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" });
onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link on Right", event: this.toggleFollowOnRight, icon: "concierge-bell" });
- onClicks.push({ description: this.Document.isLinkButton || this.layoutDoc.onClick ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" });
+ onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" });
onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" });
!existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
@@ -1072,30 +1072,34 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
anchorPanelHeight = () => this.props.PanelHeight() || 1;
@computed get anchors() {
TraceMobx();
- return this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.presBox || this.props.dontRegisterView ? (null) : DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
- <DocumentView {...this.props} key={i + 1}
- Document={d}
- ContainingCollectionView={this.props.ContainingCollectionView}
- ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection Need a better prop for passing the containing document to the LinkAnchorBox
- PanelWidth={this.anchorPanelWidth}
- PanelHeight={this.anchorPanelHeight}
- ContentScaling={returnOne}
- dontRegisterView={false}
- forcedBackgroundColor={returnTransparent}
- removeDocument={this.hideLinkAnchor}
- pointerEvents={false}
- LayoutTemplate={undefined}
- LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(d, this.props.Document)}`)}
- />);
+ return (this.props.treeViewDoc && this.props.LayoutTemplateString) || // render nothing for: tree view anchor dots
+ this.layoutDoc.presBox || // presentationbox nodes
+ this.props.dontRegisterView ? (null) : // view that are not registered
+ DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
+ <DocumentView {...this.props} key={i + 1}
+ Document={d}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection Need a better prop for passing the containing document to the LinkAnchorBox
+ PanelWidth={this.anchorPanelWidth}
+ PanelHeight={this.anchorPanelHeight}
+ ContentScaling={returnOne}
+ dontRegisterView={false}
+ forcedBackgroundColor={returnTransparent}
+ removeDocument={this.hideLinkAnchor}
+ pointerEvents={false}
+ LayoutTemplate={undefined}
+ LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(d, this.props.Document)}`)}
+ />);
}
@computed get innards() {
TraceMobx();
- if (!this.props.PanelWidth()) { // this happens when the document is a tree view label
- return <div className="documentView-linkAnchorBoxAnchor" >
+ if (this.props.treeViewDoc && !this.props.LayoutTemplateString) { // this happens when the document is a tree view label (but not an anchor dot)
+ return <div className="documentView-treeView" style={{ maxWidth: this.props.PanelWidth() || undefined }}>
{StrCast(this.props.Document.title)}
{this.anchors}
</div>;
}
+
const showTitle = StrCast(this.layoutDoc._showTitle);
const showTitleHover = StrCast(this.layoutDoc._showTitleHover);
const showCaption = StrCast(this.layoutDoc._showCaption);
@@ -1157,7 +1161,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
renderLock() {
return (this.Document.isBackground !== undefined || this.isSelected(false)) &&
((this.Document.type === DocumentType.COL && this.Document._viewType !== CollectionViewType.Pile) || this.Document.type === DocumentType.IMG) &&
- this.props.renderDepth > 0 && this.props.PanelWidth() > 0 ?
+ this.props.renderDepth > 0 && !this.props.treeViewDoc ?
<div className="documentView-lock" onClick={() => this.toggleBackground(true)}>
<FontAwesomeIcon icon={this.Document.isBackground ? "unlock" : "lock"} style={{ color: this.Document.isBackground ? "red" : undefined }} size="lg" />
</div>
@@ -1199,7 +1203,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
style={{
transformOrigin: this._animateScalingTo ? "center center" : undefined,
transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
- transition: !this._animateScalingTo ? StrCast(this.Document.transition) : this._animateScalingTo < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out",
+ transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : this._animateScalingTo < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out",
pointerEvents: this.ignorePointerEvents ? "none" : undefined,
color: StrCast(this.layoutDoc.color, "inherit"),
outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index d375466c9..b732f5f83 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -169,8 +169,8 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
getTemplate = async () => {
const parent = Docs.Create.StackingDocument([], { _width: 800, _height: 800, title: "Template" });
- parent.singleColumn = false;
- parent.columnWidth = 100;
+ parent._columnsStack = false;
+ parent._columnWidth = 100;
for (const row of this.rows.filter(row => row.isChecked)) {
await this.createTemplateField(parent, row);
row.uncheck();
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 2bcc6168b..d4ab70200 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -115,8 +115,9 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
render() {
TraceMobx();
- const x = this.props.PanelWidth() > 1 ? NumCast(this.rootDoc[this.fieldKey + "_x"], 100) : 0;
- const y = this.props.PanelWidth() > 1 ? NumCast(this.rootDoc[this.fieldKey + "_y"], 100) : 0;
+ const small = this.props.PanelWidth() <= 1; // this happens when rendered in a treeView
+ const x = NumCast(this.rootDoc[this.fieldKey + "_x"], 100);
+ const y = NumCast(this.rootDoc[this.fieldKey + "_y"], 100);
const c = StrCast(this.layoutDoc._backgroundColor, StrCast(this.layoutDoc.backgroundColor, StrCast(this.dataDoc.backgroundColor, "lightBlue"))); // note this is not where the typical lightBlue default color comes from. See Documents.Create.LinkDocument()
const anchor = this.fieldKey === "anchor1" ? "anchor2" : "anchor1";
const anchorScale = (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : .25;
@@ -131,7 +132,6 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
</div>}
</div>
);
- const small = this.props.PanelWidth() <= 1;
return <div className={`linkAnchorBox-cont${small ? "-small" : ""} ${this.rootDoc[Id]}`}
onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
onPointerEnter={action(e => LinkDocPreview.LinkInfo = {
@@ -143,8 +143,8 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle} onContextMenu={this.specificContextMenu}
ref={this._ref} style={{
background: c,
- left: !small ? `calc(${x}% - 7.5px)` : undefined,
- top: !small ? `calc(${y}% - 7.5px)` : undefined,
+ left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`,
+ top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`,
transform: `scale(${anchorScale / this.props.ContentScaling()})`
}} >
{!this._editing && !this._forceOpen ? (null) :
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 126dc240a..92b443d3b 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -25,8 +25,8 @@ export class LinkDocPreview extends React.Component<Props> {
@observable _targetDoc: Opt<Doc>;
@observable _toolTipText = "";
- componentDidUpdate() { this.updatePreview() }
- componentDidMount() { this.updatePreview() }
+ componentDidUpdate() { this.updatePreview(); }
+ componentDidMount() { this.updatePreview(); }
async updatePreview() {
const linkDoc = this.props.linkDoc;
const linkSrc = this.props.linkSrc;
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 3e09fe519..c6a83b662 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -1,7 +1,7 @@
.pdfBox,
.pdfBox-interactive {
display: inline-block;
- position: absolute;
+ position: relative;
height: 100%;
width: 100%;
overflow: hidden;
@@ -199,7 +199,7 @@
}
.pdfBox {
- .pdfViewer-text {
+ .pdfViewerDash-text {
.textLayer {
span {
user-select: none;
@@ -210,7 +210,7 @@
.pdfBox-interactive {
pointer-events: all;
- .pdfViewer-text {
+ .pdfViewerDash-text {
.textLayer {
span {
user-select: text;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 6b1c9fcde..eb2a85eeb 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -55,25 +55,28 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
const backup = "oldPath";
const { Document } = this.props;
- const { url: { href } } = Cast(this.dataDoc[this.props.fieldKey], PdfField)!;
- const pathCorrectionTest = /upload\_[a-z0-9]{32}.(.*)/g;
- const matches = pathCorrectionTest.exec(href);
- console.log("\nHere's the { url } being fed into the outer regex:");
- console.log(href);
- console.log("And here's the 'properPath' build from the captured filename:\n");
- if (matches !== null && href.startsWith(window.location.origin)) {
- const properPath = Utils.prepend(`/files/pdfs/${matches[0]}`);
- console.log(properPath);
- if (!properPath.includes(href)) {
- console.log(`The two (url and proper path) were not equal`);
- const proto = Doc.GetProto(Document);
- proto[this.props.fieldKey] = new PdfField(properPath);
- proto[backup] = href;
+ const pdf = Cast(this.dataDoc[this.props.fieldKey], PdfField);
+ const href = pdf?.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(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(`The two (url and proper path) were equal`);
+ console.log("Outer matches was null!");
}
- } else {
- console.log("Outer matches was null!");
}
}
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index dbc879920..8818d375e 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -62,8 +62,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const lastFrame = Cast(presTargetDoc.lastFrame, "number", null);
const curFrame = NumCast(presTargetDoc.currentFrame);
if (lastFrame !== undefined && curFrame < lastFrame) {
- presTargetDoc.transition = "all 1s";
- setTimeout(() => presTargetDoc.transition = undefined, 1010);
+ presTargetDoc._viewTransition = "all 1s";
+ setTimeout(() => presTargetDoc._viewTransition = undefined, 1010);
presTargetDoc.currentFrame = curFrame + 1;
}
else if (this.childDocs[this.itemIndex + 1] !== undefined) {
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 8c16f4a1a..8718bf329 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -184,9 +184,9 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
if (container) {
const alias = Doc.MakeAlias(container.props.Document);
alias.viewType = CollectionViewType.Time;
- let list = Cast(alias.schemaColumns, listSpec(SchemaHeaderField));
+ let list = Cast(alias._columnHeaders, listSpec(SchemaHeaderField));
if (!list) {
- alias.schemaColumns = list = new List<SchemaHeaderField>();
+ alias._columnHeaders = list = new List<SchemaHeaderField>();
}
list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, "#f1efeb"));
list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb"));
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 20e13a599..678494b27 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -305,37 +305,37 @@ footnote::after {
margin-left: 1em;
font-family: inherit;
}
- .bullet { p {display: inline; font-family: inherit} margin-left: 0; }
- .bullet1 { p {display: inline; font-family: inherit} }
- .bullet2,.bullet3,.bullet4,.bullet5,.bullet6 { p {display: inline; font-family: inherit} font-size: smaller; }
-
- .decimal1-ol { counter-reset: deci1; p {display: inline; font-family: inherit} margin-left: 0; }
- .decimal2-ol { counter-reset: deci2; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 1em;}
- .decimal3-ol { counter-reset: deci3; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 2em;}
- .decimal4-ol { counter-reset: deci4; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 3em;}
- .decimal5-ol { counter-reset: deci5; p {display: inline; font-family: inherit} font-size: smaller; }
- .decimal6-ol { counter-reset: deci6; p {display: inline; font-family: inherit} font-size: smaller; }
- .decimal7-ol { counter-reset: deci7; p {display: inline; font-family: inherit} font-size: smaller; }
-
- .multi1-ol { counter-reset: multi1; p {display: inline; font-family: inherit} margin-left: 0; padding-left: 1.2em }
- .multi2-ol { counter-reset: multi2; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 1.4em;}
- .multi3-ol { counter-reset: multi3; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 2em;}
- .multi4-ol { counter-reset: multi4; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 3.4em;}
+ .bullet { p {display: inline-block; font-family: inherit} margin-left: 0; }
+ .bullet1 { p {display: inline-block; font-family: inherit} }
+ .bullet2,.bullet3,.bullet4,.bullet5,.bullet6 { p {display: inline-block; font-family: inherit} font-size: smaller; }
+
+ .decimal1-ol { counter-reset: deci1; p {display: inline-block; font-family: inherit} margin-left: 0; }
+ .decimal2-ol { counter-reset: deci2; p {display: inline-block; font-family: inherit} font-size: smaller; padding-left: 1em;}
+ .decimal3-ol { counter-reset: deci3; p {display: inline-block; font-family: inherit} font-size: smaller; padding-left: 2em;}
+ .decimal4-ol { counter-reset: deci4; p {display: inline-block; font-family: inherit} font-size: smaller; padding-left: 3em;}
+ .decimal5-ol { counter-reset: deci5; p {display: inline-block; font-family: inherit} font-size: smaller; }
+ .decimal6-ol { counter-reset: deci6; p {display: inline-block; font-family: inherit} font-size: smaller; }
+ .decimal7-ol { counter-reset: deci7; p {display: inline-block; font-family: inherit} font-size: smaller; }
+
+ .multi1-ol { counter-reset: multi1; p {display: inline-block; font-family: inherit} margin-left: 0; padding-left: 1.2em }
+ .multi2-ol { counter-reset: multi2; p {display: inline-block; font-family: inherit} font-size: smaller; padding-left: 1.4em;}
+ .multi3-ol { counter-reset: multi3; p {display: inline-block; font-family: inherit} font-size: smaller; padding-left: 2em;}
+ .multi4-ol { counter-reset: multi4; p {display: inline-block; font-family: inherit} font-size: smaller; padding-left: 3.4em;}
- .bullet:before, .bullet1:before, .bullet2:before, .bullet3:before, .bullet4:before, .bullet5:before { transition: 0.5s; display: inline-block; margin-left: -1em; width: 1em; content:" " }
-
- .decimal1:before { transition: 0.5s;counter-increment: deci1; display: inline-block; margin-left: -1em; width: 1em; content: counter(deci1) ". "; }
- .decimal2:before { transition: 0.5s;counter-increment: deci2; display: inline-block; margin-left: -2.1em; width: 2.1em; content: counter(deci1) "."counter(deci2) ". "; }
- .decimal3:before { transition: 0.5s;counter-increment: deci3; display: inline-block; margin-left: -2.85em;width: 2.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) ". "; }
- .decimal4:before { transition: 0.5s;counter-increment: deci4; display: inline-block; margin-left: -3.85em;width: 3.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) ". "; }
- .decimal5:before { transition: 0.5s;counter-increment: deci5; display: inline-block; margin-left: -2em; width: 5em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) ". "; }
- .decimal6:before { transition: 0.5s;counter-increment: deci6; display: inline-block; margin-left: -2em; width: 6em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) ". "; }
- .decimal7:before { transition: 0.5s;counter-increment: deci7; display: inline-block; margin-left: -2em; width: 7em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) "."counter(deci7) ". "; }
+ .bullet:before, .bullet1:before, .bullet2:before, .bullet3:before, .bullet4:before, .bullet5:before { transition: 0.5s; display: inline-block; vertical-align: top; margin-left: -1em; width: 1em; content:" " }
+
+ .decimal1:before { transition: 0.5s;counter-increment: deci1; display: inline-block; vertical-align: top; margin-left: -1em; width: 1em; content: counter(deci1) ". "; }
+ .decimal2:before { transition: 0.5s;counter-increment: deci2; display: inline-block; vertical-align: top; margin-left: -2.1em; width: 2.1em; content: counter(deci1) "."counter(deci2) ". "; }
+ .decimal3:before { transition: 0.5s;counter-increment: deci3; display: inline-block; vertical-align: top; margin-left: -2.85em;width: 2.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) ". "; }
+ .decimal4:before { transition: 0.5s;counter-increment: deci4; display: inline-block; vertical-align: top; margin-left: -3.85em;width: 3.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) ". "; }
+ .decimal5:before { transition: 0.5s;counter-increment: deci5; display: inline-block; vertical-align: top; margin-left: -2em; width: 5em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) ". "; }
+ .decimal6:before { transition: 0.5s;counter-increment: deci6; display: inline-block; vertical-align: top; margin-left: -2em; width: 6em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) ". "; }
+ .decimal7:before { transition: 0.5s;counter-increment: deci7; display: inline-block; vertical-align: top; margin-left: -2em; width: 7em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) "."counter(deci7) ". "; }
- .multi1:before { transition: 0.5s;counter-increment: multi1; display: inline-block; margin-left: -1em; width: 1.2em; content: counter(multi1, upper-alpha) ". "; }
- .multi2:before { transition: 0.5s;counter-increment: multi2; display: inline-block; margin-left: -2em; width: 2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) ". "; }
- .multi3:before { transition: 0.5s;counter-increment: multi3; display: inline-block; margin-left: -2.85em; width:2.85em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) ". "; }
- .multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; margin-left: -4.2em; width: 4.2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; }
+ .multi1:before { transition: 0.5s;counter-increment: multi1; display: inline-block; vertical-align: top; margin-left: -1em; width: 1.2em; content: counter(multi1, upper-alpha) ". "; }
+ .multi2:before { transition: 0.5s;counter-increment: multi2; display: inline-block; vertical-align: top; margin-left: -2em; width: 2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) ". "; }
+ .multi3:before { transition: 0.5s;counter-increment: multi3; display: inline-block; vertical-align: top; margin-left: -2.85em; width:2.85em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) ". "; }
+ .multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; vertical-align: top; margin-left: -4.2em; width: 4.2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; }
}
.formattedTextBox-inner-rounded-selected,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 0fc8258fc..90f379525 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -878,7 +878,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
setTimeout(async () => {
const targetField = Doc.LayoutFieldKey(pdfDoc);
const targetAnnotations = await DocListCastAsync(pdfDoc[DataSym][targetField + "-annotations"]);// bcz: better to have the PDF's view handle updating its own annotations
- targetAnnotations?.push(pdfRegion);
+ if (targetAnnotations) targetAnnotations.push(pdfRegion);
+ else Doc.AddDocToList(pdfDoc[DataSym], targetField + "-annotations", pdfRegion);
});
const link = DocUtils.MakeLink({ doc: this.rootDoc }, { doc: pdfRegion }, "PDF pasted");
@@ -1030,9 +1031,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const editor = this._editorView!;
FormattedTextBoxComment.textBox = this;
const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
- const node = pcords && editor.state.doc.nodeAt(pcords.pos); // get what prosemirror thinks the clicked node is (if it's null, then we didn't click on any text)
- !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(node && pcords ?
- new NodeSelection(editor.state.doc.resolve(pcords.pos)) : new TextSelection(editor.state.doc.resolve(pcords?.pos || 0))));
+ !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0))));
FormattedTextBoxComment.update(editor, undefined, (e.target as any)?.className === "prosemirror-dropdownlink" ? (e.target as any).href : "");
}
(e.nativeEvent as any).formattedHandled = true;
@@ -1220,13 +1219,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
tryUpdateHeight(limitHeight?: number) {
let scrollHeight = this._ref.current?.scrollHeight;
if (this.props.renderDepth && this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
- scrollHeight = scrollHeight * NumCast(this.layoutDoc.scale, 1);
+ scrollHeight = scrollHeight * NumCast(this.layoutDoc._viewScale, 1);
if (limitHeight && scrollHeight > limitHeight) {
scrollHeight = limitHeight;
this.layoutDoc.limitHeight = undefined;
this.layoutDoc._autoHeight = false;
}
- const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.dataDoc._nativeHeight, 0);
+ const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.layoutDoc._nativeHeight, 0);
const dh = NumCast(this.rootDoc._height, 0);
const newHeight = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0));
if (this.rootDoc !== this.layoutDoc.doc && !this.layoutDoc.resolvedDataDoc) {
@@ -1234,11 +1233,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
console.log("Delayed height adjustment...");
setTimeout(() => {
this.rootDoc._height = newHeight;
- this.dataDoc._nativeHeight = nh ? scrollHeight : undefined;
+ this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
}, 10);
} else {
- this.rootDoc._height = newHeight;
- this.dataDoc._nativeHeight = nh ? scrollHeight : undefined;
+ this.layoutDoc._height = newHeight;
+ this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
}
}
}
@@ -1249,7 +1248,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
@computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); }
render() {
TraceMobx();
- const scale = this.props.ContentScaling() * NumCast(this.layoutDoc.scale, 1);
+ const scale = this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1);
const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc.isBackground;
if (this.props.isSelected()) {
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 9c91d8007..1bbcb9fa8 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -1,4 +1,4 @@
-import { chainCommands, exitCode, joinDown, joinUp, lift, selectParentNode, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn, newlineInCode } from "prosemirror-commands";
+import { chainCommands, exitCode, joinDown, joinUp, lift, deleteSelection, joinBackward, selectNodeBackward, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn, newlineInCode } from "prosemirror-commands";
import { liftTarget } from "prosemirror-transform";
import { redo, undo } from "prosemirror-history";
import { undoInputRule } from "prosemirror-inputrules";
@@ -12,6 +12,7 @@ import { Doc, DataSym } from "../../../../fields/Doc";
import { FormattedTextBox } from "./FormattedTextBox";
import { Id } from "../../../../fields/FieldSymbols";
import { Docs } from "../../../documents/Documents";
+import { update } from "lodash";
const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
@@ -43,7 +44,6 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
//History commands
bind("Mod-z", undo);
- bind("Backspace", undoInputRule);
bind("Shift-Mod-z", redo);
!mac && bind("Mod-y", redo);
@@ -175,6 +175,25 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
}
});
+ // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward);
+ bind("Backspace", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
+ if (!deleteSelection(state, (tx: Transaction<Schema<any, any>>) => {
+ dispatch(updateBullets(tx, schema));
+ })) {
+ if (!joinBackward(state, (tx: Transaction<Schema<any, any>>) => {
+ dispatch(updateBullets(tx, schema));
+ })) {
+ if (!selectNodeBackward(state, (tx: Transaction<Schema<any, any>>) => {
+ dispatch(updateBullets(tx, schema));
+ })) {
+ return false;
+ }
+ }
+ }
+ return true;
+ });
+
+ //newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock
//command to break line
bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
if (addTextOnRight(false)) return true;
@@ -190,7 +209,12 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
const cr = state.selection.$from.node().textContent.endsWith("\n");
if (cr || !newlineInCode(state, dispatch)) {
- if (!splitListItem(schema.nodes.list_item)(state, dispatch)) {
+ if (!splitListItem(schema.nodes.list_item)(state, (tx2: Transaction) => {
+ const tx3 = updateBullets(tx2, schema);
+ marks && tx3.ensureMarks([...marks]);
+ marks && tx3.setStoredMarks([...marks]);
+ dispatch(tx3);
+ })) {
if (!splitBlockKeepMarks(state, (tx3: Transaction) => {
splitMetadata(marks, tx3);
if (!liftListItem(schema.nodes.list_item)(tx3, dispatch as ((tx: Transaction<Schema<any, any>>) => void))) {
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index ba3230801..6a85e3b7c 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -275,7 +275,7 @@ export class RichTextRules {
if (!fieldKey) {
if (docid) {
DocServer.GetRefField(docid).then(docx => {
- const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500, _LODdisable: true, }, docid);
+ const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500, }, docid);
DocUtils.Publish(target, docid, returnFalse, returnFalse);
DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to");
});
@@ -305,7 +305,7 @@ export class RichTextRules {
if (!fieldKey && !docid) return state.tr;
docid && DocServer.GetRefField(docid).then(docx => {
if (!(docx instanceof Doc && docx)) {
- const docx = Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500, _LODdisable: true }, docid);
+ const docx = Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500 }, docid);
DocUtils.Publish(docx, docid, returnFalse, returnFalse);
}
});
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 0a867912f..afb1f57b7 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -302,7 +302,7 @@ export const nodes: { [index: string]: NodeSpec } = {
mapStyle: { default: "decimal" }, // "decimal", "multi", "bullet"
visibility: { default: true }
},
- content: 'paragraph block*',
+ content: 'paragraph+ | (paragraph ordered_list)',
parseDOM: [{
tag: "li", getAttrs(dom: any) {
return { mapStyle: dom.getAttribute("data-mapStyle"), bulletStyle: dom.getAttribute("data-bulletStyle") };
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index affffc44e..86c73bfee 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -1,5 +1,5 @@
-.pdfViewer, .pdfViewer-interactive {
+.pdfViewerDash, .pdfViewerDash-interactive {
width: 100%;
height: 100%;
position: absolute;
@@ -12,14 +12,14 @@
// transform-origin: top left;
// }
.textLayer {
-
+ opacity: unset;
mix-blend-mode: multiply;// bcz: makes text fuzzy!
span {
padding-right: 5px;
padding-bottom: 4px;
}
}
- .textLayer ::selection { background: yellow; } // should match the backgroundColor in createAnnotation()
+ .textLayer ::selection { background: #ACCEF7; } // should match the backgroundColor in createAnnotation()
.textLayer .highlight {
background-color: yellow;
}
@@ -31,26 +31,26 @@
position: relative;
border: unset;
}
- .pdfViewer-text-selected {
+ .pdfViewerDash-text-selected {
.textLayer{
pointer-events: all;
user-select: text;
}
}
- .pdfViewer-text {
+ .pdfViewerDash-text {
transform-origin: top left;
.textLayer {
will-change: transform;
}
}
- .pdfViewer-dragAnnotationBox {
+ .pdfViewerDash-dragAnnotationBox {
position:absolute;
background-color: transparent;
opacity: 0.1;
}
- .pdfViewer-overlay, .pdfViewer-overlay-inking {
+ .pdfViewerDash-overlay, .pdfViewerDash-overlay-inking {
transform-origin: left top;
position: absolute;
top: 0px;
@@ -58,11 +58,11 @@
display: inline-block;
width:100%;
}
- .pdfViewer-overlay {
+ .pdfViewerDash-overlay {
pointer-events: none;
}
- .pdfViewer-annotationLayer {
+ .pdfViewerDash-annotationLayer {
position: absolute;
transform-origin: left top;
top: 0;
@@ -70,12 +70,12 @@
pointer-events: none;
mix-blend-mode: multiply; // bcz: makes text fuzzy!
- .pdfViewer-annotationBox {
+ .pdfViewerDash-annotationBox {
position: absolute;
background-color: rgba(245, 230, 95, 0.616);
}
}
- .pdfViewer-waiting {
+ .pdfViewerDash-waiting {
width: 70%;
height: 70%;
margin : 15%;
@@ -86,7 +86,7 @@
}
}
-.pdfViewer-interactive {
+.pdfViewerDash-interactive {
pointer-events: all;
}
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 91d0feafe..372d01b9c 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,5 +1,6 @@
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
+const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
import { Dictionary } from "typescript-collections";
@@ -41,7 +42,10 @@ export const pageSchema = createSchema({
serachMatch: "boolean"
});
-pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
+//pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
+// The workerSrc property shall be specified.
+pdfjsLib.GlobalWorkerOptions.workerSrc = "https://unpkg.com/pdfjs-dist@2.4.456/build/pdf.worker.min.js";
+
type PdfDocument = makeInterface<[typeof documentSchema, typeof pageSchema]>;
const PdfDocument = makeInterface(documentSchema, pageSchema);
@@ -260,16 +264,18 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
}
document.removeEventListener("copy", this.copy);
document.addEventListener("copy", this.copy);
- document.addEventListener("pagesinit", this.pagesinit);
- document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false));
+ const eventBus = new PDFJSViewer.EventBus(true);
+ eventBus._on("pagesinit", this.pagesinit);
+ eventBus._on("pagerendered", action(() => this._showCover = this._showWaiting = false));
const pdfLinkService = new PDFJSViewer.PDFLinkService();
- const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService });
+ const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, eventBus });
this._pdfViewer = new PDFJSViewer.PDFViewer({
container: this._mainCont.current,
viewer: this._viewer.current,
linkService: pdfLinkService,
findController: pdfFindController,
renderer: "canvas",
+ eventBus
});
pdfLinkService.setViewer(this._pdfViewer);
pdfLinkService.setDocument(this.props.pdf, null);
@@ -287,7 +293,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
let minY = Number.MAX_VALUE;
if ((this._savedAnnotations.values()[0][0] as any).marqueeing) {
const anno = this._savedAnnotations.values()[0][0];
- const annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, _LODdisable: true, title: "Annotation on " + this.Document.title });
+ const annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, title: "Annotation on " + this.Document.title });
if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
if (anno.style.height) annoDoc._height = parseInt(anno.style.height);
@@ -375,7 +381,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
div.style.top = (parseInt(div.style.top)/*+ this.getScrollFromPage(page)*/).toString();
}
this._annotationLayer.current.append(div);
- div.style.backgroundColor = "yellow";
+ div.style.backgroundColor = "#ACCEF7";
div.style.opacity = "0.5";
const savedPage = this._savedAnnotations.getValue(page);
if (savedPage) {
@@ -393,7 +399,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
if (!searchString) {
fwd ? this.nextAnnotation() : this.prevAnnotation();
}
- else if (this._pdfViewer._pageViewsReady) {
+ else if (this._pdfViewer.pageViewsReady) {
this._pdfViewer.findController.executeCommand('findagain', {
caseSensitive: false,
findPrevious: !fwd,
@@ -427,7 +433,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
this._downX = e.clientX;
this._downY = e.clientY;
addStyleSheetRule(PDFViewer._annotationStyle, "pdfAnnotation", { "pointer-events": "none" });
- if ((this.Document.scale || 1) !== 1) return;
+ if ((this.Document._viewScale || 1) !== 1) return;
if ((e.button !== 0 || e.altKey) && this.active(true)) {
this._setPreviewCursor?.(e.clientX, e.clientY, true);
//e.stopPropagation();
@@ -491,9 +497,9 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
if (rect.width !== this._mainCont.current.clientWidth &&
(i === 0 || !intersectRect(clientRects[i], clientRects[i - 1]))) {
const annoBox = document.createElement("div");
- annoBox.className = "pdfViewer-annotationBox";
+ annoBox.className = "pdfViewerDash-annotationBox";
// transforms the positions from screen onto the pdf div
- annoBox.style.top = ((rect.top - boundingRect.top) * scaleY / this._zoomed + this._mainCont.current.scrollTop).toString();
+ annoBox.style.top = ((rect.top - boundingRect.top) * scaleX / this._zoomed + this._mainCont.current.scrollTop).toString();
annoBox.style.left = ((rect.left - boundingRect.left) * scaleX / this._zoomed).toString();
annoBox.style.width = (rect.width * this._mainCont.current.offsetWidth / boundingRect.width / this._zoomed).toString();
annoBox.style.height = (rect.height * this._mainCont.current.offsetHeight / boundingRect.height / this._zoomed).toString();
@@ -518,7 +524,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
this._savedAnnotations.clear();
if (this._marqueeing) {
if (this._marqueeWidth > 10 || this._marqueeHeight > 10) {
- const marquees = this._mainCont.current!.getElementsByClassName("pdfViewer-dragAnnotationBox");
+ const marquees = this._mainCont.current!.getElementsByClassName("pdfViewerDash-dragAnnotationBox");
if (marquees && marquees.length) { // copy the marquee and convert it to a permanent annotation.
const style = (marquees[0] as HTMLDivElement).style;
const copy = document.createElement("div");
@@ -529,7 +535,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
copy.style.border = style.border;
copy.style.opacity = style.opacity;
(copy as any).marqueeing = true;
- copy.className = "pdfViewer-annotationBox";
+ copy.className = "pdfViewerDash-annotationBox";
this.createAnnotation(copy, this.getPageFromScroll(this._marqueeY));
}
@@ -635,17 +641,19 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
@action
onZoomWheel = (e: React.WheelEvent) => {
- e.stopPropagation();
- if (e.ctrlKey) {
- const curScale = Number(this._pdfViewer.currentScaleValue);
- this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - curScale * e.deltaY / 1000));
- this._zoomed = Number(this._pdfViewer.currentScaleValue);
+ if (this.active()) {
+ e.stopPropagation();
+ if (e.ctrlKey) {
+ const curScale = Number(this._pdfViewer.currentScaleValue);
+ this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - curScale * e.deltaY / 1000));
+ this._zoomed = Number(this._pdfViewer.currentScaleValue);
+ }
}
}
@computed get annotationLayer() {
TraceMobx();
- return <div className="pdfViewer-annotationLayer" style={{ height: NumCast(this.Document._nativeHeight), transform: `scale(${this._zoomed})` }} ref={this._annotationLayer}>
+ return <div className="pdfViewerDash-annotationLayer" style={{ height: NumCast(this.Document._nativeHeight), transform: `scale(${this._zoomed})` }} ref={this._annotationLayer}>
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
<Annotation {...this.props} focus={this.props.focus} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)
}
@@ -655,7 +663,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
panelWidth = () => (this.Document.scrollHeight || this.Document._nativeHeight || 0);
panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : (this.Document._nativeWidth || 0);
@computed get overlayLayer() {
- return <div className={`pdfViewer-overlay${Doc.GetSelectedTool() !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`} id="overlay"
+ return <div className={`pdfViewerDash-overlay${Doc.GetSelectedTool() !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`} id="overlay"
style={{ transform: `scale(${this._zoomed})` }}>
<CollectionFreeFormView {...this.props}
LibraryPath={this.props.ContainingCollectionView?.props.LibraryPath ?? emptyPath}
@@ -687,13 +695,13 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
</div>;
}
@computed get pdfViewerDiv() {
- return <div className={"pdfViewer-text" + ((!DocumentDecorations.Instance.Interacting && (this.props.isSelected() || this.props.isChildActive())) ? "-selected" : "")} ref={this._viewer} />;
+ return <div className={"pdfViewerDash-text" + ((!DocumentDecorations.Instance.Interacting && (this.props.isSelected() || this.props.isChildActive())) ? "-selected" : "")} ref={this._viewer} />;
}
@computed get contentScaling() { return this.props.ContentScaling(); }
@computed get standinViews() {
return <>
{this._showCover ? this.getCoverImage() : (null)}
- {this._showWaiting ? <img className="pdfViewer-waiting" key="waiting" src={"/assets/loading.gif"} /> : (null)}
+ {this._showWaiting ? <img className="pdfViewerDash-waiting" key="waiting" src={"/assets/loading.gif"} /> : (null)}
</>;
}
marqueeWidth = () => this._marqueeWidth;
@@ -705,7 +713,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
contentZoom = () => this._zoomed;
render() {
TraceMobx();
- return <div className={"pdfViewer" + (this.active() ? "-interactive" : "")} ref={this._mainCont}
+ return <div className={"pdfViewerDash" + (this.active() ? "-interactive" : "")} ref={this._mainCont}
onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick}
style={{
overflowX: this._zoomed !== 1 ? "scroll" : undefined,
@@ -733,7 +741,7 @@ interface PdfViewerMarqueeProps {
@observer
class PdfViewerMarquee extends React.Component<PdfViewerMarqueeProps> {
render() {
- return !this.props.isMarqueeing() ? (null) : <div className="pdfViewer-dragAnnotationBox"
+ return !this.props.isMarqueeing() ? (null) : <div className="pdfViewerDash-dragAnnotationBox"
style={{
left: `${this.props.x()}px`, top: `${this.props.y()}px`,
width: `${this.props.width()}px`, height: `${this.props.height()}px`,
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 74262d81a..2436bf418 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -85,7 +85,7 @@ export class SelectorContextMenu extends React.Component<SearchItemProps> {
const item = React.createRef<HTMLDivElement>();
return <div className="collection" key={doc.col[Id] + doc.target[Id]} ref={item}>
<div className="collection-item" onPointerDown={
- SetupDrag(item, () => doc.col, undefined, undefined, undefined, undefined, () => SearchBox.Instance.closeSearch())}>
+ SetupDrag(item, () => doc.col, undefined, undefined, () => SearchBox.Instance.closeSearch())}>
<FontAwesomeIcon icon={faStickyNote} />
</div>
<a onClick={this.getOnClick(doc)}>{doc.col.title}</a>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 8ca85cae9..bef8acb06 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -976,7 +976,7 @@ export namespace Doc {
export function toggleNativeDimensions(layoutDoc: Doc, contentScale: number, panelWidth: number, panelHeight: number) {
runInAction(() => {
if (layoutDoc._nativeWidth || layoutDoc._nativeHeight) {
- layoutDoc.scale = NumCast(layoutDoc.scale, 1) * contentScale;
+ layoutDoc._viewScale = NumCast(layoutDoc._viewScale, 1) * contentScale;
layoutDoc._nativeWidth = undefined;
layoutDoc._nativeHeight = undefined;
}
@@ -1162,7 +1162,6 @@ Scripting.addGlobal(function activePresentationItem() {
const curPres = Doc.UserDoc().activePresentation as Doc;
return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
});
-Scripting.addGlobal(function selectDoc(doc: any) { Doc.UserDoc().activeSelection = new List([doc]); });
Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
const docs = DocListCast(Doc.UserDoc().activeSelection).
filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCHOLDER && d.type !== DocumentType.KVP &&
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 51a5768bf..7cfd74cc4 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -1,7 +1,7 @@
import { Deserializable } from "../client/util/SerializationHelper";
import { serializable, custom, createSimpleSchema, list, object, map } from "serializr";
import { ObjectField } from "./ObjectField";
-import { Copy, ToScriptString, ToString } from "./FieldSymbols";
+import { Copy, ToScriptString, ToString, Update } from "./FieldSymbols";
export enum InkTool {
None = "none",
@@ -31,6 +31,8 @@ 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/List.ts b/src/fields/List.ts
index fdabea365..a9da75abb 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -291,9 +291,10 @@ class ListImpl<T extends Field> extends ObjectField {
this.___fields = value;
for (const key in value) {
const field = value[key];
- if (!(field instanceof ObjectField)) continue;
- (field as ObjectField)[Parent] = this[Self];
- (field as ObjectField)[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]);
+ if (field instanceof ObjectField) {
+ field[Parent] = this[Self];
+ field[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]);
+ }
}
}
diff --git a/src/fields/ObjectField.ts b/src/fields/ObjectField.ts
index 9aa1c9b04..92b2cfa60 100644
--- a/src/fields/ObjectField.ts
+++ b/src/fields/ObjectField.ts
@@ -3,8 +3,8 @@ import { OnUpdate, Parent, Copy, ToScriptString, ToString } from "./FieldSymbols
import { Scripting } from "../client/util/Scripting";
export abstract class ObjectField {
- protected [OnUpdate](diff?: any) { }
- private [Parent]?: RefField | ObjectField;
+ public [OnUpdate](diff?: any) { }
+ public [Parent]?: RefField | ObjectField;
abstract [Copy](): ObjectField;
abstract [ToScriptString](): string;
diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts
index 72bce283d..98ef3e087 100644
--- a/src/fields/Schema.ts
+++ b/src/fields/Schema.ts
@@ -65,9 +65,8 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu
return obj;
};
return function (doc?: Doc | Doc[]) {
- doc = doc || new Doc;
- if (doc instanceof Doc) {
- return fn(doc);
+ if (doc instanceof Doc || doc === undefined) {
+ return fn(doc || new Doc);
} else {
return doc.map(fn);
}
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 40dadf5a8..97f62c9d4 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -2,6 +2,8 @@ import { makeInterface, createSchema, listSpec } from "./Schema";
import { ScriptField } from "./ScriptField";
import { Doc } from "./Doc";
import { DateField } from "./DateField";
+import { SchemaHeaderField } from "./SchemaHeaderField";
+import { Schema } from "prosemirror-model";
export const documentSchema = createSchema({
// content properties
@@ -43,10 +45,16 @@ export const documentSchema = createSchema({
_showTitleHover: "string", // the showTitle should be shown only on hover
_showAudio: "boolean", // whether to show the audio record icon on documents
_freeformLayoutEngine: "string",// the string ID for the layout engine to use to layout freeform view documents
- _LODdisable: "boolean", // whether to disbale LOD switching for CollectionFreeFormViews
+ _freeformLOD: "boolean", // whether to enable LOD switching for CollectionFreeFormViews
_pivotField: "string", // specifies which field key should be used as the timeline/pivot axis
_replacedChrome: "string", // what the default chrome is replaced with. Currently only supports the value of 'replaced' for PresBox's.
_chromeStatus: "string", // determines the state of the collection chrome. values allowed are 'replaced', 'enabled', 'disabled', 'collapsed'
+ _columnsFill: "boolean", // whether documents in a stacking view column should be sized to fill the column
+ _columnsSort: "string", // how a document should be sorted "ascending", "descending", undefined (none)
+ _columnsStack: "boolean", // whether a stacking document stacks vertically (as opposed to masonry horizontal)
+ _columnsHideIfEmpty: "boolean", // whether empty stacking view column headings should be hidden
+ _columnHeaders: listSpec(SchemaHeaderField), // header descriptions for stacking/masonry
+ _schemaHeaders: listSpec(SchemaHeaderField), // header descriptions for schema views
_fontSize: "number",
_fontFamily: "string",
_sidebarWidthPercent: "string", // percent of text window width taken up by sidebar
diff --git a/src/fields/util.ts b/src/fields/util.ts
index ad7b6ea7a..2869ebed1 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -10,6 +10,7 @@ import { DocServer } from "../client/DocServer";
import { ComputedField } from "./ScriptField";
import { ScriptCast } from "./Types";
+
function _readOnlySetter(): never {
throw new Error("Documents can't be modified in read-only mode");
}
@@ -107,10 +108,10 @@ export function OVERRIDE_ACL(val: boolean) {
}
const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox",
- "LODdisable", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"];
+ "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"];
export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
let prop = in_prop;
- if (target[AclSym] && !_overrideAcl) return true;
+ if (target[AclSym] && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return true;
if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {
if (!prop.startsWith("_")) {
console.log(prop + " is deprecated - switch to _" + prop);
@@ -155,9 +156,6 @@ export function getter(target: any, in_prop: string | symbol | number, receiver:
function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreProto: boolean = false): any {
receiver = receiver || target[SelfProxy];
- if (target === undefined) {
- console.log("");
- }
let field = target.__fields[prop];
for (const plugin of getterPlugins) {
const res = plugin(receiver, prop, field);
diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py
index ed122e544..312915e2d 100644
--- a/src/scraping/buxton/scraper.py
+++ b/src/scraping/buxton/scraper.py
@@ -91,14 +91,13 @@ def write_collection(parse_results, display_fields, storage_key, viewType):
"zIndex": 2,
"libraryBrush": False,
"_viewType": viewType,
- "_LODdisable": True
},
"__type": "Doc"
}
fields["proto"] = protofy(common_proto_id)
fields[storage_key] = listify(proxify_guids(view_guids))
- fields["schemaColumns"] = listify(display_fields)
+ fields["_columnHeaders"] = listify(display_fields)
fields["author"] = "Bill Buxton"
fields["creationDate"] = {
"date": datetime.datetime.utcnow().microsecond,
diff --git a/src/server/ApiManagers/PDFManager.ts b/src/server/ApiManagers/PDFManager.ts
index d2a9e9cce..e028d628d 100644
--- a/src/server/ApiManagers/PDFManager.ts
+++ b/src/server/ApiManagers/PDFManager.ts
@@ -2,6 +2,7 @@ import ApiManager, { Registration } from "./ApiManager";
import { Method } from "../RouteManager";
import RouteSubscriber from "../RouteSubscriber";
import { existsSync, createReadStream, createWriteStream } from "fs";
+const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
import * as Pdfjs from 'pdfjs-dist';
import { createCanvas } from "canvas";
const imageSize = require("probe-image-size");
@@ -51,11 +52,13 @@ async function getOrCreateThumbnail(coreFilename: string, pageNum: number, res:
}
async function CreateThumbnail(coreFilename: string, pageNum: number, res: express.Response, subtree?: string) {
- const sourcePath = resolve(pathToDirectory(Directory.pdfs), `${subtree ?? ""}${coreFilename}.pdf`);
+ const part1 = subtree ?? "";
+ const filename = `${part1}${coreFilename}.pdf`;
+ const sourcePath = resolve(pathToDirectory(Directory.pdfs), filename);
const documentProxy = await Pdfjs.getDocument(sourcePath).promise;
const factory = new NodeCanvasFactory();
const page = await documentProxy.getPage(pageNum);
- const viewport = page.getViewport(1 as any);
+ const viewport = page.getViewport({ scale: 1, rotation: 0, dontFlip: false });
const { canvas, context } = factory.create(viewport.width, viewport.height);
const renderContext = {
canvasContext: context,