diff options
-rw-r--r-- | src/client/documents/Documents.ts | 3 | ||||
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 5 | ||||
-rw-r--r-- | src/client/util/DropConverter.ts | 72 | ||||
-rw-r--r-- | src/client/util/SelectionManager.ts | 3 | ||||
-rw-r--r-- | src/client/views/InkStrokeProperties.ts | 10 | ||||
-rw-r--r-- | src/client/views/InkingStroke.tsx | 11 | ||||
-rw-r--r-- | src/client/views/collections/CollectionMenu.tsx | 44 | ||||
-rw-r--r-- | src/client/views/collections/TabDocView.tsx | 39 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 19 | ||||
-rw-r--r-- | src/client/views/nodes/KeyValueBox.tsx | 272 | ||||
-rw-r--r-- | src/client/views/nodes/button/FontIconBox.tsx | 96 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/RichTextMenu.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 358 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresElementBox.tsx | 8 | ||||
-rw-r--r-- | src/fields/ScriptField.ts | 12 |
16 files changed, 360 insertions, 599 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e579bfd8a..7c50f21a5 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1716,7 +1716,8 @@ export namespace DocUtils { .replace(/\.[a-z0-9]*$/, ''); if (Upload.isImageInformation(result)) { const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim); - proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (StrCast((result.exifData?.data as any)?.Orientation).includes('Rotate 90') ? 5 : undefined); + 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) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 17d58595c..99a8c895f 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -267,7 +267,7 @@ export class CurrentUserUtils { {key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }}, // {key: "DataViz", creator: opts => Docs.Create.DataVizDocument(opts), opts: { _width: 300, _height: 300 }}, {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true,}}, - {key: "Presentation",creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 500, _viewType: CollectionViewType.Stacking, targetDropAction: "alias" as any, _chromeHidden: true, boxShadow: "0 0" }}, + {key: "Presentation",creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 500, _viewType: CollectionViewType.Stacking, targetDropAction: "alias" as any, treeViewHideTitle: true, _chromeHidden: true, boxShadow: "0 0" }}, {key: "Tab", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 500, _height: 800, _backgroundGridShow: true, }}, {key: "Slide", creator: opts => Docs.Create.TreeDocument([], opts), opts: { _width: 300, _height: 200, _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, @@ -675,8 +675,9 @@ export class CurrentUserUtils { CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, CollectionViewType.Grid, CollectionViewType.NoteTaking]), title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}}, + { title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "tab")'}, width: 20, scripts: { onClick: 'pinWithView(_readOnly_)'}}, { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, - { title: "Num", icon: "", toolTip: "Frame Number", btnType: ButtonType.TextButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement().currentFrame.toString()'}, width: 20, scripts: {}}, + { title: "Num", icon: "", toolTip: "Frame Number", btnType: ButtonType.TextButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement()?.currentFrame.toString()'}, width: 20, scripts: {}}, { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}}, { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, funcs: {hidden: '!SelectionManager_selectedDocType()'}, ignoreClick: true, width: 20, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, funcs: {hidden: '!SelectionManager_selectedDocType()'}, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}}, diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 256ab5c44..7c209d1e0 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -1,37 +1,41 @@ -import { DragManager } from "./DragManager"; -import { Doc, DocListCast, Opt } from "../../fields/Doc"; -import { DocumentType } from "../documents/DocumentTypes"; -import { ObjectField } from "../../fields/ObjectField"; -import { StrCast, Cast } from "../../fields/Types"; -import { Docs } from "../documents/Documents"; -import { ScriptField, ComputedField } from "../../fields/ScriptField"; -import { RichTextField } from "../../fields/RichTextField"; -import { ImageField } from "../../fields/URLField"; -import { ScriptingGlobals } from "./ScriptingGlobals"; -import { listSpec } from "../../fields/Schema"; +import { DragManager } from './DragManager'; +import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { DocumentType } from '../documents/DocumentTypes'; +import { ObjectField } from '../../fields/ObjectField'; +import { StrCast, Cast } from '../../fields/Types'; +import { Docs } from '../documents/Documents'; +import { ScriptField, ComputedField } from '../../fields/ScriptField'; +import { RichTextField } from '../../fields/RichTextField'; +import { ImageField } from '../../fields/URLField'; +import { ScriptingGlobals } from './ScriptingGlobals'; +import { listSpec } from '../../fields/Schema'; +import { ButtonType } from '../views/nodes/button/FontIconBox'; -export function MakeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = undefined, templateField: string = "") { +export function MakeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = undefined, templateField: string = '') { if (templateField) Doc.GetProto(doc).title = templateField; /// the title determines which field is being templated doc.isTemplateDoc = makeTemplate(doc, first, rename); return doc; } -// +// // converts 'doc' into a template that can be used to render other documents. // the title of doc is used to determine which field is being templated, so -// passing a value for 'rename' allows the doc to be given a meangingful name +// passing a value for 'rename' allows the doc to be given a meangingful name // after it has been converted to function makeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = undefined): boolean { const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; - if (layoutDoc.layout instanceof Doc) { // its already a template + if (layoutDoc.layout instanceof Doc) { + // its already a template return true; } const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)![0]; - const fieldKey = layout.replace("fieldKey={'", "").replace(/'}$/, ""); + const fieldKey = layout.replace("fieldKey={'", '').replace(/'}$/, ''); const docs = DocListCast(layoutDoc[fieldKey]); let any = false; docs.forEach(d => { - if (!StrCast(d.title).startsWith("-")) { - const params = StrCast(d.title).match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1].replace("()", ""); + if (!StrCast(d.title).startsWith('-')) { + const params = StrCast(d.title) + .match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1] + .replace('()', ''); if (params) { any = makeTemplate(d, false) || any; d.PARAMS = params; @@ -43,12 +47,13 @@ function makeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = und } }); if (first) { - if (!docs.length) { // bcz: feels hacky : if the root level document has items, it's not a field template + if (!docs.length) { + // bcz: feels hacky : if the root level document has items, it's not a field template any = Doc.MakeMetadataFieldTemplate(doc, Doc.GetProto(layoutDoc)) || any; } } if (layoutDoc[fieldKey] instanceof RichTextField || layoutDoc[fieldKey] instanceof ImageField) { - if (!StrCast(layoutDoc.title).startsWith("-")) { + if (!StrCast(layoutDoc.title).startsWith('-')) { any = Doc.MakeMetadataFieldTemplate(layoutDoc, Doc.GetProto(layoutDoc)); } } @@ -59,23 +64,29 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { data?.draggedDocuments.map((doc, i) => { let dbox = doc; // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant - if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes("FontIconBox")) { + if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes('FontIconBox')) { if (data.removeDropProperties || dbox.removeDropProperties) { //dbox = Doc.MakeAlias(doc); // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon dbox = Doc.MakeAlias(dbox); - const dragProps = Cast(dbox.removeDropProperties, listSpec("string"), []); + const dragProps = Cast(dbox.removeDropProperties, listSpec('string'), []); const remProps = (data.removeDropProperties || []).concat(Array.from(dragProps)); - remProps.map(prop => dbox[prop] = undefined); + remProps.map(prop => (dbox[prop] = undefined)); } } else if (!doc.onDragStart && !doc.isButtonBar) { - const layoutDoc = doc;// doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; + const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; if (layoutDoc.type !== DocumentType.FONTICON) { !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc); } layoutDoc.isTemplateDoc = true; dbox = Docs.Create.FontIconDocument({ - _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, - backgroundColor: StrCast(doc.backgroundColor), title: StrCast(layoutDoc.title), icon: layoutDoc.isTemplateDoc ? "font" : "bolt" + _nativeWidth: 100, + _nativeHeight: 100, + _width: 100, + _height: 100, + backgroundColor: StrCast(doc.backgroundColor), + title: StrCast(layoutDoc.title), + btnType: ButtonType.ClickButton, + icon: layoutDoc.isTemplateDoc ? 'font' : 'bolt', }); dbox.dragFactory = layoutDoc; dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; @@ -86,5 +97,10 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { data.droppedDocuments[i] = dbox; }); } -ScriptingGlobals.add(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); }, - "converts the dropped data to buttons", "(dragData: any)");
\ No newline at end of file +ScriptingGlobals.add( + function convertToButtons(dragData: any) { + convertDropDataToButtons(dragData as DragManager.DocumentDragData); + }, + 'converts the dropped data to buttons', + '(dragData: any)' +); diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 1c84af94a..7a555d5f8 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -101,6 +101,9 @@ export namespace SelectionManager { } } ScriptingGlobals.add(function SelectionManager_selectedDocType(docType?: DocumentType, colType?: CollectionViewType, checkContext?: boolean) { + if (colType === ('tab' as any)) { + return SelectionManager.Views().lastElement()?.props.renderDepth === 0; + } let selected = (sel => (checkContext ? DocCast(sel?.context) : sel))(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement()); return docType ? selected?.type === docType : colType ? selected?.viewType === colType : true; }); diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 1f5f16592..d19a916f9 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -4,7 +4,6 @@ import { Doc, NumListCast, Opt } from '../../fields/Doc'; import { InkData, InkField, InkTool, PointData } from '../../fields/InkField'; import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; -import { ComputedField } from '../../fields/ScriptField'; import { Cast, NumCast } from '../../fields/Types'; import { Point } from '../../pen-gestures/ndollar'; import { DocumentType } from '../documents/DocumentTypes'; @@ -12,7 +11,6 @@ import { FitOneCurve } from '../util/bezierFit'; import { DocumentManager } from '../util/DocumentManager'; import { undoBatch } from '../util/UndoManager'; import { InkingStroke } from './InkingStroke'; -import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView'; import { DocumentView } from './nodes/DocumentView'; export class InkStrokeProperties { @@ -67,13 +65,7 @@ export class InkStrokeProperties { doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.strokeWidth); doc.x = oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale; doc.y = oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale; - if (doc.activeFrame !== undefined) { - const findexed = Cast(doc[`data-indexed`], listSpec(InkField), []).slice(); - findexed[NumCast(doc.activeFrame)] = new InkField(newPoints); - doc[`data-indexed`] = new List<InkField>(findexed); - } else { - Doc.GetProto(doc).data = new InkField(newPoints); - } + Doc.GetProto(doc).data = new InkField(newPoints); appliedFunc = true; } } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index ceaabd0e1..dae1c10bb 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -68,17 +68,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() { componentDidMount() { this.props.setContentView?.(this); - this._disposers.activeFrame = reaction( - () => this.rootDoc.activeFrame !== undefined && !(ComputedField.WithoutComputed(() => FieldValue(this.rootDoc[this.fieldKey])) instanceof ComputedField), - () => { - const newPoints = Cast(this.rootDoc[this.fieldKey], InkField, null).inkData; - this.rootDoc[this.fieldKey] = ComputedField.MakeInterpolated(this.fieldKey, 'activeFrame', this.rootDoc, NumCast(this.rootDoc.activeFrame)); - const findexed = Cast(this.rootDoc[`data-indexed`], listSpec(InkField), []).slice(); - findexed[NumCast(this.rootDoc.activeFrame)] = new InkField(newPoints); - this.rootDoc[this.fieldKey + '-indexed'] = new List<InkField>(findexed); - }, - { fireImmediately: true } - ); this._disposers.selfDisper = reaction( () => this.props.isSelected(), // react to stroke being deselected by turning off ink handles selected => !selected && (InkStrokeProperties.Instance._controlButton = false) diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index eb55650e4..0dc30e0fd 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -587,49 +587,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu } @undoBatch - @action - pinWithView = (targetDoc: Opt<Doc>) => { - if (targetDoc) { - TabDocView.PinDoc(targetDoc); - const presArray: Doc[] = PresBox.Instance?.sortArray(); - const size: number = PresBox.Instance?._selectedArray.size; - const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined; - const activeDoc = presSelected ? PresBox.Instance?.childDocs[PresBox.Instance?.childDocs.indexOf(presSelected) + 1] : PresBox.Instance?.childDocs[PresBox.Instance?.childDocs.length - 1]; - if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking || targetDoc._viewType === CollectionViewType.NoteTaking) { - const scroll = targetDoc._scrollTop; - activeDoc.presPinView = true; - activeDoc.presPinViewScroll = scroll; - } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.MAP) { - const x = targetDoc._panX; - const y = targetDoc._panY; - const scale = targetDoc._viewScale; - activeDoc.presPinView = true; - activeDoc.presPinViewX = x; - activeDoc.presPinViewY = y; - activeDoc.presPinViewScale = scale; - } else if (targetDoc.type === DocumentType.VID) { - activeDoc.presPinView = true; - } else if (targetDoc.type === DocumentType.COMPARISON) { - const width = targetDoc._clipWidth; - activeDoc.presPinClipWidth = width; - activeDoc.presPinView = true; - } - } - }; - - @computed - get pinWithViewButton() { - const presPinWithViewIcon = <img src={`/assets/pinWithView.png`} style={{ margin: 'auto', width: 19 }} />; - return !this.selectedDoc ? null : ( - <Tooltip title={<div className="dash-tooltip">{'Pin with current view'}</div>} placement="top"> - <button className="antimodeMenu-button" style={{ justifyContent: 'center' }} onClick={() => this.pinWithView(this.selectedDoc)}> - {presPinWithViewIcon} - </button> - </Tooltip> - ); - } - - @undoBatch onAlias = () => { if (this.selectedDoc && this.selectedDocumentView) { // const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true); @@ -722,7 +679,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu {this.aliasButton} {/* {this.pinButton} */} {this.toggleOverlayButton} - {this.pinWithViewButton} <div className="collectionMenu-divider" key="divider2"></div> {this.subChrome} <div className="collectionMenu-divider" key="divider3"></div> diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index e147f34d2..73574bdb3 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -242,43 +242,8 @@ export class TabDocView extends React.Component<TabDocViewProps> { const size: number = PresBox.Instance?._selectedArray.size; const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined; const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null); - // If pinWithView option set then update scale and x / y props of slide - if (pinProps?.pinWithView) { - const viewProps = pinProps.pinWithView; - pinDoc.presPinView = true; - pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2; - pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2; - pinDoc.presPinViewScale = viewProps.scale; - pinDoc.contentBounds = new List<number>([viewProps.bounds.left, viewProps.bounds.top, viewProps.bounds.left + viewProps.bounds.width, viewProps.bounds.top + viewProps.bounds.height]); - } - if (pinProps?.pinDocView) { - const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(pinDoc.type as any) || pinDoc._viewType === CollectionViewType.Stacking; - const pannable: boolean = (pinDoc.type === DocumentType.COL && doc._viewType === CollectionViewType.Freeform) || doc.type === DocumentType.IMG; - if (scrollable) { - const scroll = doc._scrollTop; - pinDoc.presPinView = true; - pinDoc.presPinViewScroll = scroll; - } else if ([DocumentType.AUDIO, DocumentType.VID].includes(doc.type as any)) { - pinDoc.presPinView = true; - pinDoc.presStartTime = doc._currentTimecode; - pinDoc.presEndTime = NumCast(doc._currentTimecode) + 0.1; - } else if (pannable) { - pinDoc.presPinView = true; - pinDoc.presPinViewX = pinDoc._panX; - pinDoc.presPinViewY = pinDoc._panY; - pinDoc.presPinViewScale = pinDoc._viewScale; - const pw = NumCast(pinProps.panelWidth); - const ph = NumCast(pinProps.panelHeight); - const ps = NumCast(pinDoc._viewScale); - if (pw && ph && ps) { - pinDoc.contentBounds = new List<number>([NumCast(pinDoc.panX) - pw / 2 / ps, NumCast(pinDoc.panY) - ph / 2 / ps, NumCast(pinDoc.panX) + pw / 2 / ps, NumCast(pinDoc.panY) + ph / 2 / ps]); - } - } else if (doc.type === DocumentType.COMPARISON) { - const width = doc._clipWidth; - pinDoc.presPinClipWidth = width; - pinDoc.presPinView = true; - } - } + + PresBox.pinDocView(pinDoc, pinProps); pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)'); Doc.AddDocToList(curPres, 'data', pinDoc, presSelected); if (!pinProps?.audioRange && duration !== undefined) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 03beaf65e..052cbd3bb 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -2213,3 +2213,6 @@ ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) { !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true); }); +ScriptingGlobals.add(function pinWithView(readOnly: boolean) { + !readOnly && SelectionManager.Views().forEach(view => TabDocView.PinDoc(view.rootDoc, { pinDocView: true, panelWidth: view.props.PanelWidth(), panelHeight: view.props.PanelHeight() })); +}); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 45f68e0f0..86566ac6a 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -16,6 +16,11 @@ import { StyleProp } from '../StyleProvider'; import './CollectionFreeFormDocumentView.scss'; import { DocumentView, DocumentViewProps } from './DocumentView'; import React = require('react'); +import { InkField } from '../../../fields/InkField'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { Field } from '../../util/ProsemirrorCopy/prompt'; +import { RefField } from '../../../fields/RefField'; +import { ObjectField } from '../../../fields/ObjectField'; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; color?: string; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined; @@ -33,6 +38,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps>() { public static animFields = ['_height', '_width', 'x', 'y', '_scrollTop', 'opacity']; // fields that are configured to be animatable using animation frames public static animStringFields = ['backgroundColor', 'color']; // fields that are configured to be animatable using animation frames + public static animDataFields = ['data', 'text']; // fields that are configured to be animatable using animation frames @observable _animPos: number[] | undefined = undefined; @observable _contentView: DocumentView | undefined | null; get displayName() { @@ -118,6 +124,10 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF const findexed = Cast(doc[`${val}-indexed`], listSpec('string'), null); findexed?.length <= timecode + 1 && findexed.push(undefined as any as string); }); + CollectionFreeFormDocumentView.animDataFields.forEach(val => { + const findexed = Cast(doc[`${val}-indexed`], listSpec(InkField), null); + findexed?.length <= timecode + 1 && findexed.push(undefined as any); + }); }) ); setTimeout( @@ -164,10 +174,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF // opacity is unlike other fields because it's value should not be undefined before it appears to enable it to fade-in doc['opacity-indexed'] = new List<number>(numberRange(currTimecode + 1).map(t => (!doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1))); } - CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val] = ComputedField.MakeInterpolated(val, 'activeFrame', doc, currTimecode))); + CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedNumber(val, 'activeFrame', doc, currTimecode))); CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode))); - doc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0'); - doc.dataTransition = 'inherit'; + CollectionFreeFormDocumentView.animDataFields.forEach(val => (Doc.GetProto(doc)[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', Doc.GetProto(doc), currTimecode))); + const targetDoc = doc.type === DocumentType.RTF ? Doc.GetProto(doc) : doc; // data fields, like rtf 'text' exist on the data doc, so + doc !== targetDoc && (targetDoc.context = doc.context); // the computed fields don't see the layout doc -- need to copy the context to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!) + targetDoc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0'); + targetDoc.dataTransition = 'inherit'; }); } diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 4b1fbaf7d..d9f46509e 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -1,34 +1,37 @@ - -import { action, computed, observable } from "mobx"; -import { observer } from "mobx-react"; -import { Doc, Field, FieldResult } from "../../../fields/Doc"; -import { List } from "../../../fields/List"; -import { RichTextField } from "../../../fields/RichTextField"; -import { listSpec } from "../../../fields/Schema"; -import { ComputedField, ScriptField } from "../../../fields/ScriptField"; -import { Cast, FieldValue, NumCast } from "../../../fields/Types"; -import { ImageField } from "../../../fields/URLField"; -import { Docs } from "../../documents/Documents"; -import { SetupDrag } from "../../util/DragManager"; -import { CompiledScript, CompileScript, ScriptOptions } from "../../util/Scripting"; -import { undoBatch } from "../../util/UndoManager"; +import { action, computed, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import { Doc, Field, FieldResult } from '../../../fields/Doc'; +import { List } from '../../../fields/List'; +import { RichTextField } from '../../../fields/RichTextField'; +import { listSpec } from '../../../fields/Schema'; +import { ComputedField, ScriptField } from '../../../fields/ScriptField'; +import { Cast, DocCast, FieldValue, NumCast } from '../../../fields/Types'; +import { ImageField } from '../../../fields/URLField'; +import { Docs } from '../../documents/Documents'; +import { SetupDrag } from '../../util/DragManager'; +import { CompiledScript, CompileScript, ScriptOptions } from '../../util/Scripting'; +import { undoBatch } from '../../util/UndoManager'; import { FieldView, FieldViewProps } from './FieldView'; -import "./KeyValueBox.scss"; -import { KeyValuePair } from "./KeyValuePair"; -import React = require("react"); -import { ContextMenu } from "../ContextMenu"; -import { ContextMenuProps } from "../ContextMenuItem"; -import e = require("express"); +import './KeyValueBox.scss'; +import { KeyValuePair } from './KeyValuePair'; +import React = require('react'); +import { ContextMenu } from '../ContextMenu'; +import { ContextMenuProps } from '../ContextMenuItem'; +import e = require('express'); +import { FormattedTextBox } from './formattedText/FormattedTextBox'; +import { ImageBox } from './ImageBox'; export type KVPScript = { script: CompiledScript; - type: "computed" | "script" | false; + type: 'computed' | 'script' | false; onDelegate: boolean; }; @observer export class KeyValueBox extends React.Component<FieldViewProps> { - public static LayoutString(fieldStr: string) { return FieldView.LayoutString(KeyValueBox, fieldStr); } + public static LayoutString(fieldStr: string) { + return FieldView.LayoutString(KeyValueBox, fieldStr); + } private _mainCont = React.createRef<HTMLDivElement>(); private _keyHeader = React.createRef<HTMLTableHeaderCellElement>(); @@ -37,8 +40,12 @@ export class KeyValueBox extends React.Component<FieldViewProps> { @observable private rows: KeyValuePair[] = []; - @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); } - get fieldDocToLayout() { return this.props.fieldKey ? Cast(this.props.Document[this.props.fieldKey], Doc, null) : this.props.Document; } + @computed get splitPercentage() { + return NumCast(this.props.Document.schemaSplitPercentage, 50); + } + get fieldDocToLayout() { + return this.props.fieldKey ? Cast(this.props.Document[this.props.fieldKey], Doc, null) : this.props.Document; + } @action onEnterKey = (e: React.KeyboardEvent): void => { @@ -46,19 +53,19 @@ export class KeyValueBox extends React.Component<FieldViewProps> { e.stopPropagation(); if (this._keyInput.current?.value && this._valInput.current?.value && this.fieldDocToLayout) { if (KeyValueBox.SetField(this.fieldDocToLayout, this._keyInput.current.value, this._valInput.current.value)) { - this._keyInput.current.value = ""; - this._valInput.current.value = ""; + this._keyInput.current.value = ''; + this._valInput.current.value = ''; document.body.focus(); } } } - } + }; public static CompileKVPScript(value: string): KVPScript | undefined { - const eq = value.startsWith("="); + const eq = value.startsWith('='); value = eq ? value.substr(1) : value; - const dubEq = value.startsWith(":=") ? "computed" : value.startsWith(";=") ? "script" : false; + const dubEq = value.startsWith(':=') ? 'computed' : value.startsWith(';=') ? 'script' : false; value = dubEq ? value.substr(2) : value; - const options: ScriptOptions = { addReturn: true, params: { this: Doc.name, self: Doc.name, _last_: "any", _readOnly_: "boolean" }, editable: false }; + const options: ScriptOptions = { addReturn: true, params: { this: Doc.name, self: Doc.name, _last_: 'any', _readOnly_: 'boolean' }, editable: false }; if (dubEq) options.typecheck = false; const script = CompileScript(value, options); return !script.compiled ? undefined : { script, type: dubEq, onDelegate: eq }; @@ -67,11 +74,11 @@ export class KeyValueBox extends React.Component<FieldViewProps> { public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean): boolean { const { script, type, onDelegate } = kvpScript; //const target = onDelegate ? Doc.Layout(doc.layout) : Doc.GetProto(doc); // bcz: TODO need to be able to set fields on layout templates - const target = forceOnDelegate || onDelegate || key.startsWith("_") ? doc : doc.proto || doc; + const target = forceOnDelegate || onDelegate || key.startsWith('_') ? doc : doc.proto || doc; let field: Field; - if (type === "computed") { + if (type === 'computed') { field = new ComputedField(script); - } else if (type === "script") { + } else if (type === 'script') { field = new ScriptField(script); } else { const res = script.run({ this: target }, console.log); @@ -96,7 +103,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> { if (e.buttons === 1 && this.props.isSelected(true)) { e.stopPropagation(); } - } + }; onPointerWheel = (e: React.WheelEvent): void => e.stopPropagation(); rowHeight = () => 30; @@ -104,7 +111,11 @@ export class KeyValueBox extends React.Component<FieldViewProps> { @computed get createTable() { const doc = this.fieldDocToLayout; if (!doc) { - return <tr><td>Loading...</td></tr>; + return ( + <tr> + <td>Loading...</td> + </tr> + ); } const realDoc = doc; @@ -122,83 +133,102 @@ export class KeyValueBox extends React.Component<FieldViewProps> { let i = 0; const self = this; for (const key of Object.keys(ids).slice().sort()) { - rows.push(<KeyValuePair doc={realDoc} addDocTab={this.props.addDocTab} PanelWidth={this.props.PanelWidth} PanelHeight={this.rowHeight} - ref={(function () { - let oldEl: KeyValuePair | undefined; - return (el: KeyValuePair) => { - if (oldEl) self.rows.splice(self.rows.indexOf(oldEl), 1); - oldEl = el; - if (el) self.rows.push(el); - }; - })()} keyWidth={100 - this.splitPercentage} rowStyle={"keyValueBox-" + (i++ % 2 ? "oddRow" : "evenRow")} key={key} keyName={key} />); + rows.push( + <KeyValuePair + doc={realDoc} + addDocTab={this.props.addDocTab} + PanelWidth={this.props.PanelWidth} + PanelHeight={this.rowHeight} + ref={(function () { + let oldEl: KeyValuePair | undefined; + return (el: KeyValuePair) => { + if (oldEl) self.rows.splice(self.rows.indexOf(oldEl), 1); + oldEl = el; + if (el) self.rows.push(el); + }; + })()} + keyWidth={100 - this.splitPercentage} + rowStyle={'keyValueBox-' + (i++ % 2 ? 'oddRow' : 'evenRow')} + key={key} + keyName={key} + /> + ); } return rows; } @computed get newKeyValue() { - return <tr className="keyValueBox-valueRow"> - <td className="keyValueBox-td-key" onClick={(e) => { this._keyInput.current!.select(); e.stopPropagation(); }} style={{ width: `${100 - this.splitPercentage}%` }}> - <input style={{ width: "100%" }} ref={this._keyInput} type="text" placeholder="Key" /> - </td> - <td className="keyValueBox-td-value" onClick={(e) => { this._valInput.current!.select(); e.stopPropagation(); }} style={{ width: `${this.splitPercentage}%` }}> - <input style={{ width: "100%" }} ref={this._valInput} type="text" placeholder="Value" onKeyDown={this.onEnterKey} /> - </td> - </tr>; + return ( + <tr className="keyValueBox-valueRow"> + <td + className="keyValueBox-td-key" + onClick={e => { + this._keyInput.current!.select(); + e.stopPropagation(); + }} + style={{ width: `${100 - this.splitPercentage}%` }}> + <input style={{ width: '100%' }} ref={this._keyInput} type="text" placeholder="Key" /> + </td> + <td + className="keyValueBox-td-value" + onClick={e => { + this._valInput.current!.select(); + e.stopPropagation(); + }} + style={{ width: `${this.splitPercentage}%` }}> + <input style={{ width: '100%' }} ref={this._valInput} type="text" placeholder="Value" onKeyDown={this.onEnterKey} /> + </td> + </tr> + ); } @action onDividerMove = (e: PointerEvent): void => { const nativeWidth = this._mainCont.current!.getBoundingClientRect(); - this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100)); - } + this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round(((e.clientX - nativeWidth.left) / nativeWidth.width) * 100)); + }; @action onDividerUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onDividerMove); + document.removeEventListener('pointermove', this.onDividerMove); document.removeEventListener('pointerup', this.onDividerUp); - } + }; onDividerDown = (e: React.PointerEvent) => { e.stopPropagation(); e.preventDefault(); - document.addEventListener("pointermove", this.onDividerMove); + document.addEventListener('pointermove', this.onDividerMove); document.addEventListener('pointerup', this.onDividerUp); - } + }; - getTemplate = async () => { - const parent = Docs.Create.StackingDocument([], { _width: 800, _height: 800, title: "Template", _chromeHidden: true }); - parent._columnWidth = 100; - for (const row of this.rows.filter(row => row.isChecked)) { - await this.createTemplateField(parent, row); - row.uncheck(); + getFieldView = async () => { + const rows = this.rows.filter(row => row.isChecked); + if (rows.length > 1) { + const parent = Docs.Create.StackingDocument([], { _autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document.data).title}`, _chromeHidden: true }); + for (const row of rows) { + const field = this.createFieldView(DocCast(this.props.Document.data), row); + field && Doc.AddDocToList(parent, 'data', field); + row.uncheck(); + } + return parent; } - return parent; - } + return this.createFieldView(DocCast(this.props.Document.data), rows.lastElement()); + }; - createTemplateField = async (parentStackingDoc: Doc, row: KeyValuePair) => { + createFieldView = (templateDoc: Doc, row: KeyValuePair) => { const metaKey = row.props.keyName; - const sourceDoc = await Cast(this.props.Document.data, Doc); - if (!sourceDoc) { - return; - } + const fieldTemplate = Doc.MakeAlias(templateDoc); + fieldTemplate.title = metaKey; + fieldTemplate.layout = this.inferType(templateDoc[metaKey], metaKey); + return fieldTemplate; + }; - const fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey); - if (!fieldTemplate) { - return; - } - const previousViewType = fieldTemplate._viewType; - Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(parentStackingDoc)); - previousViewType && (fieldTemplate._viewType = previousViewType); - - Cast(parentStackingDoc.data, listSpec(Doc))!.push(fieldTemplate); - } - - inferType = async (data: FieldResult, metaKey: string) => { + inferType = (data: FieldResult, metaKey: string) => { const options = { _width: 300, _height: 300, title: metaKey }; - if (data instanceof RichTextField || typeof data === "string" || typeof data === "number") { - return Docs.Create.TextDocument("", options); + if (data instanceof RichTextField || typeof data === 'string' || typeof data === 'number') { + return FormattedTextBox.LayoutString(metaKey); } else if (data instanceof List) { if (data.length === 0) { return Docs.Create.StackingDocument([], options); } - const first = await Cast(data[0], Doc); + const first = DocCast(data[0]); if (!first || !first.data) { return Docs.Create.StackingDocument([], options); } @@ -212,44 +242,52 @@ export class KeyValueBox extends React.Component<FieldViewProps> { return undefined; } } else if (data instanceof ImageField) { - return Docs.Create.ImageDocument("https://image.flaticon.com/icons/png/512/23/23765.png", options); + return ImageBox.LayoutString(metaKey); } - return new Doc; - } + return new Doc(); + }; specificContextMenu = (e: React.MouseEvent): void => { const cm = ContextMenu.Instance; - const open = cm.findByDescription("Change Perspective..."); - const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : []; + const open = cm.findByDescription('Change Perspective...'); + const openItems: ContextMenuProps[] = open && 'subitems' in open ? open.subitems : []; openItems.push({ - description: "Default Perspective", event: () => { - this.props.addDocTab(this.props.Document, "close"); - this.props.addDocTab(this.fieldDocToLayout, "add:right"); - }, icon: "image" + description: 'Default Perspective', + event: () => { + this.props.addDocTab(this.props.Document, 'close'); + this.props.addDocTab(this.fieldDocToLayout, 'add:right'); + }, + icon: 'image', }); - !open && cm.addItem({ description: "Change Perspective...", subitems: openItems, icon: "external-link-alt" }); - } + !open && cm.addItem({ description: 'Change Perspective...', subitems: openItems, icon: 'external-link-alt' }); + }; render() { - const dividerDragger = this.splitPercentage === 0 ? (null) : - <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}> - <div className="keyValueBox-dividerDraggerThumb" onPointerDown={this.onDividerDown} /> - </div>; - - return (<div className="keyValueBox-cont" onWheel={this.onPointerWheel} onContextMenu={this.specificContextMenu} ref={this._mainCont}> - <table className="keyValueBox-table"> - <tbody className="keyValueBox-tbody"> - <tr className="keyValueBox-header"> - <th className="keyValueBox-key" style={{ width: `${100 - this.splitPercentage}%` }} ref={this._keyHeader} - onPointerDown={SetupDrag(this._keyHeader, this.getTemplate)} - >Key</th> - <th className="keyValueBox-fields" style={{ width: `${this.splitPercentage}%` }}>Fields</th> - </tr> - {this.createTable} - {this.newKeyValue} - </tbody> - </table> - {dividerDragger} - </div>); + const dividerDragger = + this.splitPercentage === 0 ? null : ( + <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}> + <div className="keyValueBox-dividerDraggerThumb" onPointerDown={this.onDividerDown} /> + </div> + ); + + return ( + <div className="keyValueBox-cont" onWheel={this.onPointerWheel} onContextMenu={this.specificContextMenu} ref={this._mainCont}> + <table className="keyValueBox-table"> + <tbody className="keyValueBox-tbody"> + <tr className="keyValueBox-header"> + <th className="keyValueBox-key" style={{ width: `${100 - this.splitPercentage}%` }} ref={this._keyHeader} onPointerDown={SetupDrag(this._keyHeader, this.getFieldView)}> + Key + </th> + <th className="keyValueBox-fields" style={{ width: `${this.splitPercentage}%` }}> + Fields + </th> + </tr> + {this.createTable} + {this.newKeyValue} + </tbody> + </table> + {dividerDragger} + </div> + ); } } diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index fc5bf86f4..c72b5ca9b 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -254,16 +254,13 @@ export class FontIconBox extends DocComponent<ButtonProps>() { const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); const script = ScriptCast(this.rootDoc.script); - if (!script) { - return null; - } let noviceList: string[] = []; let text: string | undefined; let dropdown = true; let icon: IconProp = 'caret-down'; try { - if (script.script.originalScript.startsWith('setView')) { + if (script?.script.originalScript.startsWith('setView')) { const selected = SelectionManager.Docs().lastElement(); if (selected) { if (StrCast(selected.type) === DocumentType.COL) { @@ -279,7 +276,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { text = 'User Default'; } noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking]; - } else if (script.script.originalScript.startsWith('setFont')) { + } else if (script?.script.originalScript.startsWith('setFont')) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); noviceList = ['Roboto', 'Times New Roman', 'Arial', 'Georgia', 'Comic Sans MS', 'Tahoma', 'Impact', 'Crimson Text']; @@ -289,11 +286,9 @@ export class FontIconBox extends DocComponent<ButtonProps>() { } // Get items to place into the list - const list = this.buttonList.map(value => { - if (Doc.noviceMode && !noviceList.includes(value)) { - return; - } - return ( + const list = this.buttonList + .filter(value => !Doc.noviceMode || noviceList.includes(value)) + .map(value => ( <div className="list-item" key={`${value}`} @@ -304,8 +299,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { onClick={() => script.script.run({ value }).result}> {value[0].toUpperCase() + value.slice(1)} </div> - ); - }); + )); const label = !this.label || !FontIconBox.GetShowLabels() ? null : ( @@ -451,7 +445,6 @@ export class FontIconBox extends DocComponent<ButtonProps>() { @computed get defaultButton() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const active: string = StrCast(this.rootDoc.dropDownOpen); return ( <div className={`menuButton ${this.type}`} onContextMenu={this.specificContextMenu} style={{ backgroundColor: 'transparent', borderBottomLeftRadius: this.dropdown ? 0 : undefined }}> <div className="menuButton-wrap"> @@ -487,89 +480,50 @@ export class FontIconBox extends DocComponent<ButtonProps>() { render() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const label = - !this.label || !FontIconBox.GetShowLabels() ? null : ( - <div className="fontIconBox-label" style={{ color, backgroundColor }}> - {this.label} - </div> - ); - - const menuLabel = + const label = (noBackground: boolean = false) => !this.label || !FontIconBox.GetShowLabels() ? null : ( - <div className="fontIconBox-label" style={{ color, backgroundColor: 'transparent' }}> + <div className="fontIconBox-label" style={{ color, backgroundColor: noBackground ? 'transparent' : backgroundColor }}> {this.label} </div> ); - // TODO:glr Add label of button type - let button: JSX.Element | null = this.defaultButton; + let button: JSX.Element = this.defaultButton; + // prettier-ignore switch (this.type) { - case ButtonType.TextButton: - button = ( + case ButtonType.DropdownList: return this.dropdownListButton; + case ButtonType.ColorButton: return this.colorButton; + case ButtonType.NumberButton: return this.numberButton; + case ButtonType.EditableText: return this.editableText; + case ButtonType.DropdownButton: button = this.dropdownButton; break; + case ButtonType.ToggleButton: button = this.toggleButton; break; + case ButtonType.TextButton: button = ( <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}> {this.Icon(color)} {StrCast(this.rootDoc.buttonText) ? <div className="button-text">{StrCast(this.rootDoc.buttonText)}</div> : null} - {label} - </div> - ); - // button = <TextButton {...buttonProps}></TextButton> - break; - case ButtonType.EditableText: - button = this.editableText; - break; - case ButtonType.NumberButton: - button = this.numberButton; - break; - case ButtonType.DropdownButton: - button = this.dropdownButton; - break; - case ButtonType.DropdownList: - button = this.dropdownListButton; - break; - case ButtonType.ColorButton: - button = this.colorButton; - break; - case ButtonType.ToolButton: - button = ( - <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ opacity: 1, backgroundColor, color }}> - {this.Icon(color)} - {label} + {label()} </div> ); break; - case ButtonType.ToggleButton: - button = this.toggleButton; - // button = <ToggleButton {...buttonProps}></ToggleButton> - break; case ButtonType.ClickButton: - button = ( - <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ color, backgroundColor, opacity: 1 }}> + case ButtonType.ToolButton: button = ( + <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ backgroundColor, color, opacity: 1 }}> {this.Icon(color)} - {label} + {label()} </div> ); break; - case ButtonType.MenuButton: - button = ( + case ButtonType.MenuButton: button = ( <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}> {this.Icon(color)} - {menuLabel} + {label(true)} <FontIconBadge value={Cast(this.Document.badgeValue, 'string', null)} /> </div> ); break; - default: - break; } - const retval = - !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? ( - button - ) : button !== null ? ( - <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>{button}</Tooltip> - ) : null; - return retval; + return !this.layoutDoc.toolTip ? button : <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>{button}</Tooltip>; } } @@ -698,7 +652,7 @@ ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boo ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { - return RichTextMenu.Instance.fontSize.replace('px', ''); + return RichTextMenu.Instance?.fontSize.replace('px', ''); } if (typeof size === 'number') size = size.toString(); if (size && Number(size).toString() === size) size += 'px'; diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 2a77210ae..0cbe60c0c 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -16,7 +16,7 @@ import { SelectionManager } from '../../../util/SelectionManager'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; import { FieldViewProps } from '../FieldView'; -import { FormattedTextBox, FormattedTextBoxProps } from './FormattedTextBox'; +import { FormattedTextBox } from './FormattedTextBox'; import { updateBullets } from './ProsemirrorExampleTransfer'; import './RichTextMenu.scss'; import { schema } from './schema_rts'; @@ -29,7 +29,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { private _linkToRef = React.createRef<HTMLInputElement>(); @observable public view?: EditorView; - public editorProps: (FieldViewProps & FormattedTextBoxProps) | undefined; + public editorProps: FieldViewProps | undefined; public _brushMap: Map<string, Set<Mark>> = new Map(); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 05e09361b..eb40089ec 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -358,20 +358,24 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { if (bestTarget) this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false); }; + static pinDataTypes(target: Doc) { + const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(target.type as any) || target._viewType === CollectionViewType.Stacking; + const pannable = [DocumentType.IMG].includes(target.type as any) || (target.type === DocumentType.COL && target._viewType === CollectionViewType.Freeform); + const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(target.type as any); + const clippable = [DocumentType.COMPARISON].includes(target.type as any); + return { scrollable, pannable, temporal, clippable }; + } // navigates to the bestTarget document by making sure it is on screen, // then it applies the view specs stored in activeItem to @action static navigateToDoc(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) { - if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) { - bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s'; - bestTarget._scrollTop = activeItem.presPinViewScroll; - } else if (bestTarget.type === DocumentType.COMPARISON) { - bestTarget._clipWidth = activeItem.presPinClipWidth; - } else if ([DocumentType.AUDIO, DocumentType.VID].includes(bestTarget.type as any)) { - bestTarget._currentTimecode = activeItem.presStartTime; - } else { + bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s'; + const { scrollable, pannable, temporal, clippable } = this.pinDataTypes(bestTarget); + if (clippable) bestTarget._clipWidth = activeItem.presPinClipWidth; + if (temporal) bestTarget._currentTimecode = activeItem.presStartTime; + if (scrollable) bestTarget._scrollTop = activeItem.presPinViewScroll; + if (pannable) { const contentBounds = Cast(activeItem.contentBounds, listSpec('number')); - bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s'; if (contentBounds) { bestTarget._panX = (contentBounds[0] + contentBounds[2]) / 2; bestTarget._panY = (contentBounds[1] + contentBounds[3]) / 2; @@ -388,6 +392,43 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { return setTimeout(() => (bestTarget._viewTransition = undefined), activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510); } + /// copies values from the targetDoc (which is the prototype of the pinDoc) to + /// reserved fields on the pinDoc so that those values can be restored to the + /// target doc when navigating to it. + @action + static pinDocView(pinDoc: Doc, pinProps: PinProps | undefined) { + if (pinProps?.pinWithView) { + // If pinWithView option set then update scale and x / y props of slide + const bounds = pinProps.pinWithView.bounds; + pinDoc.presPinView = true; + pinDoc.presPinViewX = bounds.left + bounds.width / 2; + pinDoc.presPinViewY = bounds.top + bounds.height / 2; + pinDoc.presPinViewScale = pinProps.pinWithView.scale; + pinDoc.contentBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]); + } + if (pinProps?.pinDocView) { + const { scrollable, pannable, temporal, clippable } = this.pinDataTypes(pinDoc); + pinDoc.presPinView = (pinProps?.pinWithView ? true : false) || scrollable || temporal || pannable || clippable; + + if (scrollable) pinDoc.presPinViewScroll = pinDoc._scrollTop; + else if (clippable) pinDoc.presPinClipWidth = pinDoc._clipWidth; + else if (temporal) pinDoc.presEndTime = NumCast((pinDoc.presStartTime = pinDoc._currentTimecode)) + 0.1; + else if (pannable) { + const panX = NumCast(pinDoc._panX); + const panY = NumCast(pinDoc._panY); + const pw = NumCast(pinProps.panelWidth); + const ph = NumCast(pinProps.panelHeight); + const ps = NumCast(pinDoc._viewScale); + if (pw && ph && ps) { + pinDoc.contentBounds = new List<number>([panX - pw / 2 / ps, panY - ph / 2 / ps, panX + pw / 2 / ps, panY + ph / 2 / ps]); + } + pinDoc.presPinViewX = panX; + pinDoc.presPinViewY = panY; + pinDoc.presPinViewScale = ps; + } + } + } + /** * This method makes sure that cursor navigates to the element that * has the option open and last in the group. @@ -1540,26 +1581,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } } - @computed get effectDirection(): string { - let effect = ''; + @computed get effectDirection() { + // prettier-ignore switch (this.activeItem.presEffectDirection) { - case 'left': - effect = 'Enter from left'; - break; - case 'right': - effect = 'Enter from right'; - break; - case 'top': - effect = 'Enter from top'; - break; - case 'bottom': - effect = 'Enter from bottom'; - break; - default: - effect = 'Enter from center'; - break; + case 'left': return 'Enter from left'; + case 'right': return 'Enter from right'; + case 'top': return'Enter from top'; + case 'bottom': return 'Enter from bottom'; } - return effect; + return 'Enter from center'; } @undoBatch @@ -1582,179 +1612,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { }); }; - @computed get presPinViewOptionsDropdown() { - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; - const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: 'auto', width: 16, filter: 'invert(1)' }} />; - return ( - <> - {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : null} - <div className="ribbon-doubleButton"> - <Tooltip - title={ - <> - <div className="dash-tooltip">{activeItem.presPinView ? 'Turn off pin with view' : 'Turn on pin with view'}</div> - </> - }> - <div - className="ribbon-toggle" - style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? Colors.LIGHT_BLUE : '' }} - onClick={() => { - activeItem.presPinView = !activeItem.presPinView; - targetDoc.presPinView = activeItem.presPinView; - if (activeItem.presPinView) { - if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) { - const scroll = targetDoc._scrollTop; - activeItem.presPinView = true; - activeItem.presPinViewScroll = scroll; - } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) { - activeItem.presStartTime = targetDoc._currentTimecode; - activeItem.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1; - } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) { - const x = targetDoc._panX; - const y = targetDoc._panY; - const scale = targetDoc._viewScale; - activeItem.presPinView = true; - activeItem.presPinViewX = x; - activeItem.presPinViewY = y; - activeItem.presPinViewScale = scale; - } else if (targetDoc.type === DocumentType.COMPARISON) { - const width = targetDoc._clipWidth; - activeItem.presPinClipWidth = width; - activeItem.presPinView = true; - } - } - }}> - {presPinWithViewIcon} - </div> - </Tooltip> - {activeItem.presPinView ? ( - <Tooltip - title={ - <> - <div className="dash-tooltip">{'Update the pinned view with the view of the selected document'}</div> - </> - }> - <div - className="ribbon-button" - onClick={() => { - if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) { - const scroll = targetDoc._scrollTop; - activeItem.presPinViewScroll = scroll; - } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) { - activeItem.presStartTime = targetDoc._currentTimecode; - activeItem.presStartTime = NumCast(targetDoc._currentTimecode) + 0.1; - } else if (targetDoc.type === DocumentType.COMPARISON) { - const clipWidth = targetDoc._clipWidth; - activeItem.presPinClipWidth = clipWidth; - } else { - const x = targetDoc._panX; - const y = targetDoc._panY; - const scale = targetDoc._viewScale; - activeItem.presPinViewX = x; - activeItem.presPinViewY = y; - activeItem.presPinViewScale = scale; - } - }}> - Update - </div> - </Tooltip> - ) : null} - </div> - </> - ); - } - - @computed get panOptionsDropdown() { - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; - return ( - <> - {this.panable ? ( - <div style={{ display: activeItem.presPinView ? 'block' : 'none' }}> - <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> - <div className="presBox-subheading">Pan X</div> - <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> - <input - className="presBox-input" - style={{ textAlign: 'left', width: 50 }} - type="number" - value={NumCast(activeItem.presPinViewX)} - onKeyDown={e => e.stopPropagation()} - onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { - const val = e.target.value; - activeItem.presPinViewX = Number(val); - })} - /> - </div> - </div> - <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> - <div className="presBox-subheading">Pan Y</div> - <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> - <input - className="presBox-input" - style={{ textAlign: 'left', width: 50 }} - type="number" - value={NumCast(activeItem.presPinViewY)} - onKeyDown={e => e.stopPropagation()} - onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { - const val = e.target.value; - activeItem.presPinViewY = Number(val); - })} - /> - </div> - </div> - <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> - <div className="presBox-subheading">Scale</div> - <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> - <input - className="presBox-input" - style={{ textAlign: 'left', width: 50 }} - type="number" - value={NumCast(activeItem.presPinViewScale)} - onKeyDown={e => e.stopPropagation()} - onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { - const val = e.target.value; - activeItem.presPinViewScale = Number(val); - })} - /> - </div> - </div> - </div> - ) : null} - </> - ); - } - - @computed get scrollOptionsDropdown() { - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; - return ( - <> - {this.scrollable ? ( - <div style={{ display: activeItem.presPinView ? 'block' : 'none' }}> - <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> - <div className="presBox-subheading">Scroll</div> - <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> - <input - className="presBox-input" - style={{ textAlign: 'left', width: 50 }} - type="number" - value={NumCast(activeItem.presPinViewScroll)} - onKeyDown={e => e.stopPropagation()} - onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { - const val = e.target.value; - activeItem.presPinViewScroll = Number(val); - })} - /> - </div> - </div> - </div> - ) : null} - </> - ); - } - @computed get mediaStopSlides() { const activeItem: Doc = this.activeItem; const list = this.childDocs.map((doc, i) => { @@ -2102,42 +1959,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { }; createTemplate = (layout: string, input?: string) => { - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; - let x = 0; - let y = 0; - if (activeItem && targetDoc) { - x = NumCast(targetDoc.x); - y = NumCast(targetDoc.y) + NumCast(targetDoc._height) + 20; - } - let doc = undefined; - const title = Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _fontSize: '24pt' }); - const subtitle = Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _fontSize: '16pt' }); - const header = Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _fontSize: '20pt' }); - const contentTitle = Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _fontSize: '24pt' }); - const content = Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _fontSize: '14pt' }); - const content1 = Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _fontSize: '14pt' }); - const content2 = Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _fontSize: '14pt' }); + const x = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.x) : 0; + const y = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.y) + NumCast(this.targetDoc._height) + 20 : 0; + const title = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _fontSize: '24pt' }); + const subtitle = () => Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _fontSize: '16pt' }); + const header = () => Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _fontSize: '20pt' }); + const contentTitle = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _fontSize: '24pt' }); + const content = () => Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _fontSize: '14pt' }); + const content1 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _fontSize: '14pt' }); + const content2 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _fontSize: '14pt' }); + // prettier-ignore switch (layout) { - case 'blank': - doc = Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x: x, y: y }); - break; - case 'title': - doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : 'Title slide', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y }); - break; - case 'header': - doc = Docs.Create.FreeformDocument([header], { title: input ? input : 'Section header', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y }); - break; - case 'content': - doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : 'Title and content', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y }); - break; - case 'twoColumns': - doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y }); - break; - default: - break; + case 'blank': return Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x, y }); + case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _fitContentsToBox: true, x, y }); + case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _fitContentsToBox: true, x, y }); + case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _fitContentsToBox: true, x, y }); + case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _fitContentsToBox: true, x, y }) } - return doc; }; // Dropdown that appears when the user wants to begin presenting (either minimize or sidebar view) @@ -2201,42 +2039,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { /** * Returns the collection type as a string for headers */ - @computed get stringType(): string { - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; - let type: string = ''; - if (activeItem) { - switch (targetDoc.type) { - case DocumentType.PDF: - type = 'PDF'; - break; - case DocumentType.RTF: - type = 'Text node'; - break; - case DocumentType.COL: - type = 'Collection'; - break; - case DocumentType.AUDIO: - type = 'Audio'; - break; - case DocumentType.VID: - type = 'Video'; - break; - case DocumentType.IMG: - type = 'Image'; - break; - case DocumentType.WEB: - type = 'Web page'; - break; - case DocumentType.MAP: - type = 'Map'; - break; - default: - type = 'Other node'; - break; + @computed get stringType() { + if (this.activeItem) { + // prettier-ignore + switch (this.targetDoc.type) { + case DocumentType.PDF: return 'PDF'; + case DocumentType.RTF: return 'Text node'; + case DocumentType.COL: return 'Collection'; + case DocumentType.AUDIO: return 'Audio'; + case DocumentType.VID: return 'Video'; + case DocumentType.IMG: return 'Image'; + case DocumentType.WEB: return 'Web page'; + case DocumentType.MAP: return 'Map'; + default: return 'Other node'; } } - return type; + return ''; } @observable private openActiveColorPicker: boolean = false; @@ -2845,10 +2663,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { case DocumentType.PDF || DocumentType.RTF || DocumentType.WEB: this.updateList(activeItem.frameList); break; - case DocumentType.COL: - break; - default: - break; } }; diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index f4b97479a..91196ca21 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -498,7 +498,13 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor), boxShadow: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isSelected ? '0 0 0px 1.5px' + presBoxColor : undefined) : undefined, }}> - <div className="presItem-name" style={{ maxWidth: showMore ? toolbarWidth - 195 : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}> + <div + className="presItem-name" + style={{ + pointerEvents: isSelected ? undefined : 'none', + maxWidth: showMore ? toolbarWidth - 195 : toolbarWidth - 105, + cursor: isSelected ? 'text' : 'grab', + }}> <div>{`${this.indexInPres + 1}. `}</div> <EditableView ref={this._titleRef} editing={!isSelected ? false : undefined} contents={activeItem.title} overflow={'ellipsis'} GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} /> </div> diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 0fd992d3b..48d5c5563 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -189,7 +189,7 @@ export class ComputedField extends ScriptField { const compiled = ScriptField.CompileScript(script, params, true, capturedVariables); return compiled.compiled ? new ComputedField(compiled) : undefined; } - public static MakeInterpolated(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) { + public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) { if (!doc[`${fieldKey}-indexed`]) { const flist = new List<number>(numberRange(curTimecode + 1).map(i => undefined) as any as number[]); flist[curTimecode] = NumCast(doc[fieldKey]); @@ -209,6 +209,16 @@ export class ComputedField extends ScriptField { const setField = ScriptField.CompileScript(`setIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, value)`, { value: 'any' }, true, {}); return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined; } + public static MakeInterpolatedDataField(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) { + if (!doc[`${fieldKey}-indexed`]) { + const flist = new List<Field>(numberRange(curTimecode + 1).map(i => undefined) as any as Field[]); + flist[curTimecode] = Field.Copy(doc[fieldKey]); + doc[`${fieldKey}-indexed`] = flist; + } + const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey})`, {}, true, {}); + const setField = ScriptField.CompileScript(`setIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, value)`, { value: 'any' }, true, {}); + return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined; + } } export namespace ComputedField { let useComputed = true; |