From d818ef151ca65008e5c6bb5e92b709decb3026d8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 14 Apr 2025 18:35:49 -0400 Subject: fixed how templates are expanded to avoid template sub-component conflicts by changing how field keys are named. fixed various Cast functions to be more typesafe by including undefined as part of return type. overhaul of Doc.MakeClone, MakeCopy, FindRefernces - makeClone is no longer async. fixed inlined docs in text docs. --- src/client/views/nodes/trails/PresBox.tsx | 187 ++++--- src/client/views/nodes/trails/PresElementBox.scss | 308 ----------- src/client/views/nodes/trails/PresElementBox.tsx | 628 ---------------------- src/client/views/nodes/trails/PresSlideBox.scss | 308 +++++++++++ src/client/views/nodes/trails/PresSlideBox.tsx | 628 ++++++++++++++++++++++ src/client/views/nodes/trails/index.ts | 2 +- 6 files changed, 1029 insertions(+), 1032 deletions(-) delete mode 100644 src/client/views/nodes/trails/PresElementBox.scss delete mode 100644 src/client/views/nodes/trails/PresElementBox.tsx create mode 100644 src/client/views/nodes/trails/PresSlideBox.scss create mode 100644 src/client/views/nodes/trails/PresSlideBox.tsx (limited to 'src/client/views/nodes/trails') diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 23155ebf3..11f35b8ef 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -149,7 +149,7 @@ export class PresBox extends ViewBoxBaseComponent() { setIsLoading = action((input?: boolean) => { this._isLoading = !!input; }); // prettier-ignore setShowAIGalleryVisibilty = action((visible: boolean) => { this._showAIGallery = visible; }); // prettier-ignore setBezierControlPoints = action((newPoints: { p1: number[]; p2: number[] }) => { - this.setEaseFunc(this.activeItem, `cubic-bezier(${newPoints.p1[0]}, ${newPoints.p1[1]}, ${newPoints.p2[0]}, ${newPoints.p2[1]})`); + this.activeItem && this.setEaseFunc(this.activeItem, `cubic-bezier(${newPoints.p1[0]}, ${newPoints.p1[1]}, ${newPoints.p2[0]}, ${newPoints.p2[1]})`); }); @computed get showEaseFunctions() { @@ -158,7 +158,7 @@ export class PresBox extends ViewBoxBaseComponent() { @computed get currCPoints() { - return EaseFuncToPoints(this.activeItem.presentation_easeFunc ? StrCast(this.activeItem.presentation_easeFunc) : 'ease'); + return EaseFuncToPoints(this.activeItem?.presentation_easeFunc ? StrCast(this.activeItem.presentation_easeFunc) : 'ease'); } @computed @@ -175,7 +175,7 @@ export class PresBox extends ViewBoxBaseComponent() { return DocListCast(this.Document[this.presFieldKey]); } @computed get tagDocs() { - return this.childDocs.map(doc => Cast(doc.presentation_targetDoc, Doc, null)); + return this.childDocs.map(doc => DocCast(doc.presentation_targetDoc)!).filter(doc => doc); } @computed get itemIndex() { return NumCast(this.Document._itemIndex); @@ -187,11 +187,11 @@ export class PresBox extends ViewBoxBaseComponent() { return DocCast(this.activeItem?.presentation_targetDoc); } public static targetRenderedDoc = (doc: Doc) => { - const targetDoc = Cast(doc?.presentation_targetDoc, Doc, null); + const targetDoc = DocCast(doc?.presentation_targetDoc); return targetDoc?.layout_unrendered ? DocCast(targetDoc.annotationOn) : targetDoc; }; @computed get scrollable() { - if ([DocumentType.PDF, DocumentType.WEB, DocumentType.RTF].includes(this.targetDoc.type as DocumentType) || this.targetDoc._type_collection === CollectionViewType.Stacking) return true; + if ([DocumentType.PDF, DocumentType.WEB, DocumentType.RTF].includes(this.targetDoc?.type as DocumentType) || this.targetDoc?._type_collection === CollectionViewType.Stacking) return true; return false; } @computed get selectedDocumentView() { @@ -273,8 +273,8 @@ export class PresBox extends ViewBoxBaseComponent() { }; stopTempMedia = (targetDocField: FieldResult) => { - const targetDoc = DocCast(DocCast(targetDocField).annotationOn) ?? DocCast(targetDocField); - if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as DocumentType)) { + const targetDoc = DocCast(DocCast(targetDocField)?.annotationOn) ?? DocCast(targetDocField); + if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc?.type as DocumentType)) { const targMedia = DocumentView.getDocumentView(targetDoc); targMedia?.ComponentView?.Pause?.(); } @@ -295,13 +295,13 @@ export class PresBox extends ViewBoxBaseComponent() { this.setIsLoading(true); const slideDefaults: { [key: string]: FieldResult } = { presentation_transition: 500, config_zoom: 1 }; const currSlideProperties = gptSlideProperties.reduce( - (prev, key) => { prev[key] = Field.toString(this.activeItem[key]) ?? prev[key]; return prev; }, + (prev, key) => { prev[key] = Field.toString(this.activeItem?.[key]) ?? prev[key]; return prev; }, slideDefaults); // prettier-ignore gptTrailSlideCustomization(input, JSON.stringify(currSlideProperties)) .then(res => (Object.entries(JSON.parse(res)) as string[][]).forEach(([key, val]) => { - this.activeItem[key] = (+val).toString() === val ? +val : (val ?? this.activeItem[key]); + this.activeItem && (this.activeItem[key] = (+val).toString() === val ? +val : (val ?? this.activeItem[key])); }) ) .catch(e => console.error(e)) @@ -325,7 +325,7 @@ export class PresBox extends ViewBoxBaseComponent() { const serial = nextSelected + 1 < this.childDocs.length && NumCast(this.childDocs[nextSelected + 1].presentation_groupWithUp) > 1; if (serial) { this.gotoDocument(nextSelected, this.activeItem, true, async () => { - const waitTime = NumCast(this.activeItem.presentation_duration); + const waitTime = NumCast(this.activeItem?.presentation_duration); await new Promise(res => { setTimeout(res, Math.max(0, waitTime)); }); @@ -346,7 +346,7 @@ export class PresBox extends ViewBoxBaseComponent() { progressivizedItems = (doc: Doc) => { const targetList = PresBox.targetRenderedDoc(doc); if (doc.presentation_indexed !== undefined && targetList) { - const listItems = (Cast(targetList[Doc.LayoutFieldKey(targetList)], listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[]) ?? DocListCast(targetList[Doc.LayoutFieldKey(targetList) + '_annotations']); + const listItems = (Cast(targetList[Doc.LayoutDataKey(targetList)], listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[]) ?? DocListCast(targetList[Doc.LayoutDataKey(targetList) + '_annotations']); return listItems.filter(ldoc => !ldoc.layout_unrendered); } return undefined; @@ -364,7 +364,7 @@ export class PresBox extends ViewBoxBaseComponent() { next = () => { const progressiveReveal = (first: boolean) => { const presIndexed = Cast(this.activeItem?.presentation_indexed, 'number', null); - if (presIndexed !== undefined) { + if (presIndexed !== undefined && this.activeItem) { const listItems = this.progressivizedItems(this.activeItem); const listItemDoc = listItems?.[presIndexed]; if (listItems && listItemDoc) { @@ -395,7 +395,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (this.childDocs[this.itemIndex + 1] !== undefined) { // Case 1: No more frames in current doc and next slide is defined, therefore move to next slide const slides = DocListCast(this.Document[StrCast(this.presFieldKey, 'data')]); - const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex; + const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d) ?? d))) : this.itemIndex; // before moving onto next slide, run the subroutines :) const currentDoc = this.childDocs[this.itemIndex]; @@ -422,7 +422,7 @@ export class PresBox extends ViewBoxBaseComponent() { const { activeItem } = this; let prevSelected = this.itemIndex; // Functionality for group with up - let didZoom = activeItem.presentation_movement; + let didZoom = activeItem?.presentation_movement; for (; prevSelected > 0 && this.childDocs[Math.max(0, prevSelected - 1)].presentation_groupWithUp; prevSelected--) { didZoom = didZoom === 'none' ? this.childDocs[prevSelected].presentation_movement : didZoom; } @@ -452,7 +452,7 @@ export class PresBox extends ViewBoxBaseComponent() { this.stopTempMedia(from.presentation_targetDoc); } // If next slide is audio / video 'Play automatically' then the next slide should be played - if (this.layoutDoc.presentation_status !== PresStatus.Edit && (this.targetDoc.type === DocumentType.AUDIO || this.targetDoc.type === DocumentType.VID) && this.activeItem.presentation_mediaStart === 'auto') { + if (this.layoutDoc.presentation_status !== PresStatus.Edit && (this.targetDoc?.type === DocumentType.AUDIO || this.targetDoc?.type === DocumentType.VID) && this.activeItem?.presentation_mediaStart === 'auto') { this.startTempMedia(this.targetDoc, this.activeItem); } if (!group) this.clearSelectedArray(); @@ -516,8 +516,8 @@ export class PresBox extends ViewBoxBaseComponent() { const activeFrame = activeItem.config_activeFrame ?? activeItem.config_currentFrame; if (activeFrame !== undefined) { const frameTime = NumCast(activeItem.presentation_transition, 500); - const acontext = activeItem.config_activeFrame !== undefined ? DocCast(DocCast(activeItem.presentation_targetDoc).embedContainer) : DocCast(activeItem.presentation_targetDoc); - const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext; + const acontext = activeItem.config_activeFrame !== undefined ? DocCast(DocCast(activeItem?.presentation_targetDoc)?.embedContainer) : DocCast(activeItem.presentation_targetDoc); + const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext)?.annotationOn) : acontext; if (context) { const ffview = CollectionFreeFormView.from(DocumentView.getFirstDocumentView(context)); if (ffview?.childDocs) { @@ -528,7 +528,7 @@ export class PresBox extends ViewBoxBaseComponent() { } if ((pinDataTypes?.dataview && activeItem.config_data !== undefined) || (!pinDataTypes && activeItem.config_data !== undefined)) { bestTarget._dataTransition = `all ${transTime}ms`; - const fkey = Doc.LayoutFieldKey(bestTarget); + const fkey = Doc.LayoutDataKey(bestTarget); const setData = bestTargetView?.ComponentView?.setData; if (setData) setData(activeItem.config_data); else { @@ -553,7 +553,7 @@ export class PresBox extends ViewBoxBaseComponent() { } } if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.config_clipWidth !== undefined)) { - const fkey = '_' + Doc.LayoutFieldKey(bestTarget); + const fkey = '_' + Doc.LayoutDataKey(bestTarget); if (bestTarget[fkey + '_clipWidth'] !== activeItem.config_clipWidth) { bestTarget[fkey + '_clipWidth'] = activeItem.config_clipWidth; changed = true; @@ -655,7 +655,7 @@ export class PresBox extends ViewBoxBaseComponent() { } } if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.config_annotations !== undefined)) { - const fkey = Doc.LayoutFieldKey(bestTarget); + const fkey = Doc.LayoutDataKey(bestTarget); const oldItems = DocListCast(bestTarget[fkey + '_annotations']).filter(doc => doc.layout_unrendered); const newItems = DocListCast(activeItem.config_annotations).map(doc => { doc.hidden = false; @@ -672,7 +672,7 @@ export class PresBox extends ViewBoxBaseComponent() { } if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.config_pinLayoutData !== undefined)) { changed = true; - const layoutField = Doc.LayoutFieldKey(bestTarget); + const layoutField = Doc.LayoutDataKey(bestTarget); const transitioned = new Set(); StrListCast(activeItem.config_pinLayoutData) .map(str => JSON.parse(str) as { id: string; x: number; y: number; back: string; fill: string; w: number; h: number; data: string; text: string }) @@ -740,7 +740,7 @@ export class PresBox extends ViewBoxBaseComponent() { const { activeItem, targetDoc } = this; const finished = (options: FocusViewOptions) => { afterNav?.(options); - targetDoc[Animation] = undefined; + targetDoc && (targetDoc[Animation] = undefined); }; const selViewCache = Array.from(this.selectedArray); const dragViewCache = Array.from(this._dragArray); @@ -756,7 +756,7 @@ export class PresBox extends ViewBoxBaseComponent() { } finished(options); }); - PresBox.NavigateToTarget(targetDoc, activeItem, resetSelection); + targetDoc && activeItem && PresBox.NavigateToTarget(targetDoc, activeItem, resetSelection); }; static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: (options: FocusViewOptions) => void) { @@ -806,42 +806,44 @@ export class PresBox extends ViewBoxBaseComponent() { @action doHideBeforeAfter = () => { this.childDocs.forEach((doc, index) => { - const curDoc = Cast(doc, Doc, null); - const tagDoc = PresBox.targetRenderedDoc(curDoc); - const itemIndexes = this.getAllIndexes(this.tagDocs, curDoc); - let opacity = index === this.itemIndex ? 1 : undefined; - if (curDoc.presentation_hide) { - if (index !== this.itemIndex) { - opacity = 1; + const curDoc = DocCast(doc); + if (curDoc) { + const tagDoc = PresBox.targetRenderedDoc(curDoc) ?? curDoc; + const itemIndexes = this.getAllIndexes(this.tagDocs, curDoc); + let opacity = index === this.itemIndex ? 1 : undefined; + if (curDoc.presentation_hide) { + if (index !== this.itemIndex) { + opacity = 1; + } } - } - const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex) ?? itemIndexes.slice().reverse().lastElement(); - if (curDoc.presentation_hideBefore && index === hidingIndBef) { - if (index > this.itemIndex) { - opacity = 0; - } else if (index === this.itemIndex || !curDoc.presentation_hideAfter) { - opacity = 1; + const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex) ?? itemIndexes.slice().reverse().lastElement(); + if (curDoc.presentation_hideBefore && index === hidingIndBef) { + if (index > this.itemIndex) { + opacity = 0; + } else if (index === this.itemIndex || !curDoc.presentation_hideAfter) { + opacity = 1; + } } - } - const hidingIndAft = - itemIndexes - .slice() - .reverse() - .find(item => item <= this.itemIndex) ?? itemIndexes.lastElement(); - if (curDoc.presentation_hideAfter && index === hidingIndAft) { - if (index < this.itemIndex) { - opacity = 0; - } else if (index === this.itemIndex || !curDoc.presentation_hideBefore) { - opacity = 1; + const hidingIndAft = + itemIndexes + .slice() + .reverse() + .find(item => item <= this.itemIndex) ?? itemIndexes.lastElement(); + if (curDoc.presentation_hideAfter && index === hidingIndAft) { + if (index < this.itemIndex) { + opacity = 0; + } else if (index === this.itemIndex || !curDoc.presentation_hideBefore) { + opacity = 1; + } } - } - const hidingInd = itemIndexes.find(item => item === this.itemIndex); - if (curDoc.presentation_hide && index === hidingInd) { - if (index === this.itemIndex) { - opacity = 0; + const hidingInd = itemIndexes.find(item => item === this.itemIndex); + if (curDoc.presentation_hide && index === hidingInd) { + if (index === this.itemIndex) { + opacity = 0; + } } + opacity !== undefined && (tagDoc.opacity = opacity === 1 ? undefined : opacity); } - opacity !== undefined && (tagDoc.opacity = opacity === 1 ? undefined : opacity); }); }; @@ -940,7 +942,7 @@ export class PresBox extends ViewBoxBaseComponent() { initializePresState = (startIndex: number) => { this.childDocs.forEach((doc, index) => { - const tagDoc = PresBox.targetRenderedDoc(doc); + const tagDoc = PresBox.targetRenderedDoc(doc) ?? doc; if (doc.presentation_hideBefore && index > startIndex) tagDoc.opacity = 0; if (doc.presentation_hideAfter && index < startIndex) tagDoc.opacity = 0; if (doc.presentation_indexed !== undefined && index >= startIndex) { @@ -969,7 +971,7 @@ export class PresBox extends ViewBoxBaseComponent() { this.layoutDoc.presentation_status = PresStatus.Autoplay; this.initializePresState(startIndex); const func = () => { - const delay = NumCast(this.activeItem.presentation_duration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presentation_transition); + const delay = NumCast(this.activeItem?.presentation_duration, this.activeItem?.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem?.presentation_transition); this._presTimer = setTimeout(() => { if (this.next() === false) this.layoutDoc.presentation_status = this._exitTrail?.() ?? PresStatus.Manual; this.layoutDoc.presentation_status === PresStatus.Autoplay && func(); @@ -1060,7 +1062,7 @@ export class PresBox extends ViewBoxBaseComponent() { return !results.some(r => !r); }; - childLayoutTemplate = () => Docs.Create.PresElementBoxDocument(); + childLayoutTemplate = () => Docs.Create.PresSlideDocument(); removeDocument = (doc: Doc | Doc[]) => !toList(doc) .map(d => Doc.RemoveDocFromList(this.Document, this.fieldKey, d)) @@ -1077,13 +1079,14 @@ export class PresBox extends ViewBoxBaseComponent() { */ @computed get listOfSelected() { return Array.from(this.selectedArray).map((doc, index) => { - const curDoc = Cast(doc, Doc, null); + const curDoc = DocCast(doc); + if (!curDoc) return null; const tagDoc = Cast(curDoc.presentation_targetDoc, Doc, null); if (curDoc && curDoc === this.activeItem) return (
- {index + 1}. {StrCast(curDoc.title)}) + {index + 1}. {StrCast(curDoc.title)}
); @@ -1277,7 +1280,7 @@ export class PresBox extends ViewBoxBaseComponent() { const presCollection = collection; const dv = DocumentView.getDocumentView(presCollection); this.childDocs.forEach((doc, index) => { - const tagDoc = PresBox.targetRenderedDoc(doc); + const tagDoc = PresBox.targetRenderedDoc(doc) ?? doc; const srcContext = Cast(tagDoc.embedContainer, Doc, null); const labelCreator = (top: number, left: number, edge: number, fontSize: number) => (
this.selectElement(doc)}> @@ -1526,17 +1529,19 @@ export class PresBox extends ViewBoxBaseComponent() { // Applies the slide transiiton settings to all docs in the array @undoBatch applyTo = (array: Doc[]) => { - this.updateMovement(this.activeItem.presentation_movement as PresMovement, true); - this.updateEffect(this.activeItem.presentation_effect as PresEffect, false, true); - this.updateEffect(this.activeItem.presBulletEffect as PresEffect, true, true); - this.updateEffectDirection(this.activeItem.presentation_effectDirection as PresEffectDirection, true); - const { presentation_transition: pt, presentation_duration: pd, presentation_hideBefore: ph, presentation_hideAfter: pa } = this.activeItem; - array.forEach(curDoc => { - curDoc.presentation_transition = pt; - curDoc.presentation_duration = pd; - curDoc.presentation_hideBefore = ph; - curDoc.presentation_hideAfter = pa; - }); + if (this.activeItem) { + this.updateMovement(this.activeItem.presentation_movement as PresMovement, true); + this.updateEffect(this.activeItem.presentation_effect as PresEffect, false, true); + this.updateEffect(this.activeItem.presBulletEffect as PresEffect, true, true); + this.updateEffectDirection(this.activeItem.presentation_effectDirection as PresEffectDirection, true); + const { presentation_transition: pt, presentation_duration: pd, presentation_hideBefore: ph, presentation_hideAfter: pa } = this.activeItem; + array.forEach(curDoc => { + curDoc.presentation_transition = pt; + curDoc.presentation_duration = pd; + curDoc.presentation_hideBefore = ph; + curDoc.presentation_hideAfter = pa; + }); + } }; @computed get visibilityDurationDropdown() { @@ -1673,15 +1678,15 @@ export class PresBox extends ViewBoxBaseComponent() { onClick={() => { activeItem.presentation_indexed = activeItem.presentation_indexed === undefined ? 0 : undefined; activeItem.presentation_hideBefore = activeItem.presentation_indexed !== undefined; - const tagDoc = PresBox.targetRenderedDoc(this.activeItem); + const tagDoc = PresBox.targetRenderedDoc(activeItem) ?? activeItem; const type = DocCast(tagDoc?.annotationOn)?.type ?? tagDoc.type; activeItem.presentation_indexedStart = type === DocumentType.COL ? 1 : 0; // a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized. // to avoid creating a new slide to correspond to each of the target's data list, we create a computedField to refernce the target's data list. - let dataField = Doc.LayoutFieldKey(tagDoc); + let dataField = Doc.LayoutDataKey(tagDoc); if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField += '_annotations'; - if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc.annotationOn?.["${dataField}"]`); + if (DocCast(activeItem.presentation_targetDoc)?.annotationOn) activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc.annotationOn?.["${dataField}"]`); else activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc?.["${dataField}"]`); }}> Enable @@ -1797,12 +1802,13 @@ export class PresBox extends ViewBoxBaseComponent() { onClick={() => { this.updateEffect(elem.effect, false); this.updateEffectDirection(elem.direction); - this.updateEffectTiming(this.activeItem, { - type: SpringType.CUSTOM, - stiffness: elem.stiffness, - damping: elem.damping, - mass: elem.mass, - }); + this.activeItem && + this.updateEffectTiming(this.activeItem, { + type: SpringType.CUSTOM, + stiffness: elem.stiffness, + damping: elem.damping, + mass: elem.mass, + }); }}>
@@ -1947,8 +1953,8 @@ export class PresBox extends ViewBoxBaseComponent() { formLabelPlacement="left" closeOnSelect items={easeItems} - selectedVal={this.activeItem.presentation_easeFunc ? (StrCast(this.activeItem.presentation_easeFunc).startsWith('cubic') ? 'custom' : StrCast(this.activeItem.presentation_easeFunc)) : 'ease'} - setSelectedVal={val => typeof val === 'string' && this.setEaseFunc(this.activeItem, val !== 'custom' ? val : TIMING_DEFAULT_MAPPINGS.ease)} + selectedVal={this.activeItem?.presentation_easeFunc ? (StrCast(this.activeItem.presentation_easeFunc).startsWith('cubic') ? 'custom' : StrCast(this.activeItem.presentation_easeFunc)) : 'ease'} + setSelectedVal={val => typeof val === 'string' && this.activeItem && this.setEaseFunc(this.activeItem, val !== 'custom' ? val : TIMING_DEFAULT_MAPPINGS.ease)} dropdownType={DropdownType.SELECT} type={Type.TERT} /> @@ -2145,9 +2151,9 @@ export class PresBox extends ViewBoxBaseComponent() { @computed get mediaOptionsDropdown() { const { activeItem } = this; if (activeItem && this.targetDoc) { - const renderTarget = PresBox.targetRenderedDoc(this.activeItem); + const renderTarget = PresBox.targetRenderedDoc(this.activeItem ?? this.targetDoc) ?? this.targetDoc; const clipStart = NumCast(renderTarget.clipStart); - const clipEnd = NumCast(renderTarget.clipEnd, clipStart + NumCast(renderTarget[Doc.LayoutFieldKey(renderTarget) + '_duration'])); + const clipEnd = NumCast(renderTarget.clipEnd, clipStart + NumCast(renderTarget[Doc.LayoutDataKey(renderTarget) + '_duration'])); const configClipEnd = NumCast(activeItem.config_clipEnd) < NumCast(activeItem.config_clipStart) ? clipEnd - clipStart : NumCast(activeItem.config_clipEnd); return (
e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> @@ -2699,7 +2705,7 @@ export class PresBox extends ViewBoxBaseComponent() { const presEnd = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1 && - (this.activeItem.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0)); + (this.activeItem?.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0)); const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0; const inOverlay = Doc.IsInMyOverlay(this.Document); // Case 1: There are still other frames and should go through all frames before going to next slide @@ -2892,7 +2898,7 @@ export class PresBox extends ViewBoxBaseComponent() { const presEnd = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1 && - (this.activeItem.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0)); + (this.activeItem?.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0)); const presStart = !this.layoutDoc.presLoop && this.itemIndex === 0; return this._props.addDocTab === returnFalse ? ( // bcz: hack!! - addDocTab === returnFalse only when this is being rendered by the OverlayView which means the doc is a mini player
() { /> ) : null}
- {/* { - // if the document type is a presentation, then the collection stacking view has a "+ new slide" button at the bottom of the stack - {'Click on document to pin to presentaiton or make a marquee selection to pin your desired view'}
}> - - - } */}
- {/* presbox chatbox */} {this._chatActive &&
}
); diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss deleted file mode 100644 index 9ac2b5a94..000000000 --- a/src/client/views/nodes/trails/PresElementBox.scss +++ /dev/null @@ -1,308 +0,0 @@ -$light-blue: #aeddf8; -$dark-blue: #5b9fdd; -$light-background: #ececec; -$slide-background: #d5dce2; -$slide-active: #5b9fdd; - -.testingv2 { - background-color: red; -} - -.presItem-container { - cursor: grab; - display: flex; - grid-template-columns: 20px auto; - font-family: Roboto; - letter-spacing: normal; - position: relative; - pointer-events: all; - width: 100%; - height: 100%; - font-weight: 400; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - align-items: center; - - // .presItem-number { - // margin-top: 3.5px; - // font-size: 12px; - // font-weight: 700; - // text-align: center; - // justify-self: center; - // align-self: flex-start; - // position: relative; - // display: inline-block; - // overflow: hidden; - // } -} - -.presItem-slide { - position: relative; - height: 100%; - width: 100%; - border-bottom: 0.5px solid grey; - display: flex; - align-items: center; - - .presItem-number { - cursor: pointer; - &:hover { - background-color: $light-blue; - } - } - .presItem-name { - display: flex; - min-width: 20px; - z-index: 300; - top: 2px; - align-self: center; - font-size: 11px; - font-family: Roboto; - font-weight: 500; - position: relative; - padding-left: 10px; - padding-right: 10px; - letter-spacing: normal; - width: max-content; - text-overflow: ellipsis; - overflow: hidden; - white-space: pre; - } - - .presItem-docName { - min-width: 20px; - z-index: 300; - align-self: center; - font-size: 9px; - font-family: Roboto; - font-weight: 300; - position: relative; - padding-left: 10px; - padding-right: 10px; - letter-spacing: normal; - width: max-content; - text-overflow: ellipsis; - overflow: hidden; - white-space: pre; - grid-row: 2; - grid-column: 1/6; - } - - .presItem-time { - align-self: center; - position: relative; - padding-right: 10px; - top: 1px; - font-size: 10; - font-weight: 300; - font-family: Roboto; - z-index: 300; - letter-spacing: normal; - } - - .presItem-embedded { - overflow: hidden; - grid-row: 3; - grid-column: 1/8; - position: relative; - display: inline-block; - } - - .presItem-embeddedMask { - width: 100%; - height: 100%; - position: absolute; - border-radius: 3px; - top: 0; - left: 0; - z-index: 1; - overflow: hidden; - } - - .presItem-slideButtons { - display: flex; - position: absolute; - width: max-content; - justify-self: right; - justify-content: flex-end; - - .slideButton { - cursor: pointer; - position: relative; - border-radius: 100px; - z-index: 300; - width: 18px; - height: 18px; - display: flex; - font-size: 12px; - justify-self: center; - align-self: center; - background-color: rgba(0, 0, 0, 0.5); - color: white; - justify-content: center; - align-items: center; - transition: 0.2s; - margin-right: 3px; - } - - .slideButton:hover { - background-color: rgba(0, 0, 0, 1); - transform: scale(1.2); - } - } -} - -// .presItem-slide:hover { -// .presItem-slideButtons { -// display: flex; -// grid-column: 7; -// grid-row: 1/3; -// width: max-content; -// justify-self: right; -// justify-content: flex-end; - -// .slideButton { -// cursor: pointer; -// position: relative; -// border-radius: 100px; -// z-index: 300; -// width: 18px; -// height: 18px; -// display: flex; -// font-size: 12px; -// justify-self: center; -// align-self: center; -// background-color: rgba(0, 0, 0, 0.5); -// color: white; -// justify-content: center; -// align-items: center; -// transition: 0.2s; -// margin-right: 3px; -// } - -// .slideButton:hover { -// background-color: rgba(0, 0, 0, 1); -// transform: scale(1.2); -// } -// } -// } - -.presItem-slide.active { - //box-shadow: 0 0 0px 2.5px $dark-blue; - border: $dark-blue solid 2.5px; -} - -.presItem-slide.group { - border-radius: 5px; -} - -.presItem-slide.activeGroup { - border-radius: 5px; - box-shadow: 0 0 0px 2.5px $dark-blue; -} - -.presItem-groupSlideContainer { - position: absolute; - /* grid-row: 3; */ - /* grid-column: 1/8; */ - top: 28; - display: block; - background: #92adb9; - width: 100%; - height: 10px; - border-radius: 0px 0px 5px 5px; - height: calc(100% - 28px); - display: grid; - grid-template-rows: auto auto auto; - grid-template-columns: 100%; - justify-items: left; - align-items: center; -} - -.presItem-groupSlide { - position: relative; - background-color: #d5dce2; - border-radius: 5px; - height: calc(100% - 7px); - width: calc(100% - 20px); - left: 15px; - /* height: 20px; */ - /* width: calc(100% - 19px); */ - display: flex; - grid-template-rows: 16px 10px auto; - grid-template-columns: max-content max-content max-content max-content auto; -} - -.presItem-multiDrag { - font-family: Roboto; - font-weight: 600; - color: white; - text-align: center; - justify-content: center; - align-content: center; - width: 100px; - height: 30px; - position: absolute; - background-color: $dark-blue; - z-index: 4000; - border-radius: 10px; - box-shadow: black 0.4vw 0.4vw 0.8vw; - line-height: 30px; -} - -.presItem-miniSlide { - font-weight: 700; - font-size: 12; - grid-column: 1/8; - align-self: center; - justify-self: center; - background-color: #d5dce2; - width: 26px; - text-align: center; - height: 26px; - line-height: 28px; - border-radius: 100%; -} - -.presItem-miniSlide.active { - box-shadow: 0 0 0px 1.5px $dark-blue; -} - -.expandButton { - cursor: pointer; - position: absolute; - border-radius: 100px; - bottom: 0; - left: -18; - z-index: 300; - width: 15px; - height: 15px; - display: flex; - font-size: 12px; - justify-self: center; - align-self: center; - background-color: #92adb9; - color: white; - justify-content: center; - align-items: center; - transition: 0.2s; - margin-right: 3px; -} - -.expandButton:hover { - background-color: rgba(0, 0, 0, 1); - transform: scale(1.2); -} - -.presItem-groupNum { - color: #d5dce2; - position: absolute; - left: -15px; - top: 1; - font-weight: 600; - font-size: 12; -} diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx deleted file mode 100644 index 7e0375275..000000000 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ /dev/null @@ -1,628 +0,0 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@mui/material'; -import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { returnFalse, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils'; -import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; -import { Id } from '../../../../fields/FieldSymbols'; -import { List } from '../../../../fields/List'; -import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction } from '../../../../Utils'; -import { Docs } from '../../../documents/Documents'; -import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; -import { DragManager } from '../../../util/DragManager'; -import { SnappingManager } from '../../../util/SnappingManager'; -import { Transform } from '../../../util/Transform'; -import { undoable, undoBatch } from '../../../util/UndoManager'; -import { TreeView } from '../../collections/TreeView'; -import { ViewBoxBaseComponent } from '../../DocComponent'; -import { EditableView } from '../../EditableView'; -import { Colors } from '../../global/globalEnums'; -import { PinDocView } from '../../PinFuncs'; -import { StyleProp } from '../../StyleProp'; -import { returnEmptyDocViewList } from '../../StyleProvider'; -import { DocumentView } from '../DocumentView'; -import { FieldView, FieldViewProps } from '../FieldView'; -import { PresBox } from './PresBox'; -import './PresElementBox.scss'; -import { PresMovement } from './PresEnums'; -/** - * This class models the view a document added to presentation will have in the presentation. - * It involves some functionality for its buttons and options. - */ -@observer -export class PresElementBox extends ViewBoxBaseComponent() { - public static LayoutString(fieldKey: string) { - return FieldView.LayoutString(PresElementBox, fieldKey); - } - private _itemRef: React.RefObject = React.createRef(); - private _dragRef: React.RefObject = React.createRef(); - private _titleRef: React.RefObject = React.createRef(); - private _heightDisposer: IReactionDisposer | undefined; - readonly expandViewHeight = 100; - readonly collapsedHeight = 35; - - constructor(props: FieldViewProps) { - super(props); - makeObservable(this); - } - - @observable _dragging = false; - - // the presentation view that renders this slide - @computed get presBoxView() { - return this.DocumentView?.() - .containerViewPath?.() - .slice() - .reverse() - .find(dv => dv?.ComponentView instanceof PresBox)?.ComponentView as PresBox; - } - - // the presentation view document that renders this slide - @computed get presBox() { - return this.presBoxView?.Document; - } - - // Since this node is being rendered with a template, this method retrieves - // the actual slide being rendered from the auto-generated rendering template - @computed get slideDoc() { - return this.rootDoc; - } - - // this is the document in the workspaces that is targeted by the slide - @computed get targetDoc() { - return Cast(this.slideDoc.presentation_targetDoc, Doc, null) || this.slideDoc; - } - - // computes index of this presentation slide in the presBox list - @computed get indexInPres() { - return this.presBoxView?.SlideIndex(this.slideDoc) ?? 0; - } - - @computed get selectedArray() { - return this.presBoxView?.selectedArray; - } - - @computed get videoRecordingIsInOverlay() { - return Doc.MyOverlayDocs.some(doc => doc.slides === this.slideDoc); - } - - componentDidMount() { - this.layoutDoc.layout_hideLinkButton = true; - this._heightDisposer = reaction( - () => ({ expand: this.slideDoc.presentation_expandInlineButton, height: this.collapsedHeight }), - ({ expand, height }) => { - this.layoutDoc._height = height + (expand ? this.expandViewHeight : 0); - }, - { fireImmediately: true } - ); - } - componentWillUnmount() { - this._heightDisposer?.(); - } - - presExpandDocumentClick = () => { - this.slideDoc.presentation_expandInlineButton = !this.slideDoc.presentation_expandInlineButton; - }; - embedHeight = () => this.collapsedHeight + this.expandViewHeight; - embedWidth = () => this._props.PanelWidth() / 2; - // prettier-ignore - styleProvider = ( doc: Doc | undefined, props: Opt, property: string ) => - (property === StyleProp.Opacity ? 1 : this._props.styleProvider?.(doc, props, property)); - /** - * The function that is responsible for rendering a preview or not for this - * presentation element. - */ - @computed get renderEmbeddedInline() { - return !this.slideDoc.presentation_expandInlineButton || !this.targetDoc ? null : ( -
- -
- ); - } - - @computed get renderGroupSlides() { - const childDocs = DocListCast(this.targetDoc.data); - const groupSlides = childDocs.map((doc: Doc, ind: number) => ( -
{ - e.stopPropagation(); - e.preventDefault(); - this.presBoxView?.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, e.shiftKey || e.ctrlKey || e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); - this.presExpandDocumentClick(); - }}> -
{`${ind + 1}.`}
-
- StrCast(doc.title)} - SetValue={(value: string) => { - doc.title = !value.trim().length ? '-untitled-' : value; - return true; - }} - /> -
-
- )); - return groupSlides; - } - - @computed get transition() { - let transitionInS: number; - if (this.slideDoc.presentation_transition) transitionInS = NumCast(this.slideDoc.presentation_transition) / 1000; - else transitionInS = 0.5; - return this.slideDoc.presentation_movement === PresMovement.Jump || this.slideDoc.presentation_movement === PresMovement.None ? null : 'M: ' + transitionInS + 's'; - } - - @action - headerDown = (e: React.PointerEvent) => { - const element = e.target as HTMLDivElement; - e.stopPropagation(); - e.preventDefault(); - if (element && !(e.ctrlKey || e.metaKey || e.button === 2)) { - this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false); - setupMoveUpEvents(this, e, this.startDrag, emptyFunction, clickEv => { - clickEv.stopPropagation(); - clickEv.preventDefault(); - this.presBoxView?.modifierSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, clickEv.shiftKey || clickEv.ctrlKey || clickEv.metaKey, clickEv.ctrlKey || clickEv.metaKey, clickEv.shiftKey); - this.presBoxView?.activeItem && this.showRecording(this.presBoxView?.activeItem); - }); - } - }; - - /** - * Function to drag and drop the pres element to a diferent location - */ - startDrag = (e: PointerEvent) => { - this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false); - const miniView: boolean = this.toolbarWidth <= 100; - const activeItem = this.slideDoc; - const dragArray = this.presBoxView?._dragArray ?? []; - const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []); - if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.slideDoc); - dragData.treeViewDoc = this.presBox?._type_collection === CollectionViewType.Tree ? this.presBox : undefined; // this.DocumentView?.()?._props.treeViewDoc; - dragData.moveDocument = this._props.moveDocument; - const dragItem: HTMLElement[] = []; - const classesToRestore = new Map(); - if (dragArray.length === 1) { - const doc = this._itemRef.current || dragArray[0]; - if (doc) { - classesToRestore.set(doc, doc.className); - doc.className = miniView ? 'presItem-miniSlide' : 'presItem-slide'; - dragItem.push(doc); - } - } else if (dragArray.length >= 1) { - const doc = document.createElement('div'); - doc.className = 'presItem-multiDrag'; - doc.innerText = 'Move ' + (this.selectedArray?.size ?? 0) + ' slides'; - doc.style.position = 'absolute'; - doc.style.top = e.clientY + 'px'; - doc.style.left = e.clientX - 50 + 'px'; - dragItem.push(doc); - } - - if (activeItem) { - runInAction(() => { - this._dragging = true; - }); - DragManager.StartDocumentDrag( - dragItem.map(ele => ele), - dragData, - e.clientX, - e.clientY, - undefined, - action(() => { - Array.from(classesToRestore).forEach(pair => (pair[0].className = pair[1])); - this._dragging = false; - }) - ); - return true; - } - return false; - }; - - onPointerOver = () => { - document.removeEventListener('pointermove', this.onPointerMove); - document.addEventListener('pointermove', this.onPointerMove); - }; - - onPointerMove = (e: PointerEvent) => { - const slide = this._itemRef.current; - const dragIsPresItem = DragManager.docsBeingDragged.some(d => d.presentation_targetDoc); - if (slide && dragIsPresItem) { - const rect = slide.getBoundingClientRect(); - const y = e.clientY - rect.top; // y position within the element. - const height = slide.clientHeight; - const halfLine = height / 2; - if (y <= halfLine) { - slide.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`; - slide.style.borderBottom = '0px'; - } else if (y > halfLine) { - slide.style.borderTop = '0px'; - slide.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`; - } - } - document.removeEventListener('pointermove', this.onPointerMove); - }; - - onPointerLeave = () => { - const slide = this._itemRef.current; - if (slide) { - slide.style.borderTop = '0px'; - slide.style.borderBottom = '0px'; - } - document.removeEventListener('pointermove', this.onPointerMove); - }; - - @action - toggleProperties = () => { - if (SnappingManager.PropertiesWidth < 5) { - SnappingManager.SetPropertiesWidth(250); - } - }; - - removePresentationItem = undoable( - action((e: React.MouseEvent) => { - e.stopPropagation(); - if (this.presBox && this.indexInPres < (this.presBoxView?.itemIndex || 0)) { - this.presBox.itemIndex = (this.presBoxView?.itemIndex || 0) - 1; - } - this._props.removeDocument?.(this.slideDoc); - this.presBoxView?.removeFromSelectedArray(this.slideDoc); - this.removeAllRecordingInOverlay(); - }), - 'Remove doc from pres trail' - ); - - // set title of the individual pres slide - onSetValue = undoable( - action((value: string) => { - this.slideDoc.title = !value.trim().length ? '-untitled-' : value; - return true; - }), - 'set title of pres element' - ); - - /** - * Method called for updating the view of the currently selected document - * - * @param targetDoc - * @param activeItem - */ - @undoBatch - updateCapturedContainerLayout = (presTargetDoc: Doc, activeItem: Doc) => { - const targetDoc = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc; - activeItem.config_x = NumCast(targetDoc.x); - activeItem.config_y = NumCast(targetDoc.y); - activeItem.config_rotation = NumCast(targetDoc.rotation); - activeItem.config_width = NumCast(targetDoc.width); - activeItem.config_height = NumCast(targetDoc.height); - activeItem.config_pinLayout = !activeItem.config_pinLayout; - // activeItem.config_pinLayout = true; - }; - - /** - * Method called for updating the view of the currently selected document - * - * @param presTargetDoc - * @param activeItem - */ - updateCapturedViewContents = undoable( - action((presTargetDoc: Doc, activeItem: Doc) => { - const target = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc; - PinDocView(activeItem, { pinData: PresBox.pinDataTypes(target) }, target); - }), - 'updated captured view contents' - ); - - // a previously recorded video will have timecode defined - static videoIsRecorded = (activeItem: Opt) => 'layout_currentTimecode' in (DocCast(activeItem?.recording) ?? {}); - - removeAllRecordingInOverlay = () => Doc.MyOverlayDocs.filter(doc => doc.slides === this.slideDoc).forEach(Doc.RemFromMyOverlay); - - /// remove all videos that have been recorded from overlay (leave videso that are being recorded to avoid losing data) - static removeEveryExistingRecordingInOverlay = () => { - Doc.MyOverlayDocs.filter(doc => doc.slides !== null && PresElementBox.videoIsRecorded(DocCast(doc.slides))) // - .forEach(Doc.RemFromMyOverlay); - }; - - hideRecording = undoable( - action((e: React.MouseEvent) => { - e.stopPropagation(); - this.removeAllRecordingInOverlay(); - }), - 'hide video recording' - ); - - showRecording = undoable( - action((activeItem: Doc, iconClick: boolean = false) => { - // remove the overlays on switch *IF* not opened from the specific icon - if (!iconClick) PresElementBox.removeEveryExistingRecordingInOverlay(); - - activeItem.recording && Doc.AddToMyOverlay(DocCast(activeItem.recording)); - }), - 'show video recording' - ); - - startRecording = undoable( - action((e: React.MouseEvent, activeItem: Doc) => { - e.stopPropagation(); - if (PresElementBox.videoIsRecorded(activeItem)) { - // if we already have an existing recording - this.showRecording(activeItem, true); - // // if we already have an existing recording - // Doc.AddToMyOverlay(Cast(activeItem.recording, Doc, null)); - } else { - // we dont have any recording - // Remove every recording that already exists in overlay view - // this is a design decision to clear to focus in on the recoding mode - PresElementBox.removeEveryExistingRecordingInOverlay(); - - // create and add a recording to the slide - // make recording box appear in the bottom right corner of the screen - Doc.AddToMyOverlay( - (activeItem.recording = Docs.Create.WebCamDocument('', { - _width: 384, - _height: 216, - overlayX: window.innerWidth - 384 - 20, - overlayY: window.innerHeight - 216 - 20, - layout_hideDocumentButtonBar: true, - layout_hideDecorationTitle: true, - layout_hideOpenButton: true, - cloneFieldFilter: new List(['isSystem']), - slides: activeItem, // attach the slide to the recording - })) - ); - } - }), - 'start video recording' - ); - - @undoBatch - lfg = (e: React.MouseEvent) => { - e.stopPropagation(); - // TODO: fix this bug - // const { toggleChildrenRun } = this.slideDoc; - TreeView.ToggleChildrenRun.get(this.slideDoc)?.(); - - // call this.slideDoc.recurChildren() to get all the children - // if (iconClick) PresElementBox.showVideo = false; - }; - - @computed - get toolbarWidth(): number { - const presBoxDocView = DocumentView.getDocumentView(this.presBox); - const width = NumCast(this.presBox?._width); - return presBoxDocView ? presBoxDocView._props.PanelWidth() : width || 300; - } - - @computed get presButtons() { - const { presBox, targetDoc, slideDoc: activeItem } = this; - const presBoxColor = StrCast(presBox?._backgroundColor); - const presColorBool = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false; - const hasChildren = BoolCast(this.slideDoc?.hasChildren); - - const items: JSX.Element[] = []; - - items.push( - Update captured doc layout
}> -
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedContainerLayout(targetDoc, activeItem), true)} - style={{ opacity: activeItem.config_pinLayout ? 1 : 0.5, fontWeight: 700, display: 'flex' }}> - L -
- - ); - items.push( - Update captured doc content}> -
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))} - style={{ opacity: activeItem.config_pinData || activeItem.config_pinView ? 1 : 0.5, fontWeight: 700, display: 'flex' }}> - C -
-
- ); - items.push( - {this.videoRecordingIsInOverlay ? 'Hide Recording' : `${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}}> -
(this.videoRecordingIsInOverlay ? this.hideRecording(e) : this.startRecording(e, activeItem))} style={{ fontWeight: 700 }}> - e.stopPropagation()} /> -
-
- ); - if (this.indexInPres !== 0) { - items.push( - - {!activeItem.presentation_groupWithUp - ? 'Not grouped with previous slide (click to group)' - : activeItem.presentation_groupWithUp === 1 - ? 'Run simultaneously with previous slide (click again to run after)' - : 'Run after previous slide (click to ungroup from previous)'} - - }> -
{ - activeItem.presentation_groupWithUp = (NumCast(activeItem.presentation_groupWithUp) + 1) % 3; - }} - style={{ - zIndex: 1000 - this.indexInPres, - fontWeight: 700, - backgroundColor: activeItem.presentation_groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined, - outline: NumCast(activeItem.presentation_groupWithUp) > 1 ? 'solid black 1px' : undefined, - height: activeItem.presentation_groupWithUp ? 53 : 18, - transform: activeItem.presentation_groupWithUp ? 'translate(0, -17px)' : undefined, - }}> -
- e.stopPropagation()} /> -
-
-
- ); - } - items.push( - {this.slideDoc.presentation_expandInlineButton ? 'Minimize' : 'Expand'}}> -
{ - e.stopPropagation(); - this.presExpandDocumentClick(); - }}> - e.stopPropagation()} /> -
-
- ); - if (!Doc.noviceMode && hasChildren) { - // TODO: replace with if treeveiw, has childrenDocs - items.push( - Run child processes (tree only)}> -
{ - e.stopPropagation(); - this.lfg(e); - }} - style={{ fontWeight: 700 }}> - e.stopPropagation()} /> -
-
- ); - } - items.push( - Remove from presentation}> -
- e.stopPropagation()} /> -
-
- ); - items.push( - Customize Slide}> -
{ - this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false); - PresBox.Instance.navigateToActiveItem(); - PresBox.Instance.openProperties(); - PresBox.Instance.slideToModify = this.Document; - }}> - e.stopPropagation()} /> -
-
- ); - return items; - } - - @computed get mainItem() { - const { presBox, slideDoc: activeItem } = this; - const isSelected: boolean = !!this.selectedArray?.has(activeItem); - const isCurrent: boolean = this.presBox?._itemIndex === this.indexInPres; - const miniView: boolean = this.toolbarWidth <= 110; - const presBoxColor: string = StrCast(presBox?._backgroundColor); - const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false; - - return ( -
{ - this.toggleProperties(); - this.presBoxView?.regularSelect(activeItem, this._itemRef.current!, this._dragRef.current!, false); - })} - onPointerOver={this.onPointerOver} - onPointerLeave={this.onPointerLeave} - onPointerDown={this.headerDown}> - {miniView ? ( -
- {`${this.indexInPres + 1}.`} -
- ) : ( -
-
-
{ - e.stopPropagation(); - if (this._itemRef.current && this._dragRef.current) { - this.presBoxView?.modifierSelect(activeItem, this._itemRef.current, this._dragRef.current, true, false, false); - } - }} - onClick={e => e.stopPropagation()}>{`${this.indexInPres + 1}. `}
- StrCast(activeItem.title)} SetValue={this.onSetValue} /> -
- {/*
{"Movement speed"}
}>
{this.transition}
*/} - {/*
{"Duration"}
}>
{this.duration}
*/} -
- {...this.presButtons} -
- {this.renderEmbeddedInline} -
- )} -
- ); - } - - render() { - return !(this.slideDoc instanceof Doc) || this.targetDoc instanceof Promise ? null : this.mainItem; - } -} - -Docs.Prototypes.TemplateMap.set(DocumentType.PRESELEMENT, { - layout: { view: PresElementBox, dataField: 'data' }, - options: { acl: '', title: 'pres element template', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' }, -}); diff --git a/src/client/views/nodes/trails/PresSlideBox.scss b/src/client/views/nodes/trails/PresSlideBox.scss new file mode 100644 index 000000000..9ac2b5a94 --- /dev/null +++ b/src/client/views/nodes/trails/PresSlideBox.scss @@ -0,0 +1,308 @@ +$light-blue: #aeddf8; +$dark-blue: #5b9fdd; +$light-background: #ececec; +$slide-background: #d5dce2; +$slide-active: #5b9fdd; + +.testingv2 { + background-color: red; +} + +.presItem-container { + cursor: grab; + display: flex; + grid-template-columns: 20px auto; + font-family: Roboto; + letter-spacing: normal; + position: relative; + pointer-events: all; + width: 100%; + height: 100%; + font-weight: 400; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + align-items: center; + + // .presItem-number { + // margin-top: 3.5px; + // font-size: 12px; + // font-weight: 700; + // text-align: center; + // justify-self: center; + // align-self: flex-start; + // position: relative; + // display: inline-block; + // overflow: hidden; + // } +} + +.presItem-slide { + position: relative; + height: 100%; + width: 100%; + border-bottom: 0.5px solid grey; + display: flex; + align-items: center; + + .presItem-number { + cursor: pointer; + &:hover { + background-color: $light-blue; + } + } + .presItem-name { + display: flex; + min-width: 20px; + z-index: 300; + top: 2px; + align-self: center; + font-size: 11px; + font-family: Roboto; + font-weight: 500; + position: relative; + padding-left: 10px; + padding-right: 10px; + letter-spacing: normal; + width: max-content; + text-overflow: ellipsis; + overflow: hidden; + white-space: pre; + } + + .presItem-docName { + min-width: 20px; + z-index: 300; + align-self: center; + font-size: 9px; + font-family: Roboto; + font-weight: 300; + position: relative; + padding-left: 10px; + padding-right: 10px; + letter-spacing: normal; + width: max-content; + text-overflow: ellipsis; + overflow: hidden; + white-space: pre; + grid-row: 2; + grid-column: 1/6; + } + + .presItem-time { + align-self: center; + position: relative; + padding-right: 10px; + top: 1px; + font-size: 10; + font-weight: 300; + font-family: Roboto; + z-index: 300; + letter-spacing: normal; + } + + .presItem-embedded { + overflow: hidden; + grid-row: 3; + grid-column: 1/8; + position: relative; + display: inline-block; + } + + .presItem-embeddedMask { + width: 100%; + height: 100%; + position: absolute; + border-radius: 3px; + top: 0; + left: 0; + z-index: 1; + overflow: hidden; + } + + .presItem-slideButtons { + display: flex; + position: absolute; + width: max-content; + justify-self: right; + justify-content: flex-end; + + .slideButton { + cursor: pointer; + position: relative; + border-radius: 100px; + z-index: 300; + width: 18px; + height: 18px; + display: flex; + font-size: 12px; + justify-self: center; + align-self: center; + background-color: rgba(0, 0, 0, 0.5); + color: white; + justify-content: center; + align-items: center; + transition: 0.2s; + margin-right: 3px; + } + + .slideButton:hover { + background-color: rgba(0, 0, 0, 1); + transform: scale(1.2); + } + } +} + +// .presItem-slide:hover { +// .presItem-slideButtons { +// display: flex; +// grid-column: 7; +// grid-row: 1/3; +// width: max-content; +// justify-self: right; +// justify-content: flex-end; + +// .slideButton { +// cursor: pointer; +// position: relative; +// border-radius: 100px; +// z-index: 300; +// width: 18px; +// height: 18px; +// display: flex; +// font-size: 12px; +// justify-self: center; +// align-self: center; +// background-color: rgba(0, 0, 0, 0.5); +// color: white; +// justify-content: center; +// align-items: center; +// transition: 0.2s; +// margin-right: 3px; +// } + +// .slideButton:hover { +// background-color: rgba(0, 0, 0, 1); +// transform: scale(1.2); +// } +// } +// } + +.presItem-slide.active { + //box-shadow: 0 0 0px 2.5px $dark-blue; + border: $dark-blue solid 2.5px; +} + +.presItem-slide.group { + border-radius: 5px; +} + +.presItem-slide.activeGroup { + border-radius: 5px; + box-shadow: 0 0 0px 2.5px $dark-blue; +} + +.presItem-groupSlideContainer { + position: absolute; + /* grid-row: 3; */ + /* grid-column: 1/8; */ + top: 28; + display: block; + background: #92adb9; + width: 100%; + height: 10px; + border-radius: 0px 0px 5px 5px; + height: calc(100% - 28px); + display: grid; + grid-template-rows: auto auto auto; + grid-template-columns: 100%; + justify-items: left; + align-items: center; +} + +.presItem-groupSlide { + position: relative; + background-color: #d5dce2; + border-radius: 5px; + height: calc(100% - 7px); + width: calc(100% - 20px); + left: 15px; + /* height: 20px; */ + /* width: calc(100% - 19px); */ + display: flex; + grid-template-rows: 16px 10px auto; + grid-template-columns: max-content max-content max-content max-content auto; +} + +.presItem-multiDrag { + font-family: Roboto; + font-weight: 600; + color: white; + text-align: center; + justify-content: center; + align-content: center; + width: 100px; + height: 30px; + position: absolute; + background-color: $dark-blue; + z-index: 4000; + border-radius: 10px; + box-shadow: black 0.4vw 0.4vw 0.8vw; + line-height: 30px; +} + +.presItem-miniSlide { + font-weight: 700; + font-size: 12; + grid-column: 1/8; + align-self: center; + justify-self: center; + background-color: #d5dce2; + width: 26px; + text-align: center; + height: 26px; + line-height: 28px; + border-radius: 100%; +} + +.presItem-miniSlide.active { + box-shadow: 0 0 0px 1.5px $dark-blue; +} + +.expandButton { + cursor: pointer; + position: absolute; + border-radius: 100px; + bottom: 0; + left: -18; + z-index: 300; + width: 15px; + height: 15px; + display: flex; + font-size: 12px; + justify-self: center; + align-self: center; + background-color: #92adb9; + color: white; + justify-content: center; + align-items: center; + transition: 0.2s; + margin-right: 3px; +} + +.expandButton:hover { + background-color: rgba(0, 0, 0, 1); + transform: scale(1.2); +} + +.presItem-groupNum { + color: #d5dce2; + position: absolute; + left: -15px; + top: 1; + font-weight: 600; + font-size: 12; +} diff --git a/src/client/views/nodes/trails/PresSlideBox.tsx b/src/client/views/nodes/trails/PresSlideBox.tsx new file mode 100644 index 000000000..3dbb3da88 --- /dev/null +++ b/src/client/views/nodes/trails/PresSlideBox.tsx @@ -0,0 +1,628 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Tooltip } from '@mui/material'; +import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { returnFalse, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils'; +import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; +import { Id } from '../../../../fields/FieldSymbols'; +import { List } from '../../../../fields/List'; +import { BoolCast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; +import { emptyFunction } from '../../../../Utils'; +import { Docs } from '../../../documents/Documents'; +import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; +import { DragManager } from '../../../util/DragManager'; +import { SnappingManager } from '../../../util/SnappingManager'; +import { Transform } from '../../../util/Transform'; +import { undoable, undoBatch } from '../../../util/UndoManager'; +import { TreeView } from '../../collections/TreeView'; +import { ViewBoxBaseComponent } from '../../DocComponent'; +import { EditableView } from '../../EditableView'; +import { Colors } from '../../global/globalEnums'; +import { PinDocView } from '../../PinFuncs'; +import { StyleProp } from '../../StyleProp'; +import { returnEmptyDocViewList } from '../../StyleProvider'; +import { DocumentView } from '../DocumentView'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { PresBox } from './PresBox'; +import './PresSlideBox.scss'; +import { PresMovement } from './PresEnums'; +/** + * This class models the view a document added to presentation will have in the presentation. + * It involves some functionality for its buttons and options. + */ +@observer +export class PresSlideBox extends ViewBoxBaseComponent() { + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(PresSlideBox, fieldKey); + } + private _itemRef: React.RefObject = React.createRef(); + private _dragRef: React.RefObject = React.createRef(); + private _titleRef: React.RefObject = React.createRef(); + private _heightDisposer: IReactionDisposer | undefined; + readonly expandViewHeight = 100; + readonly collapsedHeight = 35; + + constructor(props: FieldViewProps) { + super(props); + makeObservable(this); + } + + @observable _dragging = false; + + // the presentation view that renders this slide + @computed get presBoxView() { + return this.DocumentView?.() + .containerViewPath?.() + .slice() + .reverse() + .find(dv => dv?.ComponentView instanceof PresBox)?.ComponentView as Opt; + } + + // the presentation view document that renders this slide + @computed get presBox() { + return this.presBoxView?.Document; + } + + // Since this node is being rendered with a template, this method retrieves + // the actual slide being rendered from the auto-generated rendering template + @computed get slideDoc() { + return this.rootDoc; + } + + // this is the document in the workspaces that is targeted by the slide + @computed get targetDoc() { + return DocCast(this.slideDoc.presentation_targetDoc, this.slideDoc)!; + } + + // computes index of this presentation slide in the presBox list + @computed get indexInPres() { + return this.presBoxView?.SlideIndex(this.slideDoc) ?? 0; + } + + @computed get selectedArray() { + return this.presBoxView?.selectedArray; + } + + @computed get videoRecordingIsInOverlay() { + return Doc.MyOverlayDocs.some(doc => doc.slides === this.slideDoc); + } + + componentDidMount() { + this.layoutDoc.layout_hideLinkButton = true; + this._heightDisposer = reaction( + () => ({ expand: this.slideDoc.presentation_expandInlineButton, height: this.collapsedHeight }), + ({ expand, height }) => { + this.layoutDoc._height = height + (expand ? this.expandViewHeight : 0); + }, + { fireImmediately: true } + ); + } + componentWillUnmount() { + this._heightDisposer?.(); + } + + presExpandDocumentClick = () => { + this.slideDoc.presentation_expandInlineButton = !this.slideDoc.presentation_expandInlineButton; + }; + embedHeight = () => this.collapsedHeight + this.expandViewHeight; + embedWidth = () => this._props.PanelWidth() / 2; + // prettier-ignore + styleProvider = ( doc: Doc | undefined, props: Opt, property: string ) => + (property === StyleProp.Opacity ? 1 : this._props.styleProvider?.(doc, props, property)); + /** + * The function that is responsible for rendering a preview or not for this + * presentation element. + */ + @computed get renderEmbeddedInline() { + return !this.slideDoc.presentation_expandInlineButton || !this.targetDoc ? null : ( +
+ +
+ ); + } + + @computed get renderGroupSlides() { + const childDocs = DocListCast(this.targetDoc.data); + const groupSlides = childDocs.map((doc: Doc, ind: number) => ( +
{ + e.stopPropagation(); + e.preventDefault(); + this.presBoxView?.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, e.shiftKey || e.ctrlKey || e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); + this.presExpandDocumentClick(); + }}> +
{`${ind + 1}.`}
+
+ StrCast(doc.title)} + SetValue={(value: string) => { + doc.title = !value.trim().length ? '-untitled-' : value; + return true; + }} + /> +
+
+ )); + return groupSlides; + } + + @computed get transition() { + let transitionInS: number; + if (this.slideDoc.presentation_transition) transitionInS = NumCast(this.slideDoc.presentation_transition) / 1000; + else transitionInS = 0.5; + return this.slideDoc.presentation_movement === PresMovement.Jump || this.slideDoc.presentation_movement === PresMovement.None ? null : 'M: ' + transitionInS + 's'; + } + + @action + headerDown = (e: React.PointerEvent) => { + const element = e.target as HTMLDivElement; + e.stopPropagation(); + e.preventDefault(); + if (element && !(e.ctrlKey || e.metaKey || e.button === 2)) { + this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false); + setupMoveUpEvents(this, e, this.startDrag, emptyFunction, clickEv => { + clickEv.stopPropagation(); + clickEv.preventDefault(); + this.presBoxView?.modifierSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, clickEv.shiftKey || clickEv.ctrlKey || clickEv.metaKey, clickEv.ctrlKey || clickEv.metaKey, clickEv.shiftKey); + this.presBoxView?.activeItem && this.showRecording(this.presBoxView?.activeItem); + }); + } + }; + + /** + * Function to drag and drop the pres element to a diferent location + */ + startDrag = (e: PointerEvent) => { + this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false); + const miniView: boolean = this.toolbarWidth <= 100; + const activeItem = this.slideDoc; + const dragArray = this.presBoxView?._dragArray ?? []; + const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []); + if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.slideDoc); + dragData.treeViewDoc = this.presBox?._type_collection === CollectionViewType.Tree ? this.presBox : undefined; // this.DocumentView?.()?._props.treeViewDoc; + dragData.moveDocument = this._props.moveDocument; + const dragItem: HTMLElement[] = []; + const classesToRestore = new Map(); + if (dragArray.length === 1) { + const doc = this._itemRef.current || dragArray[0]; + if (doc) { + classesToRestore.set(doc, doc.className); + doc.className = miniView ? 'presItem-miniSlide' : 'presItem-slide'; + dragItem.push(doc); + } + } else if (dragArray.length >= 1) { + const doc = document.createElement('div'); + doc.className = 'presItem-multiDrag'; + doc.innerText = 'Move ' + (this.selectedArray?.size ?? 0) + ' slides'; + doc.style.position = 'absolute'; + doc.style.top = e.clientY + 'px'; + doc.style.left = e.clientX - 50 + 'px'; + dragItem.push(doc); + } + + if (activeItem) { + runInAction(() => { + this._dragging = true; + }); + DragManager.StartDocumentDrag( + dragItem.map(ele => ele), + dragData, + e.clientX, + e.clientY, + undefined, + action(() => { + Array.from(classesToRestore).forEach(pair => (pair[0].className = pair[1])); + this._dragging = false; + }) + ); + return true; + } + return false; + }; + + onPointerOver = () => { + document.removeEventListener('pointermove', this.onPointerMove); + document.addEventListener('pointermove', this.onPointerMove); + }; + + onPointerMove = (e: PointerEvent) => { + const slide = this._itemRef.current; + const dragIsPresItem = DragManager.docsBeingDragged.some(d => d.presentation_targetDoc); + if (slide && dragIsPresItem) { + const rect = slide.getBoundingClientRect(); + const y = e.clientY - rect.top; // y position within the element. + const height = slide.clientHeight; + const halfLine = height / 2; + if (y <= halfLine) { + slide.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`; + slide.style.borderBottom = '0px'; + } else if (y > halfLine) { + slide.style.borderTop = '0px'; + slide.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`; + } + } + document.removeEventListener('pointermove', this.onPointerMove); + }; + + onPointerLeave = () => { + const slide = this._itemRef.current; + if (slide) { + slide.style.borderTop = '0px'; + slide.style.borderBottom = '0px'; + } + document.removeEventListener('pointermove', this.onPointerMove); + }; + + @action + toggleProperties = () => { + if (SnappingManager.PropertiesWidth < 5) { + SnappingManager.SetPropertiesWidth(250); + } + }; + + removePresentationItem = undoable( + action((e: React.MouseEvent) => { + e.stopPropagation(); + if (this.presBox && this.indexInPres < (this.presBoxView?.itemIndex || 0)) { + this.presBox.itemIndex = (this.presBoxView?.itemIndex || 0) - 1; + } + this._props.removeDocument?.(this.slideDoc); + this.presBoxView?.removeFromSelectedArray(this.slideDoc); + this.removeAllRecordingInOverlay(); + }), + 'Remove doc from pres trail' + ); + + // set title of the individual pres slide + onSetValue = undoable( + action((value: string) => { + this.slideDoc.title = !value.trim().length ? '-untitled-' : value; + return true; + }), + 'set title of pres element' + ); + + /** + * Method called for updating the view of the currently selected document + * + * @param targetDoc + * @param activeItem + */ + @undoBatch + updateCapturedContainerLayout = (presTargetDoc: Doc, activeItem: Doc) => { + const targetDoc = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc; + activeItem.config_x = NumCast(targetDoc.x); + activeItem.config_y = NumCast(targetDoc.y); + activeItem.config_rotation = NumCast(targetDoc.rotation); + activeItem.config_width = NumCast(targetDoc.width); + activeItem.config_height = NumCast(targetDoc.height); + activeItem.config_pinLayout = !activeItem.config_pinLayout; + // activeItem.config_pinLayout = true; + }; + + /** + * Method called for updating the view of the currently selected document + * + * @param presTargetDoc + * @param activeItem + */ + updateCapturedViewContents = undoable( + action((presTargetDoc: Doc, activeItem: Doc) => { + const target = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc; + PinDocView(activeItem, { pinData: PresBox.pinDataTypes(target) }, target); + }), + 'updated captured view contents' + ); + + // a previously recorded video will have timecode defined + static videoIsRecorded = (activeItem: Opt) => 'layout_currentTimecode' in (DocCast(activeItem?.recording) ?? {}); + + removeAllRecordingInOverlay = () => Doc.MyOverlayDocs.filter(doc => doc.slides === this.slideDoc).forEach(Doc.RemFromMyOverlay); + + /// remove all videos that have been recorded from overlay (leave videso that are being recorded to avoid losing data) + static removeEveryExistingRecordingInOverlay = () => { + Doc.MyOverlayDocs.filter(doc => doc.slides !== null && PresSlideBox.videoIsRecorded(DocCast(doc.slides))) // + .forEach(Doc.RemFromMyOverlay); + }; + + hideRecording = undoable( + action((e: React.MouseEvent) => { + e.stopPropagation(); + this.removeAllRecordingInOverlay(); + }), + 'hide video recording' + ); + + showRecording = undoable( + action((activeItem: Doc, iconClick: boolean = false) => { + // remove the overlays on switch *IF* not opened from the specific icon + if (!iconClick) PresSlideBox.removeEveryExistingRecordingInOverlay(); + + DocCast(activeItem.recording) && Doc.AddToMyOverlay(DocCast(activeItem.recording)!); + }), + 'show video recording' + ); + + startRecording = undoable( + action((e: React.MouseEvent, activeItem: Doc) => { + e.stopPropagation(); + if (PresSlideBox.videoIsRecorded(activeItem)) { + // if we already have an existing recording + this.showRecording(activeItem, true); + // // if we already have an existing recording + // Doc.AddToMyOverlay(Cast(activeItem.recording, Doc, null)); + } else { + // we dont have any recording + // Remove every recording that already exists in overlay view + // this is a design decision to clear to focus in on the recoding mode + PresSlideBox.removeEveryExistingRecordingInOverlay(); + + // create and add a recording to the slide + // make recording box appear in the bottom right corner of the screen + Doc.AddToMyOverlay( + (activeItem.recording = Docs.Create.WebCamDocument('', { + _width: 384, + _height: 216, + overlayX: window.innerWidth - 384 - 20, + overlayY: window.innerHeight - 216 - 20, + layout_hideDocumentButtonBar: true, + layout_hideDecorationTitle: true, + layout_hideOpenButton: true, + cloneFieldFilter: new List(['isSystem']), + slides: activeItem, // attach the slide to the recording + })) + ); + } + }), + 'start video recording' + ); + + @undoBatch + lfg = (e: React.MouseEvent) => { + e.stopPropagation(); + // TODO: fix this bug + // const { toggleChildrenRun } = this.slideDoc; + TreeView.ToggleChildrenRun.get(this.slideDoc)?.(); + + // call this.slideDoc.recurChildren() to get all the children + // if (iconClick) PresSlideBox.showVideo = false; + }; + + @computed + get toolbarWidth(): number { + const presBoxDocView = DocumentView.getDocumentView(this.presBox); + const width = NumCast(this.presBox?._width); + return presBoxDocView ? presBoxDocView._props.PanelWidth() : width || 300; + } + + @computed get presButtons() { + const { presBox, targetDoc, slideDoc: activeItem } = this; + const presBoxColor = StrCast(presBox?._backgroundColor); + const presColorBool = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false; + const hasChildren = BoolCast(this.slideDoc?.hasChildren); + + const items: JSX.Element[] = []; + + items.push( + Update captured doc layout}> +
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedContainerLayout(targetDoc, activeItem), true)} + style={{ opacity: activeItem.config_pinLayout ? 1 : 0.5, fontWeight: 700, display: 'flex' }}> + L +
+
+ ); + items.push( + Update captured doc content}> +
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))} + style={{ opacity: activeItem.config_pinData || activeItem.config_pinView ? 1 : 0.5, fontWeight: 700, display: 'flex' }}> + C +
+
+ ); + items.push( + {this.videoRecordingIsInOverlay ? 'Hide Recording' : `${PresSlideBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}}> +
(this.videoRecordingIsInOverlay ? this.hideRecording(e) : this.startRecording(e, activeItem))} style={{ fontWeight: 700 }}> + e.stopPropagation()} /> +
+
+ ); + if (this.indexInPres !== 0) { + items.push( + + {!activeItem.presentation_groupWithUp + ? 'Not grouped with previous slide (click to group)' + : activeItem.presentation_groupWithUp === 1 + ? 'Run simultaneously with previous slide (click again to run after)' + : 'Run after previous slide (click to ungroup from previous)'} + + }> +
{ + activeItem.presentation_groupWithUp = (NumCast(activeItem.presentation_groupWithUp) + 1) % 3; + }} + style={{ + zIndex: 1000 - this.indexInPres, + fontWeight: 700, + backgroundColor: activeItem.presentation_groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined, + outline: NumCast(activeItem.presentation_groupWithUp) > 1 ? 'solid black 1px' : undefined, + height: activeItem.presentation_groupWithUp ? 53 : 18, + transform: activeItem.presentation_groupWithUp ? 'translate(0, -17px)' : undefined, + }}> +
+ e.stopPropagation()} /> +
+
+
+ ); + } + items.push( + {this.slideDoc.presentation_expandInlineButton ? 'Minimize' : 'Expand'}}> +
{ + e.stopPropagation(); + this.presExpandDocumentClick(); + }}> + e.stopPropagation()} /> +
+
+ ); + if (!Doc.noviceMode && hasChildren) { + // TODO: replace with if treeveiw, has childrenDocs + items.push( + Run child processes (tree only)}> +
{ + e.stopPropagation(); + this.lfg(e); + }} + style={{ fontWeight: 700 }}> + e.stopPropagation()} /> +
+
+ ); + } + items.push( + Remove from presentation}> +
+ e.stopPropagation()} /> +
+
+ ); + items.push( + Customize Slide}> +
{ + this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false); + PresBox.Instance.navigateToActiveItem(); + PresBox.Instance.openProperties(); + PresBox.Instance.slideToModify = this.Document; + }}> + e.stopPropagation()} /> +
+
+ ); + return items; + } + + @computed get mainItem() { + const { presBox, slideDoc: activeItem } = this; + const isSelected: boolean = !!this.selectedArray?.has(activeItem); + const isCurrent: boolean = this.presBox?._itemIndex === this.indexInPres; + const miniView: boolean = this.toolbarWidth <= 110; + const presBoxColor: string = StrCast(presBox?._backgroundColor); + const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false; + + return ( +
{ + this.toggleProperties(); + this.presBoxView?.regularSelect(activeItem, this._itemRef.current!, this._dragRef.current!, false); + })} + onPointerOver={this.onPointerOver} + onPointerLeave={this.onPointerLeave} + onPointerDown={this.headerDown}> + {miniView ? ( +
+ {`${this.indexInPres + 1}.`} +
+ ) : ( +
+
+
{ + e.stopPropagation(); + if (this._itemRef.current && this._dragRef.current) { + this.presBoxView?.modifierSelect(activeItem, this._itemRef.current, this._dragRef.current, true, false, false); + } + }} + onClick={e => e.stopPropagation()}>{`${this.indexInPres + 1}. `}
+ StrCast(activeItem.title)} SetValue={this.onSetValue} /> +
+ {/*
{"Movement speed"}
}>
{this.transition}
*/} + {/*
{"Duration"}
}>
{this.duration}
*/} +
+ {...this.presButtons} +
+ {this.renderEmbeddedInline} +
+ )} +
+ ); + } + + render() { + return !(this.slideDoc instanceof Doc) || this.targetDoc instanceof Promise ? null : this.mainItem; + } +} + +Docs.Prototypes.TemplateMap.set(DocumentType.PRESSLIDE, { + layout: { view: PresSlideBox, dataField: 'data' }, + options: { acl: '', title: 'presSlide', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true }, +}); diff --git a/src/client/views/nodes/trails/index.ts b/src/client/views/nodes/trails/index.ts index 7b18974df..a5bc55221 100644 --- a/src/client/views/nodes/trails/index.ts +++ b/src/client/views/nodes/trails/index.ts @@ -1,3 +1,3 @@ export * from './PresBox'; -export * from './PresElementBox'; +export * from './PresSlideBox'; export * from './PresEnums'; -- cgit v1.2.3-70-g09d2