diff options
Diffstat (limited to 'src/client/views/PropertiesView.tsx')
-rw-r--r-- | src/client/views/PropertiesView.tsx | 125 |
1 files changed, 66 insertions, 59 deletions
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index c2c0d553b..fad2f5284 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1,16 +1,17 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Checkbox, Tooltip } from "@material-ui/core"; +import { intersection } from "lodash"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { ColorState, SketchPicker } from "react-color"; -import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, Field, HeightSym, WidthSym } from "../../fields/Doc"; +import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, WidthSym, Opt } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { InkField } from "../../fields/InkField"; import { ComputedField } from "../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../fields/Types"; -import { GetEffectiveAcl, SharingPermissions } from "../../fields/util"; -import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../Utils"; +import { GetEffectiveAcl, SharingPermissions, denormalizeEmail } from "../../fields/util"; +import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne } from "../../Utils"; import { DocumentType } from "../documents/DocumentTypes"; import { DocumentManager } from "../util/DocumentManager"; import { SelectionManager } from "../util/SelectionManager"; @@ -26,7 +27,7 @@ import { PresBox } from "./nodes/PresBox"; import { PropertiesButtons } from "./PropertiesButtons"; import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector"; import "./PropertiesView.scss"; -import { intersection } from "lodash"; +import { CollectionViewType } from "./collections/CollectionView"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -35,6 +36,7 @@ const _global = (window /* browser */ || global /* node */) as any; interface PropertiesViewProps { width: number; height: number; + backgroundColor: (doc: Opt<Doc>, renderDepth: number) => Opt<string>; } @observer @@ -46,7 +48,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; } @computed get selectedDocumentView() { if (SelectionManager.SelectedDocuments().length) return SelectionManager.SelectedDocuments()[0]; - if (PresBox.Instance && PresBox.Instance._selectedArray) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc); + if (PresBox.Instance?._selectedArray.size) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc); return undefined; } @computed get isPres(): boolean { @@ -60,7 +62,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { @observable openOptions: boolean = true; @observable openSharing: boolean = true; @observable openFields: boolean = true; - @observable openLayout: boolean = true; + @observable openLayout: boolean = false; @observable openContexts: boolean = true; @observable openAppearance: boolean = true; @observable openTransform: boolean = true; @@ -91,9 +93,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { docWidth = () => { if (this.selectedDoc) { const layoutDoc = this.selectedDoc; - 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.width - 20)); - return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.width - 20) : this.props.width - 20; + const aspect = Doc.NativeAspect(layoutDoc, undefined, !layoutDoc._fitWidth); + if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.width - 20)); + return Doc.NativeWidth(layoutDoc) ? Math.min(layoutDoc[WidthSym](), this.props.width - 20) : this.props.width - 20; } else { return 0; } @@ -103,17 +105,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { docHeight = () => { if (this.selectedDoc && this.dataDoc) { const layoutDoc = this.selectedDoc; - 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; - return layoutDoc._fitWidth ? (!this.dataDoc._nativeHeight ? NumCast(this.props.height) : - Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth, - NumCast(this.props.height)))) : - NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50; - })())); - } else { - return 0; + return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, + Doc.NativeAspect(layoutDoc, undefined, true) ? this.docWidth() / Doc.NativeAspect(layoutDoc, undefined, true) : + layoutDoc._fitWidth ? (!Doc.NativeHeight(this.dataDoc) ? NumCast(this.props.height) : + Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) / Doc.NativeWidth(layoutDoc) || NumCast(this.props.height))) : + NumCast(layoutDoc._height) || 50)); } + return 0; } @computed get expandedField() { @@ -147,7 +145,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { </div>); } } - rows.push(<div className="field" key={"newKeyValue"} style={{ marginTop: "3px" }}> + rows.push(<div className="propertiesView-field" key={"newKeyValue"} style={{ marginTop: "3px" }}> <EditableView key="editableView" contents={"add key:value or #tags"} @@ -175,14 +173,14 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { docs.forEach(doc => docvals.add(doc[key])); const contents = Array.from(docvals.keys()).length > 1 ? "-multiple" : docs[0][key]; if (key[0] === "#") { - rows.push(<div className="uneditable-field" key={key}> + rows.push(<div className="propertiesView-uneditable-field" key={key}> <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span> </div>); } else if (contents !== undefined) { const value = Field.toString(contents as Field); if (noviceReqFields.includes(key) || key.indexOf("lastModified") !== -1) { - rows.push(<div className="uneditable-field" key={key}> + rows.push(<div className="propertiesView-uneditable-field" key={key}> <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ": "}</span> <div style={{ whiteSpace: "nowrap", overflowX: "hidden" }}>{value}</div> </div>); @@ -203,7 +201,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { } } } - rows.push(<div className="field" key={"newKeyValue"} style={{ marginTop: "3px" }}> + rows.push(<div className="propertiesView-field" key={"newKeyValue"} style={{ marginTop: "3px" }}> <EditableView key="editableView" contents={"add key:value or #tags"} @@ -256,7 +254,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { return !this.selectedDoc ? (null) : <PropertiesDocContextSelector Document={this.selectedDoc} hideTitle={true} addDocTab={(doc, where) => CollectionDockingView.AddSplit(doc, "right")} />; } - previewBackground = () => "lightgrey"; @computed get layoutPreview() { if (SelectionManager.SelectedDocuments().length > 1) { return "-- multiple selected --"; @@ -273,7 +270,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { renderDepth={1} rootSelected={returnFalse} treeViewDoc={undefined} - backgroundColor={this.previewBackground} + backgroundColor={this.props.backgroundColor} fitToBox={true} FreezeDimensions={true} dontCenter={true} @@ -284,6 +281,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { focus={returnFalse} ScreenToLocalTransform={this.getTransform} docFilters={returnEmptyFilter} + docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} ContainingCollectionDoc={undefined} ContainingCollectionView={undefined} @@ -320,13 +318,14 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { getPermissionsSelect(user: string, permission: string) { const dropdownValues: string[] = Object.values(SharingPermissions); if (permission === "-multiple-") dropdownValues.unshift(permission); + if (user === "Override") dropdownValues.unshift("None"); return <select className="permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}> - {dropdownValues.map(permission => { + {dropdownValues.filter(permission => permission !== SharingPermissions.View).map(permission => { return ( <option key={permission} value={permission}> - {permission} + {permission === SharingPermissions.Add ? "Can Augment" : permission} </option>); })} </select>; @@ -361,7 +360,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { /** * @returns a row of the permissions panel */ - sharingItem(name: string, admin: boolean, permission: string) { + sharingItem(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) { return <div className="propertiesView-sharingTable-item" key={name + permission} // style={{ backgroundColor: this.selectedUser === name ? "#bcecfc" : "" }} // onPointerDown={action(() => this.selectedUser = this.selectedUser === name ? "" : name)} @@ -370,7 +369,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { {/* {name !== "Me" ? this.notifyIcon : null} */} <div className="propertiesView-sharingTable-item-permission"> {admin && permission !== "Owner" ? this.getPermissionsSelect(name, permission) : permission} - {permission === "Owner" ? this.expansionIcon : null} + {permission === "Owner" || showExpansionIcon ? this.expansionIcon : null} </div> </div>; } @@ -380,6 +379,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { */ @computed get sharingTable() { const AclMap = new Map<symbol, string>([ + [AclUnset, "None"], [AclPrivate, SharingPermissions.None], [AclReadonly, SharingPermissions.View], [AclAddonly, SharingPermissions.Add], @@ -406,25 +406,20 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => { if (commonKeys.length) { for (const key of commonKeys) { - const name = key.substring(4).replace("_", "."); + const name = denormalizeEmail(key.substring(4)); const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key] : doc?.[DataSym]?.[AclSym]?.[key] === docs[0]?.[DataSym]?.[AclSym]?.[key]); - if (name !== Doc.CurrentUserEmail && name !== target.author && name !== "Public"/* && sidebarUsersDisplayed![name] !== false*/) { + if (name !== Doc.CurrentUserEmail && name !== target.author && name !== "Public" && name !== "Override"/* && sidebarUsersDisplayed![name] !== false*/) { tableEntries.push(this.sharingItem(name, showAdmin, uniform ? AclMap.get(this.layoutDocAcls ? target[AclSym][key] : target[DataSym][AclSym][key])! : "-multiple-")); } } } - // 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)); - // } - // } - // }) - + 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-")); tableEntries.unshift(this.sharingItem("Public", showAdmin, docs.filter(doc => doc).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.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? "Owner" : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? AclMap.get(effectiveAcls[0])! : "-multiple-")); - if (Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author)) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, "Owner")); + tableEntries.unshift(this.sharingItem("Me", showAdmin, docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? "Owner" : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? AclMap.get(effectiveAcls[0])! : "-multiple-", !ownerSame)); + if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, "Owner")); return <div className="propertiesView-sharingTable"> {tableEntries} @@ -861,7 +856,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { if (this.selectedDoc && !this.isPres) { return <div className="propertiesView" style={{ width: this.props.width, - minWidth: this.props.width + minWidth: this.props.width, //overflowY: this.scrolling ? "scroll" : "visible" }} > <div className="propertiesView-title" style={{ width: this.props.width }}> @@ -896,14 +891,21 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { </div> {!this.openSharing ? (null) : <div className="propertiesView-sharing-content"> - {!novice ? (<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)} + <div className="propertiesView-buttonContainer"> + {!novice ? (<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> {this.sharingTable} </div>} </div> @@ -981,8 +983,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { </div>; } if (this.isPres) { - const selectedItem: boolean = PresBox.Instance?._selectedArray.length > 0; + const selectedItem: boolean = PresBox.Instance?._selectedArray.size > 0; const type = PresBox.Instance.activeItem?.type; + const viewType = PresBox.Instance.activeItem?._viewType; + const pannable: boolean = (type === DocumentType.COL && viewType === CollectionViewType.Freeform) || type === DocumentType.IMG; + const scrollable: boolean = type === DocumentType.PDF || type === DocumentType.WEB || type === DocumentType.RTF || viewType === CollectionViewType.Stacking; return <div className="propertiesView" style={{ width: this.props.width }}> <div className="propertiesView-title" style={{ width: this.props.width }}> Presentation @@ -990,7 +995,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { <div className="propertiesView-name"> {this.editableTitle} <div className="propertiesView-presSelected"> - {PresBox.Instance?._selectedArray.length} selected + <div className="propertiesView-selectedCount"> + {PresBox.Instance?._selectedArray.size} selected + </div> <div className="propertiesView-selectedList"> {PresBox.Instance?.listOfSelected} </div> @@ -1009,7 +1016,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { {PresBox.Instance.transitionDropdown} </div> : null} </div>} - {!selectedItem || type === DocumentType.VID || type === DocumentType.AUDIO ? (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" : "" }}> @@ -1021,21 +1028,21 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { {this.openPresProgressivize ? <div className="propertiesView-presTrails-content"> {PresBox.Instance.progressivizeDropdown} </div> : null} - </div>} - {!selectedItem || (type !== DocumentType.COL && type !== DocumentType.VID && type !== DocumentType.AUDIO) ? (null) : <div className="propertiesView-presTrails"> + </div>} */} + {/* {!selectedItem || (!scrollable && !pannable) ? (null) : <div className="propertiesView-presTrails"> <div className="propertiesView-presTrails-title" onPointerDown={action(() => { this.openSlideOptions = !this.openSlideOptions; })} style={{ backgroundColor: this.openSlideOptions ? "black" : "" }}> - <FontAwesomeIcon icon={"cog"} /> {PresBox.Instance.stringType} options - <div className="propertiesView-presTrails-title-icon"> + <FontAwesomeIcon icon={"cog"} /> {scrollable ? "Scroll options" : "Pan options"} + <div className="propertiesView-presTrails-title-icon"> <FontAwesomeIcon icon={this.openSlideOptions ? "caret-down" : "caret-right"} size="lg" color="white" /> </div> </div> {this.openSlideOptions ? <div className="propertiesView-presTrails-content"> {PresBox.Instance.optionsDropdown} </div> : null} - </div>} - <div className="propertiesView-presTrails"> + </div>} */} + {/* <div className="propertiesView-presTrails"> <div className="propertiesView-presTrails-title" onPointerDown={action(() => { this.openAddSlide = !this.openAddSlide; })} style={{ backgroundColor: this.openAddSlide ? "black" : "" }}> @@ -1047,7 +1054,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { {this.openAddSlide ? <div className="propertiesView-presTrails-content"> {PresBox.Instance.newDocumentDropdown} </div> : null} - </div> + </div> */} </div>; } } |