aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/PropertiesView.tsx
diff options
context:
space:
mode:
authoreperelm2 <emily_perelman@brown.edu>2023-07-18 11:40:12 -0400
committereperelm2 <emily_perelman@brown.edu>2023-07-18 11:40:12 -0400
commit5100a643fb0d98b6dd738e7024f4fe15f56ba1a8 (patch)
tree92fa39d2d5cc8f584e3346c8fe0efaa5b184a9e5 /src/client/views/PropertiesView.tsx
parentc9779f31d9ce2363e61c3c9fa7e3446203622dde (diff)
parent16a1b7de3ec26187b3a426eb037a5e4f4b9fcc55 (diff)
Merge branch 'master' into secondpropertiesmenu-emily
Diffstat (limited to 'src/client/views/PropertiesView.tsx')
-rw-r--r--src/client/views/PropertiesView.tsx1222
1 files changed, 577 insertions, 645 deletions
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 9f6c86502..31c03de4c 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1,51 +1,44 @@
import React = require('react');
import { IconLookup } from '@fortawesome/fontawesome-svg-core';
-import { faAnchor, faArrowRight, faListNumeric, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
+import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Checkbox, Icon, Tooltip } from '@material-ui/core';
-import { intersection } from 'lodash';
-import { action, computed, Lambda, observable } from 'mobx';
+import { Checkbox, Tooltip } from '@material-ui/core';
+import { Button, Colors, EditableText, NumberInput, Size, Slider, Type } from 'browndash-components';
+import { concat } from 'lodash';
+import { Lambda, action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import { ColorState, SketchPicker } from 'react-color';
-import { Doc, DocListCast, Field, FieldResult, HierarchyMapping, NumListCast, Opt, StrListCast } from '../../fields/Doc';
+import * as Icons from "react-icons/bs"; //{BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
+import { GrCircleInformation } from 'react-icons/gr';
+import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
+import { Doc, DocListCast, Field, FieldResult, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
import { AclAdmin, DocAcl, DocData, Height, Width } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { InkField } from '../../fields/InkField';
import { List } from '../../fields/List';
import { ComputedField } from '../../fields/ScriptField';
import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from '../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
+import { GetEffectiveAcl, SharingPermissions, normalizeEmail } from '../../fields/util';
import { DocumentType } from '../documents/DocumentTypes';
import { DocumentManager } from '../util/DocumentManager';
+import { GroupManager } from '../util/GroupManager';
import { LinkManager } from '../util/LinkManager';
import { SelectionManager } from '../util/SelectionManager';
import { SharingManager } from '../util/SharingManager';
import { Transform } from '../util/Transform';
-import { undoable, undoBatch, UndoManager } from '../util/UndoManager';
+import { UndoManager, undoBatch, undoable } from '../util/UndoManager';
import { EditableView } from './EditableView';
import { FilterPanel } from './FilterPanel';
-import { Colors } from './global/globalEnums';
import { InkStrokeProperties } from './InkStrokeProperties';
-import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView';
-import { KeyValueBox } from './nodes/KeyValueBox';
-import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
import { PropertiesButtons } from './PropertiesButtons';
import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector';
import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
+import { PropertiesSection } from './PropertiesSection';
import './PropertiesView.scss';
import { DefaultStyleProvider } from './StyleProvider';
-import { RichTextField } from '../../fields/RichTextField';
-import { AiFillFileText } from "react-icons/ai" //* as Icons from "react-icons/ai" //
-import * as Icons from "react-icons/bs" //{BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
-import { GrCircleInformation } from 'react-icons/gr'
-import { CgBrowser} from "react-icons/cg"
-import { ImageField, VideoField, WebField } from '../../fields/URLField';
-import { FaFileVideo } from 'react-icons/fa'; //* as Icons from "react-icons/fa"; //
-import { IconButton } from 'browndash-components';
-import { IconBase } from 'react-icons';
-import { MdOutlineAddShoppingCart, MdOutlineMedicalServices } from 'react-icons/md';
-import { createPromiseCapability } from 'pdfjs-dist';
+import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView';
+import { KeyValueBox } from './nodes/KeyValueBox';
+import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -336,26 +329,22 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@undoBatch
changePermissions = (e: any, user: string) => {
const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc! : DocCast(doc)[DocData]));
- SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
+ SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs, this.layoutDocAcls);
};
/**
* @returns the options for the permissions dropdown.
*/
- getPermissionsSelect(user: string, permission: string) {
- const dropdownValues: string[] = Object.values(SharingPermissions);
+ getPermissionsSelect(user: string, permission: string, showGuestOptions: boolean) {
+ const dropdownValues: string[] = showGuestOptions ? [SharingPermissions.None, SharingPermissions.View] : Object.values(SharingPermissions);
if (permission === '-multiple-') dropdownValues.unshift(permission);
- if (user !== 'Override') dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
return (
- <select className="permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}>
- {dropdownValues
- .filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any))
- .map(permission => (
- <option key={permission} value={permission}>
- {' '}
- {permission}{' '}
- </option>
- ))}
+ <select className="propertiesView-permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}>
+ {dropdownValues.map(permission => (
+ <option className="propertiesView-permisssions-select" key={permission} value={permission}>
+ {concat(ReverseHierarchyMap.get(permission)?.image, ' ', permission)}
+ </option>
+ ))}
</select>
);
}
@@ -367,7 +356,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
return (
<Tooltip title={<div className="dash-tooltip">Notify with message</div>}>
<div className="notify-button">
- <FontAwesomeIcon className="notify-button-icon" icon="bell" color="white" size="sm" />
+ <FontAwesomeIcon className="notify-button-icon" icon="bell" size="sm" />
</div>
</Tooltip>
);
@@ -396,6 +385,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
* @returns a row of the permissions panel
*/
sharingItem(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
+ if (name == Doc.CurrentUserEmail) {
+ name = 'Me';
+ }
return (
<div
className="propertiesView-sharingTable-item"
@@ -409,58 +401,141 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
</div>
{/* {name !== "Me" ? this.notifyIcon : null} */}
<div className="propertiesView-sharingTable-item-permission">
- {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : permission}
- {permission === 'Owner' || showExpansionIcon ? this.expansionIcon : null}
+ {this.colorACLDropDown(name, admin, permission, false)}
+ {(permission === 'Owner' && name == 'Me') || showExpansionIcon ? this.expansionIcon : null}
+ </div>
+ </div>
+ );
+ }
+
+ /**
+ * @returns a colored dropdown bar reflective of the permission
+ */
+ colorACLDropDown(name: string, admin: boolean, permission: string, showGuestOptions: boolean) {
+ var shareImage = ReverseHierarchyMap.get(permission)?.image;
+ return (
+ <div>
+ <div className={'propertiesView-shareDropDown'}>
+ <div className={`propertiesView-shareDropDown${permission}`}>
+ <div className="propertiesView-shareDropDown">{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission, showGuestOptions) : concat(shareImage, ' ', permission)}</div>
+ </div>
</div>
</div>
);
}
/**
+ * Sorting algorithm to sort users.
+ */
+ sortUsers = (u1: String, u2: String) => {
+ return u1 > u2 ? -1 : u1 === u2 ? 0 : 1;
+ };
+
+ /**
+ * Sorting algorithm to sort groups.
+ */
+ sortGroups = (group1: Doc, group2: Doc) => {
+ const g1 = StrCast(group1.title);
+ const g2 = StrCast(group2.title);
+ return g1 > g2 ? -1 : g1 === g2 ? 0 : 1;
+ };
+
+ /**
* @returns the sharing and permissions panel.
*/
@computed get sharingTable() {
// all selected docs
- const docs =
- SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutDocAcls ? this.selectedDoc : this.dataDoc!] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DocData]));
-
+ const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.selectedDoc] : SelectionManager.Views().map(docView => docView.rootDoc);
const target = docs[0];
- // tslint:disable-next-line: no-unnecessary-callback-wrapper
- const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
- const showAdmin = effectiveAcls.every(acl => acl === AclAdmin);
+ const showAdmin = GetEffectiveAcl(target) == AclAdmin;
+ const individualTableEntries = [];
+ const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
+
+ const seldoc = this.layoutDocAcls || !this.selectedDoc ? this.selectedDoc : Doc.GetProto(this.selectedDoc);
+ // adds each user to usersAdded
+ SharingManager.Instance.users.forEach(eachUser => {
+ var userOnDoc = true;
+ if (seldoc) {
+ if (Doc.GetT(seldoc, 'acl-' + normalizeEmail(eachUser.user.email), 'string', true) === '' || Doc.GetT(seldoc, 'acl-' + normalizeEmail(eachUser.user.email), 'string', true) === undefined) {
+ userOnDoc = false;
+ }
+ }
+ if (userOnDoc && !usersAdded.includes(eachUser.user.email) && eachUser.user.email !== 'guest' && eachUser.user.email != target.author) {
+ usersAdded.push(eachUser.user.email);
+ }
+ });
- // users in common between all docs
- const commonKeys: string[] = intersection(...docs.map(doc => doc?.[DocAcl] && Object.keys(doc[DocAcl]).filter(key => key !== 'acl-Me')));
+ // sorts and then adds each user to the table
+ usersAdded.sort(this.sortUsers);
+ usersAdded.map(userEmail => {
+ const userKey = `acl-${normalizeEmail(userEmail)}`;
+ var aclField = Doc.GetT(this.layoutDocAcls ? target : Doc.GetProto(target), userKey, 'string', true);
+ var permission = StrCast(aclField);
+ individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
+ });
- const tableEntries = [];
+ // adds current user
+ var userEmail = Doc.CurrentUserEmail;
+ if (userEmail == 'guest') userEmail = 'Guest';
+ const userKey = `acl-${normalizeEmail(userEmail)}`;
+ if (!usersAdded.includes(userEmail) && userEmail !== 'Guest' && userEmail != target.author) {
+ var permission;
+ if (this.layoutDocAcls) {
+ if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
+ else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[userKey]);
+ else permission = StrCast(Doc.GetProto(target)?.[userKey]);
+ } else permission = StrCast(target[userKey]);
+ individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
+ }
- // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => {
- if (commonKeys.length) {
- for (const key of commonKeys) {
- const name = denormalizeEmail(key.substring(4));
- const uniform = docs.every(doc => doc?.[DocAcl]?.[key] === docs[0]?.[DocAcl]?.[key]);
- if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
- tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[DocAcl][key])!.name : '-multiple-'));
+ // shift owner to top
+ individualTableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false);
+
+ // adds groups
+ const groupTableEntries: JSX.Element[] = [];
+ const groupList = GroupManager.Instance?.allGroups || [];
+ groupList.sort(this.sortGroups);
+ groupList.map(group => {
+ if (group.title != 'Guest' && this.selectedDoc) {
+ const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
+ if (this.selectedDoc[groupKey] != '' && this.selectedDoc[groupKey] != undefined) {
+ var permission;
+ if (this.layoutDocAcls) {
+ if (target[DocAcl][groupKey]) {
+ permission = HierarchyMapping.get(target[DocAcl][groupKey])?.name;
+ } else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[groupKey]);
+ else permission = StrCast(Doc.GetProto(target)?.[groupKey]);
+ } else permission = StrCast(target[groupKey]);
+ groupTableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission!, false));
}
}
- }
+ });
- const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
- // shifts the current user, owner, public to the top of the doc.
- // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
- if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'));
- tableEntries.unshift(this.sharingItem('Public', showAdmin, StrCast(docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-')));
- tableEntries.unshift(
- this.sharingItem(
- 'Me',
- showAdmin,
- docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? 'Owner' : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? HierarchyMapping.get(effectiveAcls[0])!.name : '-multiple-',
- !ownerSame
- )
- );
+ // public permission
+ const publicPermission = StrCast((this.layoutDocAcls ? target : Doc.GetProto(target))['acl-Guest']);
- return <div className="propertiesView-sharingTable">{tableEntries}</div>;
+ return (
+ <div>
+ <br />
+ <div className="propertiesView-sharingTable">{<div> {individualTableEntries}</div>}</div>
+ {groupTableEntries.length > 0 ? (
+ <div>
+ <div>
+ {' '}
+ <br></br> Groups with Access to this Document{' '}
+ </div>
+ <div className="propertiesView-sharingTable">{<div> {groupTableEntries}</div>}</div>
+ </div>
+ ) : null}
+ Guest
+ <div>{this.colorACLDropDown('Guest', true, publicPermission!, true)}</div>
+ <div>
+ {' '}
+ <br></br> Individual Users with Access to this Document{' '}
+ </div>
+ </div>
+ );
}
@computed get fieldsCheckbox() {
@@ -471,19 +546,46 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@action
toggleCheckbox = () => (this.layoutFields = !this.layoutFields);
+ @computed get color() {
+ return StrCast(Doc.UserDoc().userColor);
+ }
+
+ @computed get backgroundColor() {
+ return StrCast(Doc.UserDoc().userBackgroundColor);
+ }
+
+ @computed get variantColor() {
+ return StrCast(Doc.UserDoc().userVariantColor);
+ }
+
@computed get editableTitle() {
const titles = new Set<string>();
- SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.rootDoc.title)));
const title = Array.from(titles.keys()).length > 1 ? '--multiple selected--' : StrCast(this.selectedDoc?.title);
+ SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.rootDoc.title)));
return (
- <div>
- <div className="propertiesView-wordTitle">Title</div>
- <div className="editable-title">
- <EditableView key="editableView" contents={title} height={25} fontSize={14} GetValue={() => title} SetValue={this.setTitle} />
- </div>
- </div>
-
+ <EditableText
+ val={title}
+ setVal={this.setTitle}
+ color={this.color}
+ type={Type.SEC}
+ formLabel={"Title"}
+ fillWidth
+ />
+ );
+ }
+
+ @computed get type() {
+ const type = Utils.cleanDocumentType(StrCast(this.selectedDoc?.type) as DocumentType);
+ return (
+ <Button
+ formLabel={"Type"}
+ text={type}
+ type={Type.SEC}
+ color={this.color}
+ align='flex-start'
+ fillWidth
+ />
);
}
@@ -531,16 +633,14 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@undoBatch
@action
- setTitle = (value: string) => {
+ setTitle = (value: string | number) => {
+ console.log(value)
if (SelectionManager.Views().length > 1) {
SelectionManager.Views().map(dv => Doc.SetInPlace(dv.rootDoc, 'title', value, true));
- return true;
} else if (this.dataDoc) {
if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, 'title', value, true);
- else KeyValueBox.SetField(this.dataDoc, 'title', value, true);
- return true;
+ else KeyValueBox.SetField(this.dataDoc, 'title', value as string, true);
}
- return false;
};
@undoBatch
@@ -598,7 +698,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
className="inking-button-points"
style={{ backgroundColor: InkStrokeProperties.Instance._controlButton ? 'black' : '' }}
onPointerDown={action(() => (InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton))}>
- <FontAwesomeIcon icon="bezier-curve" color="white" size="lg" />
+ <FontAwesomeIcon icon="bezier-curve" size="lg" />
</div>
</Tooltip>
</div>
@@ -617,10 +717,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<input className="inputBox-input" type="text" value={value} onChange={e => setter(e.target.value)} onKeyPress={e => e.stopPropagation()} />
<div className="inputBox-button">
<div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
- <FontAwesomeIcon icon="caret-up" color="white" size="sm" />
+ <FontAwesomeIcon icon="caret-up" size="sm" />
</div>
<div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
- <FontAwesomeIcon icon="caret-down" color="white" size="sm" />
+ <FontAwesomeIcon icon="caret-down" size="sm" />
</div>
</div>
</div>
@@ -874,16 +974,28 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<input className="inputBox-input" type="text" value={value} onChange={e => setter(e.target.value)} />
<div className="inputBox-button">
<div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
- <FontAwesomeIcon icon="caret-up" color="white" size="sm" />
+ <FontAwesomeIcon icon="caret-up" size="sm" />
</div>
<div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
- <FontAwesomeIcon icon="caret-down" color="white" size="sm" />
+ <FontAwesomeIcon icon="caret-down" size="sm" />
</div>
</div>
</div>
);
};
+ @action
+ onDoubleClick = () => {
+ this.openContexts = false;
+ this.openLinks = false;
+ this.openOptions = false;
+ this.openTransform = false;
+ this.openFields = false;
+ this.openSharing = false;
+ this.openLayout = false;
+ this.openFilters = false;
+ }
+
@computed get widthAndDash() {
return (
<div className="widthAndDash">
@@ -963,111 +1075,115 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
);
}
+ getNumber = (label: string, unit: string, min: number, max: number, number: number, setNumber: any) => {
+ return <div>
+ <NumberInput
+ formLabel={label}
+ formLabelPlacement={'left'}
+ type={Type.SEC}
+ unit={unit}
+ fillWidth
+ color={this.color}
+ number={number}
+ setNumber={setNumber}
+ min={min}
+ max={max}
+ />
+ <Slider
+ multithumb={false}
+ color={this.color}
+ size={Size.XSMALL}
+ min={min}
+ max={max}
+ unit={unit}
+ number={number}
+ setNumber={setNumber}
+ fillWidth
+ />
+ </div>
+ }
+
@computed get transformEditor() {
return (
<div className="transform-editor">
{this.isInk ? this.controlPointsButton : null}
- {this.hgtInput}
- {this.XpsInput}
+ {this.getNumber(
+ "Height",
+ " px",
+ 0,
+ 1000,
+ Number(this.shapeHgt),
+ undoable((val: string) => !isNaN(Number(val)) && (this.shapeHgt = val), 'set height')
+ )}
+ {this.getNumber(
+ "Width",
+ " px",
+ 0,
+ 1000,
+ Number(this.shapeWid),
+ undoable((val: string) => !isNaN(Number(val)) && (this.shapeWid = val), 'set width')
+ )}
+ {this.getNumber(
+ "X Coordinate",
+ " px",
+ -2000,
+ 2000,
+ Number(this.shapeXps),
+ undoable((val: string) => !isNaN(Number(val)) && (this.shapeXps = val), 'set x coord')
+ )}
+ {this.getNumber(
+ "Y Coordinate",
+ " px",
+ -2000,
+ 2000,
+ Number(this.shapeYps),
+ undoable((val: string) => !isNaN(Number(val)) && (this.shapeYps = val), 'set y coord')
+ )}
</div>
);
}
@computed get optionsSubMenu() {
- let isDouble = false;
-
- return (
- <div className="propertiesView-settings" onPointerEnter={action(() => (this.inOptions = true))} onPointerLeave={action(() => (this.inOptions = false))}>
- <div className="propertiesView-settings-title" onClick ={action(() => {
- if (!isDouble){
- this.openOptions = !this.openOptions
- } } )}
- onDoubleClick={action(() => {
- isDouble = true;
-
- this.openContexts = false;
- this.openLinks = false;
- this.openOptions = true;
- this.openTransform = false;
- this.openFields = false;
- this.openSharing = false;
- this.openLayout = false;
- this.openFilters = false;
-
-
- setTimeout(() => {
- isDouble = false;
- }, 300)
- }
-
- )}
- style={{ backgroundColor: this.openOptions ? 'black' : '' }}>
- Options
- <div className="propertiesView-settings-title-icon">
- <FontAwesomeIcon icon={this.openOptions ? 'caret-down' : 'caret-right'} size="lg" color="white" />
- </div>
- </div>
- {!this.openOptions ? null : (
- <div className="propertiesView-settings-content">
- <PropertiesButtons />
- </div>
- )}
- </div>
- );
+ return <PropertiesSection
+ title="Options"
+ content={<PropertiesButtons />}
+ inSection={this.inOptions}
+ isOpen={this.openOptions}
+ setInSection={(bool) => this.inOptions = bool}
+ setIsOpen={(bool) => this.openOptions = bool}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
}
@computed get sharingSubMenu() {
- let isDouble:boolean = false;
-
- return (
- <div className="propertiesView-sharing">
- <div className="propertiesView-sharing-title" onClick ={action(() => {
- if (!isDouble){
- this.openSharing = !this.openSharing
- } } )}
- onDoubleClick={action(() => {
- isDouble = true;
-
- this.openContexts = false;
- this.openLinks = false;
- this.openOptions = false;
- this.openTransform = false;
- this.openFields = false;
- this.openSharing = true;
- this.openLayout = false;
- this.openFilters = false;
-
-
- setTimeout(() => {
- isDouble = false;
- }, 300)
- })}
- style={{ backgroundColor: this.openSharing ? 'black' : '' }}>
- Sharing {'&'} Permissions
- <div className="propertiesView-sharing-title-icon">
- <FontAwesomeIcon icon={this.openSharing ? 'caret-down' : 'caret-right'} size="lg" color="white" />
- </div>
- </div>
- {!this.openSharing ? null : (
- <div className="propertiesView-sharing-content">
+ return <PropertiesSection
+ title="Sharing & Permissions"
+ content={<>
+ <div className="propertiesView-buttonContainer">
+ {!Doc.noviceMode ? (
<div className="propertiesView-buttonContainer">
- {!Doc.noviceMode ? (
- <div className="propertiesView-acls-checkbox">
- <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
- <div className="propertiesView-acls-checkbox-text">Layout</div>
- </div>
- ) : null}
- {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
- <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
- <FontAwesomeIcon icon="redo-alt" color="white" size="1x" />
- </button>
- </Tooltip> */}
+ <div className="propertiesView-acls-checkbox">
+ <div className="propertiesView-acls-checkbox-text"> Show / Contol Layout Permissions </div>
+ <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
</div>
- {this.sharingTable}
- </div>
- )}
- </div>
- );
+ {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
+ <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
+ <FontAwesomeIcon icon="redo-alt" color="white" size="1x" />
+ </button>
+ </Tooltip> */}
+ </div>
+ ) : null}
+ {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
+ <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
+ <FontAwesomeIcon icon="redo-alt" size="1x" />
+ </button>
+ </Tooltip> */}
+ </div>
+ {this.sharingTable}
+ </>}
+ isOpen={this.openSharing}
+ setIsOpen={(bool) => this.openSharing = bool}
+ />
}
/**
@@ -1095,46 +1211,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
};
@computed get filtersSubMenu() {
- let isDouble: boolean = false;
-
- return (
- <div className="propertiesView-filters">
- <div className="propertiesView-filters-title" onClick ={action(() => {
- if (!isDouble){
- this.openFilters = !this.openFilters
- } } )}
- onDoubleClick={action(() => {
- isDouble = true;
-
- this.openContexts = false;
- this.openLinks = false;
- this.openOptions = false;
- this.openTransform = false;
- this.openFields = false;
- this.openSharing = false;
- this.openLayout = false;
- this.openFilters = true;
-
-
- setTimeout(() => {
- isDouble = false;
- }, 300)
- }
-
- )}
- style={{ backgroundColor: this.openFilters ? 'black' : '' }}>
- Filters
- <div className="propertiesView-filters-title-icon">
- <FontAwesomeIcon icon={this.openFilters ? 'caret-down' : 'caret-right'} size="lg" color="white" />
- </div>
- </div>
- {!this.openFilters ? null : (
- <div className="propertiesView-filters-content" style={{ position: 'relative', height: 'auto' }}>
- <FilterPanel rootDoc={this.selectedDoc ?? Doc.ActiveDashboard!} />
- </div>
- )}
- </div>
- );
+ return <PropertiesSection
+ title="Filters"
+ content={<div className="propertiesView-content filters" style={{ position: 'relative', height: 'auto' }}>
+ <FilterPanel rootDoc={this.selectedDoc ?? Doc.ActiveDashboard!} />
+ </div>}
+ isOpen={this.openFilters}
+ setIsOpen={(bool) => this.openFilters = bool}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
}
@computed get inkSubMenu() {
@@ -1142,217 +1227,66 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
return (
<>
- {!this.isInk ? null : (
- <div className="propertiesView-appearance">
- <div className="propertiesView-appearance-title" onPointerDown={action(() => (this.openAppearance = !this.openAppearance))} style={{ backgroundColor: this.openAppearance ? 'black' : '' }}>
- Appearance
- <div className="propertiesView-appearance-title-icon">
- <FontAwesomeIcon icon={this.openAppearance ? 'caret-down' : 'caret-right'} size="lg" color="white" />
- </div>
- </div>
- {!this.openAppearance ? null : <div className="propertiesView-appearance-content">{this.appearanceEditor}</div>}
- </div>
- )}
-
- <div className="propertiesView-transform">
- <div className="propertiesView-transform-title" onClick ={action(() => {
- if (!isDouble){
- this.openTransform = !this.openTransform
- } } )}
- onDoubleClick={action(() => {
- isDouble = true;
-
- this.openContexts = false;
- this.openLinks = false;
- this.openOptions = false;
- this.openTransform = true;
- this.openFields = false;
- this.openSharing = false;
- this.openLayout = false;
- this.openFilters = false;
-
-
- setTimeout(() => {
- isDouble = false;
- }, 300)
- })}
- style={{ backgroundColor: this.openTransform ? 'black' : '' }}>
- Transform
- <div className="propertiesView-transform-title-icon">
- <FontAwesomeIcon icon={this.openTransform ? 'caret-down' : 'caret-right'} size="lg" color="white" />
- </div>
- </div>
- {this.openTransform ? <div className="propertiesView-transform-content">{this.transformEditor}</div> : null}
- </div>
+ <PropertiesSection
+ title="Appearance"
+ content={this.isInk ? this.appearanceEditor : null}
+ isOpen={this.openAppearance}
+ setIsOpen={(bool) => this.openAppearance = bool}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
+ <PropertiesSection
+ title="Transform"
+ content={this.transformEditor}
+ isOpen={this.openTransform}
+ setIsOpen={(bool) => this.openTransform = bool}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
</>
);
}
@computed get fieldsSubMenu() {
- let isDouble: boolean = false;
-
- return (
- <div className="propertiesView-fields">
- <div className="propertiesView-fields-title" onClick ={action(() => {
- if (!isDouble){
- this.openFields = !this.openFields
- } } )}
- onDoubleClick={action(() => {
- isDouble = true;
-
- this.openContexts = false;
- this.openLinks = false;
- this.openOptions = false;
- this.openTransform = false;
- this.openFields = true;
- this.openSharing = false;
- this.openLayout = false;
- this.openFilters = false;
-
-
- setTimeout(() => {
- isDouble = false;
- }, 300)
- }
-
- )}
- style={{ backgroundColor: this.openFields ? 'black' : '' }}>
- Fields {'&'} Tags
- <div className="propertiesView-fields-title-icon">
- <FontAwesomeIcon icon={this.openFields ? 'caret-down' : 'caret-right'} size="lg" color="white" />
- </div>
- </div>
- {!Doc.noviceMode && this.openFields ? (
- <div className="propertiesView-fields-checkbox">
- {this.fieldsCheckbox}
- <div className="propertiesView-fields-checkbox-text">Embedding</div>
- </div>
- ) : null}
- {!this.openFields ? null : <div className="propertiesView-fields-content">{Doc.noviceMode ? this.noviceFields : this.expandedField}</div>}
- </div>
- );
+ return <PropertiesSection
+ title="Fields & Tags"
+ content={<div className="propertiesView-content fields">{
+ Doc.noviceMode ? this.noviceFields : this.expandedField}
+ </div>}
+ isOpen={this.openFields}
+ setIsOpen={(bool) => this.openFields = bool}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
}
@computed get contextsSubMenu() {
- let isDouble = false;
- return (
- <div className="propertiesView-contexts">
- <div className="propertiesView-contexts-title" onClick ={action(() => {
- if (!isDouble){
- this.openContexts = !this.openContexts
- } } )}
- onDoubleClick={action(() => {
- isDouble = true;
-
- this.openContexts = true;
- this.openLinks = false;
- this.openOptions = false;
- this.openTransform = false;
- this.openFields = false;
- this.openSharing = false;
- this.openLayout = false;
- this.openFilters = false;
-
-
- setTimeout(() => {
- isDouble = false;
- }, 300)
- }
-
- )}
- style={{ backgroundColor: this.openContexts ? 'black' : '' }}>
- Other Contexts
- <div className="propertiesView-contexts-title-icon">
- <FontAwesomeIcon icon={this.openContexts ? 'caret-down' : 'caret-right'} size="lg" color="white" />
- </div>
- </div>
- {!this.openContexts ? null : this.contextCount > 0 ? <div className="propertiesView-contexts-content">{this.contexts}</div> : <div className="propertiesView-contexts-content">There are no other contexts.</div>}
- </div>
- );
+ return <PropertiesSection
+ title="Other Contexts"
+ content={this.contexts}
+ isOpen={this.openContexts}
+ setIsOpen={(bool) => this.openContexts = bool}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
}
@computed get linksSubMenu() {
-//onClick={action(() => (this.openLinks = !this.openLinks))} onDoubleClick={action(() => (this.openContexts = false, this.openOptions = false, this.openTransform = false,
-//this.openFields = false, this.openSharing = false, this.openLayout = false, this.openFilters = false, this.openLinks = false ))}
-
- let isDouble = false;
-
- return (
- <div className="propertiesView-contexts">
-
- <div className="propertiesView-contexts-title" onClick = {action(() => {
- if (!isDouble){
- this.openLinks = !this.openLinks
- } } )}
-
- onDoubleClick = {action(() => {
- isDouble = true;
-
- this.openContexts = false;
- this.openOptions = false;
- this.openTransform = false;
- this.openFields = false;
- this.openSharing = false;
- this.openLayout = false;
- this.openFilters = false;
- this.openLinks = true;
-
- setTimeout(() => {
- isDouble = false;
- }, 300)
- }
-
- )}
- style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
- Linked To
- <div className="propertiesView-contexts-title-icon">
- <FontAwesomeIcon icon={this.openLinks ? 'caret-down' : 'caret-right'} size="lg" color="white" />
- </div>
- </div>
- {!this.openLinks ? null : this.linkCount > 0 ? <div className="propertiesView-contexts-content">{this.links}</div> : <div className="propertiesView-contexts-content">There are no current links.</div>}
- </div>
- );
+ return <PropertiesSection
+ title="Linked To"
+ content={this.links}
+ isOpen={this.openLinks}
+ setIsOpen={(bool) => this.openLinks = bool}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
}
@computed get layoutSubMenu() {
- let isDouble: boolean = false;
-
- return (
- <div className="propertiesView-layout">
- <div className="propertiesView-layout-title" onClick ={action(() => {
- if (!isDouble){
- this.openLayout = !this.openLayout
- } } )}
- onDoubleClick={action(() => {
- isDouble = true;
-
- this.openContexts = false;
- this.openLinks = false;
- this.openOptions = false;
- this.openTransform = false;
- this.openFields = false;
- this.openSharing = false;
- this.openLayout = true;
- this.openFilters = false;
-
-
- setTimeout(() => {
- isDouble = false;
- }, 300)
- }
-
- )}
- style={{ backgroundColor: this.openLayout ? 'black' : '' }}>
- Layout
- <div className="propertiesView-layout-title-icon">
- <FontAwesomeIcon icon={this.openLayout ? 'caret-down' : 'caret-right'} size="lg" color="white" />
- </div>
- </div>
- {this.openLayout ? <div className="propertiesView-layout-content">{this.layoutPreview}</div> : null}
- </div>
- );
+ return <PropertiesSection
+ title="Layout"
+ content={this.layoutPreview}
+ isOpen={this.openLayout}
+ setIsOpen={(bool) => this.openLayout = bool}
+ onDoubleClick={() => this.onDoubleClick()}
+ />
}
@computed get description() {
@@ -1553,6 +1487,226 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
if (scale > 1) scale = 1;
this.sourceAnchor && (this.sourceAnchor.followLinkZoomScale = scale);
};
+
+ @computed get linkProperties() {
+ const zoom = Number((NumCast(this.sourceAnchor?.followLinkZoomScale, 1) * 100).toPrecision(3));
+ const targZoom = this.sourceAnchor?.followLinkZoom;
+ const indent = 30;
+ const hasSelectedAnchor = LinkManager.Links(this.sourceAnchor).includes(LinkManager.currentLink!);
+
+ return <>
+ <div className="propertiesView-section" style={{ background: 'darkgray' }}>
+ <div className="propertiesView-input first" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
+ <p>Relationship</p>
+ {this.editRelationship}
+ </div>
+ <div className="propertiesView-input" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
+ <p>Description</p>
+ {this.editDescription}
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Show link</p>
+ <button
+ style={{ background: !LinkManager.currentLink?.link_displayLine ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleLinkProp(e, 'link_displayLine')}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
+ <p>Auto-move anchors</p>
+ <button
+ style={{ background: !LinkManager.currentLink?.link_autoMoveAnchors ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleLinkProp(e, 'link_autoMoveAnchors')}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
+ <p>Display arrow</p>
+ <button
+ style={{ background: !LinkManager.currentLink?.link_displayArrow ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleLinkProp(e, 'link_displayArrow')}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+ </button>
+ </div>
+ </div>
+ {!hasSelectedAnchor ? null : (
+ <div className="propertiesView-section">
+ <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 84px)' }}>
+ <p>Follow by</p>
+ <select onChange={e => this.changeFollowBehavior(e.currentTarget.value === 'Default' ? undefined : e.currentTarget.value)} value={Cast(this.sourceAnchor?.followLinkLocation, 'string', null)}>
+ <option value={undefined}>Default</option>
+ <option value={OpenWhere.addLeft}>Opening in new left pane</option>
+ <option value={OpenWhere.addRight}>Opening in new right pane</option>
+ <option value={OpenWhere.replaceLeft}>Replacing left tab</option>
+ <option value={OpenWhere.replaceRight}>Replacing right tab</option>
+ <option value={OpenWhere.fullScreen}>Overlaying current tab</option>
+ <option value={OpenWhere.lightbox}>Opening in lightbox</option>
+ <option value={OpenWhere.add}>Opening in new tab</option>
+ <option value={OpenWhere.replace}>Replacing current tab</option>
+ <option value={OpenWhere.inParent}>Opening in same collection</option>
+ {LinkManager.currentLink?.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
+ </select>
+ </div>
+ <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 134px) 50px' }}>
+ <p>Animation</p>
+ <select style={{ width: '100%', gridColumn: 2 }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkAnimEffect, 'default')}>
+ <option value="default">Default</option>
+ {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
+ <option key={effect.toString()} value={effect.toString()}>
+ {effect.toString()}
+ </option>
+ ))}
+ </select>
+ <div className="effectDirection" style={{ marginLeft: '10px', display: 'grid', width: 40, height: 36, gridColumn: 3, gridTemplateRows: '12px 12px 12px' }}>
+ {this.animationDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
+ {this.animationDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
+ {this.animationDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
+ {this.animationDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
+ {this.animationDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
+ </div>
+ </div>
+ {PresBox.inputter(
+ '0.1',
+ '0.1',
+ '10',
+ NumCast(this.sourceAnchor?.followLinkTransitionTime) / 1000,
+ true,
+ (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => this.sourceAnchor && (this.sourceAnchor.followLinkTransitionTime = timeInMS)),
+ indent
+ )}{' '}
+ <div
+ className={'slider-headers'}
+ style={{
+ display: 'grid',
+ justifyContent: 'space-between',
+ width: `calc(100% - ${indent * 2}px)`,
+ marginLeft: indent,
+ marginRight: indent,
+ gridTemplateColumns: 'auto auto',
+ borderTop: 'solid',
+ }}>
+ <div className="slider-text">Fast</div>
+ <div className="slider-text">Slow</div>
+ </div>{' '}
+ <div className="propertiesView-input inline">
+ <p>Play Target Audio</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkAudio', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Zoom Text Selections</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkZoomText ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoomText', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Toggle Follow to Outer Context</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkToOuterContext ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToOuterContext', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faWindowMaximize as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Toggle Target (Show/Hide)</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkToggle ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToggle', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Ease Transitions</p>
+ <button
+ style={{ background: this.sourceAnchor?.followLinkEase === 'linear' ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkEase', this.sourceAnchor, 'ease', 'linear')}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Capture Offset to Target</p>
+ <button
+ style={{ background: this.sourceAnchor?.followLinkXoffset === undefined ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => {
+ this.toggleAnchorProp(e, 'followLinkXoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.x) - NumCast(this.sourceAnchor?.x), undefined);
+ this.toggleAnchorProp(e, 'followLinkYoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.y) - NumCast(this.sourceAnchor?.y), undefined);
+ }}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Center Target (no zoom)</p>
+ <button
+ style={{ background: this.sourceAnchor?.followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
+ <p>Zoom %</p>
+ <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
+ <input className="presBox-input" style={{ width: '100%' }} readOnly={true} type="number" value={zoom} />
+ <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
+ <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
+ <FontAwesomeIcon icon={'caret-up'} />
+ </div>
+ <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
+ <FontAwesomeIcon icon={'caret-down'} />
+ </div>
+ </div>
+ </div>
+ <button
+ style={{ background: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+ </button>
+ </div>
+ {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
+ <div
+ className={'slider-headers'}
+ style={{
+ display: !targZoom ? 'none' : 'grid',
+ justifyContent: 'space-between',
+ width: `calc(100% - ${indent * 2}px)`,
+ marginLeft: indent,
+ marginRight: indent,
+ gridTemplateColumns: 'auto auto',
+ borderTop: 'solid',
+ }}>
+ <div className="slider-text">0%</div>
+ <div className="slider-text">100%</div>
+ </div>{' '}
+ </div>
+ )}
+ </>
+ }
/**
* Handles adding and removing members from the sharing panel
@@ -1567,9 +1721,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
render() {
const isNovice = Doc.noviceMode;
- const zoom = Number((NumCast(this.sourceAnchor?.followLinkZoomScale, 1) * 100).toPrecision(3));
- const targZoom = this.sourceAnchor?.followLinkZoom;
- const indent = 30;
const hasSelectedAnchor = LinkManager.Links(this.sourceAnchor).includes(LinkManager.currentLink!);
if (!this.selectedDoc && !this.isPres) {
return (
@@ -1585,9 +1736,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div
className="propertiesView"
style={{
+ background: StrCast(Doc.UserDoc().userBackgroundColor),
+ color: StrCast(Doc.UserDoc().userColor),
width: this.props.width,
minWidth: this.props.width,
- //overflowY: this.scrolling ? "scroll" : "visible"
}}>
<div className = "propertiesView-propAndInfoGrouping">
<div className="propertiesView-title" style={{ width: this.props.width }}>
@@ -1600,238 +1752,18 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-name">{this.editableTitle}</div>
-
<div className = "propertiesView-type"> {this.currentType} </div>
-
-
+ <div className="propertiesView-type">{this.type}</div>
{this.contextsSubMenu}
-
{this.linksSubMenu}
{!this.selectedDoc || !LinkManager.currentLink || (!hasSelectedAnchor && this.selectedDoc !== LinkManager.currentLink) ? null : (
- <>
- <div className="propertiesView-section" style={{ background: 'darkgray' }}>
- <div className="propertiesView-input first" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
- <p>Relationship</p>
- {this.editRelationship}
- </div>
- <div className="propertiesView-input" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
- <p>Description</p>
- {this.editDescription}
- </div>
- <div className="propertiesView-input inline">
- <p>Show link</p>
- <button
- style={{ background: !LinkManager.currentLink?.link_displayLine ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleLinkProp(e, 'link_displayLine')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
- <p>Auto-move anchors</p>
- <button
- style={{ background: !LinkManager.currentLink?.link_autoMoveAnchors ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleLinkProp(e, 'link_autoMoveAnchors')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
- <p>Display arrow</p>
- <button
- style={{ background: !LinkManager.currentLink?.link_displayArrow ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleLinkProp(e, 'link_displayArrow')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
- </button>
- </div>
- </div>
- {!hasSelectedAnchor ? null : (
- <div className="propertiesView-section">
- <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 84px)' }}>
- <p>Follow by</p>
- <select onChange={e => this.changeFollowBehavior(e.currentTarget.value === 'Default' ? undefined : e.currentTarget.value)} value={Cast(this.sourceAnchor?.followLinkLocation, 'string', null)}>
- <option value={undefined}>Default</option>
- <option value={OpenWhere.addLeft}>Opening in new left pane</option>
- <option value={OpenWhere.addRight}>Opening in new right pane</option>
- <option value={OpenWhere.replaceLeft}>Replacing left tab</option>
- <option value={OpenWhere.replaceRight}>Replacing right tab</option>
- <option value={OpenWhere.fullScreen}>Overlaying current tab</option>
- <option value={OpenWhere.lightbox}>Opening in lightbox</option>
- <option value={OpenWhere.add}>Opening in new tab</option>
- <option value={OpenWhere.replace}>Replacing current tab</option>
- <option value={OpenWhere.inParent}>Opening in same collection</option>
- {LinkManager.currentLink?.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
- </select>
- </div>
- <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 134px) 50px' }}>
- <p>Animation</p>
- <select style={{ width: '100%', gridColumn: 2 }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkAnimEffect, 'default')}>
- <option value="default">Default</option>
- {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
- <option key={effect.toString()} value={effect.toString()}>
- {effect.toString()}
- </option>
- ))}
- </select>
- <div className="effectDirection" style={{ marginLeft: '10px', display: 'grid', width: 40, height: 36, gridColumn: 3, gridTemplateRows: '12px 12px 12px' }}>
- {this.animationDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
- {this.animationDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
- {this.animationDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
- {this.animationDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
- {this.animationDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
- </div>
- </div>
- {PresBox.inputter(
- '0.1',
- '0.1',
- '10',
- NumCast(this.sourceAnchor?.followLinkTransitionTime) / 1000,
- true,
- (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => this.sourceAnchor && (this.sourceAnchor.followLinkTransitionTime = timeInMS)),
- indent
- )}{' '}
- <div
- className={'slider-headers'}
- style={{
- display: 'grid',
- justifyContent: 'space-between',
- width: `calc(100% - ${indent * 2}px)`,
- marginLeft: indent,
- marginRight: indent,
- gridTemplateColumns: 'auto auto',
- borderTop: 'solid',
- }}>
- <div className="slider-text">Fast</div>
- <div className="slider-text">Slow</div>
- </div>{' '}
- <div className="propertiesView-input inline">
- <p>Play Target Audio</p>
- <button
- style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkAudio', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Zoom Text Selections</p>
- <button
- style={{ background: !this.sourceAnchor?.followLinkZoomText ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoomText', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Toggle Follow to Outer Context</p>
- <button
- style={{ background: !this.sourceAnchor?.followLinkToOuterContext ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToOuterContext', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faWindowMaximize as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Toggle Target (Show/Hide)</p>
- <button
- style={{ background: !this.sourceAnchor?.followLinkToggle ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToggle', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Ease Transitions</p>
- <button
- style={{ background: this.sourceAnchor?.followLinkEase === 'linear' ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkEase', this.sourceAnchor, 'ease', 'linear')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Capture Offset to Target</p>
- <button
- style={{ background: this.sourceAnchor?.followLinkXoffset === undefined ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => {
- this.toggleAnchorProp(e, 'followLinkXoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.x) - NumCast(this.sourceAnchor?.x), undefined);
- this.toggleAnchorProp(e, 'followLinkYoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.y) - NumCast(this.sourceAnchor?.y), undefined);
- }}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline">
- <p>Center Target (no zoom)</p>
- <button
- style={{ background: this.sourceAnchor?.followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
- </button>
- </div>
- <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
- <p>Zoom %</p>
- <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
- <input className="presBox-input" style={{ width: '100%' }} readOnly={true} type="number" value={zoom} />
- <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
- <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
- <FontAwesomeIcon icon={'caret-up'} />
- </div>
- <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
- <FontAwesomeIcon icon={'caret-down'} />
- </div>
- </div>
- </div>
- <button
- style={{ background: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
- onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
- <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
- </button>
- </div>
- {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
- <div
- className={'slider-headers'}
- style={{
- display: !targZoom ? 'none' : 'grid',
- justifyContent: 'space-between',
- width: `calc(100% - ${indent * 2}px)`,
- marginLeft: indent,
- marginRight: indent,
- gridTemplateColumns: 'auto auto',
- borderTop: 'solid',
- }}>
- <div className="slider-text">0%</div>
- <div className="slider-text">100%</div>
- </div>{' '}
- </div>
- )}
- </>
+ this.linkProperties
)}
-
{this.inkSubMenu}
-
{this.optionsSubMenu}
-
{this.fieldsSubMenu}
-
{isNovice ? null : this.sharingSubMenu}
-
{isNovice ? null : this.filtersSubMenu}
-
{isNovice ? null : this.layoutSubMenu}
</div>
);
@@ -1843,7 +1775,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
: PresBox.targetRenderedDoc(PresBox.Instance.activeItem)?.type;
return (
<div className="propertiesView" style={{ width: this.props.width }}>
- <div className="propertiesView-title" style={{ width: this.props.width }}>
+ <div className="propertiesView-sectionTitle" style={{ width: this.props.width }}>
Presentation
</div>
<div className="propertiesView-name" style={{ borderBottom: 0 }}>
@@ -1859,7 +1791,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openPresTransitions = !this.openPresTransitions))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
&nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Transitions
<div className="propertiesView-presTrails-title-icon">
- <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+ <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg" />
</div>
</div>
{this.openPresTransitions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.transitionDropdown}</div> : null}
@@ -1873,7 +1805,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
&nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Visibilty
<div className="propertiesView-presTrails-title-icon">
- <FontAwesomeIcon icon={this.openPresVisibilityAndDuration ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+ <FontAwesomeIcon icon={this.openPresVisibilityAndDuration ? 'caret-down' : 'caret-right'} size="lg" />
</div>
</div>
{this.openPresVisibilityAndDuration ? <div className="propertiesView-presTrails-content">{PresBox.Instance.visibiltyDurationDropdown}</div> : null}
@@ -1884,7 +1816,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openPresProgressivize = !this.openPresProgressivize))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
&nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Progressivize
<div className="propertiesView-presTrails-title-icon">
- <FontAwesomeIcon icon={this.openPresProgressivize ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+ <FontAwesomeIcon icon={this.openPresProgressivize ? 'caret-down' : 'caret-right'} size="lg" />
</div>
</div>
{this.openPresProgressivize ? <div className="propertiesView-presTrails-content">{PresBox.Instance.progressivizeDropdown}</div> : null}
@@ -1895,7 +1827,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openSlideOptions = !this.openSlideOptions))} style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}>
&nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={type === DocumentType.AUDIO ? 'file-audio' : 'file-video'} /> &nbsp; {type === DocumentType.AUDIO ? 'Audio Options' : 'Video Options'}
<div className="propertiesView-presTrails-title-icon">
- <FontAwesomeIcon icon={this.openSlideOptions ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+ <FontAwesomeIcon icon={this.openSlideOptions ? 'caret-down' : 'caret-right'} size="lg" />
</div>
</div>
{this.openSlideOptions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.mediaOptionsDropdown}</div> : null}