aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Utils.ts3
-rw-r--r--src/client/documents/Documents.ts2
-rw-r--r--src/client/util/SharingManager.tsx30
-rw-r--r--src/client/views/DocumentButtonBar.tsx51
-rw-r--r--src/client/views/EditableView.tsx2
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/PropertiesView.tsx15
-rw-r--r--src/client/views/collections/CollectionMenu.tsx68
-rw-r--r--src/client/views/collections/CollectionSchemaHeaders.tsx4
-rw-r--r--src/client/views/collections/CollectionTreeView.scss112
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx810
-rw-r--r--src/client/views/collections/TabDocView.tsx50
-rw-r--r--src/client/views/collections/TreeView.scss115
-rw-r--r--src/client/views/collections/TreeView.tsx760
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss10
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx24
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx3
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx8
-rw-r--r--src/client/views/nodes/DocumentView.tsx4
-rw-r--r--src/client/views/nodes/FilterBox.tsx12
-rw-r--r--src/client/views/nodes/PresBox.scss336
-rw-r--r--src/client/views/nodes/PresBox.tsx459
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx14
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx6
-rw-r--r--src/client/views/presentationview/PresElementBox.scss1
-rw-r--r--src/client/views/presentationview/PresElementBox.tsx42
-rw-r--r--src/client/views/search/SearchBox.tsx6
-rw-r--r--src/fields/Doc.ts10
-rw-r--r--src/fields/Schema.ts2
-rw-r--r--src/fields/util.ts14
31 files changed, 1571 insertions, 1410 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 7dff1ac55..cc7ee9537 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -518,7 +518,8 @@ export function clearStyleSheetRules(sheet: any) {
return false;
}
-export function simulateMouseClick(element: Element, x: number, y: number, sx: number, sy: number, rightClick = true) {
+export function simulateMouseClick(element: Element | null | undefined, x: number, y: number, sx: number, sy: number, rightClick = true) {
+ if (!element) return;
["pointerdown", "pointerup"].map(event => element.dispatchEvent(
new PointerEvent(event, {
view: window,
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ecb1342f4..f9e3add84 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -598,7 +598,7 @@ export namespace Docs {
viewDoc.author = Doc.CurrentUserEmail;
viewDoc.type !== DocumentType.LINK && DocUtils.MakeLinkToActiveAudio(viewDoc);
- if (Doc.UserDoc()?.defaultAclPrivate) viewDoc["ACL-Public"] = dataDoc["ACL-Public"] = "Not Shared";
+ if (Doc.UserDoc()?.defaultAclPrivate) viewDoc["acl-Public"] = dataDoc["acl-Public"] = "Not Shared";
return Doc.assign(viewDoc, delegateProps, true);
}
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 87c620d6d..7991022d2 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -152,13 +152,13 @@ export class SharingManager extends React.Component<{}> {
const target = targetDoc || this.targetDoc!;
const key = StrCast(group.groupName).replace(".", "_");
- const ACL = `ACL-${key}`;
+ const acl = `acl-${key}`;
const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document);
docs.forEach(doc => {
- doc.author === Doc.CurrentUserEmail && !doc[`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc);
- GetEffectiveAcl(doc) === AclAdmin && distributeAcls(ACL, permission as SharingPermissions, doc);
+ doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`acl-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc);
+ GetEffectiveAcl(doc) === AclAdmin && distributeAcls(acl, permission as SharingPermissions, doc);
if (key !== "Public") {
const members: string[] = JSON.parse(StrCast(group.members));
@@ -198,7 +198,7 @@ export class SharingManager extends React.Component<{}> {
}
else {
docs.forEach(doc => {
- if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls("ACL-Public", permission, doc);
+ if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls("acl-Public", permission, doc);
});
}
}
@@ -225,9 +225,9 @@ export class SharingManager extends React.Component<{}> {
removeGroup = (group: Doc) => {
if (group.docsShared) {
DocListCast(group.docsShared).forEach(doc => {
- const ACL = `ACL-${StrCast(group.groupName)}`;
+ const acl = `acl-${StrCast(group.groupName)}`;
- distributeAcls(ACL, SharingPermissions.None, doc);
+ distributeAcls(acl, SharingPermissions.None, doc);
const members: string[] = JSON.parse(StrCast(group.members));
const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
@@ -244,14 +244,14 @@ export class SharingManager extends React.Component<{}> {
const { user, notificationDoc } = recipient;
const target = targetDoc || this.targetDoc!;
const key = user.email.replace('.', '_');
- const ACL = `ACL-${key}`;
+ const acl = `acl-${key}`;
const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document);
docs.forEach(doc => {
- doc.author === Doc.CurrentUserEmail && !doc[`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc);
- GetEffectiveAcl(doc) === AclAdmin && distributeAcls(ACL, permission as SharingPermissions, doc);
+ doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`acl-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc);
+ GetEffectiveAcl(doc) === AclAdmin && distributeAcls(acl, permission as SharingPermissions, doc);
if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, doc);
else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.IndexOf((doc.aliasOf as Doc || doc), DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, (doc.aliasOf as Doc || doc));
@@ -445,8 +445,8 @@ export class SharingManager extends React.Component<{}> {
const commonKeys = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym]?.[AclSym] && Object.keys(doc[DataSym][AclSym])));
// the list of users shared with
- const userListContents: (JSX.Element | null)[] = users.filter(({ user }) => docs.length > 1 ? commonKeys.includes(`ACL-${user.email.replace('.', '_')}`) : true).map(({ user, notificationDoc, userColor }) => {
- const userKey = `ACL-${user.email.replace('.', '_')}`;
+ const userListContents: (JSX.Element | null)[] = users.filter(({ user }) => docs.length > 1 ? commonKeys.includes(`acl-${user.email.replace('.', '_')}`) : true).map(({ user, notificationDoc, userColor }) => {
+ const userKey = `acl-${user.email.replace('.', '_')}`;
const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey] : doc?.[DataSym]?.[AclSym]?.[userKey] === docs[0]?.[DataSym]?.[AclSym]?.[userKey]);
const permissions = uniform ? StrCast(targetDoc?.[userKey]) : "-multiple-";
@@ -503,7 +503,7 @@ export class SharingManager extends React.Component<{}> {
<span className={"padding"}>Me</span>
<div className="edit-actions">
<div className={"permissions-dropdown"}>
- {targetDoc?.[`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`]}
+ {targetDoc?.[`acl-${Doc.CurrentUserEmail.replace(".", "_")}`]}
</div>
</div>
</div>
@@ -514,12 +514,12 @@ export class SharingManager extends React.Component<{}> {
const publicDoc = new Doc;
publicDoc.groupName = "Public";
// the list of groups shared with
- const groupListMap = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`ACL-${StrCast(groupName).replace('.', '_')}`) : true);
+ const groupListMap = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`acl-${StrCast(groupName).replace('.', '_')}`) : true);
groupListMap.unshift(publicDoc);
const groupListContents = groupListMap.map(group => {
- const groupKey = `ACL-${StrCast(group.groupName)}`;
+ const groupKey = `acl-${StrCast(group.groupName)}`;
const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]);
- const permissions = uniform ? StrCast(targetDoc?.[`ACL-${StrCast(group.groupName)}`]) : "-multiple-";
+ const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.groupName)}`]) : "-multiple-";
return !permissions ? (null) : (
<div
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index b18b2302d..c5fd3c777 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -24,6 +24,7 @@ import { DocumentView } from './nodes/DocumentView';
import { GoogleRef } from "./nodes/formattedText/FormattedTextBox";
import { TemplateMenu } from "./TemplateMenu";
import React = require("react");
+import { PresBox } from './nodes/PresBox';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -183,24 +184,40 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
@computed
get pinButton() {
const targetDoc = this.view0?.props.Document;
- let isPinned = targetDoc && Doc.isDocPinned(targetDoc);
- // More than 1 document selected then all must be in presentation for isPinned to be true (then it will unpin all)
- if (SelectionManager.SelectedDocuments().length > 1) {
- SelectionManager.SelectedDocuments().forEach((docView: DocumentView) => {
- if (Doc.isDocPinned(docView.props.Document)) isPinned = true;
- else isPinned = false;
- });
- }
- return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div></>}>
+ const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
+ return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Pin to presentation"}</div></>}>
<div className="documentButtonBar-linker"
- style={{ backgroundColor: isPinned ? "white" : "", color: isPinned ? "black" : "white", border: isPinned ? "black 1px solid " : "" }}
- onClick={e => this.props.views().map(view => view && TabDocView.PinDoc(view.props.Document, isPinned))}>
- <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin"
- />
+ style={{ color: "white" }}
+ onClick={e => this.props.views().map(view => view && TabDocView.PinDoc(view.props.Document, false))}>
+ <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
</div></Tooltip>;
}
@computed
+ get pinWithViewButton() {
+ const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 17, transform: 'translate(0, 1px)' }} />;
+ const targetDoc = this.view0?.props.Document;
+ return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Pin with current view"}</div></>}>
+ <div className="documentButtonBar-linker"
+ onClick={e => {
+ if (targetDoc) {
+ TabDocView.PinDoc(targetDoc, false);
+ const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1];
+ const x = targetDoc._panX;
+ const y = targetDoc._panY;
+ const scale = targetDoc._viewScale;
+ activeDoc.presPinView = true;
+ activeDoc.presPinViewX = x;
+ activeDoc.presPinViewY = y;
+ activeDoc.presPinViewScale = scale;
+ }
+ }}>
+ {presPinWithViewIcon}
+ </div>
+ </Tooltip>;
+ }
+
+ @computed
get shareButton() {
const targetDoc = this.view0?.props.Document;
return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Open Sharing Manager"}</div></>}>
@@ -236,7 +253,8 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{`${CurrentUserUtils.propertiesWidth > 0 ? "Close" : "Open"} Properties Panel`}</div></>}>
<div className="documentButtonBar-linker" style={{ color: "white", cursor: "e-resize" }} onClick={action(e =>
CurrentUserUtils.propertiesWidth = CurrentUserUtils.propertiesWidth > 0 ? 0 : 250)}>
- <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="ellipsis-h" />
+ <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="ellipsis-h"
+ />
</div></Tooltip >;
}
@@ -336,6 +354,9 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
<div className="documentButtonBar-button">
{this.pinButton}
</div>
+ {/* <div className="documentButtonBar-button">
+ {this.pinWithViewButton}
+ </div> */}
{!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
{this.shareButton}
</div>}
@@ -356,4 +377,4 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
</div>} */}
</div>;
}
-}
+} \ No newline at end of file
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index cbbd78a20..d35271ffd 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -204,7 +204,7 @@ export class EditableView extends React.Component<EditableProps> {
setTimeout(() => this.props.autosuggestProps?.resetValue(), 0);
return this.props.contents instanceof ObjectField ? (null) :
<div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`} ref={this._ref}
- style={{ display: this.props.display, minHeight: "17px", whiteSpace: "nowrap", height: `${this.props.height ? this.props.height : "auto"}`, maxHeight: `${this.props.maxHeight}` }}
+ style={{ display: this.props.display, minHeight: "17px", whiteSpace: "nowrap", height: this.props.height || "auto", maxHeight: this.props.maxHeight }}
onClick={this.onClick} placeholder={this.props.placeholder}>
<span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize }}>{
this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index c2290dd09..595e07326 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -213,7 +213,7 @@ export class MainView extends React.Component {
}));
}
const pres = Docs.Create.PresDocument(new List<Doc>(),
- { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeStatus: "replaced", boxShadow: "0 0", system: true });
+ { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeStatus: "replaced", boxShadow: "0 0" });
CollectionDockingView.AddSplit(pres, "right");
this.userDoc.activePresentation = pres;
Doc.AddDocToList(this.userDoc.myPresentations as Doc, "data", pres);
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 53261d8de..0dd749b07 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -43,10 +43,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@computed get MAX_EMBED_HEIGHT() { return 200; }
- @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; }
+ @computed get selectedDoc() { console.log(this.selectedDocumentView?.rootDoc.title); return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; }
@computed get selectedDocumentView() {
- if (PresBox.Instance?._selectedArray.length) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
if (SelectionManager.SelectedDocuments().length) return SelectionManager.SelectedDocuments()[0];
+ if (PresBox.Instance && PresBox.Instance._selectedArray) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
return undefined;
}
@computed get isPres(): boolean {
@@ -168,7 +168,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
const rows: JSX.Element[] = [];
const noviceReqFields = ["author", "creationDate", "tags"];
const noviceLayoutFields = ["_curPage"];
- const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("ACL"))),
+ const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("acl"))),
...noviceReqFields, ...noviceLayoutFields];
for (const key of noviceKeys.sort()) {
const docvals = new Set<any>();
@@ -416,13 +416,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
// if (Doc.UserDoc().sidebarUsersDisplayed) {
// for (const [name, value] of Object.entries(sidebarUsersDisplayed!)) {
- // if (value === true && !this.selectedDoc![`ACL-${name.substring(8).replace(".", "_")}`]) tableEntries.push(this.sharingItem(name.substring(8), effectiveAcl, SharingPermissions.None));
+ // if (value === true && !this.selectedDoc![`acl-${name.substring(8).replace(".", "_")}`]) tableEntries.push(this.sharingItem(name.substring(8), effectiveAcl, SharingPermissions.None));
// }
// }
// })
// shifts the current user, owner, public to the top of the doc.
- tableEntries.unshift(this.sharingItem("Public", showAdmin, docs.every(doc => doc["ACL-Public"] === docs[0]["ACL-Public"]) ? (AclMap.get(target[AclSym]?.["ACL-Public"]) || SharingPermissions.None) : "-multiple-"));
+ tableEntries.unshift(this.sharingItem("Public", showAdmin, docs.every(doc => doc["acl-Public"] === docs[0]["acl-Public"]) ? (AclMap.get(target[AclSym]?.["acl-Public"]) || SharingPermissions.None) : "-multiple-"));
tableEntries.unshift(this.sharingItem("Me", showAdmin, docs.every(doc => doc.author === Doc.CurrentUserEmail) ? "Owner" : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? AclMap.get(effectiveAcls[0])! : "-multiple-"));
if (Doc.CurrentUserEmail !== target.author && docs.every(doc => doc.author === docs[0].author)) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, "Owner"));
@@ -1012,6 +1012,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
}
if (this.isPres) {
const selectedItem: boolean = PresBox.Instance?._selectedArray.length > 0;
+ const type = PresBox.Instance.activeItem?.type;
return <div className="propertiesView" style={{ width: this.props.width }}>
<div className="propertiesView-title" style={{ width: this.props.width }}>
Presentation
@@ -1038,7 +1039,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
{PresBox.Instance.transitionDropdown}
</div> : null}
</div>}
- {!selectedItem ? (null) : <div className="propertiesView-presTrails">
+ {!selectedItem || type === DocumentType.VID || type === DocumentType.AUDIO ? (null) : <div className="propertiesView-presTrails">
<div className="propertiesView-presTrails-title"
onPointerDown={action(() => this.openPresProgressivize = !this.openPresProgressivize)}
style={{ backgroundColor: this.openPresProgressivize ? "black" : "" }}>
@@ -1051,7 +1052,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
{PresBox.Instance.progressivizeDropdown}
</div> : null}
</div>}
- {!selectedItem ? (null) : <div className="propertiesView-presTrails">
+ {!selectedItem || (type !== DocumentType.COL && type !== DocumentType.VID && type !== DocumentType.AUDIO) ? (null) : <div className="propertiesView-presTrails">
<div className="propertiesView-presTrails-title"
onPointerDown={action(() => { this.openSlideOptions = !this.openSlideOptions; })}
style={{ backgroundColor: this.openSlideOptions ? "black" : "" }}>
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 5ac0a8ff0..4c5a71ace 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -369,9 +369,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
}
@computed get selectedDocumentView() {
- if (SelectionManager.SelectedDocuments().length) {
- return SelectionManager.SelectedDocuments()[0];
- } else { return undefined; }
+ return SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
}
@computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
@computed get notACollection() {
@@ -387,13 +385,38 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
const targetDoc = this.selectedDoc;
const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
return !targetDoc ? (null) : <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div>} placement="top">
- <button className="antimodeMenu-button" style={{ backgroundColor: isPinned ? "121212" : undefined, borderRight: "1px solid gray" }}
+ <button className="antimodeMenu-button" style={{ backgroundColor: isPinned ? "121212" : undefined, borderLeft: "1px solid gray" }}
onClick={e => TabDocView.PinDoc(targetDoc, isPinned)}>
<FontAwesomeIcon className="documentdecorations-icon" size="lg" icon="map-pin" />
</button>
</Tooltip>;
}
+ @computed
+ get pinWithViewButton() {
+ const presPinWithViewIcon = <img src={`/assets/pinWithView.png`} style={{ margin: "auto", width: 19 }} />;
+ const targetDoc = this.selectedDoc;
+ return (!targetDoc || (targetDoc._viewType !== CollectionViewType.Freeform && targetDoc.type !== DocumentType.IMG)) ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Pin to presentation trail with current view"}</div></>} placement="top">
+ <button className="antimodeMenu-button" style={{ borderRight: "1px solid gray", borderLeft: "1px solid gray", justifyContent: 'center' }}
+ onClick={e => {
+ if (targetDoc) {
+ TabDocView.PinDoc(targetDoc, false);
+ const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1];
+ const x = targetDoc._panX;
+ const y = targetDoc._panY;
+ const scale = targetDoc._viewScale;
+ activeDoc.presPinView = true;
+ activeDoc.presPinViewX = x;
+ activeDoc.presPinViewY = y;
+ activeDoc.presPinViewScale = scale;
+ }
+ }}>
+ {presPinWithViewIcon}
+ </button>
+ </Tooltip>;
+ }
+
+
@undoBatch
onAlias = () => {
if (this.selectedDoc && this.selectedDocumentView) {
@@ -449,33 +472,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
</Tooltip>;
}
- @computed
- get pinWithViewButton() {
- const targetDoc = this.selectedDoc;
- if (targetDoc) {
- const x = targetDoc._panX;
- const y = targetDoc._panY;
- const scale = targetDoc._viewScale;
- }
- return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Pin to presentation with current view"}</div></>} placement="top">
- <button className="antidmodeMenu-button" style={{ borderRight: "1px solid gray" }}
- onClick={e => {
- if (targetDoc) {
- TabDocView.PinDoc(targetDoc, false);
- const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1];
- const x = targetDoc._panX;
- const y = targetDoc._panY;
- const scale = targetDoc._viewScale;
- activeDoc.presPinView = true;
- activeDoc.presPinViewX = x;
- activeDoc.presPinViewY = y;
- activeDoc.presPinViewScale = scale;
- }
- }}>
- <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon="map-marker" />
- </button>
- </Tooltip>;
- }
render() {
@@ -485,8 +481,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
<div className="collectionViewBaseChrome">
{this.notACollection || this.props.type === CollectionViewType.Invalid ? (null) : this.viewModes}
{!this._buttonizableCommands ? (null) : this.templateChrome}
-
-
{this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? (null) :
<Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom">
<button className={"antimodeMenu-button"} key="float"
@@ -497,7 +491,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
</Tooltip>}
{this.notACollection ? (null) : this.lightboxButton}
{this.aliasButton}
- {this.pinButton}
+ {/* {this.pinButton} */}
{this.pinWithViewButton}
</div>
{this.subChrome}
@@ -899,14 +893,14 @@ export class CollectionStackingViewChrome extends React.Component<CollectionMenu
if (docs instanceof Doc) {
const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
- (key[0].toUpperCase() === key[0] && key.substring(0, 3) !== "ACL" && key[0] !== "_"));
+ (key[0].toUpperCase() === key[0] && key.substring(0, 3) !== "acl" && key[0] !== "_"));
return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
} else {
const keys = new Set<string>();
docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
- (key[0]?.toUpperCase() === key[0] && key.substring(0, 3) !== "ACL" && key[0] !== "_"));
+ (key[0]?.toUpperCase() === key[0] && key.substring(0, 3) !== "acl" && key[0] !== "_"));
return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
}
}
@@ -1308,4 +1302,4 @@ Scripting.addGlobal(function gotoFrame(doc: any, newFrame: any) {
}
CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0);
doc._currentFrame = Math.max(0, newFrame);
-}); \ No newline at end of file
+});
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index d2a1234ed..c02e88829 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -299,7 +299,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
this.updateFilter();
let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
const blockedkeys = ["system", "title-custom", "limitHeight", "proto", "x", "y", "zIndex", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "layout", "layout_keyValue", "links"];
- keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("ACL"));
+ keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("acl"));
if (keyOptions.length) {
this.onSelect(keyOptions[0]);
} else if (this._searchTerm !== "" && this.props.canAddNew) {
@@ -333,7 +333,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
const blockedkeys = ["proto", "x", "y", "zIndex", "_timeStampOnEnter", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "layout", "layout_keyValue", "links"];
- keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("ACL"));
+ keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("acl"));
const options = keyOptions.map(key => {
return <div key={key} className="key-option" style={{
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index aaf17153a..20bfc0e9d 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -30,24 +30,6 @@
padding-left: 0;
}
- .outline-bullet {
- position: relative;
- width: 15px;
- color: $intermediate-color;
- transform: scale(0.5);
- display: inline-block;
- }
-
- .bullet {
- position: relative;
- width: 15px;
- color: $intermediate-color;
- margin-top: 3px;
- transform: scale(1.3, 1.3);
- border: #80808030 1px solid;
- border-radius: 4px;
- }
-
.editableView-container {
font-weight: bold;
}
@@ -101,98 +83,4 @@
padding-left: 3px;
padding-right: 3px;
padding-bottom: 2px;
-}
-
-.treeViewItem-container-active {
- z-index: 100;
- position: relative;;
- .formattedTextbox-sidebar {
- background-color: #ffff001f !important;
- height: 500px !important;
- }
-}
-
-.treeViewItem-openRight {
- display: none;
- height: 17px;
- width: 15px;
-}
-
-.treeViewItem-openRight:hover {
- background: #797777;
- cursor: pointer;
-}
-
-.treeViewItem-border-outline,
-.treeViewItem-border {
- display: flex;
- overflow: hidden;
-}
-.treeViewItem-border{
- border-left: dashed 1px #00000042;
-}
-
-.treeViewItem-header-editing,
-.treeViewItem-header {
- border: transparent 1px solid;
- display: flex;
- //align-items: center;
- ::-webkit-scrollbar {
- display: none;
- }
- .formattedTextBox-cont {
- .formattedTextbox-sidebar {
- overflow: visible !important;
- border-left: unset;
- }
- overflow: visible !important;
- }
-
- .editableView-container-editing-oneLine {
- min-width: 15px;
- }
-
- .documentView-node-topmost {
- width: unset;
- }
-
- >svg {
- display: none;
- }
-
-}
-
-.treeViewItem-header:hover {
- .collectionTreeView-keyHeader {
- display: inherit;
- }
-
- >svg {
- display: inherit;
- }
-
- .treeViewItem-openRight {
- display: inline-block;
- height: 17px;
- width: 15px;
-
- // display: inline;
- svg {
- display: block;
- padding: 0px;
- margin-left: 3px;
- }
- }
-}
-
-.treeViewItem-header-above {
- border-top: black 1px solid;
-}
-
-.treeViewItem-header-below {
- border-bottom: black 1px solid;
-}
-
-.treeViewItem-header-inside {
- border: black 1px solid;
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 19b8400c8..46f18099a 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,791 +1,29 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from "mobx";
+import { action, computed } from "mobx";
import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym, DocListCastOrNull } from '../../../fields/Doc';
+import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
-import { Document, listSpec } from '../../../fields/Schema';
-import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils, returnEmptyFilter, returnEmptyDoclist } from '../../../Utils';
-import { Docs, DocUtils } from '../../documents/Documents';
-import { DocumentType } from "../../documents/DocumentTypes";
-import { SnappingManager } from '../../util/SnappingManager';
+import { Document } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
+import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, Utils } from '../../../Utils';
+import { DocUtils } from '../../documents/Documents';
import { DragManager, dropActionType } from "../../util/DragManager";
-import { Scripting } from '../../util/Scripting';
import { SelectionManager } from '../../util/SelectionManager';
-import { Transform } from '../../util/Transform';
+import { SnappingManager } from '../../util/SnappingManager';
import { undoBatch, UndoManager } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from "../EditableView";
import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
-import { DocumentView } from '../nodes/DocumentView';
-import { KeyValueBox } from '../nodes/KeyValueBox';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
import { CollectionSubView } from "./CollectionSubView";
import "./CollectionTreeView.scss";
-import { CollectionViewType, CollectionView } from './CollectionView';
+import { TreeView } from "./TreeView";
import React = require("react");
-import { TraceMobx } from '../../../fields/util';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { RichTextField } from '../../../fields/RichTextField';
-import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
-import { DocumentManager } from '../../util/DocumentManager';
-
-export interface TreeViewProps {
- document: Doc;
- dataDoc?: Doc;
- containingCollection: Doc;
- prevSibling?: Doc;
- renderDepth: number;
- removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined;
- moveDocument: DragManager.MoveFunction;
- dropAction: dropActionType;
- addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
- pinToPres: (document: Doc) => void;
- panelWidth: () => number;
- panelHeight: () => number;
- ChromeHeight: undefined | (() => number);
- addDocument: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean;
- indentDocument?: () => void;
- outdentDocument?: () => void;
- ScreenToLocalTransform: () => Transform;
- backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined;
- outerXf: () => { translateX: number, translateY: number };
- treeView: CollectionTreeView;
- parentKey: string;
- active: (outsideReaction?: boolean) => boolean;
- treeViewHideHeaderFields: () => boolean;
- treeViewPreventOpen: boolean;
- renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle
- onCheckedClick?: () => ScriptField;
- onChildClick?: () => ScriptField;
- ignoreFields?: string[];
- firstLevel: boolean;
- whenActiveChanged: (isActive: boolean) => void;
-}
-
-@observer
-/**
- * Renders a treeView of a collection of documents
- *
- * special fields:
- * treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden
- * treeViewPreventOpen : ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
- * treeViewExpandedView : name of field whose contents are being displayed as the document's subtree
- */
-class TreeView extends React.Component<TreeViewProps> {
- private _editTitleScript: (() => ScriptField) | undefined;
- private _openScript: (() => ScriptField) | undefined;
- private _header?: React.RefObject<HTMLDivElement> = React.createRef();
- private _treedropDisposer?: DragManager.DragDropDisposer;
- private _dref = React.createRef<HTMLDivElement>();
- private _tref = React.createRef<HTMLDivElement>();
- private _docRef = React.createRef<DocumentView>();
- private _uniqueId = Utils.GenerateGuid();
- private _editMaxWidth: number | string = 0;
-
- get doc() { return this.props.document; }
- get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
- get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive
- get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; }
- get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode || this.outlineMode ? "layout" : "fields"); }
- get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); }
- @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
- set treeViewOpen(c: boolean) {
- if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
- else this.doc.treeViewOpen = this._overrideTreeViewOpen = c;
- }
- @computed get outlineMode() { return this.props.treeView.doc.treeViewOutlineMode; }
- @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; }
- @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); }
- @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
- @computed get dataDoc() { return this.doc[DataSym]; }
- @computed get layoutDoc() { return Doc.Layout(this.doc); }
- @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; }
- childDocList(field: string) {
- const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined;
- return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
- (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
- DocListCastOrNull(this.doc[field])); // otherwise use the document's data field
- }
- @computed get childDocs() { return this.childDocList(this.fieldKey); }
- @computed get childLinks() { return this.childDocList("links"); }
- @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
- @computed get boundsOfCollectionDocument() {
- return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 || !DocListCast(this.props.document[this.fieldKey]).length ? undefined :
- Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
- }
-
- @undoBatch openRight = () => this.props.addDocTab(this.doc, "add:right");
- @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
- return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc);
- }
- @undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
- this.props.treeView.props.select(false);
- return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
- }
- @undoBatch @action removeDoc = (doc: Doc | Doc[]) => this.remove(doc, Doc.LayoutFieldKey(this.doc));
-
- constructor(props: any) {
- super(props);
- const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" });
- const openScript = ScriptField.MakeScript(`openOnRight(self)`);
- const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`);
- this._editTitleScript = !Doc.IsSystem(this.props.document) ? titleScript && (() => titleScript) : treeOpenScript && (() => treeOpenScript);
- this._openScript = !Doc.IsSystem(this.props.document) ? openScript && (() => openScript) : undefined;
- if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false);
- }
-
- protected createTreeDropTarget = (ele: HTMLDivElement) => {
- this._treedropDisposer?.();
- ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.doc);
- }
-
- componentWillUnmount() {
- document.removeEventListener("pointermove", this.onDragMove, true);
- document.removeEventListener("pointermove", this.onDragUp, true);
- }
-
- onDragUp = (e: PointerEvent) => {
- document.removeEventListener("pointerup", this.onDragUp, true);
- document.removeEventListener("pointermove", this.onDragMove, true);
- }
- onPointerEnter = (e: React.PointerEvent): void => {
- this.props.active(true) && Doc.BrushDoc(this.dataDoc);
- if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
- this._header!.current!.className = "treeViewItem-header";
- document.removeEventListener("pointermove", this.onDragMove, true);
- document.addEventListener("pointermove", this.onDragMove, true);
- document.removeEventListener("pointerup", this.onDragUp, true);
- document.addEventListener("pointerup", this.onDragUp, true);
- }
- }
- onPointerLeave = (e: React.PointerEvent): void => {
- Doc.UnBrushDoc(this.dataDoc);
- if (this._header?.current?.className !== "treeViewItem-header-editing") {
- this._header!.current!.className = "treeViewItem-header";
- }
- document.removeEventListener("pointerup", this.onDragUp, true);
- document.removeEventListener("pointermove", this.onDragMove, true);
- }
- onDragMove = (e: PointerEvent): void => {
- Doc.UnBrushDoc(this.dataDoc);
- const pt = [e.clientX, e.clientY];
- const rect = this._header!.current!.getBoundingClientRect();
- const before = pt[1] < rect.top + rect.height / 2;
- const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
- this._header!.current!.className = "treeViewItem-header";
- if (inside) this._header!.current!.className += " treeViewItem-header-inside";
- else if (before) this._header!.current!.className += " treeViewItem-header-above";
- else if (!before) this._header!.current!.className += " treeViewItem-header-below";
- e.stopPropagation();
- }
-
- public static makeTextBullet() {
- const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 });
- Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
- Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
- Doc.GetProto(bullet).data = new List<Doc>([]);
- Doc.SetInPlace(bullet, "editTitle", "*", false);
- FormattedTextBox.SelectOnLoad = bullet[Id];
- return bullet;
- }
-
- makeTextCollection = () => {
- Doc.SetInPlace(this.doc, "editTitle", undefined, false);
- const bullet = TreeView.makeTextBullet();
- const added = this.props.addDocument(bullet);
- bullet.context = this.props.treeView.Document;
- return added;
- }
-
- editableView = (key: string, style?: string) => (<EditableView
- oneLine={true}
- display={"inline-block"}
- editing={true}
- contents={StrCast(this.doc[key])}
- height={12}
- sizeToContent={true}
- fontStyle={style}
- fontSize={12}
- GetValue={() => StrCast(this.doc[key])}
- SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
- if (this.outlineMode && enterKey) {
- Doc.SetInPlace(this.doc, key, value, false);
- this.makeTextCollection();
- } else {
- Doc.SetInPlace(this.doc, key, value, false) || true;
- Doc.SetInPlace(this.doc, "editTitle", undefined, false);
- }
- })}
- onClick={() => {
- SelectionManager.DeselectAll();
- Doc.UserDoc().activeSelection = new List([this.doc]);
- return false;
- }}
- OnEmpty={undoBatch(() => this.props.treeView.doc.treeViewOutlineMode && this.props.removeDoc?.(this.doc))}
- OnTab={undoBatch((shift?: boolean) => {
- shift ? this.props.outdentDocument?.() : this.props.indentDocument?.();
- setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", `${this.props.treeView._uniqueId}`, false), 0);
- })}
- />)
-
- preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
- const dragData = de.complete.docDragData;
- dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? "same" : dragData.dropAction);
- }
-
- @undoBatch
- treeDrop = (e: Event, de: DragManager.DropEvent) => {
- const pt = [de.x, de.y];
- const rect = this._header!.current!.getBoundingClientRect();
- const before = pt[1] < rect.top + rect.height / 2;
- const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
- const complete = de.complete;
- if (complete.linkDragData) {
- const sourceDoc = complete.linkDragData.linkSourceDocument;
- const destDoc = this.doc;
- DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link", "");
- e.stopPropagation();
- }
- const docDragData = complete.docDragData;
- if (docDragData) {
- e.stopPropagation();
- if (docDragData.draggedDocuments[0] === this.doc) return true;
- const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before);
- let addDoc = parentAddDoc;
- if (inside) {
- const localAdd = (doc: Doc) => {
- const added = Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
- added && (doc.context = this.doc.context);
- return added;
- };
- addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce(
- (flg: boolean, doc) => flg && localAdd(doc), true) || parentAddDoc(doc);
- }
- const move = (!docDragData.dropAction || docDragData.dropAction === "move" || docDragData.dropAction === "same") && docDragData.moveDocument;
- return docDragData.droppedDocuments.reduce((added, d) => (move ? docDragData.moveDocument?.(d, undefined, addDoc) : addDoc(d)) || added, false);
- }
- return false;
- }
-
- refTransform = (ref: HTMLDivElement) => {
- const { scale, translateX, translateY } = Utils.GetScreenTransform(ref);
- const outerXf = this.props.outerXf();
- const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
- return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
- }
- docTransform = () => this.refTransform(this._dref.current!);
- getTransform = () => this.refTransform(this._tref.current!);
- docWidth = () => {
- const layoutDoc = this.layoutDoc;
- const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
- if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20));
- return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20;
- }
- docHeight = () => {
- const layoutDoc = this.layoutDoc;
- const bounds = this.boundsOfCollectionDocument;
- return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => {
- const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
- if (aspect) return this.docWidth() * aspect;
- if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
- return layoutDoc._fitWidth ? (!this.doc._nativeHeight ? NumCast(this.props.containingCollection._height) :
- Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth,
- NumCast(this.props.containingCollection._height)))) :
- NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50;
- })()));
- }
-
- @computed get expandedField() {
- const ids: { [key: string]: string } = {};
- const doc = this.doc;
- doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
-
- const rows: JSX.Element[] = [];
- for (const key of Object.keys(ids).slice().sort()) {
- if (this.props.ignoreFields?.includes(key) || key === "title" || key === "treeViewOpen") continue;
- const contents = doc[key];
- let contentElement: (JSX.Element | null)[] | JSX.Element = [];
-
- if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc))) {
- const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
- const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
- const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
- added && (doc.context = this.doc.context);
- return added;
- };
- const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
- contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents),
- this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
- this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
- this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
- [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged);
- } else {
- contentElement = <EditableView key="editableView"
- contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
- height={13}
- fontSize={12}
- GetValue={() => Field.toKeyValueString(doc, key)}
- SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} />;
- }
- rows.push(<div style={{ display: "flex" }} key={key}>
- <span style={{ fontWeight: "bold" }}>{key + ":"}</span>
- &nbsp;
- {contentElement}
- </div>);
- }
- rows.push(<div style={{ display: "flex" }} key={"newKeyValue"}>
- <EditableView
- key="editableView"
- contents={"+key:value"}
- height={13}
- fontSize={12}
- GetValue={() => ""}
- SetValue={(value: string) => {
- value.indexOf(":") !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(":")), value.substring(value.indexOf(":") + 1, value.length), true);
- return true;
- }} />
- </div>);
- return rows;
- }
-
- rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20);
- rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
- rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), 20);
-
- @computed get renderContent() {
- TraceMobx();
- const expandKey = this.treeViewExpandedView;
- if (["links", "annotations", this.fieldKey].includes(expandKey)) {
- const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey);
- const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
- const added = Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
- added && (doc.context = this.doc.context);
- return added;
- };
- const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
- const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
- const sortKey = `${this.fieldKey}-sortAscending`;
- return <ul key={expandKey + "more"} className={this.doc.treeViewHideTitle ? "no-indent" : ""} onClick={(e) => {
- !this.outlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true)));
- e.stopPropagation();
- }}>
- {!docs ? (null) :
- TreeView.GetChildElements(docs, this.props.treeView, this.layoutDoc,
- this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
- StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
- this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
- [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged)}
- </ul >;
- } else if (this.treeViewExpandedView === "fields") {
- return <ul key={this.doc[Id] + this.doc.title}><div ref={this._dref} style={{ display: "inline-block" }} >
- {this.expandedField}
- </div></ul>;
- } else {
- const layoutDoc = this.layoutDoc;
- const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight;
- const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth;
- return <div ref={this._dref} style={{ display: "inline-block", height: panelHeight() }} key={this.doc[Id]}>
- <ContentFittingDocumentView
- Document={this.doc}
- DataDoc={undefined}
- LibraryPath={emptyPath}
- renderDepth={this.props.renderDepth + 1}
- rootSelected={returnTrue}
- treeViewDoc={undefined}
- backgroundColor={this.props.backgroundColor}
- fitToBox={this.boundsOfCollectionDocument !== undefined}
- FreezeDimensions={true}
- NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
- NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
- PanelWidth={panelWidth}
- PanelHeight={panelHeight}
- focus={returnFalse}
- ScreenToLocalTransform={this.docTransform}
- docFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionDoc={this.props.containingCollection}
- ContainingCollectionView={undefined}
- addDocument={returnFalse}
- moveDocument={this.move}
- removeDocument={this.props.removeDoc}
- parentActive={this.props.active}
- whenActiveChanged={this.props.whenActiveChanged}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- bringToFront={returnFalse}
- ContentScaling={returnOne}
- />
- </div>;
- }
- }
-
- get onCheckedClick() { return this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick); }
-
- @action
- bulletClick = (e: React.MouseEvent) => {
- if (this.onCheckedClick && this.doc.type !== DocumentType.COL) {
- // this.props.document.treeViewChecked = this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check";
- this.onCheckedClick?.script.run({
- this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
- heading: this.props.containingCollection.title,
- checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? undefined : "check",
- containingTreeView: this.props.treeView.props.Document,
- }, console.log);
- } else {
- this.treeViewOpen = !this.treeViewOpen;
- }
- e.stopPropagation();
- }
-
- @computed get renderOutlineBullet() {
- TraceMobx();
- return <div className="outline-bullet"
- title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
- onClick={this.bulletClick}
- style={{ opacity: NumCast(this.doc.opacity, 1) }}>
- {(this.doc.text as RichTextField)?.Text ? <FontAwesomeIcon icon={this.childDocs?.length && !this.treeViewOpen ? ["fas", "circle"] : ["far", "circle"]} /> : (null)}
- </div>;
- }
- @computed get renderBullet() {
- TraceMobx();
- const checked = this.doc.type === DocumentType.COL ? undefined : this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined;
- return <div className="bullet"
- title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
- onClick={this.bulletClick}
- style={{
- color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
- opacity: checked === "unchecked" ? undefined : 0.4
- }}>
- {<FontAwesomeIcon icon={checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? (this.childDocs?.length ? "caret-square-right" : "caret-right") : (this.childDocs?.length ? "caret-square-down" : "caret-down"))} />}
- </div>;
- }
-
- showContextMenu = (e: React.MouseEvent) => {
- this._docRef.current?.ContentDiv && simulateMouseClick(this._docRef.current.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
- }
- contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : [{ script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }, { script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }];
- truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, 0);
- @computed get showTitleEdit() {
- return ["*", this._uniqueId, this.props.treeView._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "");
- }
- onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
- onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
- /**
- * Renders the EditableView title element for placement into the tree.
- */
- @computed
- get renderTitle() {
- TraceMobx();
- const headerElements = this.props.treeViewHideHeaderFields() ? (null) :
- <>
- <FontAwesomeIcon key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />
- <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
- onPointerDown={action(() => {
- if (this.treeViewOpen) {
- this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView :
- this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields") :
- this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
- this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
- (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
- this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields");
- }
- this.treeViewOpen = true;
- })}>
- {this.treeViewExpandedView}
- </span>
- </>;
- const view = this.showTitleEdit ? this.editableView("title") :
- <DocumentView
- ref={this._docRef}
- Document={this.doc}
- DataDoc={undefined}
- treeViewDoc={this.props.treeView.props.Document}
- LibraryPath={emptyPath}
- addDocument={undefined}
- addDocTab={this.props.addDocTab}
- rootSelected={returnTrue}
- pinToPres={emptyFunction}
- onClick={this.onChildClick}
- onDoubleClick={this.onChildDoubleClick}
- dropAction={this.props.dropAction}
- moveDocument={this.move}
- removeDocument={this.props.removeDoc}
- ScreenToLocalTransform={this.getTransform}
- ContentScaling={returnOne}
- PanelWidth={this.truncateTitleWidth}
- PanelHeight={returnZero}
- contextMenuItems={this.contextMenuItems}
- opacity={this.outlineMode ? undefined : returnOne}
- renderDepth={1}
- focus={returnTrue}
- parentActive={returnTrue}
- whenActiveChanged={this.props.whenActiveChanged}
- bringToFront={emptyFunction}
- dontRegisterView={BoolCast(this.props.treeView.props.Document.dontRegisterChildViews)}
- docFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={this.props.containingCollection}
- />;
- return <>
- <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title. Double Click or Drag to Open"
- style={{
- fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
- textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
- outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined,
- pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
- }} >
- {view}
- </div >
- {Doc.IsSystem(this.doc) && Doc.UserDoc().noviceMode ? (null) : headerElements}
- </>;
- }
-
- refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
-
- render() {
- TraceMobx();
- if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return null;
- const sorting = this.doc[`${this.fieldKey}-sortAscending`];
- if (this.showTitleEdit) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll
- let par: any = this._header?.current;
- if (par) {
- while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode;
- if (par) {
- const par_rect = (par as HTMLElement).getBoundingClientRect();
- const my_recct = this._docRef.current?.ContentDiv?.getBoundingClientRect();
- this._editMaxWidth = Math.max(100, par_rect.right - (my_recct?.left || 0));
- }
- }
- } else this._editMaxWidth = "";
- const selected = SelectionManager.IsSelected(DocumentManager.Instance.getFirstDocumentView(this.doc));
- return this.doc.treeViewHideHeader || this.outlineMode ?
- !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? this.renderContent :
- <div className={`treeViewItem-container${selected ? "-active" : ""}`} ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
- onKeyDown={e => {
- e.stopPropagation();
- e.key === "Backspace" && this.doc.text && !(this.doc.text as RichTextField)?.Text && UndoManager.RunInBatch(() => this.props.removeDoc?.(this.doc), "delete");
- e.key === "Tab" && UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab");
- e.key === "Enter" && UndoManager.RunInBatch(() => this.makeTextCollection(), "bullet");
- e.key === "Tab" && setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
- }}
- >
- <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ alignItems: this.outlineMode ? "center" : undefined, maxWidth: this._editMaxWidth }}
- onClick={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
- onPointerDown={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
- onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
- {this.outlineMode ? this.renderOutlineBullet : this.renderBullet}
- <div ref={this._dref} style={{ display: "inline-block", height: this.rtfOutlineHeight() }} key={this.doc[Id]}>
- <ContentFittingDocumentView
- Document={this.doc}
- DataDoc={undefined}
- LayoutTemplateString={FormattedTextBox.LayoutString("text")}
- LibraryPath={emptyPath}
- renderDepth={this.props.renderDepth + 1}
- rootSelected={returnTrue}
- treeViewDoc={undefined}
- backgroundColor={this.props.backgroundColor}
- fitToBox={this.boundsOfCollectionDocument !== undefined}
- PanelWidth={this.rtfWidth}
- PanelHeight={this.rtfOutlineHeight}
- focus={this.refocus}
- ScreenToLocalTransform={this.docTransform}
- docFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionDoc={this.props.containingCollection}
- ContainingCollectionView={undefined}
- addDocument={this.props.addDocument}
- moveDocument={this.move}
- removeDocument={this.props.removeDoc}
- parentActive={this.props.active}
- whenActiveChanged={this.props.whenActiveChanged}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- bringToFront={returnFalse}
- ContentScaling={returnOne}
- />
- </div>
- </div>
-
- <div className={`treeViewItem-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
- {!this.treeViewOpen ? (null) : this.renderContent}
- </div>
- </div> :
- <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
- <li className="collection-child">
- <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
- if (this.props.active(true)) {
- e.stopPropagation();
- e.preventDefault();
- SelectionManager.DeselectAll();
- }
- }}
- onPointerDown={e => {
- if (this.props.active(true)) {
- e.stopPropagation();
- e.preventDefault();
- }
- }}
- onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
- {this.outlineMode ? this.renderOutlineBullet : this.renderBullet}
- {this.renderTitle}
- </div>
- <div className={`treeViewItem-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
- {!this.treeViewOpen ? (null) : this.renderContent}
- </div>
- </li>
- </div>;
- }
- public static GetChildElements(
- childDocs: Doc[],
- treeView: CollectionTreeView,
- containingCollection: Doc,
- dataDoc: Doc | undefined,
- key: string,
- parentCollectionDoc: Doc | undefined,
- parentPrevSibling: Doc | undefined,
- add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean,
- remove: ((doc: Doc | Doc[]) => boolean),
- move: DragManager.MoveFunction,
- dropAction: dropActionType,
- addDocTab: (doc: Doc, where: string) => boolean,
- pinToPres: (document: Doc) => void,
- backgroundColor: undefined | ((document: Opt<Doc>, renderDepth: number) => string | undefined),
- screenToLocalXf: () => Transform,
- outerXf: () => { translateX: number, translateY: number },
- active: (outsideReaction?: boolean) => boolean,
- panelWidth: () => number,
- ChromeHeight: undefined | (() => number),
- renderDepth: number,
- treeViewHideHeaderFields: () => boolean,
- treeViewPreventOpen: boolean,
- renderedIds: string[],
- onCheckedClick: undefined | (() => ScriptField),
- onChildClick: undefined | (() => ScriptField),
- ignoreFields: string[] | undefined,
- firstLevel: boolean,
- whenActiveChanged: (isActive: boolean) => void
- ) {
- const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
- if (viewSpecScript) {
- childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
- }
-
- const docs = childDocs.slice();
- const ascending = containingCollection?.[key + "-sortAscending"];
- if (ascending !== undefined) {
- const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => {
- const reN = /[0-9]*$/;
- const aA = a.replace(reN, ""); // get rid of trailing numbers
- const bA = b.replace(reN, "");
- if (aA === bA) { // if header string matches, then compare numbers numerically
- const aN = parseInt(a.match(reN)![0], 10);
- const bN = parseInt(b.match(reN)![0], 10);
- return aN === bN ? 0 : aN > bN ? 1 : -1;
- } else {
- return aA > bA ? 1 : -1;
- }
- };
- docs.sort(function (a, b): 0 | 1 | -1 {
- const descA = ascending ? b : a;
- const descB = ascending ? a : b;
- const first = descA.title;
- const second = descB.title;
- // TODO find better way to sort how to sort..................
- if (typeof first === 'number' && typeof second === 'number') {
- return (first - second) > 0 ? 1 : -1;
- }
- if (typeof first === 'string' && typeof second === 'string') {
- return sortAlphaNum(first, second);
- }
- if (typeof first === 'boolean' && typeof second === 'boolean') {
- // if (first === second) { // bugfixing?: otherwise, the list "flickers" because the list is resorted during every load
- // return Number(descA.x) > Number(descB.x) ? 1 : -1;
- // }
- return first > second ? 1 : -1;
- }
- return ascending ? 1 : -1;
- });
- }
-
- const rowWidth = () => panelWidth() - 20;
- return docs.map((child, i) => {
- const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, child);
- if (!pair.layout || pair.data instanceof Promise) {
- return (null);
- }
-
- const indent = i === 0 ? undefined : () => {
- if (remove && StrCast(docs[i - 1].layout).indexOf('fieldKey') !== -1) {
- const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1];
- const fieldKey = fieldKeysub.split("\'")[1];
- if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) {
- remove(child);
- FormattedTextBox.SelectOnLoad = child[Id];
- Doc.AddDocToList(docs[i - 1], fieldKey, child);
- docs[i - 1].treeViewOpen = true;
- child.context = treeView.Document;
- }
- }
- };
- const outdent = !parentCollectionDoc ? undefined : () => {
- if (remove && StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
- const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1];
- const fieldKey = fieldKeysub.split("\'")[1];
- remove(child);
- FormattedTextBox.SelectOnLoad = child[Id];
- Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
- parentCollectionDoc.treeViewOpen = true;
- child.context = treeView.Document;
- }
- };
- const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => {
- return add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
- };
- const childLayout = Doc.Layout(pair.layout);
- const rowHeight = () => {
- const aspect = NumCast(childLayout._nativeWidth, 0) / NumCast(childLayout._nativeHeight, 0);
- return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
- };
- return !(child instanceof Doc) ? (null) : <TreeView
- document={pair.layout}
- dataDoc={pair.data}
- containingCollection={containingCollection}
- prevSibling={docs[i]}
- treeView={treeView}
- key={child[Id]}
- indentDocument={indent}
- outdentDocument={outdent}
- onCheckedClick={onCheckedClick}
- onChildClick={onChildClick}
- renderDepth={renderDepth}
- removeDoc={StrCast(containingCollection.freezeChildren).includes("remove") ? undefined : remove}
- addDocument={addDocument}
- backgroundColor={backgroundColor}
- panelWidth={rowWidth}
- panelHeight={rowHeight}
- ChromeHeight={ChromeHeight}
- moveDocument={move}
- dropAction={dropAction}
- addDocTab={addDocTab}
- pinToPres={pinToPres}
- ScreenToLocalTransform={screenToLocalXf}
- outerXf={outerXf}
- parentKey={key}
- active={active}
- treeViewHideHeaderFields={treeViewHideHeaderFields}
- treeViewPreventOpen={treeViewPreventOpen}
- renderedIds={renderedIds}
- ignoreFields={ignoreFields}
- firstLevel={firstLevel}
- whenActiveChanged={whenActiveChanged} />;
- });
- }
-}
export type collectionTreeViewProps = {
treeViewHideTitle?: boolean;
@@ -797,18 +35,16 @@ export type collectionTreeViewProps = {
@observer
export class CollectionTreeView extends CollectionSubView<Document, Partial<collectionTreeViewProps>>(Document) {
private treedropDisposer?: DragManager.DragDropDisposer;
+ private _isChildActive = false;
private _mainEle?: HTMLDivElement;
-
public _uniqueId = Utils.GenerateGuid();
- _isChildActive = false;
+
@computed get doc() { return this.props.Document; }
@computed get dataDoc() { return this.props.DataDoc || this.doc; }
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this.treedropDisposer?.();
- if (this._mainEle = ele) {
- this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
- }
+ if (this._mainEle = ele) this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
}
protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
@@ -873,8 +109,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
@computed get renderClearButton() {
return <div key="toolbar">
- <button className="toolbar-button round-button" title="Empty"
- onClick={undoBatch(action(() => Doc.GetProto(this.doc)[this.props.fieldKey] = undefined))}>
+ <button className="toolbar-button round-button" title="Empty" onClick={undoBatch(action(() => Doc.GetProto(this.doc)[this.props.fieldKey] = undefined))}>
<FontAwesomeIcon icon={"trash"} size="sm" />
</button>
</div >;
@@ -984,15 +219,4 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
</div>
);
}
-}
-
-Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
- const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []);
- for (let i = 0; i < docFilters.length; i += 3) {
- const [header, value, state] = docFilters.slice(i, i + 3);
- if (header === facetHeader && value === facetValue) {
- return state;
- }
- }
- return undefined;
-}); \ No newline at end of file
+} \ No newline at end of file
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index f89285923..1be85cfc1 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -22,7 +22,7 @@ import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from "../../util/UndoManager";
import { DocumentView } from "../nodes/DocumentView";
-import { PresBox } from '../nodes/PresBox';
+import { PresBox, PresMovement } from '../nodes/PresBox';
import { CollectionDockingView } from './CollectionDockingView';
import { CollectionDockingViewMenu } from './CollectionDockingViewMenu';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
@@ -122,27 +122,27 @@ export class TabDocView extends React.Component<TabDocViewProps> {
**/
@undoBatch
@action
- public static PinDoc(doc: Doc, unpin = false) {
- if (unpin) TabDocView.UnpinDoc(doc);
- else {
- //add this new doc to props.Document
- const curPres = CurrentUserUtils.ActivePresentation;
- if (curPres) {
- const pinDoc = Doc.MakeAlias(doc);
- pinDoc.presentationTargetDoc = doc;
- pinDoc.presZoomButton = true;
- pinDoc.context = curPres;
- Doc.AddDocToList(curPres, "data", pinDoc);
- if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
- if (!DocumentManager.Instance.getDocumentView(curPres)) {
- CollectionDockingView.AddSplit(curPres, "right");
- }
- DocumentManager.Instance.jumpToDocument(doc, false, undefined, Cast(doc.context, Doc, null));
- setTimeout(() => {
- curPres._itemIndex = DocListCast(curPres.data).length - 1;
- doc.treeViewOutlineMode && PresBox.Instance.progressivizeChild(null as any);
- }, 100);
+ public static PinDoc(doc: Doc, unpin = false, audioRange?: boolean) {
+ if (unpin) console.log('remove unpin');
+ //add this new doc to props.Document
+ const curPres = CurrentUserUtils.ActivePresentation;
+ if (curPres) {
+ const pinDoc = Doc.MakeAlias(doc);
+ pinDoc.presentationTargetDoc = doc;
+ pinDoc.title = doc.title;
+ pinDoc.presMovement = PresMovement.Zoom;
+ pinDoc.context = curPres;
+ Doc.AddDocToList(curPres, "data", pinDoc);
+ if (pinDoc.type === "audio" && !audioRange) {
+ pinDoc.presStartTime = 0;
+ pinDoc.presEndTime = doc.duration;
}
+ if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
+ const curPresDocView = DocumentManager.Instance.getDocumentView(curPres);
+ if (!curPresDocView) {
+ CollectionDockingView.AddSplit(curPres, "right");
+ }
+ DocumentManager.Instance.jumpToDocument(doc, false, undefined, Cast(doc.context, Doc, null));
}
}
@@ -169,7 +169,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
})).observe(this.props.glContainer._element[0]);
this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged();
- this._tabReaction = reaction(() => ({ selected: selected(), color: this.tabColor, title: this.tab.titleElement[0] }),
+ this._tabReaction = reaction(() => ({ selected: selected(), color: this.tabColor, title: this.tab?.titleElement[0] }),
({ selected, color, title }) => title && (title.style.backgroundColor = selected ? color : ""),
{ fireImmediately: true });
}
@@ -318,7 +318,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
</>;
}
focusFunc = (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => void) => {
- this.tab.header.parent.setActiveContentItem(this.tab.contentItem);
+ if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) {
+ this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
+ }
afterFocus?.();
}
setView = action((view: DocumentView) => this._view = view);
@@ -371,4 +373,4 @@ export class TabDocView extends React.Component<TabDocViewProps> {
{this.docView}
</div >);
}
-}
+} \ No newline at end of file
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
new file mode 100644
index 000000000..17c6b0750
--- /dev/null
+++ b/src/client/views/collections/TreeView.scss
@@ -0,0 +1,115 @@
+@import "../globalCssVariables";
+
+.treeView-container,
+.treeView-container-active {
+ .bullet-outline {
+ position: relative;
+ width: 15px;
+ color: $intermediate-color;
+ transform: scale(0.5);
+ display: inline-block;
+ }
+
+ .bullet {
+ position: relative;
+ width: 15px;
+ color: $intermediate-color;
+ margin-top: 3px;
+ transform: scale(1.3, 1.3);
+ border: #80808030 1px solid;
+ border-radius: 4px;
+ }
+}
+.treeView-container-active {
+ z-index: 100;
+ position: relative;;
+ .formattedTextbox-sidebar {
+ background-color: #ffff001f !important;
+ height: 500px !important;
+ }
+}
+
+.treeView-openRight {
+ display: none;
+ height: 17px;
+ width: 15px;
+}
+
+.treeView-openRight:hover {
+ background: #797777;
+ cursor: pointer;
+}
+
+.treeView-border-outline,
+.treeView-border {
+ display: flex;
+ overflow: hidden;
+}
+.treeView-border{
+ border-left: dashed 1px #00000042;
+}
+
+.treeView-header-editing,
+.treeView-header {
+ border: transparent 1px solid;
+ display: flex;
+ //align-items: center;
+ ::-webkit-scrollbar {
+ display: none;
+ }
+ .formattedTextBox-cont {
+ .formattedTextbox-sidebar {
+ overflow: visible !important;
+ border-left: unset;
+ }
+ overflow: visible !important;
+ }
+
+ .editableView-container-editing-oneLine {
+ min-width: 15px;
+ }
+
+ .documentView-node-topmost {
+ width: unset;
+ }
+
+ >svg {
+ display: none;
+ }
+
+}
+
+.treeView-header:hover {
+ .collectionTreeView-keyHeader {
+ display: inherit;
+ }
+
+ >svg {
+ display: inherit;
+ }
+
+ .treeView-openRight {
+ display: inline-block;
+ height: 17px;
+ width: 15px;
+
+ // display: inline;
+ svg {
+ display: block;
+ padding: 0px;
+ margin-left: 3px;
+ }
+ }
+}
+
+.treeView-header-above {
+ border-top: black 1px solid;
+}
+
+.treeView-header-below {
+ border-bottom: black 1px solid;
+}
+
+.treeView-header-inside {
+ border: black 1px solid;
+} \ No newline at end of file
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
new file mode 100644
index 000000000..e509eb78d
--- /dev/null
+++ b/src/client/views/collections/TreeView.tsx
@@ -0,0 +1,760 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from "mobx";
+import { observer } from "mobx-react";
+import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { RichTextField } from '../../../fields/RichTextField';
+import { listSpec } from '../../../fields/Schema';
+import { ComputedField, ScriptField } from '../../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { DocumentType } from "../../documents/DocumentTypes";
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager, dropActionType } from "../../util/DragManager";
+import { SelectionManager } from '../../util/SelectionManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { Transform } from '../../util/Transform';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { EditableView } from "../EditableView";
+import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
+import { KeyValueBox } from '../nodes/KeyValueBox';
+import { CollectionTreeView } from './CollectionTreeView';
+import { CollectionView, CollectionViewType } from './CollectionView';
+import "./TreeView.scss";
+import React = require("react");
+
+export interface TreeViewProps {
+ document: Doc;
+ dataDoc?: Doc;
+ containingCollection: Doc;
+ prevSibling?: Doc;
+ renderDepth: number;
+ removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined;
+ moveDocument: DragManager.MoveFunction;
+ dropAction: dropActionType;
+ addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
+ pinToPres: (document: Doc) => void;
+ panelWidth: () => number;
+ panelHeight: () => number;
+ ChromeHeight: undefined | (() => number);
+ addDocument: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean;
+ indentDocument?: () => void;
+ outdentDocument?: () => void;
+ ScreenToLocalTransform: () => Transform;
+ backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined;
+ outerXf: () => { translateX: number, translateY: number };
+ treeView: CollectionTreeView;
+ parentKey: string;
+ active: (outsideReaction?: boolean) => boolean;
+ treeViewHideHeaderFields: () => boolean;
+ treeViewPreventOpen: boolean;
+ renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle
+ onCheckedClick?: () => ScriptField;
+ onChildClick?: () => ScriptField;
+ ignoreFields?: string[];
+ firstLevel: boolean;
+ whenActiveChanged: (isActive: boolean) => void;
+}
+
+@observer
+/**
+ * Renders a treeView of a collection of documents
+ *
+ * special fields:
+ * treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden
+ * treeViewPreventOpen : ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
+ * treeViewExpandedView : name of field whose contents are being displayed as the document's subtree
+ */
+export class TreeView extends React.Component<TreeViewProps> {
+ private _editTitleScript: (() => ScriptField) | undefined;
+ private _openScript: (() => ScriptField) | undefined;
+ private _header?: React.RefObject<HTMLDivElement> = React.createRef();
+ private _treedropDisposer?: DragManager.DragDropDisposer;
+ private _dref = React.createRef<HTMLDivElement>();
+ private _tref = React.createRef<HTMLDivElement>();
+ private _docRef = React.createRef<DocumentView>();
+ private _uniqueId = Utils.GenerateGuid();
+ private _editMaxWidth: number | string = 0;
+
+ get doc() { return this.props.document; }
+ get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
+ get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive
+ get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; }
+ get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode || this.outlineMode ? "layout" : "fields"); }
+ get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); }
+ @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
+ set treeViewOpen(c: boolean) {
+ if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
+ else this.doc.treeViewOpen = this._overrideTreeViewOpen = c;
+ }
+ @computed get outlineMode() { return this.props.treeView.doc.treeViewOutlineMode; }
+ @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; }
+ @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); }
+ @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
+ @computed get dataDoc() { return this.doc[DataSym]; }
+ @computed get layoutDoc() { return Doc.Layout(this.doc); }
+ @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; }
+ childDocList(field: string) {
+ const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined;
+ return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
+ (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
+ DocListCastOrNull(this.doc[field])); // otherwise use the document's data field
+ }
+ @computed get childDocs() { return this.childDocList(this.fieldKey); }
+ @computed get childLinks() { return this.childDocList("links"); }
+ @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
+ @computed get boundsOfCollectionDocument() {
+ return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 || !DocListCast(this.props.document[this.fieldKey]).length ? undefined :
+ Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
+ }
+
+ @undoBatch openRight = () => this.props.addDocTab(this.doc, "add:right");
+ @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
+ return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc);
+ }
+ @undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
+ this.props.treeView.props.select(false);
+ return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
+ }
+ @undoBatch @action removeDoc = (doc: Doc | Doc[]) => this.remove(doc, Doc.LayoutFieldKey(this.doc));
+
+ constructor(props: any) {
+ super(props);
+ const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" });
+ const openScript = ScriptField.MakeScript(`openOnRight(self)`);
+ const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`);
+ this._editTitleScript = !Doc.IsSystem(this.props.document) ? titleScript && (() => titleScript) : treeOpenScript && (() => treeOpenScript);
+ this._openScript = !Doc.IsSystem(this.props.document) ? openScript && (() => openScript) : undefined;
+ if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false);
+ }
+
+ protected createTreeDropTarget = (ele: HTMLDivElement) => {
+ this._treedropDisposer?.();
+ ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.doc);
+ }
+
+ componentWillUnmount() {
+ document.removeEventListener("pointermove", this.onDragMove, true);
+ document.removeEventListener("pointermove", this.onDragUp, true);
+ }
+
+ onDragUp = (e: PointerEvent) => {
+ document.removeEventListener("pointerup", this.onDragUp, true);
+ document.removeEventListener("pointermove", this.onDragMove, true);
+ }
+ onPointerEnter = (e: React.PointerEvent): void => {
+ this.props.active(true) && Doc.BrushDoc(this.dataDoc);
+ if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
+ this._header!.current!.className = "treeView-header";
+ document.removeEventListener("pointermove", this.onDragMove, true);
+ document.addEventListener("pointermove", this.onDragMove, true);
+ document.removeEventListener("pointerup", this.onDragUp, true);
+ document.addEventListener("pointerup", this.onDragUp, true);
+ }
+ }
+ onPointerLeave = (e: React.PointerEvent): void => {
+ Doc.UnBrushDoc(this.dataDoc);
+ if (this._header?.current?.className !== "treeView-header-editing") {
+ this._header!.current!.className = "treeView-header";
+ }
+ document.removeEventListener("pointerup", this.onDragUp, true);
+ document.removeEventListener("pointermove", this.onDragMove, true);
+ }
+ onDragMove = (e: PointerEvent): void => {
+ Doc.UnBrushDoc(this.dataDoc);
+ const pt = [e.clientX, e.clientY];
+ const rect = this._header!.current!.getBoundingClientRect();
+ const before = pt[1] < rect.top + rect.height / 2;
+ const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
+ this._header!.current!.className = "treeView-header";
+ if (inside) this._header!.current!.className += " treeView-header-inside";
+ else if (before) this._header!.current!.className += " treeView-header-above";
+ else if (!before) this._header!.current!.className += " treeView-header-below";
+ e.stopPropagation();
+ }
+
+ public static makeTextBullet() {
+ const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 });
+ Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
+ Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
+ Doc.GetProto(bullet).data = new List<Doc>([]);
+ Doc.SetInPlace(bullet, "editTitle", "*", false);
+ FormattedTextBox.SelectOnLoad = bullet[Id];
+ return bullet;
+ }
+
+ makeTextCollection = () => {
+ Doc.SetInPlace(this.doc, "editTitle", undefined, false);
+ const bullet = TreeView.makeTextBullet();
+ const added = this.props.addDocument(bullet);
+ bullet.context = this.props.treeView.Document;
+ return added;
+ }
+
+ editableView = (key: string, style?: string) => (<EditableView
+ oneLine={true}
+ display={"inline-block"}
+ editing={true}
+ contents={StrCast(this.doc[key])}
+ height={12}
+ sizeToContent={true}
+ fontStyle={style}
+ fontSize={12}
+ GetValue={() => StrCast(this.doc[key])}
+ SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
+ Doc.SetInPlace(this.doc, key, value, false);
+ if (this.outlineMode && enterKey) {
+ this.makeTextCollection();
+ } else {
+ Doc.SetInPlace(this.doc, "editTitle", undefined, false);
+ }
+ })}
+ onClick={() => {
+ SelectionManager.DeselectAll();
+ Doc.UserDoc().activeSelection = new List([this.doc]);
+ return false;
+ }}
+ OnEmpty={undoBatch(() => this.props.treeView.doc.treeViewOutlineMode && this.props.removeDoc?.(this.doc))}
+ OnTab={undoBatch((shift?: boolean) => {
+ shift ? this.props.outdentDocument?.() : this.props.indentDocument?.();
+ setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", `${this.props.treeView._uniqueId}`, false), 0);
+ })}
+ />)
+
+ preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
+ const dragData = de.complete.docDragData;
+ dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? "same" : dragData.dropAction);
+ }
+
+ @undoBatch
+ treeDrop = (e: Event, de: DragManager.DropEvent) => {
+ const pt = [de.x, de.y];
+ const rect = this._header!.current!.getBoundingClientRect();
+ const before = pt[1] < rect.top + rect.height / 2;
+ const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
+ if (de.complete.linkDragData) {
+ const sourceDoc = de.complete.linkDragData.linkSourceDocument;
+ const destDoc = this.doc;
+ DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link", "");
+ e.stopPropagation();
+ }
+ const docDragData = de.complete.docDragData;
+ if (docDragData) {
+ e.stopPropagation();
+ if (docDragData.draggedDocuments[0] === this.doc) return true;
+ const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before);
+ let addDoc = parentAddDoc;
+ if (inside) {
+ const localAdd = (doc: Doc) => {
+ const added = Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
+ added && (doc.context = this.doc.context);
+ return added;
+ };
+ addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce(
+ (flg: boolean, doc) => flg && localAdd(doc), true) || parentAddDoc(doc);
+ }
+ const move = (!docDragData.dropAction || docDragData.dropAction === "move" || docDragData.dropAction === "same") && docDragData.moveDocument;
+ return docDragData.droppedDocuments.reduce((added, d) => (move ? docDragData.moveDocument?.(d, undefined, addDoc) : addDoc(d)) || added, false);
+ }
+ return false;
+ }
+
+ refTransform = (ref: HTMLDivElement) => {
+ const { scale, translateX, translateY } = Utils.GetScreenTransform(ref);
+ const outerXf = this.props.outerXf();
+ const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
+ return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
+ }
+ docTransform = () => this.refTransform(this._dref.current!);
+ getTransform = () => this.refTransform(this._tref.current!);
+ docWidth = () => {
+ const layoutDoc = this.layoutDoc;
+ const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
+ if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20));
+ return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20;
+ }
+ docHeight = () => {
+ const layoutDoc = this.layoutDoc;
+ const bounds = this.boundsOfCollectionDocument;
+ return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => {
+ const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
+ if (aspect) return this.docWidth() * aspect;
+ if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
+ return layoutDoc._fitWidth ? (!this.doc._nativeHeight ? NumCast(this.props.containingCollection._height) :
+ Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth,
+ NumCast(this.props.containingCollection._height)))) :
+ NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50;
+ })()));
+ }
+
+ @computed get expandedField() {
+ const ids: { [key: string]: string } = {};
+ const rows: JSX.Element[] = [];
+ const doc = this.doc;
+ doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
+
+ for (const key of Object.keys(ids).slice().sort()) {
+ if (this.props.ignoreFields?.includes(key) || key === "title" || key === "treeViewOpen") continue;
+ const contents = doc[key];
+ let contentElement: (JSX.Element | null)[] | JSX.Element = [];
+
+ if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc))) {
+ const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
+ const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
+ const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
+ added && (doc.context = this.doc.context);
+ return added;
+ };
+ const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
+ contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents),
+ this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
+ this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
+ this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
+ [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged);
+ } else {
+ contentElement = <EditableView key="editableView"
+ contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
+ height={13}
+ fontSize={12}
+ GetValue={() => Field.toKeyValueString(doc, key)}
+ SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} />;
+ }
+ rows.push(<div style={{ display: "flex" }} key={key}>
+ <span style={{ fontWeight: "bold" }}>{key + ":"}</span>
+ &nbsp;
+ {contentElement}
+ </div>);
+ }
+ rows.push(<div style={{ display: "flex" }} key={"newKeyValue"}>
+ <EditableView
+ key="editableView"
+ contents={"+key:value"}
+ height={13}
+ fontSize={12}
+ GetValue={returnEmptyString}
+ SetValue={value => value.indexOf(":") !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(":")), value.substring(value.indexOf(":") + 1, value.length), true)} />
+ </div>);
+ return rows;
+ }
+
+ rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20);
+ rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
+ rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), 20);
+
+ @computed get renderContent() {
+ TraceMobx();
+ const expandKey = this.treeViewExpandedView;
+ if (["links", "annotations", this.fieldKey].includes(expandKey)) {
+ const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey);
+ const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
+ const added = Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
+ added && (doc.context = this.doc.context);
+ return added;
+ };
+ const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
+ const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
+ const sortKey = `${this.fieldKey}-sortAscending`;
+ return <ul key={expandKey + "more"} className={this.doc.treeViewHideTitle ? "no-indent" : ""} onClick={(e) => {
+ !this.outlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true)));
+ e.stopPropagation();
+ }}>
+ {!docs ? (null) :
+ TreeView.GetChildElements(docs, this.props.treeView, this.layoutDoc,
+ this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
+ StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
+ this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
+ [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged)}
+ </ul >;
+ } else if (this.treeViewExpandedView === "fields") {
+ return <ul key={this.doc[Id] + this.doc.title}><div ref={this._dref} style={{ display: "inline-block" }} >
+ {this.expandedField}
+ </div></ul>;
+ } else {
+ const layoutDoc = this.layoutDoc;
+ const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight;
+ const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth;
+ return <div ref={this._dref} style={{ display: "inline-block", height: panelHeight() }} key={this.doc[Id]}>
+ <ContentFittingDocumentView
+ Document={this.doc}
+ DataDoc={undefined}
+ LibraryPath={emptyPath}
+ renderDepth={this.props.renderDepth + 1}
+ rootSelected={returnTrue}
+ treeViewDoc={undefined}
+ backgroundColor={this.props.backgroundColor}
+ fitToBox={this.boundsOfCollectionDocument !== undefined}
+ FreezeDimensions={true}
+ NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
+ NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
+ PanelWidth={panelWidth}
+ PanelHeight={panelHeight}
+ focus={returnFalse}
+ ScreenToLocalTransform={this.docTransform}
+ docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionDoc={this.props.containingCollection}
+ ContainingCollectionView={undefined}
+ addDocument={returnFalse}
+ moveDocument={this.move}
+ removeDocument={this.props.removeDoc}
+ parentActive={this.props.active}
+ whenActiveChanged={this.props.whenActiveChanged}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ bringToFront={returnFalse}
+ ContentScaling={returnOne}
+ />
+ </div>;
+ }
+ }
+
+ get onCheckedClick() { return this.doc.type === DocumentType.COL ? undefined : this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick); }
+
+ @action
+ bulletClick = (e: React.MouseEvent) => {
+ if (this.onCheckedClick) {
+ this.onCheckedClick?.script.run({
+ this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
+ heading: this.props.containingCollection.title,
+ checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? undefined : "check",
+ containingTreeView: this.props.treeView.props.Document,
+ }, console.log);
+ } else {
+ this.treeViewOpen = !this.treeViewOpen;
+ }
+ e.stopPropagation();
+ }
+
+ @computed get renderBullet() {
+ TraceMobx();
+ const checked = this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined;
+ return <div className={`bullet${this.outlineMode ? "-outline" : ""}`} title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
+ onClick={this.bulletClick}
+ style={this.outlineMode ? { opacity: NumCast(this.doc.opacity, 1) } : {
+ color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
+ opacity: checked === "unchecked" ? undefined : 0.4
+ }}>
+ {this.outlineMode && !(this.doc.text as RichTextField)?.Text ? (null) :
+ <FontAwesomeIcon icon={this.outlineMode ? [this.childDocs?.length && !this.treeViewOpen ? "fas" : "far", "circle"] :
+ checked === "check" ? "check" :
+ (checked === "x" ? "times" : checked === "unchecked" ? "square" :
+ !this.treeViewOpen ? (this.childDocs?.length ? "caret-square-right" : "caret-right") :
+ (this.childDocs?.length ? "caret-square-down" : "caret-down"))} />}
+ </div>;
+ }
+ @computed get showTitleEditorControl() { return ["*", this._uniqueId, this.props.treeView._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || ""); }
+ @computed get headerElements() {
+ return (Doc.IsSystem(this.doc) && Doc.UserDoc().noviceMode) || this.props.treeViewHideHeaderFields() ? (null) :
+ <>
+ <FontAwesomeIcon key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />
+ <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
+ onPointerDown={action(() => {
+ if (this.treeViewOpen) {
+ this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView :
+ this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields") :
+ this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
+ this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
+ (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
+ this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields");
+ }
+ this.treeViewOpen = true;
+ })}>
+ {this.treeViewExpandedView}
+ </span>
+ </>;
+ }
+
+ showContextMenu = (e: React.MouseEvent) => simulateMouseClick(this._docRef.current?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
+ contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : [{ script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }, { script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }];
+ truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, 0);
+ onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
+ onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
+ /**
+ * Renders the EditableView title element for placement into the tree.
+ */
+ @computed
+ get renderTitle() {
+ TraceMobx();
+ const view = this.showTitleEditorControl ? this.editableView("title") :
+ <DocumentView
+ ref={this._docRef}
+ Document={this.doc}
+ DataDoc={undefined}
+ treeViewDoc={this.props.treeView.props.Document}
+ LibraryPath={emptyPath}
+ addDocument={undefined}
+ addDocTab={this.props.addDocTab}
+ rootSelected={returnTrue}
+ pinToPres={emptyFunction}
+ onClick={this.onChildClick}
+ onDoubleClick={this.onChildDoubleClick}
+ dropAction={this.props.dropAction}
+ moveDocument={this.move}
+ removeDocument={this.props.removeDoc}
+ ScreenToLocalTransform={this.getTransform}
+ ContentScaling={returnOne}
+ PanelWidth={this.truncateTitleWidth}
+ PanelHeight={returnZero}
+ contextMenuItems={this.contextMenuItems}
+ opacity={this.outlineMode ? undefined : returnOne}
+ renderDepth={1}
+ focus={returnTrue}
+ parentActive={returnTrue}
+ whenActiveChanged={this.props.whenActiveChanged}
+ bringToFront={emptyFunction}
+ dontRegisterView={BoolCast(this.props.treeView.props.Document.dontRegisterChildViews)}
+ docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={this.props.containingCollection}
+ />;
+ return <>
+ <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title. Double Click or Drag to Open"
+ style={{
+ fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
+ textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
+ outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined,
+ pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
+ }} >
+ {view}
+ </div >
+ {this.headerElements}
+ </>;
+ }
+
+ refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
+
+ render() {
+ TraceMobx();
+ if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return null;
+ const sorting = this.doc[`${this.fieldKey}-sortAscending`];
+ if (this.showTitleEditorControl) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll
+ let par: any = this._header?.current;
+ while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode;
+ if (par) {
+ const par_rect = (par as HTMLElement).getBoundingClientRect();
+ const my_recct = this._docRef.current?.ContentDiv?.getBoundingClientRect();
+ this._editMaxWidth = Math.max(100, par_rect.right - (my_recct?.left || 0));
+ }
+ }
+ else this._editMaxWidth = "";
+ const selected = SelectionManager.IsSelected(DocumentManager.Instance.getFirstDocumentView(this.doc));
+ return this.doc.treeViewHideHeader || this.outlineMode ?
+ !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ?
+ this.renderContent
+ : <div className={`treeView-container${selected ? "-active" : ""}`} ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
+ onKeyDown={e => {
+ e.stopPropagation();
+ switch (e.key) {
+ case "Backspace": return this.doc.text && !(this.doc.text as RichTextField)?.Text && UndoManager.RunInBatch(() => this.props.removeDoc?.(this.doc), "delete");
+ case "Enter": return UndoManager.RunInBatch(() => this.makeTextCollection(), "bullet");
+ case "Tab": setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
+ return UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab");
+ }
+ }} >
+ <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ alignItems: this.outlineMode ? "center" : undefined, maxWidth: this._editMaxWidth }}
+ onClick={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
+ onPointerDown={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
+ onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+ {this.renderBullet}
+ <div ref={this._dref} style={{ display: "inline-block", height: this.rtfOutlineHeight() }} key={this.doc[Id]}>
+ <ContentFittingDocumentView
+ Document={this.doc}
+ DataDoc={undefined}
+ LayoutTemplateString={FormattedTextBox.LayoutString("text")}
+ LibraryPath={emptyPath}
+ renderDepth={this.props.renderDepth + 1}
+ rootSelected={returnTrue}
+ treeViewDoc={undefined}
+ backgroundColor={this.props.backgroundColor}
+ fitToBox={this.boundsOfCollectionDocument !== undefined}
+ PanelWidth={this.rtfWidth}
+ PanelHeight={this.rtfOutlineHeight}
+ focus={this.refocus}
+ ScreenToLocalTransform={this.docTransform}
+ docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionDoc={this.props.containingCollection}
+ ContainingCollectionView={undefined}
+ addDocument={this.props.addDocument}
+ moveDocument={this.move}
+ removeDocument={this.props.removeDoc}
+ parentActive={this.props.active}
+ whenActiveChanged={this.props.whenActiveChanged}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ bringToFront={returnFalse}
+ ContentScaling={returnOne}
+ />
+ </div>
+ </div>
+
+ <div className={`treeView-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
+ {!this.treeViewOpen ? (null) : this.renderContent}
+ </div>
+ </div> :
+ <div className="treeView-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
+ <li className="collection-child">
+ <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
+ if (this.props.active(true)) {
+ e.stopPropagation();
+ e.preventDefault();
+ SelectionManager.DeselectAll();
+ }
+ }}
+ onPointerDown={e => {
+ if (this.props.active(true)) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }}
+ onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+ {this.renderBullet}
+ {this.renderTitle}
+ </div>
+ <div className={`treeView-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
+ {!this.treeViewOpen ? (null) : this.renderContent}
+ </div>
+ </li>
+ </div>;
+ }
+
+
+ public static GetChildElements(
+ childDocs: Doc[],
+ treeView: CollectionTreeView,
+ containingCollection: Doc,
+ dataDoc: Doc | undefined,
+ key: string,
+ parentCollectionDoc: Doc | undefined,
+ parentPrevSibling: Doc | undefined,
+ add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean,
+ remove: ((doc: Doc | Doc[]) => boolean),
+ move: DragManager.MoveFunction,
+ dropAction: dropActionType,
+ addDocTab: (doc: Doc, where: string) => boolean,
+ pinToPres: (document: Doc) => void,
+ backgroundColor: undefined | ((document: Opt<Doc>, renderDepth: number) => string | undefined),
+ screenToLocalXf: () => Transform,
+ outerXf: () => { translateX: number, translateY: number },
+ active: (outsideReaction?: boolean) => boolean,
+ panelWidth: () => number,
+ ChromeHeight: undefined | (() => number),
+ renderDepth: number,
+ treeViewHideHeaderFields: () => boolean,
+ treeViewPreventOpen: boolean,
+ renderedIds: string[],
+ onCheckedClick: undefined | (() => ScriptField),
+ onChildClick: undefined | (() => ScriptField),
+ ignoreFields: string[] | undefined,
+ firstLevel: boolean,
+ whenActiveChanged: (isActive: boolean) => void
+ ) {
+ const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
+ if (viewSpecScript) {
+ childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
+ }
+
+ const docs = childDocs.slice();
+ const ascending = containingCollection?.[key + "-sortAscending"];
+ if (ascending !== undefined) {
+ const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => {
+ const reN = /[0-9]*$/;
+ const aA = a.replace(reN, ""); // get rid of trailing numbers
+ const bA = b.replace(reN, "");
+ if (aA === bA) { // if header string matches, then compare numbers numerically
+ const aN = parseInt(a.match(reN)![0], 10);
+ const bN = parseInt(b.match(reN)![0], 10);
+ return aN === bN ? 0 : aN > bN ? 1 : -1;
+ } else {
+ return aA > bA ? 1 : -1;
+ }
+ };
+ docs.sort(function (a, b): 0 | 1 | -1 {
+ const first = (ascending ? b : a).title;
+ const second = (ascending ? a : b).title;
+ if (typeof first === 'number' && typeof second === 'number') return (first - second) > 0 ? 1 : -1;
+ if (typeof first === 'string' && typeof second === 'string') return sortAlphaNum(first, second);
+ return ascending ? 1 : -1;
+ });
+ }
+
+ const rowWidth = () => panelWidth() - 20;
+ return docs.map((child, i) => {
+ const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, child);
+ if (!pair.layout || pair.data instanceof Promise) {
+ return (null);
+ }
+
+ const indent = i === 0 ? undefined : () => {
+ if (remove && StrCast(docs[i - 1].layout).indexOf('fieldKey') !== -1) {
+ const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1];
+ const fieldKey = fieldKeysub.split("\'")[1];
+ if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) {
+ remove(child);
+ FormattedTextBox.SelectOnLoad = child[Id];
+ Doc.AddDocToList(docs[i - 1], fieldKey, child);
+ docs[i - 1].treeViewOpen = true;
+ child.context = treeView.Document;
+ }
+ }
+ };
+ const outdent = !parentCollectionDoc ? undefined : () => {
+ if (remove && StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
+ const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1];
+ const fieldKey = fieldKeysub.split("\'")[1];
+ remove(child);
+ FormattedTextBox.SelectOnLoad = child[Id];
+ Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
+ parentCollectionDoc.treeViewOpen = true;
+ child.context = treeView.Document;
+ }
+ };
+ const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
+ const childLayout = Doc.Layout(pair.layout);
+ const rowHeight = () => {
+ const aspect = NumCast(childLayout._nativeWidth, 0) / NumCast(childLayout._nativeHeight, 0);
+ return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
+ };
+ return !(child instanceof Doc) ? (null) : <TreeView
+ document={pair.layout}
+ dataDoc={pair.data}
+ containingCollection={containingCollection}
+ prevSibling={docs[i]}
+ treeView={treeView}
+ key={child[Id]}
+ indentDocument={indent}
+ outdentDocument={!parentCollectionDoc ? undefined : outdent}
+ onCheckedClick={onCheckedClick}
+ onChildClick={onChildClick}
+ renderDepth={renderDepth}
+ removeDoc={StrCast(containingCollection.freezeChildren).includes("remove") ? undefined : remove}
+ addDocument={addDocument}
+ backgroundColor={backgroundColor}
+ panelWidth={rowWidth}
+ panelHeight={rowHeight}
+ ChromeHeight={ChromeHeight}
+ moveDocument={move}
+ dropAction={dropAction}
+ addDocTab={addDocTab}
+ pinToPres={pinToPres}
+ ScreenToLocalTransform={screenToLocalXf}
+ outerXf={outerXf}
+ parentKey={key}
+ active={active}
+ treeViewHideHeaderFields={treeViewHideHeaderFields}
+ treeViewPreventOpen={treeViewPreventOpen}
+ renderedIds={renderedIds}
+ ignoreFields={ignoreFields}
+ firstLevel={firstLevel}
+ whenActiveChanged={whenActiveChanged} />;
+ });
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 2b07c4efb..75cbc20ca 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -52,12 +52,16 @@
.pathOrder-frame {
position: absolute;
- width: 40;
+ width: 100%;
+ height: 100%;
+ min-height: 15px;
text-align: center;
- font-size: 30;
background-color: #69a6db;
+ border-radius: 5px;
+ box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
font-family: Roboto;
- font-weight: 300;
+ font-weight: 500;
+ color: white;
}
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7b0aaef3c..8af048d67 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -209,7 +209,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const layoutDoc = Doc.Layout(d);
if (this.Document._currentFrame !== undefined) {
const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000));
- CollectionFreeFormDocumentView.setValues(this.Document._currentFrame, d, x + vals.x - dropPos[0], y + vals.y - dropPos[1], vals.h, vals.w, vals.scroll, vals.opacity);
+ CollectionFreeFormDocumentView.setValues(this.Document._currentFrame, d, x + vals.x - dropPos[0], y + vals.y - dropPos[1], vals.h, vals.w, this.Document.editScrollProgressivize ? vals.scroll : undefined, vals.opacity);
} else {
d.x = x + NumCast(d.x) - dropPos[0];
d.y = y + NumCast(d.y) - dropPos[1];
@@ -906,7 +906,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
if (DocListCast(this.dataDoc[this.props.fieldKey]).includes(doc)) {
// glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
- if (!doc.z) this.setPan(newPanX, newPanY, doc.presTransition || doc.presTransition === 0 ? `transform ${doc.presTransition}ms` : "transform 500ms", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
+ if (!doc.z) this.setPan(newPanX, newPanY, doc.focusSpeed || doc.focusSpeed === 0 ? `transform ${doc.focusSpeed}ms` : "transform 500ms", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
}
Doc.BrushDoc(this.props.Document);
this.props.focus(this.props.Document);
@@ -1595,21 +1595,17 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
<div key="presorder">{PresBox.Instance.order}</div>
<svg key="svg" className={presPaths}>
<defs>
- <marker id="arrow" markerWidth="3" overflow="visible" markerHeight="3" refX="5" refY="5" orient="auto" markerUnits="strokeWidth">
- <path d="M0,0 L0,6 L9,3 z" fill="#69a6db" />
- </marker>
- <marker id="square" markerWidth="3" markerHeight="3" overflow="visible"
- refX="5" refY="5" orient="auto" markerUnits="strokeWidth">
- <path d="M 5,1 L 9,5 5,9 1,5 z" fill="#69a6db" />
+ <marker id="markerSquare" markerWidth="3" markerHeight="3" refX="1.5" refY="1.5"
+ orient="auto" overflow="visible">
+ <rect x="0" y="0" width="3" height="3" stroke="#69a6db" strokeWidth="1" fill="white" fillOpacity="0.8" />
</marker>
- <marker id="markerSquare" markerWidth="7" markerHeight="7" refX="4" refY="4"
+ <marker id="markerSquareFilled" markerWidth="3" markerHeight="3" refX="1.5" refY="1.5"
orient="auto" overflow="visible">
- <rect x="1" y="1" width="5" height="5" fill="#69a6db" />
+ <rect x="0" y="0" width="3" height="3" stroke="#69a6db" strokeWidth="1" fill="#69a6db" />
</marker>
-
- <marker id="markerArrow" markerWidth="5" markerHeight="5" refX="2" refY="7"
+ <marker id="markerArrow" markerWidth="3" markerHeight="3" refX="2" refY="4"
orient="auto" overflow="visible">
- <path d="M2,2 L2,13 L8,7 L2,2" fill="#69a6db" />
+ <path d="M2,2 L2,6 L6,4 L2,2 Z" stroke="#69a6db" strokeLinejoin="round" strokeWidth="1" fill="white" fillOpacity="0.8" />
</marker>
</defs>
{PresBox.Instance.paths}
@@ -1637,4 +1633,4 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
{this.zoomProgressivize}
</div>;
}
-}
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 46298ec6f..fd4fa0c7e 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -24,6 +24,7 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
}
render() {
+ const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 19, transform: 'translate(-2px, -2px)' }} />;
const buttons = [
<Tooltip key="group" title={<><div className="dash-tooltip">Create a Collection</div></>} placement="bottom">
<button
@@ -53,12 +54,11 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
<FontAwesomeIcon icon="font" size="lg" />
</button>
</Tooltip>,
- <Tooltip key="pinWithView" title={<><div className="dash-tooltip">Pin to presentation with selected view</div></>} placement="bottom">
+ <Tooltip key="pinWithView" title={<><div className="dash-tooltip">Pin with selected view</div></>} placement="bottom">
<button
className="antimodeMenu-button"
onPointerDown={this.pinWithView}>
- <FontAwesomeIcon icon="map-pin" size="lg" />
- <div style={{ position: 'relative', fontSize: 25, fontWeight: 700, transform: 'translate(-4px, -22px)', color: 'rgba(250,250,250,0.55)' }}>V</div>
+ <>{presPinWithViewIcon}</>
</button>
</Tooltip>,
];
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 4f2399962..d04ef4ff9 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -25,6 +25,7 @@ import "./MarqueeView.scss";
import React = require("react");
import { Id } from "../../../../fields/FieldSymbols";
import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
+import { PresMovement } from "../../nodes/PresBox";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -394,7 +395,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (curPres) {
const pinDoc = Doc.MakeAlias(doc);
pinDoc.presentationTargetDoc = doc;
- pinDoc.presZoomButton = true;
+ pinDoc.presMovement = PresMovement.Zoom;
pinDoc.context = curPres;
Doc.AddDocToList(curPres, "data", pinDoc);
if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 57d461bf8..d6f8ce19c 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -65,7 +65,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
w: Cast(doc["w-indexed"], listSpec("number"), [NumCast(doc._width)]).reduce((p, w, i) => (i <= timecode && w !== undefined) || p === undefined ? w : p, undefined as any as number),
x: Cast(doc["x-indexed"], listSpec("number"), [NumCast(doc.x)]).reduce((p, x, i) => (i <= timecode && x !== undefined) || p === undefined ? x : p, undefined as any as number),
y: Cast(doc["y-indexed"], listSpec("number"), [NumCast(doc.y)]).reduce((p, y, i) => (i <= timecode && y !== undefined) || p === undefined ? y : p, undefined as any as number),
- scroll: Cast(doc["scroll-indexed"], listSpec("number"), [NumCast(doc._scrollTop, 0)]).reduce((p, s, i) => (i <= timecode && s !== undefined) || p === undefined ? s : p, undefined as any as number),
+ scroll: Cast(doc["scroll-indexed"], listSpec("number"), [NumCast(doc._scrollY, 0)]).reduce((p, s, i) => (i <= timecode && s !== undefined) || p === undefined ? s : p, undefined as any as number),
opacity: Cast(doc["opacity-indexed"], listSpec("number"), [NumCast(doc.opacity, 1)]).reduce((p, o, i) => i <= timecode || p === undefined ? o : p, undefined as any as number),
});
}
@@ -83,7 +83,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
hindexed[timecode] = h as any as number;
windexed[timecode] = w as any as number;
oindexed[timecode] = opacity as any as number;
- scrollIndexed[timecode] = scroll as any as number;
+ if (scroll) scrollIndexed[timecode] = scroll as any as number;
d["x-indexed"] = new List<number>(xindexed);
d["y-indexed"] = new List<number>(yindexed);
d["h-indexed"] = new List<number>(hindexed);
@@ -110,10 +110,10 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static setupScroll(doc: Doc, timecode: number) {
const scrollList = new List<number>();
- scrollList[timecode] = NumCast(doc._scrollTop);
+ scrollList[timecode] = NumCast(doc._scrollY);
doc["scroll-indexed"] = scrollList;
doc.activeFrame = ComputedField.MakeFunction("self._currentFrame");
- doc._scrollTop = ComputedField.MakeInterpolated("scroll", "activeFrame");
+ doc._scrollY = ComputedField.MakeInterpolated("scroll", "activeFrame");
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7ff8b635c..68328ea4b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1045,10 +1045,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : undefined,
}}>
<EditableView ref={this._titleRef}
- contents={this.ShowTitle.split(";").map(field => field + ":" + (this.props.DataDoc || this.props.Document)[field]?.toString()).join(" ")}
+ contents={this.ShowTitle.split(";").map(field => field + ":" + (this.dataDoc || this.props.Document)[field]?.toString()).join(" ")}
display={"block"} fontSize={10}
GetValue={() => ""}
- SetValue={undoBatch((value: string) => (Doc.GetProto(this.props.DataDoc || this.props.Document)[this.ShowTitle] = value) ? true : true)}
+ SetValue={undoBatch((value: string) => (Doc.GetProto(this.dataDoc || this.props.Document)[this.ShowTitle] = value) ? true : true)}
/>
</div>);
return !this.ShowTitle && !showCaption ?
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 7a010532f..748af89ef 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -47,7 +47,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
const keys = new Set<string>(noviceFields);
this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
- return Array.from(keys.keys()).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_") && !key.startsWith("ACL")) || noviceFields.includes(key)).sort();
+ return Array.from(keys.keys()).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_") && !key.startsWith("acl")) || noviceFields.includes(key)).sort();
}
/**
* Responds to clicking the check box in the flyout menu
@@ -193,6 +193,16 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
}
}
+Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
+ const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []);
+ for (let i = 0; i < docFilters.length; i += 3) {
+ const [header, value, state] = docFilters.slice(i, i + 3);
+ if (header === facetHeader && value === facetValue) {
+ return state;
+ }
+ }
+ return undefined;
+});
Scripting.addGlobal(function readFacetData(layoutDoc: Doc, facetHeader: string) {
const allCollectionDocs = DocListCast(CollectionDockingView.Instance?.props.Document.allDocuments);
const set = new Set<string>();
diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss
index 08160a2f4..7bc6c1dfd 100644
--- a/src/client/views/nodes/PresBox.scss
+++ b/src/client/views/nodes/PresBox.scss
@@ -144,6 +144,21 @@ $light-background: #ececec;
grid-template-columns: repeat(auto-fit, 70px);
}
+ .ribbon-colorBox {
+ cursor: pointer;
+ border: solid 1px black;
+ display: flex;
+ margin-left: 5px;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ justify-content: center;
+ align-items: center;
+ height: 15px;
+ width: 15px;
+ padding: 0px;
+ transform: translate(2px, 3px);
+ }
+
.ribbon-property {
font-size: 11;
font-weight: 200;
@@ -415,10 +430,38 @@ $light-background: #ececec;
.ribbon-button {
cursor: pointer;
font-size: 10.5;
+ font-weight: 300;
+ height: 20;
+ background-color: #979797;
+ color: white;
+ display: flex;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ border-radius: 5px;
+ margin-right: 5px;
+ width: max-content;
+ justify-content: center;
+ align-items: center;
+ padding-right: 10px;
+ padding-left: 10px;
+ }
+
+ .ribbon-button:hover {
+ transform: scale(1.03);
+ transition: all 0.4s;
+ font-weight: 400;
+ opacity: 1;
+ color: white;
+ background-color: black;
+ }
+
+ .ribbon-toggle {
+ cursor: pointer;
+ font-size: 10.5;
font-weight: 200;
height: 20;
background-color: $light-background;
- border: solid 1px black;
+ border: solid 1px rgba(0, 0, 0, 0.5);
display: flex;
margin-top: 5px;
margin-bottom: 5px;
@@ -431,12 +474,14 @@ $light-background: #ececec;
padding-left: 10px;
}
- .ribbon-button.active {
+ .ribbon-toggle.active {
background-color: #aedef8;
}
- .ribbon-button:hover {
- background-color: lightgrey;
+ .ribbon-toggle:hover {
+ transform: scale(1.03);
+ transition: all 0.4s;
+ border: solid 1px rgba(0, 0, 0, 0.75);
}
svg.svg-inline--fa.fa-thumbtack.fa-w-12.toolbar-thumbtack {
@@ -801,82 +846,6 @@ $light-background: #ececec;
}
- .presPanelOverlay {
- background-color: #323232;
- color: white;
- border-radius: 5px;
- grid-template-rows: 100%;
- height: 25;
- width: max-content;
- min-width: max-content;
- justify-content: space-evenly;
- align-items: center;
- display: flex;
- position: absolute;
- right: 10px;
- transition: all 0.2s;
-
- .presPanel-button-text {
- display: flex;
- height: 20;
- width: max-content;
- font-family: Roboto;
- font-weight: 400;
- margin-left: 3px;
- margin-right: 3px;
- padding-right: 3px;
- padding-left: 3px;
- letter-spacing: normal;
- border-radius: 5px;
- align-items: center;
- justify-content: center;
- transition: all 0.3s;
- }
-
- .presPanel-divider {
- width: 0.5px;
- height: 80%;
- border-right: solid 1px #5a5a5a;
- }
-
- .presPanel-button-frame {
- justify-self: center;
- align-self: center;
- align-items: center;
- display: grid;
- grid-template-columns: auto auto auto;
- justify-content: space-around;
- font-size: 11;
- margin-left: 7;
- width: 30;
- height: 85%;
- background-color: rgba(91, 157, 221, 0.4);
- border-radius: 5px;
- }
-
- .presPanel-button {
- cursor: pointer;
- display: flex;
- height: 20;
- min-width: 20;
- margin-left: 3px;
- margin-right: 3px;
- border-radius: 100%;
- align-items: center;
- justify-content: center;
- transition: all 0.3s;
- }
-
- .presPanel-button:hover {
- background-color: #5a5a5a;
- }
-
- .presPanel-button-text:hover {
- background-color: #5a5a5a;
- }
- }
-
-
.collectionViewBaseChrome-viewPicker {
min-width: 50;
@@ -952,80 +921,165 @@ $light-background: #ececec;
}
}
-.miniPres:hover {
- opacity: 1;
-}
-
.miniPres {
cursor: grab;
position: absolute;
- overflow: hidden;
right: 10;
top: 10;
opacity: 0.1;
transition: all 0.4s;
- /* border: solid 1px; */
color: white;
- /* box-shadow: black 0.4vw 0.4vw 0.8vw; */
+}
- .miniPresOverlay {
- display: grid;
- grid-template-columns: auto auto auto auto auto auto auto auto auto auto;
- grid-template-rows: 100%;
- height: 100%;
- justify-items: center;
- align-items: center;
+.miniPres:hover {
+ opacity: 1;
+}
- .miniPres-button-text {
- cursor: pointer;
- display: flex;
- height: 20;
- font-weight: 400;
- min-width: 100%;
- border-radius: 5px;
- align-items: center;
- justify-content: center;
- transition: all 0.3s;
- }
+.presPanelOverlay {
+ background-color: #323232;
+ color: white;
+ border-radius: 5px;
+ grid-template-rows: 100%;
+ height: 25;
+ width: max-content;
+ min-width: max-content;
+ justify-content: space-evenly;
+ align-items: center;
+ display: flex;
+ position: absolute;
+ right: 10px;
+ transition: all 0.2s;
- .miniPres-button-frame {
- justify-self: center;
- align-self: center;
- align-items: center;
- display: grid;
- grid-template-columns: auto auto auto;
- justify-content: space-around;
- font-size: 11;
- margin-left: 7;
- width: 30;
- height: 85%;
- background-color: rgba(91, 157, 221, 0.4);
- border-radius: 5px;
- }
+ .presPanel-button-text {
+ display: flex;
+ height: 20;
+ width: max-content;
+ font-family: Roboto;
+ font-weight: 400;
+ margin-left: 3px;
+ margin-right: 3px;
+ padding-right: 3px;
+ padding-left: 3px;
+ letter-spacing: normal;
+ border-radius: 5px;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s;
+ }
- .miniPres-divider {
- width: 0.5px;
- height: 80%;
- border-right: solid 2px #5a5a5a;
- }
+ .presPanel-divider {
+ width: 0.5px;
+ height: 80%;
+ border-right: solid 1px #5a5a5a;
+ }
- .miniPres-button {
- cursor: pointer;
- display: flex;
- height: 20;
- min-width: 20;
- border-radius: 100%;
- align-items: center;
- justify-content: center;
- transition: all 0.3s;
- }
+ .presPanel-button-frame {
+ justify-self: center;
+ align-self: center;
+ align-items: center;
+ display: grid;
+ grid-template-columns: auto auto auto;
+ justify-content: space-around;
+ font-size: 11;
+ margin-left: 7;
+ width: 30;
+ height: 85%;
+ background-color: rgba(91, 157, 221, 0.4);
+ border-radius: 5px;
+ }
- .miniPres-button:hover {
- background-color: #5a5a5a;
- }
+ .presPanel-button {
+ cursor: pointer;
+ display: flex;
+ height: 20;
+ min-width: 20;
+ margin-left: 3px;
+ margin-right: 3px;
+ border-radius: 100%;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s;
+ }
- .miniPres-button-text:hover {
- background-color: #5a5a5a;
- }
+ .presPanel-button:hover {
+ background-color: #5a5a5a;
}
-} \ No newline at end of file
+
+ .presPanel-button-text:hover {
+ background-color: #5a5a5a;
+ }
+}
+
+// .miniPres {
+// cursor: grab;
+// position: absolute;
+// overflow: hidden;
+// right: 10;
+// top: 10;
+// opacity: 0.1;
+// transition: all 0.4s;
+// /* border: solid 1px; */
+// color: white;
+// /* box-shadow: black 0.4vw 0.4vw 0.8vw; */
+
+// .miniPresOverlay {
+// display: grid;
+// grid-template-columns: auto auto auto auto auto auto auto auto auto auto;
+// grid-template-rows: 100%;
+// height: 100%;
+// justify-items: center;
+// align-items: center;
+
+// .miniPres-button-text {
+// cursor: pointer;
+// display: flex;
+// height: 20;
+// font-weight: 400;
+// min-width: 100%;
+// border-radius: 5px;
+// align-items: center;
+// justify-content: center;
+// transition: all 0.3s;
+// }
+
+// .miniPres-button-frame {
+// justify-self: center;
+// align-self: center;
+// align-items: center;
+// display: grid;
+// grid-template-columns: auto auto auto;
+// justify-content: space-around;
+// font-size: 11;
+// margin-left: 7;
+// width: 30;
+// height: 85%;
+// background-color: rgba(91, 157, 221, 0.4);
+// border-radius: 5px;
+// }
+
+// .miniPres-divider {
+// width: 0.5px;
+// height: 80%;
+// border-right: solid 2px #5a5a5a;
+// }
+
+// .miniPres-button {
+// cursor: pointer;
+// display: flex;
+// height: 20;
+// min-width: 20;
+// border-radius: 100%;
+// align-items: center;
+// justify-content: center;
+// transition: all 0.3s;
+// }
+
+// .miniPres-button:hover {
+// background-color: #5a5a5a;
+// }
+
+// .miniPres-button-text:hover {
+// background-color: #5a5a5a;
+// }
+// }
+// } \ No newline at end of file
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index b0563c373..c5a64af3e 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -4,7 +4,7 @@ import { Tooltip } from "@material-ui/core";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import { ColorState, SketchPicker } from "react-color";
-import { Doc, DocCastAsync, DocListCast } from "../../../fields/Doc";
+import { Doc, DocCastAsync, DocListCast, DocListCastAsync } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
import { InkTool } from "../../../fields/InkField";
import { List } from "../../../fields/List";
@@ -28,6 +28,23 @@ import { AudioBox } from "./AudioBox";
import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
import { FieldView, FieldViewProps } from './FieldView';
import "./PresBox.scss";
+import { VideoBox } from "./VideoBox";
+
+export enum PresMovement {
+ Zoom = "zoom",
+ Pan = "pan",
+ Jump = "jump",
+ None = "none",
+}
+
+export enum PresEffect {
+ Fade = "fade",
+ Flip = "flip",
+ Rotate = "rotate",
+ Bounce = "bounce",
+ Roll = "roll",
+ None = "none",
+}
type PresBoxSchema = makeInterface<[typeof documentSchema]>;
const PresBoxDocument = makeInterface(documentSchema);
@@ -50,11 +67,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@observable private transitionTools: boolean = false;
@observable private newDocumentTools: boolean = false;
@observable private progressivizeTools: boolean = false;
- @observable private moreInfoTools: boolean = false;
- @observable private playTools: boolean = false;
@observable private presentTools: boolean = false;
@observable private pathBoolean: boolean = false;
- @observable private expandBoolean: boolean = false;
@observable private openMovementDropdown: boolean = false;
@observable private openEffectDropdown: boolean = false;
@computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
@@ -79,11 +93,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox
}
@computed get selectedDocumentView() {
- if (SelectionManager.SelectedDocuments().length) {
- return SelectionManager.SelectedDocuments()[0];
- } else if (PresBox.Instance._selectedArray.length) {
- return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
- } else { return undefined; }
+ if (SelectionManager.SelectedDocuments().length) return SelectionManager.SelectedDocuments()[0];
+ if (PresBox.Instance && PresBox.Instance._selectedArray) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
+ return undefined;
}
@computed get isPres(): boolean {
document.removeEventListener("keydown", this.keyEvents, true);
@@ -97,18 +109,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
componentWillUnmount() {
document.removeEventListener("keydown", this.keyEvents, true);
- this.turnOffEdit();
+ // Turn of progressivize editors
+ this.turnOffEdit(true);
}
- componentDidMount() {
+ componentDidMount = async () => {
this.rootDoc.presBox = this.rootDoc;
this.rootDoc._forceRenderEngine = "timeline";
this.rootDoc._replacedChrome = "replaced";
this.layoutDoc.presStatus = "edit";
this.layoutDoc._gridGap = 5;
- if (!DocListCast((Doc.UserDoc().myPresentations as Doc).data).includes(this.rootDoc)) {
- Doc.AddDocToList(Doc.UserDoc().myPresentations as Doc, "data", this.rootDoc);
- }
+ this.turnOffEdit(true);
+ DocListCastAsync((Doc.UserDoc().myPresentations as Doc).data).then(async pres => {
+ await Promise.all(pres!);
+ if (!DocListCast((Doc.UserDoc().myPresentations as Doc).data).includes(this.rootDoc)) Doc.AddDocToList(Doc.UserDoc().myPresentations as Doc, "data", this.rootDoc);
+ });
}
updateCurrentPresentation = () => {
@@ -131,32 +146,38 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const lastFrame = Cast(targetDoc.lastFrame, "number", null);
const curFrame = NumCast(targetDoc._currentFrame);
let internalFrames: boolean = false;
- if (targetDoc.presProgressivize || activeItem.zoomProgressivize || targetDoc.scrollProgressivize) internalFrames = true;
+ if (activeItem.presProgressivize || activeItem.zoomProgressivize || targetDoc.scrollProgressivize) internalFrames = true;
// Case 1: There are still other frames and should go through all frames before going to next slide
if (internalFrames && lastFrame !== undefined && curFrame < lastFrame) {
targetDoc._viewTransition = "all 1s";
setTimeout(() => targetDoc._viewTransition = undefined, 1010);
- targetDoc._currentFrame = curFrame + 1;
- if (targetDoc.scrollProgressivize) CollectionFreeFormDocumentView.updateScrollframe(targetDoc, currentFrame);
- if (targetDoc.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, targetDoc);
+ // targetDoc._currentFrame = curFrame + 1;
+ this.nextKeyframe(targetDoc, activeItem);
+ // if (targetDoc.scrollProgressivize) CollectionFreeFormDocumentView.updateScrollframe(targetDoc, currentFrame);
+ if (activeItem.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, targetDoc);
else targetDoc.editing = true;
- if (activeItem.zoomProgressivize) this.zoomProgressivizeNext(targetDoc);
- // Case 2: Audio or video therefore wait to play the audio or video before moving on
- } else if ((targetDoc.type === DocumentType.AUDIO) && !this._moveOnFromAudio && this.layoutDoc.presStatus !== 'auto') {
- AudioBox.Instance.playFrom(0);
- this._moveOnFromAudio = true;
- // Case 3: No more frames in current doc and next slide is defined, therefore move to next slide
+ // if (activeItem.zoomProgressivize) this.zoomProgressivizeNext(targetDoc);
+ // Case 2: 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played
+ } else if ((targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && !activeItem.playAuto && activeItem.playNow && this.layoutDoc.presStatus !== 'auto') {
+ if (targetDoc.type === DocumentType.AUDIO) AudioBox.Instance.playFrom(NumCast(activeItem.presStartTime));
+ // if (targetDoc.type === DocumentType.VID) { VideoBox.Instance.Play() };
+ activeItem.playNow = false;
+ // Case 3: No more frames in current doc and next slide is defined, therefore move to next slide
} else if (this.childDocs[this.itemIndex + 1] !== undefined) {
+ if (activeNext.presPinView) setTimeout(() => this.selectPres(), 0);
+ else this.selectPres();
const nextSelected = this.itemIndex + 1;
- if (targetDoc.type === DocumentType.AUDIO) AudioBox.Instance.pause();
- this.gotoDocument(nextSelected, this.itemIndex);
+ if (targetDoc.type === DocumentType.AUDIO) { if (AudioBox.Instance._ele) AudioBox.Instance.pause(); }
+ // if (targetDoc.type === DocumentType.VID) { if (AudioBox.Instance._ele) VideoBox.Instance.Pause(); }
const targetNext = Cast(activeNext.presentationTargetDoc, Doc, null);
- if (activeNext && targetNext.type === DocumentType.AUDIO && activeNext.playAuto) {
- AudioBox.Instance.playFrom(0);
- this._moveOnFromAudio = true;
- } else this._moveOnFromAudio = false;
+ // If next slide is audio / video 'Play automatically' then the next slide should be played
+ if (activeNext && (targetNext.type === DocumentType.AUDIO || targetNext.type === DocumentType.VID) && activeNext.playAuto) {
+ console.log('play next automatically');
+ if (targetNext.type === DocumentType.AUDIO) AudioBox.Instance.playFrom(NumCast(activeNext.presStartTime));
+ // if (targetNext.type === DocumentType.VID) { VideoBox.Instance.Play() };
+ } else if (targetNext.type === DocumentType.AUDIO || targetNext.type === DocumentType.VID) { activeNext.playNow = true; console.log('play next after it is navigated to'); }
+ this.gotoDocument(nextSelected, this.itemIndex);
} else if (this.childDocs[this.itemIndex + 1] === undefined && this.layoutDoc.presLoop) {
- const nextSelected = 0;
this.gotoDocument(0, this.itemIndex);
}
}
@@ -176,6 +197,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const prevTargetDoc = Cast(prevItem.presentationTargetDoc, Doc, null);
const lastFrame = Cast(targetDoc.lastFrame, "number", null);
const curFrame = NumCast(targetDoc._currentFrame);
+ if (prevItem.presPinView) setTimeout(() => this.selectPres(), 0);
+ else this.selectPres();
if (lastFrame !== undefined && curFrame >= 1) {
this.prevKeyframe(targetDoc, activeItem);
} else if (activeItem) {
@@ -193,9 +216,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
Doc.UnBrushAllDocs();
if (index >= 0 && index < this.childDocs.length) {
this.rootDoc._itemIndex = index;
+ const activeItem: Doc = this.activeItem;
const presTargetDoc = Cast(this.childDocs[this.itemIndex].presentationTargetDoc, Doc, null);
+ if (activeItem.presPinView) {
+ const bestTarget = DocumentManager.Instance.getFirstDocumentView(presTargetDoc)?.props.Document;
+ bestTarget && runInAction(() => {
+ if (activeItem.presMovement === PresMovement.Jump) {
+ bestTarget._viewTransition = '0s';
+ } else {
+ bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 1s';
+ setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 1010);
+ }
+ });
+ } else {
+ presTargetDoc && runInAction(() => {
+ if (activeItem.presMovement === PresMovement.Jump) presTargetDoc.focusSpeed = 0;
+ else presTargetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500;
+ });
+ setTimeout(() => presTargetDoc.focusSpeed = 500, this.activeItem.presTransition ? NumCast(this.activeItem.presTransition) + 10 : 510);
+ }
if (presTargetDoc?.lastFrame !== undefined) {
- presTargetDoc.currentFrame = 0;
+ presTargetDoc._currentFrame = 0;
}
this._selectedArray = [this.childDocs[index]]; //Update selected array
//Handles movement to element
@@ -219,7 +260,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
const collectionDocView = presCollection ? await DocumentManager.Instance.getDocumentView(presCollection) : undefined;
this.turnOffEdit();
-
if (this.itemIndex >= 0) {
if (srcContext && targetDoc) {
this.layoutDoc.presCollection = srcContext;
@@ -244,9 +284,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.zoomProgressivizeNext(targetDoc);
} else if (docToJump === curDoc) {
//checking if curDoc has navigation open
- if (curDoc.presNavButton && targetDoc) {
+ if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
await DocumentManager.Instance.jumpToDocument(targetDoc, false, undefined, srcContext);
- } else if (curDoc.presZoomButton && targetDoc) {
+ } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
//awaiting jump so that new scale can be found, since jumping is async
await DocumentManager.Instance.jumpToDocument(targetDoc, true, undefined, srcContext);
}
@@ -261,7 +301,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
// if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target
const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
bestTarget && runInAction(() => {
- bestTarget._viewTransition = "all 1s";
+ bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
bestTarget._panX = activeItem.presPinViewX;
bestTarget._panY = activeItem.presPinViewY;
bestTarget._viewScale = activeItem.presPinViewScale;
@@ -358,13 +398,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.updateCurrentPresentation();
let activeItem: Doc = this.activeItem;
let targetDoc: Doc = this.targetDoc;
- let duration = NumCast(targetDoc.presDuration) + NumCast(targetDoc.presTransition);
+ let duration = NumCast(activeItem.presDuration) + NumCast(activeItem.presTransition);
const timer = (ms: number) => new Promise(res => this._presTimer = setTimeout(res, ms));
const load = async () => { // Wrap the loop into an async function for this to work
for (var i = startSlide; i < this.childDocs.length; i++) {
activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- duration = NumCast(targetDoc.presDuration) + NumCast(targetDoc.presTransition);
+ duration = NumCast(activeItem.presDuration) + NumCast(activeItem.presTransition);
if (duration <= 100) { duration = 2500; }
if (NumCast(targetDoc.lastFrame) > 0) {
for (var f = 0; f < NumCast(targetDoc.lastFrame); f++) {
@@ -404,7 +444,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.rootDoc._itemIndex = 0;
}
- @action togglePath = () => this.pathBoolean = !this.pathBoolean;
+ @action togglePath = (srcContext: Doc, off?: boolean) => {
+ if (off) {
+ this.pathBoolean = false;
+ srcContext.presPathView = false;
+ } else {
+ this.pathBoolean = !this.pathBoolean;
+ srcContext.presPathView = this.pathBoolean;
+ }
+ }
@action toggleExpandMode = () => {
this.rootDoc.expandBoolean = !this.rootDoc.expandBoolean;
this.childDocs.forEach((doc) => {
@@ -445,7 +493,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
CollectionDockingView.AddSplit(this.rootDoc, "right");
this.layoutDoc.inOverlay = false;
} else if (this.layoutDoc.context && docView) {
- this.layoutDoc.presStatus = 'manual';
+ this.layoutDoc.presStatus = 'edit';
+ clearTimeout(this._presTimer);
const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
this.rootDoc.x = pt[0] + (this.props.PanelWidth() - 250);
this.rootDoc.y = pt[1] + 10;
@@ -454,7 +503,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
docView.props.removeDocument?.(this.layoutDoc);
Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
} else {
- this.layoutDoc.presStatus = 'manual';
+ this.layoutDoc.presStatus = 'edit';
+ clearTimeout(this._presTimer);
const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
this.rootDoc.x = pt[0] + (this.props.PanelWidth() - 250);
this.rootDoc.y = pt[1] + 10;
@@ -485,33 +535,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@undoBatch
updateMovement = action((movement: any, activeItem: Doc, targetDoc: Doc) => {
switch (movement) {
- case 'zoom': //Pan and zoom
- activeItem.presNavButton = false;
- activeItem.presZoomButton = !activeItem.presZoomButton;
- targetDoc.presTransition = activeItem.presTransition;
- if (activeItem.presZoomButton) activeItem.presMovement = 'zoom';
- else activeItem.presMovement = 'none';
+ case PresMovement.Zoom: //Pan and zoom
+ activeItem.presMovement = PresMovement.Zoom;
break;
- case 'pan': //Pan
- activeItem.presZoomButton = false;
- activeItem.presNavButton = !activeItem.presNavButton;
- targetDoc.presTransition = activeItem.presTransition;
- if (activeItem.presNavButton) activeItem.presMovement = 'pan';
- else activeItem.presMovement = 'none';
+ case PresMovement.Pan: //Pan
+ activeItem.presMovement = PresMovement.Pan;
break;
- case 'jump': //Jump Cut
- activeItem.presTransition = targetDoc.presTransition;
- targetDoc.presTransition = 0;
- activeItem.presZoomButton = true;
- activeItem.presSwitchButton = !activeItem.presSwitchButton;
- if (activeItem.presSwitchButton) activeItem.presMovement = 'jump';
- else activeItem.presMovement = 'none';
+ case PresMovement.Jump: //Jump Cut
+ activeItem.presJump = true;
+ activeItem.presMovement = PresMovement.Jump;
break;
- case 'none': default:
- activeItem.presMovement = 'none';
- activeItem.presZoomButton = false;
- activeItem.presNavButton = false;
- activeItem.presSwitchButton = false;
+ case PresMovement.None: default:
+ activeItem.presMovement = PresMovement.None;
break;
}
});
@@ -519,10 +554,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
setMovementName = action((movement: any, activeItem: Doc): string => {
let output: string = 'none';
switch (movement) {
- case 'zoom': output = 'Zoom'; break; //Pan and zoom
- case 'pan': output = 'Pan'; break; //Pan
- case 'jump': output = 'Jump cut'; break; //Jump Cut
- case 'none': output = 'None'; break; //None
+ case PresMovement.Zoom: output = 'Zoom'; break; //Pan and zoom
+ case PresMovement.Pan: output = 'Pan'; break; //Pan
+ case PresMovement.Jump: output = 'Jump cut'; break; //Jump Cut
+ case PresMovement.None: output = 'None'; break; //None
default: output = 'Zoom'; activeItem.presMovement = 'zoom'; break; //default set as zoom
}
return output;
@@ -535,9 +570,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
docs.forEach((doc, i) => {
if (this.childDocs.includes(doc)) {
if (docs.length === i + 1) return false;
+ } else if (doc.type === DocumentType.LABEL) {
+ const audio = Cast(doc.annotationOn, Doc, null);
+ if (audio) {
+ audio.aliasOf instanceof Doc;
+ audio.presStartTime = NumCast(doc.audioStart);
+ audio.presEndTime = NumCast(doc.audioEnd);
+ audio.presDuration = NumCast(doc.audioEnd) - NumCast(doc.audioStart);
+ TabDocView.PinDoc(audio, false, true);
+ setTimeout(() => this.removeDocument(doc), 1);
+ return false;
+ }
} else {
doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf);
- !this.childDocs.includes(doc) && (doc.presZoomButton = true);
+ !this.childDocs.includes(doc) && (doc.presMovement = PresMovement.Zoom);
if (this.rootDoc.expandBoolean) doc.presExpandInlineButton = true;
}
});
@@ -571,16 +617,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const list = this._selectedArray.map((doc: Doc, index: any) => {
const curDoc = Cast(doc, Doc, null);
const tagDoc = Cast(curDoc.presentationTargetDoc!, Doc, null);
- return (
- <div className="selectedList-items">{index + 1}. {tagDoc.title}</div>
- );
+ if (tagDoc) return <div className="selectedList-items">{index + 1}. {curDoc.title}</div>;
+ else if (curDoc) return <div className="selectedList-items">{index + 1}. {curDoc.title}</div>;
});
return list;
}
@action
selectPres = () => {
- const presDocView = DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc)!;
+ const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc)!;
SelectionManager.SelectDoc(presDocView, false);
}
@@ -588,7 +633,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
selectElement = (doc: Doc) => {
this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.itemIndex));
- this.selectPres();
+ if (doc.presPinView) setTimeout(() => this.selectPres(), 0);
+ else this.selectPres();
}
//Command click
@@ -645,7 +691,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
handled = true;
} if (e.keyCode === 32) { // spacebar to 'present' or autoplay
if (this.layoutDoc.presStatus !== "edit") this.startAutoPres(0);
- else this.layoutDoc.presStatus = "manual"; if (this._presTimer) clearTimeout(this._presTimer);
+ else this.next();
handled = true;
} if (e.keyCode === 8) { // delete selected items
if (this.layoutDoc.presStatus === "edit") {
@@ -680,14 +726,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
*/
@undoBatch
@action
- viewPaths = async () => {
+ viewPaths = () => {
const srcContext = Cast(this.rootDoc.presCollection, Doc, null);
- if (this.pathBoolean && srcContext) {
- this.togglePath();
- srcContext.presPathView = false;
- } else if (srcContext) {
- this.togglePath();
- srcContext.presPathView = true;
+ if (srcContext) {
+ this.togglePath(srcContext);
}
}
@@ -697,10 +739,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.childDocs.forEach((doc, index) => {
const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
const srcContext = Cast(tagDoc?.context, Doc, null);
+ const width = NumCast(tagDoc._width) / 10;
+ const height = Math.max(NumCast(tagDoc._height) / 10, 15);
+ const edge = Math.max(width, height);
+ const fontSize = edge * 0.8;
// Case A: Document is contained within the colleciton
if (this.rootDoc.presCollection === srcContext) {
order.push(
- <div className="pathOrder" style={{ top: NumCast(tagDoc.y), left: NumCast(tagDoc.x) }}>
+ <div className="pathOrder" style={{ top: NumCast(tagDoc.y) - (edge / 2), left: NumCast(tagDoc.x) - (edge / 2), width: edge, height: edge, fontSize: fontSize }}>
<div className="pathOrder-frame">{index + 1}</div>
</div>);
// Case B: Document is not inside of the collection
@@ -744,11 +790,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
stroke: "#69a6db",
strokeWidth: 5,
strokeDasharray: '10 5',
+ boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
}}
fill="none"
- markerStart="url(#markerSquare)"
+ markerStart="url(#markerArrow)"
markerMid="url(#markerSquare)"
- markerEnd="url(#markerArrow)"
+ markerEnd="url(#markerSquareFilled)"
/>);
}
@@ -781,7 +828,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (change) timeInMS += change;
if (timeInMS < 100) timeInMS = 100;
if (timeInMS > 10000) timeInMS = 10000;
- if (this.targetDoc) this.targetDoc.presTransition = timeInMS;
+ if (this.activeItem) this.activeItem.presTransition = timeInMS;
}
// Converts seconds to ms and updates presDuration
@@ -790,7 +837,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (change) timeInMS += change;
if (timeInMS < 100) timeInMS = 100;
if (timeInMS > 20000) timeInMS = 20000;
- if (this.targetDoc) this.targetDoc.presDuration = timeInMS;
+ if (this.activeItem) this.activeItem.presDuration = timeInMS;
}
@@ -798,9 +845,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
if (activeItem && targetDoc) {
- const transitionSpeed = targetDoc.presTransition ? NumCast(targetDoc.presTransition) / 1000 : 0.5;
- let duration = targetDoc.presDuration ? NumCast(targetDoc.presDuration) / 1000 : 2;
- if (targetDoc.type === DocumentType.AUDIO) duration = NumCast(targetDoc.duration);
+ const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5;
+ let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 2;
+ if (activeItem.type === DocumentType.AUDIO) duration = NumCast(activeItem.duration);
const effect = targetDoc.presEffect ? targetDoc.presEffect : 'None';
activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : 'Zoom';
return (
@@ -811,18 +858,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
{this.setMovementName(activeItem.presMovement, activeItem)}
<FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? '#5B9FDD' : 'black' }} icon={"angle-down"} />
<div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? "grid" : "none" }}>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === 'none' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement('none', activeItem, targetDoc)}>None</div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === 'zoom' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement('zoom', activeItem, targetDoc)}>Pan and Zoom</div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === 'pan' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement('pan', activeItem, targetDoc)}>Pan</div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === 'jump' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement('jump', activeItem, targetDoc)}>Jump cut</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None, activeItem, targetDoc)}>None</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom, activeItem, targetDoc)}>Pan and Zoom</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Pan ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan, activeItem, targetDoc)}>Pan</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Jump ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump, activeItem, targetDoc)}>Jump cut</div>
</div>
</div>
- <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === 'pan' || activeItem.presMovement === 'zoom' ? "inline-flex" : "none" }}>
+ <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "inline-flex" : "none" }}>
<div className="presBox-subheading">Transition Speed</div>
<div className="ribbon-property">
<input className="presBox-input"
type="number" value={transitionSpeed}
- onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={action((e) => this.setTransitionTime(e.target.value))} /> s
</div>
<div className="ribbon-propertyUpDown">
@@ -834,8 +880,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
</div>
</div>
- <input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed} className={`toolbar-slider ${activeItem.presMovement === 'pan' || activeItem.presMovement === 'zoom' ? "" : "none"}`} id="toolbar-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setTransitionTime(e.target.value); }} />
- <div className={`slider-headers ${activeItem.presMovement === 'pan' || activeItem.presMovement === 'zoom' ? "" : "none"}`}>
+ <input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed} className={`toolbar-slider ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`} id="toolbar-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setTransitionTime(e.target.value); }} />
+ <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}>
<div className="slider-text">Fast</div>
<div className="slider-text">Medium</div>
<div className="slider-text">Slow</div>
@@ -844,15 +890,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-box">
Visibility {"&"} Duration
<div className="ribbon-doubleButton">
- <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-button ${activeItem.presHideTillShownButton ? "active" : ""}`} onClick={() => activeItem.presHideTillShownButton = !activeItem.presHideTillShownButton}>Hide before</div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-button ${activeItem.presHideAfterButton ? "active" : ""}`} onClick={() => activeItem.presHideAfterButton = !activeItem.presHideAfterButton}>Hide after</div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideTillShownButton ? "active" : ""}`} onClick={() => activeItem.presHideTillShownButton = !activeItem.presHideTillShownButton}>Hide before</div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideAfterButton ? "active" : ""}`} onClick={() => activeItem.presHideAfterButton = !activeItem.presHideAfterButton}>Hide after</div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Open document in a new tab"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? "#aedef8" : "" }} onClick={() => activeItem.openDocument = !activeItem.openDocument}>Open</div></Tooltip>
</div>
<div className="ribbon-doubleButton" >
<div className="presBox-subheading">Slide Duration</div>
<div className="ribbon-property">
<input className="presBox-input"
type="number" value={duration}
- onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
+ // onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={action((e) => this.setDurationTime(e.target.value))} /> s
</div>
<div className="ribbon-propertyUpDown">
@@ -929,14 +976,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
applyTo = (array: Doc[]) => {
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
- array.forEach((doc, index) => {
+ array.forEach((doc) => {
const curDoc = Cast(doc, Doc, null);
const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
if (tagDoc && targetDoc) {
- tagDoc.presTransition = targetDoc.presTransition;
- tagDoc.presDuration = targetDoc.presDuration;
+ curDoc.presTransition = activeItem.presTransition;
+ curDoc.presDuration = activeItem.presDuration;
tagDoc.presEffect = targetDoc.presEffect;
tagDoc.presEffectDirection = targetDoc.presEffectDirection;
+ tagDoc.presMovement = targetDoc.presMovement;
curDoc.presMovement = activeItem.presMovement;
this.updateMovement(activeItem.presMovement, curDoc, tagDoc);
curDoc.presHideTillShownButton = activeItem.presHideTillShownButton;
@@ -947,20 +995,38 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get optionsDropdown() {
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)' }} />;
if (activeItem && targetDoc) {
return (
<div>
<div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
<div className="ribbon-box">
<div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.AUDIO ? "inline-flex" : "none" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.playAuto ? "#aedef8" : "" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play automatically</div>
- <div className="ribbon-button" style={{ display: "flex", backgroundColor: activeItem.playAuto ? "" : "#aedef8" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play on next</div>
- </div>
- <div className="ribbon-doubleButton" style={{ display: "flex" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.openDocument ? "#aedef8" : "" }} onClick={() => activeItem.openDocument = !activeItem.openDocument}>Open document</div>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.playAuto ? "#aedef8" : "" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play automatically</div>
+ <div className="ribbon-toggle" style={{ display: "flex", backgroundColor: activeItem.playAuto ? "" : "#aedef8" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play on next</div>
</div>
+ {targetDoc.type === DocumentType.VID ? <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presVidFullScreen ? "#aedef8" : "" }} onClick={() => activeItem.presVidFullScreen = !activeItem.presVidFullScreen}>Full screen</div> : (null)}
+ {targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.AUDIO ? <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+ <div className="presBox-subheading">Start time</div>
+ <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+ <input className="presBox-input"
+ style={{ textAlign: 'left', width: 50 }}
+ type="number" value={NumCast(activeItem.presStartTime)}
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { activeItem.presStartTime = Number(e.target.value); })} />
+ </div>
+ </div> : (null)}
+ {targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.AUDIO ? <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+ <div className="presBox-subheading">End time</div>
+ <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+ <input className="presBox-input"
+ style={{ textAlign: 'left', width: 50 }}
+ type="number" value={NumCast(activeItem.presEndTime)}
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presEndTime = Number(val); })} />
+ </div>
+ </div> : (null)}
+ {targetDoc.type === DocumentType.COL ? 'Presentation Pin View' : (null)}
<div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.COL ? "inline-flex" : "none" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.presPinView ? "#aedef8" : "" }}
+ <div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? "#aedef8" : "" }}
onClick={() => {
activeItem.presPinView = !activeItem.presPinView;
targetDoc.presPinView = activeItem.presPinView;
@@ -972,7 +1038,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
activeItem.presPinViewY = y;
activeItem.presPinViewScale = scale;
}
- }}>Presentation pin view</div>
+ }}>{presPinWithViewIcon}</div>
+ {activeItem.presPinView ? <div className="ribbon-button"
+ onClick={() => {
+ const x = targetDoc._panX;
+ const y = targetDoc._panY;
+ const scale = targetDoc._viewScale;
+ activeItem.presPinViewX = x;
+ activeItem.presPinViewY = y;
+ activeItem.presPinViewScale = scale;
+ }}>Update</div> : (null)};
</div>
<div style={{ display: activeItem.presPinView ? "block" : "none" }}>
<div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
@@ -981,7 +1056,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<input className="presBox-input"
style={{ textAlign: 'left', width: 50 }}
type="number" value={NumCast(activeItem.presPinViewX)}
- onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewX = Number(val); })} />
</div>
</div>
@@ -991,7 +1065,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<input className="presBox-input"
style={{ textAlign: 'left', width: 50 }}
type="number" value={NumCast(activeItem.presPinViewY)}
- onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewY = Number(val); })} />
</div>
</div>
@@ -1001,13 +1074,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<input className="presBox-input"
style={{ textAlign: 'left', width: 50 }}
type="number" value={NumCast(activeItem.presPinViewScale)}
- onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScale = Number(val); })} />
</div>
</div>
</div>
{/* <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.WEB ? "inline-flex" : "none" }}>
- <div className="ribbon-button" onClick={this.progressivizeText}>Store original website</div>
+ <div className="ribbon-toggle" onClick={this.progressivizeText}>Store original website</div>
</div> */}
</div>
</div>
@@ -1051,18 +1123,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-box">
Slide Title: <br></br>
<input className="ribbon-textInput" placeholder="..." type="text" name="fname"
- onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={(e) => {
e.stopPropagation();
e.preventDefault();
runInAction(() => this.title = e.target.value);
- }}></input>
+ }}>
+ </input>
</div>
<div className="ribbon-box">
Choose type:
<div className="ribbon-doubleButton">
- <div title="Text" className={'ribbon-button'} style={{ background: this.addFreeform ? "" : "#aedef8" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div>
- <div title="Freeform" className={'ribbon-button'} style={{ background: this.addFreeform ? "#aedef8" : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div>
+ <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "" : "#aedef8" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div>
+ <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "#aedef8" : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div>
</div>
</div>
<div className="ribbon-box" style={{ display: this.addFreeform ? "grid" : "none" }}>
@@ -1129,13 +1201,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
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: "24px", });
- const subtitle = Docs.Create.TextDocument("Click to change subtitle", { title: "Slide subtitle", _width: 380, _height: 50, x: 10, y: 118, _fontSize: "16px" });
- const header = Docs.Create.TextDocument("Click to change header", { title: "Slide header", _width: 380, _height: 65, x: 10, y: 80, _fontSize: "20px" });
- const contentTitle = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 10, _fontSize: "24px" });
- const content = Docs.Create.TextDocument("Click to change text", { title: "Slide text", _width: 380, _height: 145, x: 10, y: 70, _fontSize: "14px" });
- const content1 = Docs.Create.TextDocument("Click to change text", { title: "Column 1", _width: 185, _height: 140, x: 10, y: 80, _fontSize: "14px" });
- const content2 = Docs.Create.TextDocument("Click to change text", { title: "Column 2", _width: 185, _height: 140, x: 205, y: 80, _fontSize: "14px" });
+ 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" });
switch (layout) {
case 'blank':
doc = Docs.Create.FreeformDocument([], { title: input ? input : "Blank slide", _width: 400, _height: 225, x: x, y: y });
@@ -1162,10 +1234,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get presentDropdown() {
return (
<div className={`dropdown-play ${this.presentTools ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
- <div className="dropdown-play-button" onClick={this.updateMinimize}>
+ <div className="dropdown-play-button" onClick={(action(() => { this.updateMinimize(); this.turnOffEdit(true); }))}>
Minimize
</div>
- <div className="dropdown-play-button" onClick={(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(); }))}>
+ <div className="dropdown-play-button" onClick={(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(true); }))}>
Sidebar view
</div>
</div>
@@ -1179,7 +1251,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
const currentFrame = Cast(tagDoc._currentFrame, "number", null);
if (currentFrame === undefined) {
- tagDoc.currentFrame = 0;
+ tagDoc._currentFrame = 0;
CollectionFreeFormDocumentView.setupScroll(tagDoc, 0);
CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
}
@@ -1187,15 +1259,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, tagDoc);
tagDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1);
tagDoc.lastFrame = Math.max(NumCast(tagDoc._currentFrame), NumCast(tagDoc.lastFrame));
- if (curDoc.zoomProgressivize) {
- const resize = document.getElementById('resizable');
- if (resize) {
- resize.style.width = this.checkList(tagDoc, curDoc["viewfinder-width-indexed"]) + 'px';
- resize.style.height = this.checkList(tagDoc, curDoc["viewfinder-height-indexed"]) + 'px';
- resize.style.top = this.checkList(tagDoc, curDoc["viewfinder-top-indexed"]) + 'px';
- resize.style.left = this.checkList(tagDoc, curDoc["viewfinder-left-indexed"]) + 'px';
- }
- }
+ // if (curDoc.zoomProgressivize) {
+ // const resize = document.getElementById('resizable');
+ // if (resize) {
+ // resize.style.width = this.checkList(tagDoc, curDoc["viewfinder-width-indexed"]) + 'px';
+ // resize.style.height = this.checkList(tagDoc, curDoc["viewfinder-height-indexed"]) + 'px';
+ // resize.style.top = this.checkList(tagDoc, curDoc["viewfinder-top-indexed"]) + 'px';
+ // resize.style.left = this.checkList(tagDoc, curDoc["viewfinder-left-indexed"]) + 'px';
+ // }
+ // }
}
@undoBatch
@@ -1209,15 +1281,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
CollectionFreeFormDocumentView.gotoKeyframe(childDocs.slice());
tagDoc._currentFrame = Math.max(0, (currentFrame || 0) - 1);
- if (actItem.zoomProgressivize) {
- const resize = document.getElementById('resizable');
- if (resize) {
- resize.style.width = this.checkList(tagDoc, actItem["viewfinder-width-indexed"]) + 'px';
- resize.style.height = this.checkList(tagDoc, actItem["viewfinder-height-indexed"]) + 'px';
- resize.style.top = this.checkList(tagDoc, actItem["viewfinder-top-indexed"]) + 'px';
- resize.style.left = this.checkList(tagDoc, actItem["viewfinder-left-indexed"]) + 'px';
- }
- }
+ // if (actItem.zoomProgressivize) {
+ // const resize = document.getElementById('resizable');
+ // if (resize) {
+ // resize.style.width = this.checkList(tagDoc, actItem["viewfinder-width-indexed"]) + 'px';
+ // resize.style.height = this.checkList(tagDoc, actItem["viewfinder-height-indexed"]) + 'px';
+ // resize.style.top = this.checkList(tagDoc, actItem["viewfinder-top-indexed"]) + 'px';
+ // resize.style.left = this.checkList(tagDoc, actItem["viewfinder-left-indexed"]) + 'px';
+ // }
+ // }
}
/**
@@ -1227,9 +1299,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
let type: string = '';
- const effectiveType = targetDoc.treeViewOutlineMode ? DocumentType.COL : targetDoc.type;// bcz: Argh .. .need a better way to identify a slide doc
if (activeItem) {
- switch (effectiveType) {
+ switch (targetDoc.type) {
case DocumentType.PDF: type = "PDF"; break;
case DocumentType.RTF: type = "Text node"; break;
case DocumentType.COL: type = "Collection"; break;
@@ -1249,9 +1320,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get progressivizeDropdown() {
- const activeItem = this.activeItem;
- const targetDoc = this.targetDoc;
- const effectiveType = targetDoc.treeViewOutlineMode ? DocumentType.COL : targetDoc.type;
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
if (activeItem && targetDoc) {
const activeFontColor = targetDoc["pres-text-color"] ? StrCast(targetDoc["pres-text-color"]) : "Black";
const viewedFontColor = targetDoc["pres-text-viewed-color"] ? StrCast(targetDoc["pres-text-viewed-color"]) : "Black";
@@ -1260,29 +1330,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className={`presBox-ribbon ${this.progressivizeTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
<div className="ribbon-box">
{this.stringType} selected
- <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: effectiveType === DocumentType.COL ? "inline-flex" : "none" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.presProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeChild}>Contents</div>
- <div className="ribbon-button" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? "#aedef8" : "" }} onClick={this.editProgressivize}>Edit</div>
+ <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeChild}>Contents</div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? "#aedef8" : "" }} onClick={this.editProgressivize}>Edit</div>
</div>
<div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}>
<div className="presBox-subheading">Active text color</div>
- <div className="ribbon-property" style={{ backgroundColor: activeFontColor }} onClick={action(() => { this.openActiveColorPicker = !this.openActiveColorPicker; })}>
+ <div className="ribbon-colorBox" style={{ backgroundColor: activeFontColor, height: 15, width: 15 }} onClick={action(() => { this.openActiveColorPicker = !this.openActiveColorPicker; })}>
</div>
</div>
{this.activeColorPicker}
<div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}>
<div className="presBox-subheading">Viewed font color</div>
- <div className="ribbon-property" style={{ backgroundColor: viewedFontColor }} onClick={action(() => this.openViewedColorPicker = !this.openViewedColorPicker)}>
+ <div className="ribbon-colorBox" style={{ backgroundColor: viewedFontColor, height: 15, width: 15 }} onClick={action(() => this.openViewedColorPicker = !this.openViewedColorPicker)}>
</div>
</div>
{this.viewedColorPicker}
- {/* <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (effectiveType === DocumentType.COL && targetDoc._viewType === 'freeform') || effectiveType === DocumentType.IMG ? "inline-flex" : "none" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.zoomProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeZoom}>Zoom</div>
- <div className="ribbon-button" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? "#aedef8" : "" }} onClick={this.editZoomProgressivize}>Edit</div>
+ {/* <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeZoom}>Zoom</div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? "#aedef8" : "" }} onClick={this.editZoomProgressivize}>Edit</div>
</div> */}
- <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || effectiveType === DocumentType.PDF || effectiveType === DocumentType.WEB || effectiveType === DocumentType.RTF ? "inline-flex" : "none" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.scrollProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeScroll}>Scroll</div>
- <div className="ribbon-button" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? "#aedef8" : "" }} onClick={this.editScrollProgressivize}>Edit</div>
+ <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeScroll}>Scroll</div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? "#aedef8" : "" }} onClick={this.editScrollProgressivize}>Edit</div>
</div>
</div>
<div className="ribbon-final-box" style={{ display: activeItem.zoomProgressivize || activeItem.scrollProgressivize || activeItem.presProgressivize || activeItem.textProgressivize ? "grid" : "none" }}>
@@ -1302,7 +1372,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
<Tooltip title={<><div className="dash-tooltip">{"Last frame"}</div></>}><div className="ribbon-property">{NumCast(targetDoc.lastFrame)}</div></Tooltip>
</div>
- <div className="ribbon-button" style={{ height: 20, backgroundColor: "#AEDDF8" }} onClick={() => console.log(" TODO: play frames")}>Play</div>
+ <div className="ribbon-toggle" style={{ height: 20, backgroundColor: "#AEDDF8" }} onClick={() => console.log(" TODO: play frames")}>Play</div>
</div>
</div>
</div>
@@ -1349,15 +1419,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
color={StrCast(targetDoc["pres-text-viewed-color"])} />;
}
- turnOffEdit = () => {
+ @action
+ turnOffEdit = (paths?: boolean) => {
+ if (paths) {
+ // Turn off paths
+ const srcContext = Cast(this.rootDoc.presCollection, Doc, null);
+ if (srcContext) this.togglePath(srcContext, true);
+ }
+ // Turn off the progressivize editors for each
this.childDocs.forEach((doc) => {
doc.editSnapZoomProgressivize = false;
doc.editZoomProgressivize = false;
- doc.editScrollProgressivize = false;
const targetDoc = Cast(doc.presentationTargetDoc, Doc, null);
if (targetDoc) {
targetDoc.editZoomProgressivize = false;
- targetDoc.editScrollProgressivize = false;
+ // targetDoc.editScrollProgressivize = false;
}
});
}
@@ -1437,28 +1513,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
@action
- progressivizeChild = (e?: React.MouseEvent) => {
- e?.stopPropagation();
+ progressivizeChild = (e: React.MouseEvent) => {
+ e.stopPropagation();
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
+ const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
if (!activeItem.presProgressivize) {
targetDoc.editing = false;
activeItem.presProgressivize = true;
targetDoc.presProgressivize = true;
targetDoc._currentFrame = 0;
- let count = 0;
- const setupProgressivize = (doc: Doc) => {
- CollectionFreeFormDocumentView.setupKeyframes([doc], count++, true);
- targetDoc.treeViewOutlineMode && DocListCast(doc[Doc.LayoutFieldKey(doc)]).forEach(setupProgressivize);
- };
- setupProgressivize(targetDoc);
- targetDoc.lastFrame = count;
+ docs.forEach((doc, i) => CollectionFreeFormDocumentView.setupKeyframes([doc], i, true));
+ targetDoc.lastFrame = targetDoc.lastFrame ? NumCast(targetDoc.lastFrame) : docs.length - 1;
} else {
targetDoc.editProgressivize = false;
activeItem.presProgressivize = false;
targetDoc.presProgressivize = false;
targetDoc._currentFrame = 0;
- targetDoc.lastFrame = 0;
targetDoc.editing = true;
}
}
@@ -1667,9 +1738,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const targetDoc: Doc = this.targetDoc;
return (
<>
- {this.targetDoc ? <div className="miniPres-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? "inline-flex" : "none" }}>
+ {this.targetDoc ? <div className="presPanel-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? "inline-flex" : "none" }}>
<div>{targetDoc._currentFrame}</div>
- <div className="miniPres-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
+ <div className="presPanel-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
<div>{targetDoc.lastFrame}</div>
</div> : null}
</>
@@ -1690,7 +1761,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
{this.playButtonFrames}
</div>
<div className="presPanel-divider"></div>
- {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={() => this.layoutDoc.presStatus = "edit"}>EXIT</div>
+ {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={() => { this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }}>EXIT</div>
: <div className="presPanel-button" onClick={() => this.layoutDoc.presStatus = "edit"}>
<FontAwesomeIcon icon={"times"} />
</div>}
@@ -1699,7 +1770,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
startOrPause = () => {
- if (this.layoutDoc.presStatus === "manual") this.startAutoPres(this.itemIndex);
+ if (this.layoutDoc.presStatus === "manual" || this.layoutDoc.presStatus === "edit") this.startAutoPres(this.itemIndex);
else this.pauseAutoPres();
}
@@ -1710,21 +1781,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.childDocs.slice();
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
return this.layoutDoc.inOverlay ?
- <div className="miniPres" style={{ width: 250, height: 35, background: '#323232', top: 0, zIndex: 3000000 }}>
- {<div className="miniPresOverlay">
- <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="miniPres-button" style={{ color: this.layoutDoc.presLoop ? '#AEDDF8' : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
- <div className="miniPres-divider"></div>
- <div className="miniPres-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div>
- <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === "auto" ? "Pause" : "Autoplay"}</div></>}><div className="miniPres-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip>
- <div className="miniPres-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div>
- <div className="miniPres-divider"></div>
- <div className="miniPres-button-text">
+ <div className="miniPres">
+ <div className="presPanelOverlay" style={{ display: "inline-flex", height: 35, background: '#323232', top: 0, zIndex: 3000000 }}>
+ <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? '#AEDDF8' : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
+ <div className="presPanel-divider"></div>
+ <div className="presPanel-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div>
+ <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === "auto" ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip>
+ <div className="presPanel-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div>
+ <div className="presPanel-divider"></div>
+ <div className="presPanel-button-text">
Slide {this.itemIndex + 1} / {this.childDocs.length}
{this.playButtonFrames}
</div>
- <div className="miniPres-divider"></div>
- <div className="miniPres-button-text" onClick={this.updateMinimize}>EXIT</div>
- </div>}
+ <div className="presPanel-divider"></div>
+ <div className="presPanel-button-text" onClick={this.updateMinimize}>EXIT</div>
+ </div>
</div>
:
<div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} >
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index a4114ed2c..b39a845db 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,5 +1,5 @@
import { IReactionDisposer, observable, computed, action } from "mobx";
-import { Doc, DocListCast, Field } from "../../../../fields/Doc";
+import { Doc, DocListCast, Field, DataSym } from "../../../../fields/Doc";
import { List } from "../../../../fields/List";
import { listSpec } from "../../../../fields/Schema";
import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
@@ -82,7 +82,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
// set the display of the field's value (checkbox for booleans, span of text for strings)
@computed get fieldValueContent() {
if (this._dashDoc) {
- const dashVal = this._dashDoc[this._fieldKey] || (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
+ const dashVal = this._dashDoc[DataSym][this._fieldKey] ?? this._dashDoc[this._fieldKey] ?? (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
const fval = dashVal instanceof List ? dashVal.join(this.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(this._textBoxDoc)[this._fieldKey] : dashVal;
const boolVal = Cast(fval, "boolean", null);
const strVal = Field.toString(fval as Field) || "";
@@ -94,7 +94,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
type="checkbox" checked={boolVal}
onChange={e => {
if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = e.target.checked;
- this._dashDoc![this._fieldKey] = e.target.checked;
+ Doc.SetInPlace(this._dashDoc!, this._fieldKey, e.target.checked, true);
}}
/>;
}
@@ -155,22 +155,22 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
if (modText) {
// elementfieldSpan.innerHTML = this._dashDoc![this._fieldKey as string] = modText;
DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, []);
- this._dashDoc![this._fieldKey] = modText;
+ Doc.SetInPlace(this._dashDoc!, this._fieldKey, modText, true);
} // if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key
else if (nodeText.startsWith(":=")) {
- this._dashDoc![this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(2));
+ this._dashDoc![DataSym][this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(2));
} else if (nodeText.startsWith("=:=")) {
Doc.Layout(this._textBoxDoc)[this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(3));
} else {
if (Number(newText).toString() === newText) {
if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = Number(newText);
- this._dashDoc![this._fieldKey] = Number(newText);
+ Doc.SetInPlace(this._dashDoc!, this._fieldKey, newText, true);
} else {
const splits = newText.split(this.multiValueDelimeter);
if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
const strVal = splits.length > 1 ? new List<string>(splits) : newText;
if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal;
- this._dashDoc![this._fieldKey] = strVal;
+ Doc.SetInPlace(this._dashDoc!, this._fieldKey, strVal, true);
}
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 83012bab5..dc0fafd24 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -22,7 +22,7 @@ import { RichTextField } from "../../../../fields/RichTextField";
import { RichTextUtils } from '../../../../fields/RichTextUtils';
import { makeInterface } from "../../../../fields/Schema";
import { Cast, DateCast, NumCast, StrCast, ScriptCast, BoolCast } from "../../../../fields/Types";
-import { TraceMobx, OVERRIDE_ACL, GetEffectiveAcl } from '../../../../fields/util';
+import { TraceMobx, OVERRIDE_acl, GetEffectiveAcl } from '../../../../fields/util';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents, OmitKeys } from '../../../../Utils';
import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
import { DocServer } from "../../../DocServer";
@@ -794,9 +794,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
tr = tr.addMark(pos, pos + node.nodeSize, link);
}
});
- OVERRIDE_ACL(true);
+ OVERRIDE_acl(true);
this._editorView!.dispatch(tr.removeMark(sel.from, sel.to, splitter));
- OVERRIDE_ACL(false);
+ OVERRIDE_acl(false);
}
}
componentDidMount() {
diff --git a/src/client/views/presentationview/PresElementBox.scss b/src/client/views/presentationview/PresElementBox.scss
index 6ee190b82..4642caeb2 100644
--- a/src/client/views/presentationview/PresElementBox.scss
+++ b/src/client/views/presentationview/PresElementBox.scss
@@ -114,6 +114,7 @@ $light-background: #ececec;
}
.presElementBox-name {
+ z-index: 300;
align-self: center;
font-size: 13px;
font-family: Roboto;
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 048d3a3d0..e9ab5911d 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, reaction } from "mobx";
+import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Doc, DataSym, DocListCast } from "../../../fields/Doc";
import { documentSchema } from '../../../fields/documentSchemas';
@@ -15,12 +15,13 @@ import { FieldView, FieldViewProps } from '../nodes/FieldView';
import "./PresElementBox.scss";
import React = require("react");
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { PresBox } from "../nodes/PresBox";
+import { PresBox, PresMovement } from "../nodes/PresBox";
import { DocumentType } from "../../documents/DocumentTypes";
import { Tooltip } from "@material-ui/core";
import { DragManager } from "../../util/DragManager";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { undoBatch } from "../../util/UndoManager";
+import { EditableView } from "../EditableView";
export const presSchema = createSchema({
presentationTargetDoc: Doc,
@@ -114,20 +115,23 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
@computed get duration() {
let durationInS: number;
- if (this.targetDoc.presDuration) durationInS = NumCast(this.targetDoc.presDuration) / 1000;
+ if (this.rootDoc.type === DocumentType.AUDIO) { durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime); durationInS = Math.round(durationInS * 10) / 10; }
+ else if (this.rootDoc.presDuration) durationInS = NumCast(this.rootDoc.presDuration) / 1000;
else durationInS = 2;
- return "D: " + durationInS + "s";
+ return this.rootDoc.presMovement === PresMovement.Jump ? (null) : "D: " + durationInS + "s";
}
@computed get transition() {
let transitionInS: number;
- if (this.targetDoc.presTransition) transitionInS = NumCast(this.targetDoc.presTransition) / 1000;
+ if (this.rootDoc.presTransition) transitionInS = NumCast(this.rootDoc.presTransition) / 1000;
else transitionInS = 0.5;
return "M: " + transitionInS + "s";
}
private _itemRef: React.RefObject<HTMLDivElement> = React.createRef();
private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
+ private _titleRef: React.RefObject<EditableView> = React.createRef();
+
@action
headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
@@ -180,19 +184,19 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
onPointerTop = (e: React.PointerEvent<HTMLDivElement>) => {
- if (DragManager.docsBeingDragged.length > 0) {
+ if (DragManager.docsBeingDragged.length > 1) {
this._highlightTopRef.current!.style.borderTop = "solid 2px #5B9FDD";
}
}
onPointerBottom = (e: React.PointerEvent<HTMLDivElement>) => {
- if (DragManager.docsBeingDragged.length > 0) {
+ if (DragManager.docsBeingDragged.length > 1) {
this._highlightBottomRef.current!.style.borderBottom = "solid 2px #5B9FDD";
}
}
onPointerLeave = (e: React.PointerEvent<HTMLDivElement>) => {
- if (DragManager.docsBeingDragged.length > 0) {
+ if (DragManager.docsBeingDragged.length > 1) {
this._highlightBottomRef.current!.style.borderBottom = "0px";
this._highlightTopRef.current!.style.borderTop = "0px";
}
@@ -200,8 +204,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
@action
toggleProperties = () => {
- if (CurrentUserUtils.propertiesWidth === 0) {
- CurrentUserUtils.propertiesWidth = 250;
+ if (CurrentUserUtils.propertiesWidth < 5) {
+ action(() => (CurrentUserUtils.propertiesWidth = 250));
}
}
@@ -214,9 +218,14 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
e.stopPropagation();
});
+ @action
+ onSetValue = (value: string) => {
+ this.rootDoc.title = value;
+ return true;
+ }
+
render() {
const className = "presElementBox-item" + (PresBox.Instance._selectedArray.includes(this.rootDoc) ? " presElementBox-active" : "");
- const pbi = "presElementBox-interaction";
return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? (null) : (
<div className={className} key={this.props.Document[Id] + this.indexInPres}
ref={this._itemRef}
@@ -240,6 +249,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
}
}}
onDoubleClick={e => {
+ console.log('double click to open');
this.toggleProperties();
this.props.focus(this.rootDoc);
PresBox.Instance._eleArray = [];
@@ -255,7 +265,15 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
{`${this.indexInPres + 1}.`}
</div>
<div ref={this._dragRef} className="presElementBox-name" style={{ maxWidth: (PresBox.Instance.toolbarWidth - 70) }}>
- {`${this.targetDoc?.title}`}
+ <EditableView
+ ref={this._titleRef}
+ contents={this.rootDoc.title}
+ GetValue={() => StrCast(this.rootDoc.title)}
+ SetValue={action((value: string) => {
+ this.onSetValue(value);
+ return true;
+ })}
+ />
</div>
<Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presElementBox-time" style={{ display: PresBox.Instance.toolbarWidth > 300 ? "block" : "none" }}>{this.transition}</div></Tooltip>
<Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presElementBox-time" style={{ display: PresBox.Instance.toolbarWidth > 300 ? "block" : "none" }}>{this.duration}</div></Tooltip>
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index b37ae02c3..3238e4dc6 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -530,10 +530,10 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
style={{ cursor: "hand", color: "black", padding: 1, position: "relative" }} /></div>
</Tooltip>
</div>
- <div style={{ position: "absolute", left: 200, width: 30, zIndex: 9000, color: "grey", background: "white", }}>
+ <div style={{ position: "absolute", left: 220, width: 30, zIndex: 9000, color: "grey", background: "white", }}>
{`${this._results.length}` + " of " + `${this.realTotalResults}`}
</div>
- <div style={{ cursor: "default", left: 235, position: "absolute", }}>
+ {/* <div style={{ cursor: "default", left: 235, position: "absolute", }}>
<Tooltip title={<div className="dash-tooltip" >only display documents matching search</div>} >
<div>
<FontAwesomeIcon icon={"filter"} size="lg"
@@ -542,7 +542,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
onClick={action(() => this.setSearchFilter(this.currentSelectedCollection, this.filter ? undefined : this.docsforfilter))} />
</div>
</Tooltip>
- </div>
+ </div> */}
{this.scopeButtons}
</div>
</div >
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index a8950f1ee..ba7c9c7da 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -121,7 +121,7 @@ const AclMap = new Map<string, symbol>([
export function fetchProto(doc: Doc) {
const permissions: { [key: string]: symbol } = {};
- Object.keys(doc).filter(key => key.startsWith("ACL")).forEach(key => permissions[key] = AclMap.get(StrCast(doc[key]))!);
+ Object.keys(doc).filter(key => key.startsWith("acl")).forEach(key => permissions[key] = AclMap.get(StrCast(doc[key]))!);
if (Object.keys(permissions).length) doc[AclSym] = permissions;
@@ -253,7 +253,7 @@ export class Doc extends RefField {
const prev = GetEffectiveAcl(this);
this[UpdatingFromServer] = true;
this[fKey] = value;
- if (fKey.startsWith("ACL")) {
+ if (fKey.startsWith("acl")) {
fetchProto(this);
}
this[UpdatingFromServer] = false;
@@ -261,7 +261,7 @@ export class Doc extends RefField {
DocServer.GetRefField(this[Id], true);
}
};
- if (sameAuthor || fKey.startsWith("ACL") || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) {
+ if (sameAuthor || fKey.startsWith("acl") || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) {
delete this[CachedUpdates][fKey];
await fn();
} else {
@@ -775,7 +775,7 @@ export namespace Doc {
}
});
copy.author = Doc.CurrentUserEmail;
- Doc.UserDoc().defaultAclPrivate && (copy["ACL-Public"] = "Not Shared");
+ Doc.UserDoc().defaultAclPrivate && (copy["acl-Public"] = "Not Shared");
return copy;
}
@@ -803,7 +803,7 @@ export namespace Doc {
const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")");
target.layoutKey = targetKey;
applied && (Doc.GetProto(applied).type = templateDoc.type);
- Doc.UserDoc().defaultAclPrivate && (applied["ACL-Public"] = "Not Shared");
+ Doc.UserDoc().defaultAclPrivate && (applied["acl-Public"] = "Not Shared");
return applied;
}
return undefined;
diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts
index 23ac50f74..4607e0fd5 100644
--- a/src/fields/Schema.ts
+++ b/src/fields/Schema.ts
@@ -52,7 +52,7 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu
return field;
},
set(target: any, prop, value, receiver) {
- receiver.doc && (receiver.doc[prop] = value); // receiver.doc may be undefined as the result of a change in ACLs
+ receiver.doc && (receiver.doc[prop] = value); // receiver.doc may be undefined as the result of a change in acls
return true;
}
});
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 82525f92b..00e1683bd 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -111,7 +111,7 @@ export function makeEditable() {
_setter = _setterImpl;
}
var _overrideAcl = false;
-export function OVERRIDE_ACL(val: boolean) {
+export function OVERRIDE_acl(val: boolean) {
_overrideAcl = val;
}
@@ -168,7 +168,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number,
if (currentUserGroups.includes("Admin")) return AclAdmin;
- // if the ACL is being overriden or the property being modified is one of the playground fields (which can be freely modified)
+ // if the acl is being overriden or the property being modified is one of the playground fields (which can be freely modified)
if (_overrideAcl || (in_prop && DocServer.PlaygroundFields?.includes(in_prop.toString()))) return AclEdit;
let effectiveAcl = AclPrivate;
@@ -198,11 +198,11 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number,
}
/**
* Recursively distributes the access right for a user across the children of a document and its annotations.
- * @param key the key storing the access right (e.g. ACL-groupname)
+ * @param key the key storing the access right (e.g. acl-groupname)
* @param acl the access right being stored (e.g. "Can Edit")
* @param target the document on which this access right is being set
- * @param inheritingFromCollection whether the target is being assigned rights after being dragged into a collection (and so is inheriting the ACLs from the collection)
- * inheritingFromCollection is not currently being used but could be used if ACL assignment defaults change
+ * @param inheritingFromCollection whether the target is being assigned rights after being dragged into a collection (and so is inheriting the acls from the collection)
+ * inheritingFromCollection is not currently being used but could be used if acl assignment defaults change
*/
export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean) {
@@ -271,8 +271,8 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin) return true;
// if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't
- if (typeof prop === "string" && prop.startsWith("ACL") && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true;
- // if (typeof prop === "string" && prop.startsWith("ACL") && !["Can Edit", "Can Add", "Can View", "Not Shared", undefined].includes(value)) return true;
+ if (typeof prop === "string" && prop.startsWith("acl") && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true;
+ // if (typeof prop === "string" && prop.startsWith("acl") && !["Can Edit", "Can Add", "Can View", "Not Shared", undefined].includes(value)) return true;
if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {
if (!prop.startsWith("_")) {