aboutsummaryrefslogtreecommitdiff
path: root/src/client/documents/Documents.ts
diff options
context:
space:
mode:
authorSophie Zhang <sophie_zhang@brown.edu>2024-04-09 12:17:03 -0400
committerSophie Zhang <sophie_zhang@brown.edu>2024-04-09 12:17:03 -0400
commitb158b7ffb564db8dc60da9b80b01b10f1ed8b7cf (patch)
treeaf35e320eb876c12c617fda2eb70ce19ef376b67 /src/client/documents/Documents.ts
parenteecc7ee1d14719d510ec2975826022c565a35e5f (diff)
parent3b90916af8ffcbeaf5b8a3336009b84e19c22fa9 (diff)
Merge branch 'master' into sophie-ai-images
Diffstat (limited to 'src/client/documents/Documents.ts')
-rw-r--r--src/client/documents/Documents.ts281
1 files changed, 167 insertions, 114 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2d2f5fe4a..b160379df 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,9 +1,10 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { action, reaction, runInAction } from 'mobx';
import { basename } from 'path';
+import { OmitKeys, Utils } from '../../Utils';
import { DateField } from '../../fields/DateField';
import { Doc, DocListCast, Field, LinkedTo, Opt, StrListCast, updateCachedAcls } from '../../fields/Doc';
-import { Initializing } from '../../fields/DocSymbols';
+import { DocData, Initializing } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { HtmlField } from '../../fields/HtmlField';
import { InkField, PointData } from '../../fields/InkField';
@@ -13,30 +14,29 @@ import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
import { ComputedField, ScriptField } from '../../fields/ScriptField';
import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types';
import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField, YoutubeField } from '../../fields/URLField';
-import { inheritParentAcls, SharingPermissions } from '../../fields/util';
+import { SharingPermissions, inheritParentAcls } from '../../fields/util';
import { Upload } from '../../server/SharedMediaTypes';
-import { OmitKeys, Utils } from '../../Utils';
-import { YoutubeBox } from '../apis/youtube/YoutubeBox';
import { DocServer } from '../DocServer';
import { Networking } from '../Network';
+import { YoutubeBox } from '../apis/youtube/YoutubeBox';
import { DragManager, dropActionType } from '../util/DragManager';
import { FollowLinkScript } from '../util/LinkFollower';
import { LinkManager } from '../util/LinkManager';
import { ScriptingGlobals } from '../util/ScriptingGlobals';
-import { undoable, UndoManager } from '../util/UndoManager';
-import { CollectionDockingView } from '../views/collections/CollectionDockingView';
-import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView';
-import { CollectionView } from '../views/collections/CollectionView';
+import { UndoManager, undoable } from '../util/UndoManager';
import { ContextMenu } from '../views/ContextMenu';
import { ContextMenuProps } from '../views/ContextMenuItem';
import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke } from '../views/InkingStroke';
+import { CollectionDockingView } from '../views/collections/CollectionDockingView';
+import { CollectionView } from '../views/collections/CollectionView';
+import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView';
import { AudioBox, media_state } from '../views/nodes/AudioBox';
import { ComparisonBox } from '../views/nodes/ComparisonBox';
import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox';
+import { OpenWhere } from '../views/nodes/DocumentView';
import { EquationBox } from '../views/nodes/EquationBox';
import { FieldViewProps } from '../views/nodes/FieldView';
import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox';
-import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox';
import { FunctionPlotBox } from '../views/nodes/FunctionPlotBox';
import { ImageBox } from '../views/nodes/ImageBox';
import { KeyValueBox } from '../views/nodes/KeyValueBox';
@@ -52,15 +52,15 @@ import { RecordingBox } from '../views/nodes/RecordingBox/RecordingBox';
import { ScreenshotBox } from '../views/nodes/ScreenshotBox';
import { ScriptingBox } from '../views/nodes/ScriptingBox';
import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
-import { PresBox } from '../views/nodes/trails/PresBox';
-import { PresElementBox } from '../views/nodes/trails/PresElementBox';
import { VideoBox } from '../views/nodes/VideoBox';
import { WebBox } from '../views/nodes/WebBox';
+import { CalendarBox } from '../views/nodes/calendarBox/CalendarBox';
+import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox';
+import { PresBox } from '../views/nodes/trails/PresBox';
+import { PresElementBox } from '../views/nodes/trails/PresElementBox';
import { SearchBox } from '../views/search/SearchBox';
import { CollectionViewType, DocumentType } from './DocumentTypes';
-import { CalendarBox } from '../views/nodes/calendarBox/CalendarBox';
-import { OpenWhere } from '../views/nodes/DocumentView';
-const { default: { DFLT_IMAGE_NATIVE_DIM } } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore
+const { DFLT_IMAGE_NATIVE_DIM } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore
const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', ''));
class EmptyBox {
@@ -68,13 +68,24 @@ class EmptyBox {
return '';
}
}
+
+export enum FInfoFieldType {
+ string = 'string',
+ boolean = 'boolean',
+ number = 'number',
+ Doc = 'Doc',
+ enumeration = 'enum',
+ date = 'date',
+ list = 'list',
+ rtf = 'rich text',
+}
export class FInfo {
description: string = '';
readOnly: boolean = false;
- fieldType?: string = '';
+ fieldType?: FInfoFieldType;
values?: Field[];
- filterable?: boolean = true;
+ filterable?: boolean = true; // can be used as a Filter in FilterPanel
// format?: string; // format to display values (e.g, decimal places, $, etc)
// parse?: ScriptField; // parse a value from a string
constructor(d: string, readOnly?: boolean) {
@@ -84,7 +95,7 @@ export class FInfo {
searchable = () => true;
}
class BoolInfo extends FInfo {
- fieldType? = 'boolean';
+ fieldType? = FInfoFieldType.boolean;
values?: boolean[] = [true, false];
constructor(d: string, filterable?: boolean) {
super(d);
@@ -93,7 +104,7 @@ class BoolInfo extends FInfo {
override searchable = () => false;
}
class NumInfo extends FInfo {
- fieldType? = 'number';
+ fieldType? = FInfoFieldType.number;
values?: number[] = [];
constructor(d: string, filterable?: boolean, readOnly?: boolean, values?: number[]) {
super(d, readOnly);
@@ -103,7 +114,7 @@ class NumInfo extends FInfo {
override searchable = () => false;
}
class StrInfo extends FInfo {
- fieldType? = 'string';
+ fieldType? = FInfoFieldType.string;
values?: string[] = [];
constructor(d: string, filterable?: boolean, readOnly?: boolean, values?: string[]) {
super(d, readOnly);
@@ -112,7 +123,7 @@ class StrInfo extends FInfo {
}
}
class DocInfo extends FInfo {
- fieldType? = 'Doc';
+ fieldType? = FInfoFieldType.Doc;
values?: Doc[] = [];
constructor(d: string, filterable?: boolean, values?: Doc[]) {
super(d, true);
@@ -122,45 +133,55 @@ class DocInfo extends FInfo {
override searchable = () => false;
}
class DimInfo extends FInfo {
- fieldType? = 'enumeration';
+ fieldType? = FInfoFieldType.enumeration;
values? = [DimUnit.Pixel, DimUnit.Ratio];
readOnly = false;
filterable = false;
override searchable = () => false;
}
class PEInfo extends FInfo {
- fieldType? = 'enumeration';
+ fieldType? = FInfoFieldType.enumeration;
values? = ['all', 'none'];
readOnly = false;
filterable = false;
override searchable = () => false;
}
class DAInfo extends FInfo {
- fieldType? = 'enumeration';
- values? = ['embed', 'copy', 'move', 'same', 'proto', 'none'];
+ fieldType? = FInfoFieldType.enumeration;
+ values? = ['embed', 'copy', 'move', 'same', 'add', 'inSame', 'proto'];
readOnly = false;
filterable = false;
override searchable = () => false;
}
class CTypeInfo extends FInfo {
- fieldType? = 'enumeration';
+ fieldType? = FInfoFieldType.enumeration;
values? = Array.from(Object.keys(CollectionViewType));
readOnly = false;
filterable = false;
override searchable = () => false;
}
class DTypeInfo extends FInfo {
- fieldType? = 'enumeration';
+ fieldType? = FInfoFieldType.enumeration;
values? = Array.from(Object.keys(DocumentType));
override searchable = () => false;
}
class DateInfo extends FInfo {
- fieldType? = 'date';
+ constructor(d: string, filterable?: boolean) {
+ super(d, true);
+ this.filterable = filterable;
+ }
+ fieldType? = FInfoFieldType.date;
values?: DateField[] = [];
- filterable = true;
+}
+class RtfInfo extends FInfo {
+ constructor(d: string, filterable?: boolean) {
+ super(d);
+ this.filterable = filterable;
+ }
+ fieldType? = FInfoFieldType.rtf;
}
class ListInfo extends FInfo {
- fieldType? = 'list';
+ fieldType? = FInfoFieldType.list;
values?: List<any>[] = [];
}
type BOOLt = BoolInfo | boolean;
@@ -168,6 +189,7 @@ type NUMt = NumInfo | number;
type STRt = StrInfo | string;
type LISTt = ListInfo | List<any>;
type DOCt = DocInfo | Doc;
+type RTFt = RtfInfo | RichTextField;
type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio;
type PEVt = PEInfo | 'none' | 'all';
type COLLt = CTypeInfo | CollectionViewType;
@@ -176,19 +198,21 @@ type DATEt = DateInfo | number;
type DTYPEt = DTypeInfo | string;
export class DocumentOptions {
// coordinate and dimensions depending on view
- x?: NUMt = new NumInfo('x coordinate of document in a freeform view', false);
- y?: NUMt = new NumInfo('y coordinate of document in a freeform view', false);
+ x?: NUMt = new NumInfo('horizontal coordinate in freeform view', false);
+ y?: NUMt = new NumInfo('vertical coordinate in freeform view', false);
z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, false, [1, 0]);
- overlayX?: NUMt = new NumInfo('x coordinate of document in a overlay view', false);
- overlayY?: NUMt = new NumInfo('y coordinate of document in a overlay view', false);
+ overlayX?: NUMt = new NumInfo('horizontal coordinate in overlay view', false);
+ overlayY?: NUMt = new NumInfo('vertical coordinate in overlay view', false);
+ text?: RTFt = new RtfInfo('plain or rich text', true);
+ text_html?: STRt = new StrInfo('plain text or html', true);
_dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", false);
_dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
- latitude?: NUMt = new NumInfo('latitude coordinate for map views', false);
- longitude?: NUMt = new NumInfo('longitude coordinate for map views', false);
+ latitude?: NUMt = new NumInfo('latitude coordinate', false);
+ longitude?: NUMt = new NumInfo('longitude coordinate', false);
routeCoordinates?: STRt = new StrInfo("stores a route's/direction's coordinates (stringified version)"); // for a route document, this stores the route's coordinates
- markerType?: STRt = new StrInfo('Defines the marker type for a pushpin document');
- markerColor?: STRt = new StrInfo('Defines the marker color for a pushpin document');
- map?: STRt = new StrInfo('text location of map');
+ markerType?: STRt = new StrInfo('marker type for a pushpin document');
+ markerColor?: STRt = new StrInfo('marker color for a pushpin document');
+ map?: STRt = new StrInfo('map location name');
map_type?: STRt = new StrInfo('type of map view', false);
map_zoom?: NUMt = new NumInfo('zoom of a map view', false);
map_pitch?: NUMt = new NumInfo('pitch of a map view', false);
@@ -198,11 +222,11 @@ export class DocumentOptions {
date_range?: STRt = new StrInfo('date range for calendar', false);
wikiData?: STRt = new StrInfo('WikiData ID related to map location');
- description?: STRt = new StrInfo('A description of the document');
- _timecodeToShow?: NUMt = new NumInfo('the time that a document should be displayed (e.g., when an annotation shows up as a video plays)', false);
- _timecodeToHide?: NUMt = new NumInfo('the time that a document should be hidden', false);
- _width?: NUMt = new NumInfo('displayed width of a document');
- _height?: NUMt = new NumInfo('displayed height of document');
+ description?: STRt = new StrInfo('description of document');
+ _timecodeToShow?: NUMt = new NumInfo('media timecode when document should appear (e.g., when an annotation shows up as a video plays)', false);
+ _timecodeToHide?: NUMt = new NumInfo('media timecode when document should disappear', false);
+ _width?: NUMt = new NumInfo("width of document in container's coordinates");
+ _height?: NUMt = new NumInfo("height of document in container's coordiantes");
data_nativeWidth?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)', false);
data_nativeHeight?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)', false);
linearBtnWidth?: NUMt = new NumInfo('unexpanded width of a linear menu button (button "width" changes when it expands)', false);
@@ -230,9 +254,9 @@ export class DocumentOptions {
dontRegisterView?: BOOLt = new BoolInfo('are views of this document registered so that they can be found when following links, etc', false);
_undoIgnoreFields?: List<string>; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'
undoIgnoreFields?: List<string>; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'
- _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title', false);
- _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes', false);
- _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume');
+ _header_height?: NUMt = new NumInfo('height of document header used for displaying title', false);
+ _header_fontSize?: NUMt = new NumInfo('font size of header of custom notes', false);
+ _header_pointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume');
_lockedPosition?: BOOLt = new BoolInfo("lock the x,y coordinates of the document so that it can't be dragged");
_lockedTransform?: BOOLt = new BoolInfo('lock the freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed');
@@ -250,19 +274,21 @@ export class DocumentOptions {
layout_hideResizeHandles?: BOOLt = new BoolInfo('whether to hide the resize handles when selected');
layout_hideLinkButton?: BOOLt = new BoolInfo('whether the blue link counter button should be hidden');
layout_hideDecorationTitle?: BOOLt = new BoolInfo('whether to suppress the document decortations title when selected');
- _layout_hideContextMenu?: BOOLt = new BoolInfo('whether the context menu can be shown');
+ layout_hideContextMenu?: BOOLt = new BoolInfo('whether the context menu can be shown');
layout_borderRounding?: string;
+ _layout_borderRounding?: STRt = new StrInfo('amount of rounding to document view corners');
_layout_modificationDate?: DATEt = new DateInfo('last modification date of doc layout', false);
_layout_nativeDimEditable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers', false);
- _layout_reflowVertical?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers');
- _layout_reflowHorizontal?: BOOLt = new BoolInfo('whether a doc with a native size can be horizonally resized, causing some form of reflow');
+ _layout_reflowVertical?: BOOLt = new BoolInfo('permit vertical resizing with content "reflow"');
+ _layout_reflowHorizontal?: BOOLt = new BoolInfo('permit horizontal resizing with content reflow');
layout_boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
+ layout_maxShown?: NUMt = new NumInfo('maximum number of children to display at one time (see multicolumnview)');
_layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
+ _layout_autoHeightMargins?: NUMt = new NumInfo('Margin heights to be added to the computed auto height of a Doc');
_layout_curPage?: NUMt = new NumInfo('current page of a PDF or other? paginated document', false);
_layout_currentTimecode?: NUMt = new NumInfo('the current timecode of a time-based document (e.g., current time of a video) value is in seconds', false);
_layout_centered?: BOOLt = new BoolInfo('whether text should be vertically centered in Doc');
_layout_fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
- _layout_fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
_layout_fieldKey?: STRt = new StrInfo('the field key containing the current layout definition', false);
_layout_enableAltContentUI?: BOOLt = new BoolInfo('whether to show alternate content button');
_layout_showTitle?: string; // field name to display in header (:hover is an optional suffix)
@@ -294,7 +320,7 @@ export class DocumentOptions {
_label_minFontSize?: NUMt = new NumInfo('minimum font size for labelBoxes', false);
_label_maxFontSize?: NUMt = new NumInfo('maximum font size for labelBoxes', false);
stroke_width?: NUMt = new NumInfo('width of an ink stroke', false);
- icon_label?: STRt = new StrInfo('label to use for a fontIcon doc (otherwise, the title is used)', false);
+ stroke_showLabel?: BOOLt = new BoolInfo('show label inside of stroke');
mediaState?: STRt = new StrInfo(`status of audio/video media document: ${media_state.PendingRecording}, ${media_state.Recording}, ${media_state.Paused}, ${media_state.Playing}`, false);
recording?: BOOLt = new BoolInfo('whether WebCam is recording or not');
slides?: DOCt = new DocInfo('presentation slide associated with video recording (bcz: should be renamed!!)');
@@ -373,6 +399,7 @@ export class DocumentOptions {
_freeform_panY?: NUMt = new NumInfo('vertical pan location of a freeform view');
_freeform_noAutoPan?: BOOLt = new BoolInfo('disables autopanning when this item is dragged');
_freeform_noZoom?: BOOLt = new BoolInfo('disables zooming (used by Pile docs)');
+ _freeform_fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
//BUTTONS
buttonText?: string;
@@ -459,7 +486,6 @@ export class DocumentOptions {
sidebar_type_collection?: string; // collection type of text sidebar
data_dashboards?: List<any>; // list of dashboards used in shareddocs;
- text?: string;
textTransform?: string;
letterSpacing?: string;
iconTemplate?: string; // name of icon template style
@@ -629,8 +655,8 @@ export namespace Docs {
[
DocumentType.LABEL,
{
- layout: { view: LabelBox, dataField: defaultDataKey },
- options: { _singleLine: true },
+ layout: { view: LabelBox, dataField: 'title' },
+ options: { _singleLine: true, layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true },
},
],
[
@@ -661,8 +687,8 @@ export namespace Docs {
[
DocumentType.BUTTON,
{
- layout: { view: LabelBox, dataField: 'onClick' },
- options: {},
+ layout: { view: LabelBox, dataField: 'title' },
+ options: { layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true },
},
],
[
@@ -676,7 +702,7 @@ export namespace Docs {
DocumentType.FONTICON,
{
layout: { view: FontIconBox, dataField: 'icon' },
- options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideLinkButton: true, _width: 40, _height: 40 },
+ options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideContextMenu: true, layout_hideLinkButton: true, _width: 40, _height: 40 },
},
],
[
@@ -728,7 +754,7 @@ export namespace Docs {
{
data: '',
layout: { view: ComparisonBox, dataField: defaultDataKey },
- options: { backgroundColor: 'gray', dropAction: 'move', waitForDoubleClickToClick: 'always', layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsLayoutSplit' },
+ options: { backgroundColor: 'gray', dropAction: dropActionType.move, waitForDoubleClickToClick: 'always', layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsLayoutSplit' },
},
],
[
@@ -894,8 +920,6 @@ export namespace Docs {
title,
type,
isBaseProto: true,
- x: 0,
- y: 0,
_width: 300,
'acl-Guest': SharingPermissions.View,
...(template.options || {}),
@@ -994,14 +1018,23 @@ export namespace Docs {
}
export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}, overwriteDoc?: Doc) {
- const imgField = url instanceof ImageField ? url : new ImageField(url);
- return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options }, undefined, undefined, undefined, overwriteDoc);
+ const imgField = url instanceof ImageField ? url : url ? new ImageField(url) : undefined;
+ return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField?.url.href ?? '-no image-'), ...options }, undefined, undefined, undefined, overwriteDoc);
}
export function PresDocument(options: DocumentOptions = {}) {
return InstanceFromProto(Prototypes.get(DocumentType.PRES), new List<Doc>(), options);
}
+ /**
+ * Creates a Doc to edit a script and write the compiled script into the specified field.
+ * Typically, this would be used to create a template that can then be applied to some other Doc
+ * in order to customize a behavior, such as onClick.
+ * @param script
+ * @param options
+ * @param fieldKey the field that the compiled script is written into.
+ * @returns the Scripting Doc
+ */
export function ScriptingDocument(script: Opt<ScriptField> | null, options: DocumentOptions = {}, fieldKey?: string) {
return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script : undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined });
}
@@ -1174,15 +1207,11 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.CONFIG), options?.data, options, id, '', undefined, undefined, true);
}
- export function HTMLMarkerDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _type_collection: CollectionViewType.Freeform }, id);
- }
-
export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
return InstanceFromProto(
Prototypes.get(DocumentType.COL),
new List(documents),
- { backgroundColor: 'transparent', dropAction: 'move', _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile },
+ { backgroundColor: 'transparent', dropAction: dropActionType.move, _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile },
id
);
}
@@ -1488,6 +1517,9 @@ export namespace DocUtils {
return linkDoc;
});
+ const a = source.layout_unrendered ? 'link_anchor_1?.annotationOn' : 'link_anchor_1';
+ const b = target.layout_unrendered ? 'link_anchor_2?.annotationOn' : 'link_anchor_2';
+
return makeLink(
Docs.Create.LinkDocument(
source,
@@ -1501,7 +1533,10 @@ export namespace DocUtils {
link_displayLine: linkSettings.link_displayLine,
link_relationship: linkSettings.link_relationship,
link_description: linkSettings.link_description,
+ x: ComputedField.MakeFunction(`((this.${a}?.x||0)+(this.${b}?.x||0))/2`) as any,
+ y: ComputedField.MakeFunction(`((this.${a}?.y||0)+(this.${b}?.y||0))/2`) as any,
link_autoMoveAnchors: true,
+ _lockedPosition: true,
_layout_showCaption: '', // removed since they conflict with showing a link with a LinkBox (ie, line, not comparison box)
_layout_showTitle: '',
// _layout_showCaption: 'link_description',
@@ -1562,7 +1597,7 @@ export namespace DocUtils {
}
});
items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), 'data', item));
- items && DocListCast(doc.data).forEach(item => !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), 'data', item));
+ items && DocListCast(doc.data).forEach(item => Doc.IsSystem(item) && !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), 'data', item));
}
return doc;
}
@@ -1745,13 +1780,23 @@ export namespace DocUtils {
subitems: userDocList,
icon: 'file',
});
- } // applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView)
- export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) {
+ }
+
+ // applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView)
+
+ /**
+ * Applies a template to a Doc and logs the action with the UndoManager
+ * If the template already exists and has been registered, it can be specified by it's signature name (e.g., 'icon' not 'layout_icon').
+ * Alternatively, the signature can be omitted and the template can be provided.
+ * @param doc the Doc to apply the template to.
+ * @param creator a function that will create the template if it doesn't exist
+ * @param templateSignature the signature name for a template that has already been created and registered on the userDoc. (can be "" if template is provide)
+ * @param template the template to use (optional if templateSignature is provided)
+ * @returns doc
+ */
+ export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', template?: Doc) {
const batch = UndoManager.StartBatch('makeCustomViewClicked');
- runInAction(() => {
- doc.layout_fieldKey = 'layout_' + templateSignature;
- createCustomView(doc, creator, templateSignature, docLayoutTemplate);
- });
+ createCustomView(doc, creator, templateSignature || StrCast(template?.title), template);
batch.end();
return doc;
}
@@ -1760,10 +1805,12 @@ export namespace DocUtils {
const iconViews = DocListCast(Cast(Doc.UserDoc()['template_icons'], Doc, null)?.data);
const templBtns = DocListCast(Cast(Doc.UserDoc()['template_buttons'], Doc, null)?.data);
const noteTypes = DocListCast(Cast(Doc.UserDoc()['template_notes'], Doc, null)?.data);
+ const userTypes = DocListCast(Cast(Doc.UserDoc()['template_user'], Doc, null)?.data);
const clickFuncs = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data);
const allTemplates = iconViews
.concat(templBtns)
.concat(noteTypes)
+ .concat(userTypes)
.concat(clickFuncs)
.map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc)
.filter(doc => doc.isTemplateDoc);
@@ -1775,6 +1822,7 @@ export namespace DocUtils {
}
export function createCustomView(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) {
const templateName = templateSignature.replace(/\(.*\)/, '');
+ doc.layout_fieldKey = 'layout_' + (templateSignature || docLayoutTemplate?.title);
docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc.isGroup && doc.transcription ? 'transcription' : doc.type), templateSignature);
const customName = 'layout_' + templateSignature;
@@ -1858,8 +1906,7 @@ export namespace DocUtils {
const hasContextAnchor = LinkManager.Links(doc).some(l => (l.link_anchor_2 === doc && Cast(l.link_anchor_1, Doc, null)?.annotationOn === context) || (l.link_anchor_1 === doc && Cast(l.link_anchor_2, Doc, null)?.annotationOn === context));
if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) {
const pushpin = Docs.Create.FontIconDocument({
- title: 'pushpin',
- icon_label: '',
+ title: '',
annotationOn: Cast(doc.annotationOn, Doc, null),
followLinkToggle: true,
icon: 'map-pin',
@@ -1908,6 +1955,31 @@ export namespace DocUtils {
return dd;
}
+ export function assignImageInfo(result: Upload.FileInformation, proto: Doc) {
+ if (Upload.isImageInformation(result)) {
+ const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim);
+ const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase();
+ proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined);
+ proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+ proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+ if (NumCast(proto['data-nativeOrientation']) >= 5) {
+ proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+ proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+ }
+ proto.data_exif = JSON.stringify(result.exifData?.data);
+ proto.data_contentSize = result.contentSize;
+ // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates
+ const latitude = result.exifData?.data?.GPSLatitude;
+ const latitudeDirection = result.exifData?.data?.GPSLatitudeRef;
+ const longitude = result.exifData?.data?.GPSLongitude;
+ const longitudeDirection = result.exifData?.data?.GPSLongitudeRef;
+ if (latitude !== undefined && longitude !== undefined && latitudeDirection !== undefined && longitudeDirection !== undefined) {
+ proto.latitude = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection);
+ proto.longitude = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection);
+ }
+ }
+ }
+
async function processFileupload(generatedDocuments: Doc[], name: string, type: string, result: Error | Upload.FileInformation, options: DocumentOptions, overwriteDoc?: Doc) {
if (result instanceof Error) {
alert(`Upload failed: ${result.message}`);
@@ -1919,28 +1991,7 @@ export namespace DocUtils {
if (doc) {
const proto = Doc.GetProto(doc);
proto.text = result.rawText;
- if (Upload.isImageInformation(result)) {
- const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim);
- const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase();
- proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined);
- proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
- proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
- if (NumCast(proto['data-nativeOrientation']) >= 5) {
- proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
- proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
- }
- proto.data_exif = JSON.stringify(result.exifData?.data);
- proto.data_contentSize = result.contentSize;
- // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates
- const latitude = result.exifData?.data?.GPSLatitude;
- const latitudeDirection = result.exifData?.data?.GPSLatitudeRef;
- const longitude = result.exifData?.data?.GPSLongitude;
- const longitudeDirection = result.exifData?.data?.GPSLongitudeRef;
- if (latitude !== undefined && longitude !== undefined && latitudeDirection !== undefined && longitudeDirection !== undefined) {
- proto.latitude = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection);
- proto.longitude = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection);
- }
- }
+ !(result instanceof Error) && DocUtils.assignImageInfo(result, proto);
if (Upload.isVideoInformation(result)) {
proto.data_duration = result.duration;
}
@@ -1951,27 +2002,29 @@ export namespace DocUtils {
}
}
- export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, backgroundColor?: string) {
+ export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, annotationOn?: Doc, backgroundColor?: string) {
+ const defaultTextTemplate = DocCast(Doc.UserDoc().defaultTextLayout);
const tbox = Docs.Create.TextDocument('', {
- _xMargin: noMargins ? 0 : undefined,
- _yMargin: noMargins ? 0 : undefined,
annotationOn,
backgroundColor,
- _width: width || 200,
- _height: 35,
- x: x,
- y: y,
- _layout_centered: BoolCast(Doc.UserDoc().layout_centered),
- _layout_fitWidth: true,
- _layout_autoHeight: true,
- _layout_enableAltContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards),
+ x,
+ y,
title,
+ ...(defaultTextTemplate
+ ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance
+ : {
+ _width: width || 200,
+ _height: 35,
+ _layout_centered: BoolCast(Doc.UserDoc()._layout_centered),
+ _layout_fitWidth: true,
+ _layout_autoHeight: true,
+ }),
});
- const template = Doc.UserDoc().defaultTextLayout;
- if (template instanceof Doc) {
- tbox._width = NumCast(template._width);
- tbox.layout_fieldKey = 'layout_' + StrCast(template.title);
- Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = template;
+
+ if (defaultTextTemplate) {
+ tbox.layout_fieldKey = 'layout_' + StrCast(defaultTextTemplate.title);
+ Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = defaultTextTemplate; // set the text doc's layout to render with the text template
+ tbox[DocData].proto = defaultTextTemplate; // and also set the text doc to inherit from the template (this allows the template to specify default field values)
}
return tbox;
}