diff options
Diffstat (limited to 'src/client/documents/Documents.ts')
-rw-r--r-- | src/client/documents/Documents.ts | 170 |
1 files changed, 124 insertions, 46 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 92a9ddfe8..031560886 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -59,6 +59,7 @@ import { WebBox } from '../views/nodes/WebBox'; 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 defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', '')); @@ -67,39 +68,53 @@ class EmptyBox { return ''; } } + +export enum FInfoFieldType { + string, + boolean, + number, + Doc, + enumeration, + date, + list, + rtf, +} 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) { this.description = d; this.readOnly = readOnly ?? false; } + searchable = () => true; } class BoolInfo extends FInfo { - fieldType? = 'boolean'; + fieldType? = FInfoFieldType.boolean; values?: boolean[] = [true, false]; constructor(d: string, filterable?: boolean) { super(d); this.filterable = filterable; } + 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); this.values = values; this.filterable = filterable; } + 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); @@ -108,49 +123,65 @@ 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); this.values = values; this.filterable = filterable; } + 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, true); + this.filterable = filterable; + } + fieldType? = FInfoFieldType.rtf; } class ListInfo extends FInfo { - fieldType? = 'list'; + fieldType? = FInfoFieldType.list; values?: List<any>[] = []; } type BOOLt = BoolInfo | boolean; @@ -158,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; @@ -171,6 +203,7 @@ export class DocumentOptions { 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); + text?: RTFt = new RtfInfo('rich text of a text doc', 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); @@ -205,11 +238,12 @@ export class DocumentOptions { type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection _type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection title?: STRt = new StrInfo('title of document', true); + title_custom?: BOOLt = new BoolInfo('whether title is a default or has been intentionally set'); caption?: RichTextField; author?: string; // STRt = new StrInfo('creator of document'); // bcz: don't change this. Otherwise, the userDoc's field Infos will have a FieldInfo assigned to its author field which will render it unreadable author_date?: DATEt = new DateInfo('date the document was created', true); annotationOn?: DOCt = new DocInfo('document annotated by this document', false); - _embedContainer?: DOCt = new DocInfo('document that displays (contains) this discument', false); + _embedContainer?: DOCt = new DocInfo('document that displays (contains) this document', false); rootDocument?: DOCt = new DocInfo('document that supplies the information needed for a rendering template (eg, pres slide for PresElement)'); color?: STRt = new StrInfo('foreground color data doc', false); hidden?: BOOLt = new BoolInfo('whether the document is not rendered by its collection', false); @@ -239,16 +273,17 @@ 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_borderRounding?: string; + _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_boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow - layout_maxAutoHeight?: NUMt = new NumInfo('maximum height for newly created (eg, from pasting) text documents', false); + 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_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_hideContextMenu?: BOOLt = new BoolInfo('whether the context menu can be shown'); _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'); @@ -259,6 +294,7 @@ export class DocumentOptions { _layout_showCaption?: string; // which field to display in the caption area. leave empty to have no caption _chromeHidden?: BOOLt = new BoolInfo('whether the editing chrome for a document is hidden'); + hideClickBehaviors?: BOOLt = new BoolInfo('whether to hide click behaviors in context menu'); _gridGap?: NUMt = new NumInfo('gap between items in masonry view', false); _xMargin?: NUMt = new NumInfo('gap between left edge of document and start of masonry/stacking layouts', false); _yMargin?: NUMt = new NumInfo('gap between top edge of dcoument and start of masonry/stacking layouts', false); @@ -290,16 +326,16 @@ export class DocumentOptions { dontPlayLinkOnSelect?: BOOLt = new BoolInfo('whether an audio/video should start playing when a link is followed to it.'); openFactoryLocation?: string; // an OpenWhere value to place the factory created document openFactoryAsDelegate?: boolean; // - updateContentsScript?: ScriptField; // reactive script invoked when viewing a document that can update contents of a collection (or do anything) + onViewMounted?: ScriptField; // reactive script invoked Doc is viewed (used by showBackLinks view to update collection of links to Doc) toolTip?: string; // tooltip to display on hover toolType?: string; // type of pen tool expertMode?: BOOLt = new BoolInfo('something available only in expert (not novice) mode'); - contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents contextMenuFilters?: List<ScriptField>; contextMenuScripts?: List<ScriptField>; contextMenuLabels?: List<string>; contextMenuIcons?: List<string>; + childContentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents childFilters_boolean?: string; childFilters?: List<string>; childLimitHeight?: NUMt = new NumInfo('whether to limit the height of collection children. 0 - means height can be no bigger than width', false); @@ -406,7 +442,7 @@ export class DocumentOptions { onChildClick?: ScriptField; // script given to children of a collection to execute when they are clicked onChildDoubleClick?: ScriptField; // script given to children of a collection to execute when they are double clicked onClickScriptDisable?: STRt = new StrInfo('"always" disable click script, "never" disable click script, or default'); - defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen + defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or default (undefined) means open document full screen waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire. 'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait onPointerDown?: ScriptField; onPointerUp?: ScriptField; @@ -419,14 +455,14 @@ export class DocumentOptions { _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else"); _dropPropertiesToRemove?: 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 cloneFieldFilter?: List<string>; // fields not to copy when the document is clonedclipboard?: Doc; - dragWhenActive?: BOOLt = new BoolInfo('should document drag when it is active - e.g., pileView, group'); + dragWhenActive?: BOOLt = new BoolInfo('should document drag when it is active instead of interacting with its contents - e.g., pileView, group'); dragAction?: DROPt = new DAInfo('how to drag document when it is active (e.g., tree, groups)'); dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', false, true); dragFactory?: DOCt = new DocInfo('document to create when dragging with a suitable onDragStart script', false); clickFactory?: DOCt = new DocInfo('document to create when clicking on a button with a suitable onClick script', false); onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop target?: Doc; // available for use in scripts. used to provide a document parameter to the script (Note, this is a convenience entry since any field could be used for parameterizing a script) - + tags?: LISTt = new ListInfo('hashtags added to document, typically using a text view', true); treeView_HideTitle?: BOOLt = new BoolInfo('whether to hide the top document title of a tree view'); treeView_HideUnrendered?: BOOLt = new BoolInfo("tells tree view not to display documents that have an 'layout_unrendered' tag unless they also have a treeView_FieldKey tag (presBox)"); treeView_HideHeaderIfTemplate?: BOOLt = new BoolInfo('whether to hide the header for a document in a tree view only if a childLayoutTemplate is provided (presBox)'); @@ -447,7 +483,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 @@ -583,13 +618,12 @@ export namespace Docs { layout: { view: LinkBox, dataField: 'link' }, options: { childDontRegisterViews: true, - onClick: FollowLinkScript(), layout_hideLinkAnchors: true, _height: 1, _width: 1, link: '', link_description: '', - backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area + color: 'lightBlue', // lightblue is default color for linking dot and link documents text comment area _dropPropertiesToRemove: new List(['onClick']), }, }, @@ -658,7 +692,7 @@ export namespace Docs { DocumentType.PRES, { layout: { view: PresBox, dataField: defaultDataKey }, - options: { defaultDoubleClick: 'ignore', layout_hideLinkAnchors: true }, + options: { defaultDoubleClick: 'ignore', hideClickBehaviors: true, layout_hideLinkAnchors: true }, }, ], [ @@ -717,7 +751,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' }, }, ], [ @@ -1101,7 +1135,7 @@ export namespace Docs { I.stroke_isInkMask = isInkMask; I.text_align = 'center'; I.rotation = 0; - I.defaultDoubleClick = 'click'; + I.defaultDoubleClick = 'ignore'; I.author_date = new DateField(); I['acl-Guest'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View; //I['acl-Override'] = SharingPermissions.Unset; @@ -1171,7 +1205,7 @@ export namespace Docs { 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 ); } @@ -1448,24 +1482,24 @@ export namespace DocUtils { const makeLink = action((linkDoc: Doc, showPopup?: number[]) => { if (showPopup) { - LinkManager.currentLink = linkDoc; + LinkManager.Instance.currentLink = linkDoc; TaskCompletionBox.textDisplayed = 'Link Created'; TaskCompletionBox.popupX = showPopup[0]; TaskCompletionBox.popupY = showPopup[1] - 33; TaskCompletionBox.taskCompleted = true; - LinkDescriptionPopup.popupX = showPopup[0]; - LinkDescriptionPopup.popupY = showPopup[1]; - LinkDescriptionPopup.descriptionPopup = true; + LinkDescriptionPopup.Instance.popupX = showPopup[0]; + LinkDescriptionPopup.Instance.popupY = showPopup[1]; + LinkDescriptionPopup.Instance.display = true; const rect = document.body.getBoundingClientRect(); - if (LinkDescriptionPopup.popupX + 200 > rect.width) { - LinkDescriptionPopup.popupX -= 190; + if (LinkDescriptionPopup.Instance.popupX + 200 > rect.width) { + LinkDescriptionPopup.Instance.popupX -= 190; TaskCompletionBox.popupX -= 40; } - if (LinkDescriptionPopup.popupY + 100 > rect.height) { - LinkDescriptionPopup.popupY -= 40; + if (LinkDescriptionPopup.Instance.popupY + 100 > rect.height) { + LinkDescriptionPopup.Instance.popupY -= 40; TaskCompletionBox.popupY -= 40; } @@ -1656,10 +1690,37 @@ export namespace DocUtils { } export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false, pivotField?: string, pivotValue?: string): void { + const documentList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data) + .filter(btnDoc => !btnDoc.hidden) + .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) + .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc.title) + .map((dragDoc, i) => ({ + description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), + event: undoable((args: { x: number; y: number }) => { + const newDoc = DocUtils.copyDragFactory(dragDoc); + if (newDoc) { + newDoc.author = Doc.CurrentUserEmail; + newDoc.x = x; + newDoc.y = y; + EquationBox.SelectOnLoad = newDoc[Id]; + if (newDoc.type === DocumentType.RTF) FormattedTextBox.SetSelectOnLoad(newDoc); + if (pivotField) { + newDoc[pivotField] = pivotValue; + } + docAdder?.(newDoc); + } + }, StrCast(dragDoc.title)), + icon: Doc.toIcon(dragDoc), + })) as ContextMenuProps[]; + ContextMenu.Instance.addItem({ + description: 'Create document', + subitems: documentList, + icon: 'file', + }); !simpleMenu && ContextMenu.Instance.addItem({ - description: 'Quick Notes', - subitems: DocListCast((Doc.UserDoc()['template_notes'] as Doc).data).map((note, i) => ({ + description: 'Styled Notes', + subitems: DocListCast((Doc.UserDoc().template_notes as Doc).data).map((note, i) => ({ description: ':' + StrCast(note.title), event: undoable((args: { x: number; y: number }) => { const textDoc = Docs.Create.TextDocument('', { @@ -1680,14 +1741,14 @@ export namespace DocUtils { })) as ContextMenuProps[], icon: 'sticky-note', }); - const documentList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data) + const userDocList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[1]?.data) .filter(btnDoc => !btnDoc.hidden) .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyNote && doc.title) .map((dragDoc, i) => ({ description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), event: undoable((args: { x: number; y: number }) => { - const newDoc = DocUtils.copyDragFactory(dragDoc); + const newDoc = DocUtils.delegateDragFactory(dragDoc); if (newDoc) { newDoc.author = Doc.CurrentUserEmail; newDoc.x = x; @@ -1703,8 +1764,8 @@ export namespace DocUtils { icon: Doc.toIcon(dragDoc), })) as ContextMenuProps[]; ContextMenu.Instance.addItem({ - description: 'Create document', - subitems: documentList, + description: 'User Templates', + 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) @@ -1795,6 +1856,24 @@ export namespace DocUtils { return newCollection; } } + export function makeIntoPortal(doc: Doc, layoutDoc: Doc, allLinks: Doc[]) { + const portalLink = allLinks.find(d => d.link_anchor_1 === doc && d.link_relationship === 'portal to:portal from'); + if (!portalLink) { + DocUtils.MakeLink( + doc, + Docs.Create.FreeformDocument([], { + _width: NumCast(layoutDoc._width) + 10, + _height: Math.max(NumCast(layoutDoc._height), NumCast(layoutDoc._width) + 10), + _isLightbox: true, + _layout_fitWidth: true, + title: StrCast(doc.title) + ' [Portal]', + }), + { link_relationship: 'portal to:portal from' } + ); + } + doc.followLinkLocation = OpenWhere.lightbox; + doc.onClick = FollowLinkScript(); + } export function LeavePushpin(doc: Doc, annotationField: string) { if (doc.followLinkToggle) return undefined; @@ -1895,12 +1974,11 @@ export namespace DocUtils { } } - export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) { + export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, backgroundColor?: string) { const tbox = Docs.Create.TextDocument('', { _xMargin: noMargins ? 0 : undefined, _yMargin: noMargins ? 0 : undefined, annotationOn, - layout_maxAutoHeight: maxHeight, backgroundColor, _width: width || 200, _height: 35, |